diff --git a/app/src/main/java/com/ttstd/dialer/manager/AppManager.java b/app/src/main/java/com/ttstd/dialer/manager/AppManager.java index d25cbe1..56accbe 100644 --- a/app/src/main/java/com/ttstd/dialer/manager/AppManager.java +++ b/app/src/main/java/com/ttstd/dialer/manager/AppManager.java @@ -22,20 +22,23 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; public class AppManager { private static final String TAG = "AppManager"; + // 步骤常量定义,便于回调和日志统一管理 + private static final String STEP_FIRST_INIT = "首次初始化应用数据"; + private static final String STEP_REMOVE_UNINSTALLED = "移除未安装应用"; + private static final String STEP_ADD_NEW_APPS = "添加新安装应用"; + private static final String STEP_UPDATE_POSITION = "更新应用位置"; + private static final int TOTAL_STEPS_NORMAL = 3; // 正常流程总步骤 + private static final int TOTAL_STEPS_FIRST = 2; // 首次初始化总步骤 private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @@ -57,7 +60,13 @@ public class AppManager { } public void addProgressCallback(ProgressCallback progressCallback) { - mProgressCallbacks.add(progressCallback); + if (progressCallback != null) { + mProgressCallbacks.add(progressCallback); + } + } + + public void removeProgressCallback(ProgressCallback progressCallback) { + mProgressCallbacks.remove(progressCallback); } public static final List DEFAULT_APP_PACKAGES = new ArrayList() {{ @@ -88,73 +97,124 @@ public class AppManager { executeAppListProcessing(); } + // 通知进度回调 + private void notifyProgress(String step, int current, int total) { + mProgressCallbacks.forEach(callback -> callback.onProgress(step, current, total)); + } - // 异步执行方法 + // 通知完成回调 + private void notifyCompleted(boolean success, String message) { + mProgressCallbacks.forEach(callback -> callback.onCompleted(success, message)); + } + + // 异步执行应用列表处理流程 public void executeAppListProcessing() { CompletableFuture .supplyAsync(this::getAllDesktopSortApps, ASYNC_EXECUTOR) - .thenComposeAsync(desktopSortApps -> { - // 判断获取的应用列表是否为空 - if (desktopSortApps == null || desktopSortApps.isEmpty()) { - Log.i("AppListProcessor", "检测到桌面应用列表为空,直接插入新应用数据"); - return processNewApps() - .thenComposeAsync(unused -> updatePosition(), ASYNC_EXECUTOR); + .thenComposeAsync(desktopApps -> { + // 根据应用列表是否为空,选择不同处理流程 + if (desktopApps.isEmpty()) { + // 首次初始化流程:首次数据加载 -> 更新位置 + return processFirstTimeApps() + .thenComposeAsync(unused -> { + notifyProgress(STEP_UPDATE_POSITION, 2, TOTAL_STEPS_FIRST); + return updatePosition(); + }, ASYNC_EXECUTOR); } else { - return processUninstalledApps(desktopSortApps) - .thenComposeAsync(unused -> processNewApps(), ASYNC_EXECUTOR) - .thenComposeAsync(unused -> updatePosition(), ASYNC_EXECUTOR); + // 正常流程:移除未安装 -> 添加新应用 -> 更新位置 + return processUninstalledApps(desktopApps) + .thenComposeAsync(unused -> { + notifyProgress(STEP_ADD_NEW_APPS, 2, TOTAL_STEPS_NORMAL); + return processNewApps(); + }, ASYNC_EXECUTOR) + .thenComposeAsync(unused -> { + notifyProgress(STEP_UPDATE_POSITION, 3, TOTAL_STEPS_NORMAL); + return updatePosition(); + }, ASYNC_EXECUTOR); } }, ASYNC_EXECUTOR) - .thenComposeAsync(unused -> processNewApps(), ASYNC_EXECUTOR) + .thenRun(() -> notifyCompleted(true, "应用列表处理完成")) .exceptionally(throwable -> { - // 统一异常处理 - Log.e("AppListProcessor", "异步处理失败", throwable); + Log.e(TAG, "异步处理失败", throwable); + notifyCompleted(false, "处理失败: " + throwable.getMessage()); return null; }); } - // 第一步:获取所有桌面应用(增加空值安全处理) + // 第一步:获取所有桌面应用(空值安全处理) private List getAllDesktopSortApps() { try { List result = mAppRepository.getAllApp(); return result != null ? result : Collections.emptyList(); } catch (Exception e) { - Log.e("AppListProcessor", "获取桌面应用列表失败", e); + Log.e(TAG, "获取桌面应用列表失败", e); return Collections.emptyList(); } } - private Optional> getAllDesktopSortAppsSafe() { - try { - return Optional.ofNullable(mAppRepository.getAllApp()) - .filter(list -> !list.isEmpty()); - } catch (Exception e) { - Log.e("AppListProcessor", "获取应用列表失败", e); - return Optional.empty(); - } + // 首次初始化应用数据(针对首次获取为空的场景) + private CompletableFuture processFirstTimeApps() { + return CompletableFuture.runAsync(() -> { + notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST); + Log.i(TAG, "进入首次应用数据初始化流程"); + + try { + // 获取所有可启动应用 + List allLauncherApps = ApkUtils.getAllLauncherResolveInfo(mContext); + if (allLauncherApps.isEmpty()) { + Log.w(TAG, "未获取到任何可启动应用,无法完成首次初始化"); + return; + } + + List allFirstApps = allLauncherApps.stream() + .filter(Objects::nonNull) + .map(this::resolveInfoToDesktopApp) + .filter(Objects::nonNull) + .sorted((o1, o2) -> Boolean.compare(ApkUtils.isSystemApp(mContext, o1.getPackageName()), ApkUtils.isSystemApp(mContext, o2.getPackageName()))) + .sorted(getAppComparator()) + .collect(Collectors.toList()); + + IntStream.range(0, allFirstApps.size()) + .forEach(index -> allFirstApps.get(index).setPosition(index)); + + // 批量插入首次数据 + allFirstApps.forEach(app -> { + try { + mAppRepository.insert(app); + } catch (Exception e) { + Log.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e); + } + }); + + Log.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + " 个"); + } catch (Exception e) { + Log.e(TAG, "首次应用数据初始化失败", e); + throw new RuntimeException("首次初始化失败", e); // 抛出异常触发回调 + } + }, ASYNC_EXECUTOR); } - // 第二步:处理未安装的应用(删除操作) - 增加空值检查 + // 处理未安装的应用(删除操作) private CompletableFuture processUninstalledApps(List desktopSortApps) { return CompletableFuture.runAsync(() -> { - if (desktopSortApps == null || desktopSortApps.isEmpty()) { - Log.w("AppListProcessor", "桌面应用列表为空,跳过删除处理"); + notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL); + if (desktopSortApps.isEmpty()) { + Log.w(TAG, "桌面应用列表为空,跳过删除处理"); return; } List ids = desktopSortApps.stream() - .filter(desktopSortApp -> desktopSortApp != null && - !ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName())) - .map(desktopSortApp -> { + .filter(Objects::nonNull) + .filter(app -> !ApkUtils.isInstalled(mContext, app.getPackageName())) + .map(app -> { try { - return mAppRepository.getIdByPackageAndClass( - desktopSortApp.getPackageName(), desktopSortApp.getClassName()); + return mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName()); } catch (Exception e) { - Log.e("AppListProcessor", "获取应用ID失败: " + desktopSortApp.getPackageName(), e); - return -1; // 返回无效ID,后续过滤掉 + Log.e(TAG, "获取应用ID失败: " + app.getPackageName(), e); + return -1; } }) - .filter(id -> id > 0) // 过滤掉无效ID + .filter(id -> id > 0) .collect(Collectors.toList()); if (!ids.isEmpty()) { @@ -162,232 +222,160 @@ public class AppManager { try { mAppRepository.deleteById(id); } catch (Exception e) { - Log.e("AppListProcessor", "删除应用失败, ID: " + id, e); + Log.e(TAG, "删除应用失败, ID: " + id, e); } }); - Log.i("AppListProcessor", "成功删除 " + ids.size() + " 个未安装应用"); + Log.i(TAG, "成功删除 " + ids.size() + " 个未安装应用"); } }, ASYNC_EXECUTOR); } - // 第三步:处理新应用(插入操作) - 增加空值和异常处理 + // 处理新安装的应用(插入操作)- 非首次场景 private CompletableFuture processNewApps() { return CompletableFuture.runAsync(() -> { try { List resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext); if (resolveInfos.isEmpty()) { - Log.w("AppListProcessor", "未获取到启动器应用列表"); + Log.w(TAG, "未获取到启动器应用列表,跳过新应用处理"); return; } - List appList = resolveInfos.stream() + List newApps = resolveInfos.stream() .filter(Objects::nonNull) - .map(resolveInfo -> { - try { - String packageName = resolveInfo.activityInfo.packageName; - String className = resolveInfo.activityInfo.name; - ComponentName componentName = new ComponentName(packageName, className); - return new DesktopSortApp(mContext, componentName); - } catch (Exception e) { - Log.e("AppListProcessor", "创建DesktopSortApp失败", e); - return null; - } - }) + .map(this::resolveInfoToDesktopApp) .filter(Objects::nonNull) - .sorted(new Comparator() { - @Override - public int compare(DesktopSortApp o1, DesktopSortApp o2) { - return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel()); - } - }) + .filter(app -> ApkUtils.isInstalled(mContext, app.getPackageName())) + .filter(app -> !isAppExists(app)) // 过滤已存在的应用 + .sorted(getAppComparator()) .collect(Collectors.toList()); - List filterApp = appList.stream() - .filter(desktopSortApp -> ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName())) - .filter(desktopSortApp -> { - try { - return mAppRepository.checkAppInfoExists( - desktopSortApp.getPackageName(), desktopSortApp.getClassName()) <= 0; - } catch (Exception e) { - Log.e("AppListProcessor", "检查应用存在性失败", e); - return false; - } - }) - .collect(Collectors.toList()); - - if (!filterApp.isEmpty()) { - filterApp.forEach(desktopSortApp -> { - desktopSortApp.setPosition(mAppRepository.getTotalCount()); + if (!newApps.isEmpty()) { + newApps.forEach(app -> app.setPosition(mAppRepository.getTotalCount())); + newApps.forEach(app -> { try { - mAppRepository.insert(desktopSortApp); + mAppRepository.insert(app); } catch (Exception e) { - Log.e("AppListProcessor", "插入应用失败: " + desktopSortApp.getPackageName(), e); + Log.e(TAG, "插入新应用失败: " + app.getPackageName(), e); } }); - Log.i("AppListProcessor", "成功插入 " + filterApp.size() + " 个新应用"); + Log.i(TAG, "成功插入 " + newApps.size() + " 个新应用"); } else { - Log.i("AppListProcessor", "没有需要插入的新应用"); + Log.i(TAG, "没有需要插入的新应用"); } } catch (Exception e) { - Log.e("AppListProcessor", "处理新应用时发生异常", e); + Log.e(TAG, "处理新应用时发生异常", e); + throw new RuntimeException("新应用处理失败", e); } }, ASYNC_EXECUTOR); } - // 第四步:更新应用位置(您提供的方法优化) + // 更新应用位置 private CompletableFuture updatePosition() { return CompletableFuture.runAsync(() -> { try { List desktopSortApps = getAllDesktopSortApps(); - if (desktopSortApps == null || desktopSortApps.isEmpty()) { - Log.w("AppListProcessor", "无应用数据,跳过位置更新"); + if (desktopSortApps.isEmpty()) { + Log.w(TAG, "无应用数据,跳过位置更新"); return; } - // 使用Lambda简化代码 List sortedApps = desktopSortApps.stream() .filter(Objects::nonNull) - .sorted((o1, o2) -> Integer.compare(o1.getPosition(), o2.getPosition())) + .sorted(Comparator.comparingInt(DesktopSortApp::getPosition)) .collect(Collectors.toList()); - // 批量更新位置 IntStream.range(0, sortedApps.size()) .forEach(index -> { - DesktopSortApp desktopSortApp = sortedApps.get(index); - desktopSortApp.setPosition(index); + DesktopSortApp app = sortedApps.get(index); + app.setPosition(index); try { - mAppRepository.update(desktopSortApp); + mAppRepository.update(app); } catch (Exception e) { - Log.e("AppListProcessor", "更新应用位置失败: " + desktopSortApp.getPackageName(), e); + Log.e(TAG, "更新应用位置失败: " + app.getPackageName(), e); } }); - Log.i("AppListProcessor", "成功更新 " + sortedApps.size() + " 个应用的位置"); + Log.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置"); } catch (Exception e) { - Log.e("AppListProcessor", "更新应用位置时发生异常", e); + Log.e(TAG, "更新应用位置时发生异常", e); + throw new RuntimeException("位置更新失败", e); } }, ASYNC_EXECUTOR); } - public void updateApp(String packageName) { - List resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName); - if (!resolveInfos.isEmpty()) { - resolveInfos.stream().map(new Function() { - @Override - public DesktopSortApp apply(ResolveInfo resolveInfo) { - String packageName = resolveInfo.activityInfo.packageName; - String className = resolveInfo.activityInfo.name; - ComponentName componentName = new ComponentName(packageName, className); - return new DesktopSortApp(mContext, componentName); - } - }).filter(new Predicate() { - @Override - public boolean test(DesktopSortApp desktopSortApp) { - return mAppRepository.checkAppInfoExists(desktopSortApp.getPackageName(), desktopSortApp.getClassName()) <= 0; - } - }).forEach(new Consumer() { - @Override - public void accept(DesktopSortApp desktopSortApp) { - desktopSortApp.setPosition(mAppRepository.getTotalCount()); - mAppRepository.insert(desktopSortApp); - } - }); + // 工具方法:ResolveInfo转换为DesktopSortApp + private DesktopSortApp resolveInfoToDesktopApp(ResolveInfo resolveInfo) { + ComponentName component = new ComponentName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name + ); + return new DesktopSortApp(mContext, component); + } + + // 工具方法:检查应用是否已存在 + private boolean isAppExists(DesktopSortApp app) { + try { + return mAppRepository.checkAppInfoExists(app.getPackageName(), app.getClassName()) > 0; + } catch (Exception e) { + Log.e(TAG, "检查应用存在性失败: " + app.getPackageName(), e); + return false; } } - public void getAllApp() { - List desktopSortApps = mAppRepository.getAllApp(); - List ids = desktopSortApps.stream().filter(new Predicate() { - @Override - public boolean test(DesktopSortApp desktopSortApp) { - return !ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName()); - } - }).map(new java.util.function.Function() { - @Override - public Integer apply(DesktopSortApp desktopSortApp) { - return mAppRepository.getIdByPackageAndClass(desktopSortApp.getPackageName(), desktopSortApp.getClassName()); - } - }).collect(Collectors.toList()); - ids.stream().forEach(new Consumer() { - @Override - public void accept(Integer integer) { - mAppRepository.deleteById(integer); - } - }); + // 工具方法:获取应用排序器(统一排序逻辑) + private Comparator getAppComparator() { + return Comparator.comparing(DesktopSortApp::getLabel, Collator.getInstance(Locale.CHINESE)); + } - List resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext); - List appList = resolveInfos.stream().map(new Function() { - @Override - public ComponentName apply(ResolveInfo resolveInfo) { - String packageName = resolveInfo.activityInfo.packageName; - String className = resolveInfo.activityInfo.name; - ComponentName componentName = new ComponentName(packageName, className); - return componentName; - } - }).map(new Function() { - @Override - public DesktopSortApp apply(ComponentName componentName) { - DesktopSortApp desktopSortApp = new DesktopSortApp(mContext, componentName); - return desktopSortApp; - } - }).collect(Collectors.toList()); - List filterApp = appList.stream().filter(new Predicate() { - @Override - public boolean test(DesktopSortApp desktopSortApp) { - return ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName()); - } - }).filter(new Predicate() { - @Override - public boolean test(DesktopSortApp desktopSortApp) { - return mAppRepository.checkAppInfoExists(desktopSortApp.getPackageName(), desktopSortApp.getClassName()) > 0; - } - }).collect(Collectors.toList()); + public void updateApp(String packageName) { + List resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName); + if (resolveInfos.isEmpty()) return; - filterApp.stream().forEach(new Consumer() { - @Override - public void accept(DesktopSortApp desktopSortApp) { - mAppRepository.insert(desktopSortApp); + resolveInfos.stream() + .map(this::resolveInfoToDesktopApp) + .filter(Objects::nonNull) + .filter(app -> !isAppExists(app)) + .forEach(app -> { + app.setPosition(mAppRepository.getTotalCount()); + try { + mAppRepository.insert(app); + } catch (Exception e) { + Log.e(TAG, "更新应用插入失败: " + app.getPackageName(), e); + } + }); + } + + // 重构getAllApp方法,复用现有处理逻辑 + public void refreshAllApps() { + CompletableFuture.runAsync(() -> { + List allApps = getAllDesktopSortApps(); + if (allApps.isEmpty()) { + processFirstTimeApps().join(); + } else { + processUninstalledApps(allApps).join(); + processNewApps().join(); } - }); - - + updatePosition().join(); + }, ASYNC_EXECUTOR); } public String getDefaultAppList() { - PackageManager packageManager = mContext.getPackageManager(); + PackageManager pm = mContext.getPackageManager(); List resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext); - List filter = resolveInfos.stream().filter(new Predicate() { - @Override - public boolean test(ResolveInfo resolveInfo) { - return DEFAULT_APP_PACKAGES.contains(resolveInfo.activityInfo.packageName); - } - }).sorted(new Comparator() { - @Override - public int compare(ResolveInfo o1, ResolveInfo o2) { - return Collator.getInstance(Locale.CHINESE).compare(o1.activityInfo.loadLabel(packageManager), o2.activityInfo.loadLabel(packageManager)); - } - }).collect(Collectors.toList()); - List desktopSortApps = filter.stream().map(new Function() { - @Override - public DesktopSortApp apply(ResolveInfo resolveInfo) { - ComponentName componentName = new ComponentName(resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name); - return new DesktopSortApp(mContext, componentName); - } - }).sorted(new Comparator() { - @Override - public int compare(DesktopSortApp o1, DesktopSortApp o2) { - return Boolean.compare(ApkUtils.isSystemApp(mContext, o2.getPackageName()), ApkUtils.isSystemApp(mContext, o1.getPackageName())); - } - }).collect(Collectors.toCollection(LinkedList::new)); + List defaultApps = resolveInfos.stream() + .filter(ri -> DEFAULT_APP_PACKAGES.contains(ri.activityInfo.packageName)) + .sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE) + .compare(o1.activityInfo.loadLabel(pm), o2.activityInfo.loadLabel(pm))) + .map(ri -> new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name)) + .map(component -> new DesktopSortApp(mContext, component)) + .sorted((a, b) -> Boolean.compare( + ApkUtils.isSystemApp(mContext, b.getPackageName()), + ApkUtils.isSystemApp(mContext, a.getPackageName()))) + .collect(Collectors.toCollection(LinkedList::new)); - for (int i = 0; i < desktopSortApps.size(); i++) { - DesktopSortApp desktopSortApp = desktopSortApps.get(i); - desktopSortApp.setPosition(i); - } + IntStream.range(0, defaultApps.size()) + .forEach(i -> defaultApps.get(i).setPosition(i)); - return GsonUtils.toJSONString(desktopSortApps); + return GsonUtils.toJSONString(defaultApps); } - - -} +} \ No newline at end of file