Files
ElderlyDialer/app/src/main/java/com/ttstd/dialer/manager/AppManager.java

394 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.ttstd.dialer.manager;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.Log;
import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.db.app.AppRepository;
import com.ttstd.dialer.db.app.DesktopSortApp;
import com.ttstd.dialer.gson.GsonUtils;
import com.ttstd.dialer.utils.ApkUtils;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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 MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
@SuppressLint("StaticFieldLeak")
private static AppManager INSTANCE;
private Context mContext;
private AppRepository mAppRepository;
private Set<ProgressCallback> mProgressCallbacks = new CopyOnWriteArraySet<>();
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final ExecutorService ASYNC_EXECUTOR = Executors.newFixedThreadPool(CORE_POOL_SIZE);
public interface ProgressCallback {
void onProgress(String step, int current, int total);
void onCompleted(boolean success, String message);
}
public void addProgressCallback(ProgressCallback progressCallback) {
mProgressCallbacks.add(progressCallback);
}
public static final List<String> DEFAULT_APP_PACKAGES = new ArrayList<String>() {{
this.add("com.android.settings");
this.add("com.android.dialer");
this.add("com.android.camera2");
this.add("com.tencent.mm");
this.add("com.jiangjia.gif");
this.add("com.ss.android.ugc.aweme");
}};
public static void init(Context context) {
if (INSTANCE == null) {
INSTANCE = new AppManager(context);
}
}
public static AppManager getInstance() {
if (INSTANCE == null) {
throw new IllegalStateException("You must be init AppManager first");
}
return INSTANCE;
}
private AppManager(Context context) {
this.mContext = context.getApplicationContext();
this.mAppRepository = new AppRepository(context);
executeAppListProcessing();
}
// 异步执行方法
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);
} else {
return processUninstalledApps(desktopSortApps)
.thenComposeAsync(unused -> processNewApps(), ASYNC_EXECUTOR)
.thenComposeAsync(unused -> updatePosition(), ASYNC_EXECUTOR);
}
}, ASYNC_EXECUTOR)
.thenComposeAsync(unused -> processNewApps(), ASYNC_EXECUTOR)
.exceptionally(throwable -> {
// 统一异常处理
Log.e("AppListProcessor", "异步处理失败", throwable);
return null;
});
}
// 第一步:获取所有桌面应用(增加空值安全处理)
private List<DesktopSortApp> getAllDesktopSortApps() {
try {
List<DesktopSortApp> result = mAppRepository.getAllApp();
return result != null ? result : Collections.emptyList();
} catch (Exception e) {
Log.e("AppListProcessor", "获取桌面应用列表失败", e);
return Collections.emptyList();
}
}
private Optional<List<DesktopSortApp>> getAllDesktopSortAppsSafe() {
try {
return Optional.ofNullable(mAppRepository.getAllApp())
.filter(list -> !list.isEmpty());
} catch (Exception e) {
Log.e("AppListProcessor", "获取应用列表失败", e);
return Optional.empty();
}
}
// 第二步:处理未安装的应用(删除操作) - 增加空值检查
private CompletableFuture<Void> processUninstalledApps(List<DesktopSortApp> desktopSortApps) {
return CompletableFuture.runAsync(() -> {
if (desktopSortApps == null || desktopSortApps.isEmpty()) {
Log.w("AppListProcessor", "桌面应用列表为空,跳过删除处理");
return;
}
List<Integer> ids = desktopSortApps.stream()
.filter(desktopSortApp -> desktopSortApp != null &&
!ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName()))
.map(desktopSortApp -> {
try {
return mAppRepository.getIdByPackageAndClass(
desktopSortApp.getPackageName(), desktopSortApp.getClassName());
} catch (Exception e) {
Log.e("AppListProcessor", "获取应用ID失败: " + desktopSortApp.getPackageName(), e);
return -1; // 返回无效ID后续过滤掉
}
})
.filter(id -> id > 0) // 过滤掉无效ID
.collect(Collectors.toList());
if (!ids.isEmpty()) {
ids.forEach(id -> {
try {
mAppRepository.deleteById(id);
} catch (Exception e) {
Log.e("AppListProcessor", "删除应用失败, ID: " + id, e);
}
});
Log.i("AppListProcessor", "成功删除 " + ids.size() + " 个未安装应用");
}
}, ASYNC_EXECUTOR);
}
// 第三步:处理新应用(插入操作) - 增加空值和异常处理
private CompletableFuture<Void> processNewApps() {
return CompletableFuture.runAsync(() -> {
try {
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
if (resolveInfos.isEmpty()) {
Log.w("AppListProcessor", "未获取到启动器应用列表");
return;
}
List<DesktopSortApp> appList = 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;
}
})
.filter(Objects::nonNull)
.sorted(new Comparator<DesktopSortApp>() {
@Override
public int compare(DesktopSortApp o1, DesktopSortApp o2) {
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
})
.collect(Collectors.toList());
List<DesktopSortApp> 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());
try {
mAppRepository.insert(desktopSortApp);
} catch (Exception e) {
Log.e("AppListProcessor", "插入应用失败: " + desktopSortApp.getPackageName(), e);
}
});
Log.i("AppListProcessor", "成功插入 " + filterApp.size() + " 个新应用");
} else {
Log.i("AppListProcessor", "没有需要插入的新应用");
}
} catch (Exception e) {
Log.e("AppListProcessor", "处理新应用时发生异常", e);
}
}, ASYNC_EXECUTOR);
}
// 第四步:更新应用位置(您提供的方法优化)
private CompletableFuture<Void> updatePosition() {
return CompletableFuture.runAsync(() -> {
try {
List<DesktopSortApp> desktopSortApps = getAllDesktopSortApps();
if (desktopSortApps == null || desktopSortApps.isEmpty()) {
Log.w("AppListProcessor", "无应用数据,跳过位置更新");
return;
}
// 使用Lambda简化代码
List<DesktopSortApp> sortedApps = desktopSortApps.stream()
.filter(Objects::nonNull)
.sorted((o1, o2) -> Integer.compare(o1.getPosition(), o2.getPosition()))
.collect(Collectors.toList());
// 批量更新位置
IntStream.range(0, sortedApps.size())
.forEach(index -> {
DesktopSortApp desktopSortApp = sortedApps.get(index);
desktopSortApp.setPosition(index);
try {
mAppRepository.update(desktopSortApp);
} catch (Exception e) {
Log.e("AppListProcessor", "更新应用位置失败: " + desktopSortApp.getPackageName(), e);
}
});
Log.i("AppListProcessor", "成功更新 " + sortedApps.size() + " 个应用的位置");
} catch (Exception e) {
Log.e("AppListProcessor", "更新应用位置时发生异常", e);
}
}, ASYNC_EXECUTOR);
}
public void updateApp(String packageName) {
List<ResolveInfo> resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName);
if (!resolveInfos.isEmpty()) {
resolveInfos.stream().map(new Function<ResolveInfo, DesktopSortApp>() {
@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<DesktopSortApp>() {
@Override
public boolean test(DesktopSortApp desktopSortApp) {
return mAppRepository.checkAppInfoExists(desktopSortApp.getPackageName(), desktopSortApp.getClassName()) <= 0;
}
}).forEach(new Consumer<DesktopSortApp>() {
@Override
public void accept(DesktopSortApp desktopSortApp) {
desktopSortApp.setPosition(mAppRepository.getTotalCount());
mAppRepository.insert(desktopSortApp);
}
});
}
}
public void getAllApp() {
List<DesktopSortApp> desktopSortApps = mAppRepository.getAllApp();
List<Integer> ids = desktopSortApps.stream().filter(new Predicate<DesktopSortApp>() {
@Override
public boolean test(DesktopSortApp desktopSortApp) {
return !ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName());
}
}).map(new java.util.function.Function<DesktopSortApp, Integer>() {
@Override
public Integer apply(DesktopSortApp desktopSortApp) {
return mAppRepository.getIdByPackageAndClass(desktopSortApp.getPackageName(), desktopSortApp.getClassName());
}
}).collect(Collectors.toList());
ids.stream().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
mAppRepository.deleteById(integer);
}
});
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
List<DesktopSortApp> appList = resolveInfos.stream().map(new Function<ResolveInfo, ComponentName>() {
@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<ComponentName, DesktopSortApp>() {
@Override
public DesktopSortApp apply(ComponentName componentName) {
DesktopSortApp desktopSortApp = new DesktopSortApp(mContext, componentName);
return desktopSortApp;
}
}).collect(Collectors.toList());
List<DesktopSortApp> filterApp = appList.stream().filter(new Predicate<DesktopSortApp>() {
@Override
public boolean test(DesktopSortApp desktopSortApp) {
return ApkUtils.isInstalled(mContext, desktopSortApp.getPackageName());
}
}).filter(new Predicate<DesktopSortApp>() {
@Override
public boolean test(DesktopSortApp desktopSortApp) {
return mAppRepository.checkAppInfoExists(desktopSortApp.getPackageName(), desktopSortApp.getClassName()) > 0;
}
}).collect(Collectors.toList());
filterApp.stream().forEach(new Consumer<DesktopSortApp>() {
@Override
public void accept(DesktopSortApp desktopSortApp) {
mAppRepository.insert(desktopSortApp);
}
});
}
public String getDefaultAppList() {
PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
List<ResolveInfo> filter = resolveInfos.stream().filter(new Predicate<ResolveInfo>() {
@Override
public boolean test(ResolveInfo resolveInfo) {
return DEFAULT_APP_PACKAGES.contains(resolveInfo.activityInfo.packageName);
}
}).sorted(new Comparator<ResolveInfo>() {
@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<DesktopSortApp> desktopSortApps = filter.stream().map(new Function<ResolveInfo, DesktopSortApp>() {
@Override
public DesktopSortApp apply(ResolveInfo resolveInfo) {
ComponentName componentName = new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name);
return new DesktopSortApp(mContext, componentName);
}
}).sorted(new Comparator<DesktopSortApp>() {
@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));
for (int i = 0; i < desktopSortApps.size(); i++) {
DesktopSortApp desktopSortApp = desktopSortApps.get(i);
desktopSortApp.setPosition(i);
}
return GsonUtils.toJSONString(desktopSortApps);
}
}