优化天气,增加悬浮窗,增加快捷拨号,增加设置默认桌面
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user