feat: 系统功能抽象
This commit is contained in:
@@ -1,33 +1,46 @@
|
||||
package com.ttstd.dialer.manager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.usage.StorageStats;
|
||||
import android.app.usage.StorageStatsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.bean.ApkInstalledInfo;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.HashUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@@ -44,8 +57,10 @@ public class AppManager {
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static AppManager INSTANCE;
|
||||
private static volatile AppManager INSTANCE;
|
||||
private Context mContext;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private AppRepository mAppRepository;
|
||||
|
||||
private Set<ProgressCallback> mProgressCallbacks = new CopyOnWriteArraySet<>();
|
||||
@@ -83,13 +98,17 @@ public class AppManager {
|
||||
|
||||
public static void init(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AppManager(context);
|
||||
synchronized (AppManager.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AppManager(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AppManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
throw new IllegalStateException("You must be init AppManager first");
|
||||
throw new IllegalStateException("You must first initialize the AppManager");
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
@@ -97,6 +116,7 @@ public class AppManager {
|
||||
private AppManager(Context context) {
|
||||
this.mContext = context.getApplicationContext();
|
||||
this.mAppRepository = new AppRepository(context);
|
||||
this.mPackageManager = mContext.getPackageManager();
|
||||
executeAppListProcessing();
|
||||
}
|
||||
|
||||
@@ -138,7 +158,7 @@ public class AppManager {
|
||||
}, ASYNC_EXECUTOR)
|
||||
.thenRun(() -> notifyCompleted(true, "应用列表处理完成"))
|
||||
.exceptionally(throwable -> {
|
||||
Log.e(TAG, "异步处理失败", throwable);
|
||||
Logger.e(TAG, "异步处理失败", throwable);
|
||||
notifyCompleted(false, "处理失败: " + throwable.getMessage());
|
||||
return null;
|
||||
});
|
||||
@@ -150,7 +170,7 @@ public class AppManager {
|
||||
List<AppInfo> result = mAppRepository.getAllApp();
|
||||
return result != null ? result : Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "获取桌面应用列表失败", e);
|
||||
Logger.e(TAG, "获取桌面应用列表失败", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -159,13 +179,13 @@ public class AppManager {
|
||||
private CompletableFuture<Void> processFirstTimeApps() {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST);
|
||||
Log.i(TAG, "进入首次应用数据初始化流程");
|
||||
Logger.i(TAG, "进入首次应用数据初始化流程");
|
||||
|
||||
try {
|
||||
// 获取所有可启动应用
|
||||
List<ResolveInfo> allLauncherApps = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
if (allLauncherApps.isEmpty()) {
|
||||
Log.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
|
||||
Logger.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,13 +216,13 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
|
||||
Log.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + " 个");
|
||||
Logger.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + " 个");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "首次应用数据初始化失败", e);
|
||||
Logger.e(TAG, "首次应用数据初始化失败", e);
|
||||
throw new RuntimeException("首次初始化失败", e); // 抛出异常触发回调
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -213,7 +233,7 @@ public class AppManager {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
|
||||
if (appInfos.isEmpty()) {
|
||||
Log.w(TAG, "桌面应用列表为空,跳过删除处理");
|
||||
Logger.w(TAG, "桌面应用列表为空,跳过删除处理");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,7 +244,7 @@ public class AppManager {
|
||||
try {
|
||||
return mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "获取应用ID失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "获取应用ID失败: " + app.getPackageName(), e);
|
||||
return -1;
|
||||
}
|
||||
})
|
||||
@@ -236,10 +256,10 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.deleteById(id);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "删除应用失败, ID: " + id, e);
|
||||
Logger.e(TAG, "删除应用失败, ID: " + id, e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功删除 " + ids.size() + " 个未安装应用");
|
||||
Logger.i(TAG, "成功删除 " + ids.size() + " 个未安装应用");
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
}
|
||||
@@ -250,7 +270,7 @@ public class AppManager {
|
||||
try {
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
if (resolveInfos.isEmpty()) {
|
||||
Log.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
|
||||
Logger.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -269,15 +289,15 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功插入 " + newApps.size() + " 个新应用");
|
||||
Logger.i(TAG, "成功插入 " + newApps.size() + " 个新应用");
|
||||
} else {
|
||||
Log.i(TAG, "没有需要插入的新应用");
|
||||
Logger.i(TAG, "没有需要插入的新应用");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "处理新应用时发生异常", e);
|
||||
Logger.e(TAG, "处理新应用时发生异常", e);
|
||||
throw new RuntimeException("新应用处理失败", e);
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -289,7 +309,7 @@ public class AppManager {
|
||||
try {
|
||||
List<AppInfo> appInfos = getAllDesktopSortApps();
|
||||
if (appInfos.isEmpty()) {
|
||||
Log.w(TAG, "无应用数据,跳过位置更新");
|
||||
Logger.w(TAG, "无应用数据,跳过位置更新");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -305,12 +325,12 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.update(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置");
|
||||
Logger.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用位置时发生异常", e);
|
||||
Logger.e(TAG, "更新应用位置时发生异常", e);
|
||||
throw new RuntimeException("位置更新失败", e);
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -330,7 +350,7 @@ public class AppManager {
|
||||
try {
|
||||
return mAppRepository.checkAppInfoExists(app.getPackageName(), app.getClassName()) > 0;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "检查应用存在性失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "检查应用存在性失败: " + app.getPackageName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -353,7 +373,7 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用插入失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "更新应用插入失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -373,13 +393,12 @@ public class AppManager {
|
||||
}
|
||||
|
||||
public String getDefaultAppList() {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
|
||||
List<AppInfo> 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)))
|
||||
.compare(o1.activityInfo.loadLabel(mPackageManager), o2.activityInfo.loadLabel(mPackageManager)))
|
||||
.map(ri -> new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name))
|
||||
.map(component -> new AppInfo(mContext, component))
|
||||
.sorted((a, b) -> Boolean.compare(
|
||||
@@ -392,4 +411,64 @@ public class AppManager {
|
||||
|
||||
return GsonUtils.toJSONString(defaultApps);
|
||||
}
|
||||
|
||||
private static final Set<String> WHITE_SYSTEM_PACKAGES = new HashSet<String>() {{
|
||||
// this.add("com.android.luancher3");
|
||||
}};
|
||||
|
||||
public List<ApkInstalledInfo> getApkInstallInfos() {
|
||||
StorageStatsManager ssm = mContext.getSystemService(StorageStatsManager.class);
|
||||
// 获取所有已安装的应用
|
||||
List<PackageInfo> installedApps = mPackageManager.getInstalledPackages(0);
|
||||
// 遍历并筛选第三方应用
|
||||
List<ApkInstalledInfo> apkInstalledInfos = installedApps.stream().filter(new Predicate<PackageInfo>() {
|
||||
@Override
|
||||
public boolean test(PackageInfo packageInfo) {
|
||||
String packageName = packageInfo.packageName;
|
||||
return !ApkUtils.isSystemApp(mContext, packageName) || WHITE_SYSTEM_PACKAGES.contains(packageName);
|
||||
}
|
||||
}).map(new Function<PackageInfo, ApkInstalledInfo>() {
|
||||
@Override
|
||||
public ApkInstalledInfo apply(PackageInfo packageInfo) {
|
||||
ApkInstalledInfo apkInstalledInfo = new ApkInstalledInfo();
|
||||
apkInstalledInfo.setPackageName(packageInfo.packageName);
|
||||
apkInstalledInfo.setAppName(packageInfo.applicationInfo.loadLabel(mPackageManager).toString());
|
||||
apkInstalledInfo.setVersionName(packageInfo.versionName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
apkInstalledInfo.setVersionCode(packageInfo.getLongVersionCode());
|
||||
} else {
|
||||
apkInstalledInfo.setVersionCode(packageInfo.versionCode);
|
||||
}
|
||||
apkInstalledInfo.setInstallTime(packageInfo.firstInstallTime);
|
||||
apkInstalledInfo.setLastUpdateTime(packageInfo.lastUpdateTime);
|
||||
|
||||
try {
|
||||
StorageStats stats = ssm.queryStatsForPackage(
|
||||
UUID.fromString(packageInfo.applicationInfo.storageUuid.toString()),
|
||||
mContext.getPackageName(),
|
||||
UserHandle.of(UserHandle.myUserId())
|
||||
);
|
||||
|
||||
long appSize = stats.getAppBytes(); // 应用大小
|
||||
Log.e(TAG, "apply: appSize = " + appSize);
|
||||
long dataSize = stats.getDataBytes(); // 用户数据
|
||||
long cacheSize = stats.getCacheBytes(); // 缓存
|
||||
|
||||
apkInstalledInfo.setApkSize(appSize);
|
||||
apkInstalledInfo.setDataSize(dataSize);
|
||||
apkInstalledInfo.setCacheSize(cacheSize);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getApkInstallInfos: " + e.getMessage());
|
||||
}
|
||||
|
||||
Log.e(TAG, "apply: " + packageInfo.applicationInfo.publicSourceDir);
|
||||
Log.e(TAG, "apply: publicSourceDir = " + new File(packageInfo.applicationInfo.publicSourceDir).length());
|
||||
Log.e(TAG, "apply: " + packageInfo.applicationInfo.dataDir);
|
||||
apkInstalledInfo.setMd5(HashUtils.getFileMd5(new File(packageInfo.applicationInfo.publicSourceDir)));
|
||||
apkInstalledInfo.setSystemApp(ApkUtils.isSystemApp(mContext, packageInfo.packageName));
|
||||
return apkInstalledInfo;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return apkInstalledInfos;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user