优化天气,增加悬浮窗,增加快捷拨号,增加设置默认桌面

This commit is contained in:
2026-03-28 00:56:54 +08:00
parent 1977fd1cb2
commit b0ea6eff0a
74 changed files with 4055 additions and 242 deletions

View File

@@ -2,14 +2,47 @@ package com.ttstd.dialer.utils;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityTaskManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.os.Process;
import android.view.SurfaceControl;
import android.view.WindowManager;
import com.ttstd.dialer.BuildConfig;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
public class SystemUtils {
private static final String TAG = "SystemUtils";
/**
* 获取设备序列号
@@ -72,4 +105,364 @@ public class SystemUtils {
}
public static String getForegroundActivityPackageName(Context context) {
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(1);
if (runningTaskInfos != null && !runningTaskInfos.isEmpty()) {
ComponentName componentName = runningTaskInfos.get(0).topActivity;
if (componentName != null) {
String currentPackageName = componentName.getPackageName();
return currentPackageName;
}
}
return "";
}
/**
* 获取栈顶的应用包名
*/
public static String getForegroundActivityClassName(Context context) {
ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
String currentClassName = manager.getRunningTasks(1).get(0).topActivity.getClassName();
return currentClassName;
}
public static List<String> getRunningTaskPackages(Context context) {
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(Integer.MAX_VALUE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return runningTaskInfos.stream().map(new Function<ActivityManager.RunningTaskInfo, String>() {
@Override
public String apply(ActivityManager.RunningTaskInfo runningTaskInfo) {
return runningTaskInfo.topActivity.getPackageName();
}
}).collect(Collectors.toList());
} else {
List<String> packageNames = new ArrayList<>();
for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos) {
packageNames.add(runningTaskInfo.topActivity.getPackageName());
}
return packageNames;
}
}
public static void killBackgroundProcesses(Context context, String processName) {
Log.e(TAG, "killBackgroundProcesses: " + processName);
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
String packageName;
try {
if (!processName.contains(":")) {
packageName = processName;
} else {
packageName = processName.split(":")[0];
}
activityManager.killBackgroundProcesses(packageName);
activityManager.forceStopPackage(packageName);
// removeTask(context, processName);
// Method forceStopPackage = activityManager.getClass()
// .getDeclaredMethod("forceStopPackage", String.class);
// forceStopPackage.setAccessible(true);
// forceStopPackage.invoke(activityManager, packageName);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void removeTask(Context context, String packageName) {
Log.e(TAG, "removeTask: " + packageName);
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
List<ActivityManager.RecentTaskInfo> list = getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), getCurrentUserId());
HashMap<String, Integer> taskMap = new HashMap<>();
for (ActivityManager.RecentTaskInfo info : list) {
taskMap.put(info.realActivity.getPackageName(), info.id);
}
try {
ActivityManagerNative.getDefault().removeTask(taskMap.get(packageName));
} catch (RemoteException e) {
e.printStackTrace();
Log.e(TAG, "removeTask: " + e.getMessage());
} catch (NullPointerException e) {
Log.e(TAG, "removeTask: " + e.getMessage());
}
// } else {
// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//
// }
}
/**
* @return a list of the recents tasks.
* 获取近期任务列表
*/
public static List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
try {
return ActivityTaskManager.getService().getRecentTasks(numTasks,
RECENT_IGNORE_UNAVAILABLE, userId).getList();
} catch (RemoteException e) {
Log.e(TAG, "Failed to get recent tasks " + e);
return new ArrayList<>();
}
}
/**
* @return the current user's id.
* 获取userId
*/
public static int getCurrentUserId() {
UserInfo ui;
try {
ui = ActivityManager.getService().getCurrentUser();
return ui != null ? ui.id : 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public static boolean isDefaultLauncher(Context context, Class<?> launcherActivityClass) {
ComponentName componentName = new ComponentName(context, launcherActivityClass);
Log.e(TAG, "isDefaultLauncher: componentName = " + componentName);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
if (resolveInfo != null && resolveInfo.activityInfo != null) {
ComponentName defaultComponentName = resolveInfo.getComponentInfo().getComponentName();
Log.e(TAG, "isDefaultLauncher: defaultComponentName = " + defaultComponentName);
return componentName.equals(defaultComponentName);
}
return false;
}
/**
* 将指定的 Activity 设置为系统默认桌面(需要系统签名)
*
* @param context 上下文
* @param launcherActivityClass 你的桌面 Activity 类,例如 MyLauncherActivity.class
*/
public static void setDefaultLauncher(Context context, Class<?> launcherActivityClass) {
PackageManager pm = context.getPackageManager();
// 1. 创建桌面过滤的 IntentFilter
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
// 2. 查询当前系统中所有的桌面应用(用来构建 ComponentName 数组)
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
int bestMatch = 0;
ComponentName[] set = new ComponentName[resolveInfos.size()];
for (int i = 0; i < resolveInfos.size(); i++) {
ResolveInfo info = resolveInfos.get(i);
set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
if (info.match > bestMatch) {
bestMatch = info.match; // 获取最匹配的值
}
}
// 3. 构建你自己的桌面 ComponentName
ComponentName myLauncher = new ComponentName(context, launcherActivityClass);
// 4. 替换首选 Activity核心步骤
try {
// 注意API 29 (Android 10) 中此方法对第三方应用废弃,但对系统签名应用依然有效
pm.replacePreferredActivity(filter, bestMatch, set, myLauncher);
Log.i("LauncherHelper", "成功设置为默认桌面");
// 可选:发送一个回到桌面的 Intent 来验证
// Intent startHome = new Intent(Intent.ACTION_MAIN);
// startHome.addCategory(Intent.CATEGORY_HOME);
// startHome.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// context.startActivity(startHome);
} catch (SecurityException e) {
Log.e("LauncherHelper", "缺少权限或未生效,请检查系统签名是否正确", e);
} catch (Exception e) {
Log.e("LauncherHelper", "设置默认桌面失败", e);
}
}
public static void setDefaultLauncher(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
// 检查当前应用是否已经是桌面角色持有者
if (roleManager != null && !roleManager.isRoleHeld(RoleManager.ROLE_HOME)) {
// 检查该角色是否可用
if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME)) {
// 创建请求意图
Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME);
// 注意:在普通应用中,这会弹出系统选择框
// 在系统签名应用中,这通常能直接提升优先级或简化流程
context.startActivity(roleRequestIntent);
}
}
} else {
// Android 10 以下的传统做法:清除当前默认并弹出选择
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
/**
* 将指定包名设置为默认桌面 (需要系统签名)
*/
public static void addRoleHolderAsUser(Context context, String packageName) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Log.e(TAG, "RoleManager requires Android 10 or higher.");
return;
}
RoleManager roleManager = context.getSystemService(RoleManager.class);
if (roleManager == null) return;
String roleName = RoleManager.ROLE_HOME;
// 1. 检查是否已经是当前角色持有者,避免重复调用
if (roleManager.isRoleHeld(roleName)) {
// 注意:这里最好再判断一下持有的包名是否为目标包名
Log.i(TAG, "Role " + roleName + " is already held by some package.");
// 如果已经是自己,直接返回
}
try {
UserHandle user = Process.myUserHandle();
Executor executor = context.getMainExecutor();
// 定义回调
Consumer<Boolean> callback = successful -> {
if (successful) {
Log.i(TAG, "Successfully set " + packageName + " as " + roleName);
} else {
Log.e(TAG, "Failed to set " + packageName + " as " + roleName + ". Check system logs.");
}
};
// 2. 使用反射调用隐藏方法 addRoleHolderAsUser
// 方法签名: addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer<Boolean>)
Method method = RoleManager.class.getMethod("addRoleHolderAsUser",
String.class, String.class, int.class, UserHandle.class, Executor.class, Consumer.class);
Log.d(TAG, "Invoking addRoleHolderAsUser for package: " + packageName);
method.invoke(roleManager, roleName, packageName, 0, user, executor, callback);
// roleManager.addRoleHolderAsUser(roleName, packageName, 0, user, executor, callback);
} catch (NoSuchMethodException e) {
Log.e(TAG, "Method not found. Is this a non-standard ROM?", e);
} catch (Exception e) {
Log.e(TAG, "Error invoking addRoleHolderAsUser", e);
}
}
public static void setOtherDefaultLauncher(Context context) {
PackageManager pm = context.getPackageManager();
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
Optional<ResolveInfo> systemLauncher = resolveInfos.stream().filter(new Predicate<ResolveInfo>() {
@Override
public boolean test(ResolveInfo resolveInfo) {
return !BuildConfig.APPLICATION_ID.equals(resolveInfo.activityInfo.packageName);
}
}).filter(new Predicate<ResolveInfo>() {
@Override
public boolean test(ResolveInfo resolveInfo) {
return ApkUtils.isSystemApp(context, resolveInfo.activityInfo.packageName);
}
}).findAny();
systemLauncher.ifPresent(resolveInfo -> addRoleHolderAsUser(context, resolveInfo.activityInfo.packageName));
}
/**
* 系统签名应用专用静默获取全屏截图Bitmap
*
* @param context 上下文
* @return 全屏截图Bitmap失败返回null
*/
public static Bitmap takeFullScreenshot(Context context) {
// 获取屏幕真实宽高(包含状态栏、导航栏)
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getRealMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
Log.e(TAG, "takeFullScreenshot: screenWidth " + screenWidth);
Log.e(TAG, "takeFullScreenshot: screenHeight " + screenHeight);
try {
// 注意:不同 Android 版本的 SurfaceControl.screenshot 方法签名可能不同
// 以下代码适用于 Android 9.0 及以上版本常见的隐藏 API 调用方式
// 1. 获取屏幕显示的 IBinder (通常为 DisplayControl 或 SurfaceControl 的内部方法)
// 在较高版本中,可能需要通过 SurfaceControl.getInternalDisplayToken() 获取
// 2. 反射调用 screenshot 方法
Class<?> surfaceControlClass = Class.forName("android.view.SurfaceControl");
// Android 10+ 建议使用新的反射路径,这里以通用逻辑为例:
// 对于 Android 11+Google 引入了 SurfaceControl.LayerCaptureArgs 等内部类
// 这是一个针对 Android 9/10 的简化逻辑参考:
Bitmap bitmap = (Bitmap) surfaceControlClass.getDeclaredMethod("screenshot",
Rect.class, Integer.TYPE, Integer.TYPE, Integer.TYPE)
.invoke(null, new Rect(), screenWidth, screenHeight, 0);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Bitmap takeScreenshotHighVersion() {
try {
// 1. 获取主屏幕的 Token (Internal Display)
// 反射调用 SurfaceControl.getInternalDisplayToken()
Method getInternalDisplayTokenMethod = SurfaceControl.class.getDeclaredMethod("getInternalDisplayToken");
getInternalDisplayTokenMethod.setAccessible(true);
IBinder displayToken = (IBinder) getInternalDisplayTokenMethod.invoke(null);
if (displayToken == null) return null;
// 2. 构造 DisplayCaptureArgs.Builder (Android 11+ 的新包装类)
Class<?> builderClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs$Builder");
Constructor<?> builderConstructor = builderClass.getConstructor(IBinder.class);
Object builder = builderConstructor.newInstance(displayToken);
// 可以通过 Builder 设置缩放、格式等,这里直接 build()
Method buildMethod = builderClass.getDeclaredMethod("build");
Object captureArgs = buildMethod.invoke(builder);
// 3. 调用 SurfaceControl.screenshot(DisplayCaptureArgs)
// 返回值是一个 ScreenshotHardwareBuffer 对象
Class<?> captureArgsClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs");
Method screenshotMethod = SurfaceControl.class.getDeclaredMethod("screenshot", captureArgsClass);
screenshotMethod.setAccessible(true);
Object screenshotBuffer = screenshotMethod.invoke(null, captureArgs);
if (screenshotBuffer == null) return null;
// 4. 从 ScreenshotHardwareBuffer 中提取 Bitmap
Method asBitmapMethod = screenshotBuffer.getClass().getDeclaredMethod("asBitmap");
asBitmapMethod.setAccessible(true);
return (Bitmap) asBitmapMethod.invoke(screenshotBuffer);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}