1.锁屏页
实现思路
自定义锁屏页大多使用悬浮窗和弹出Activity的方式实现,这里我参考了QQ音乐和网易云音乐的实现方式,在锁屏页面弹出Activity。首先,动态注册一个广播接收器用于监听手机熄屏和亮屏的广播,在接收到熄屏广播后启动Activity,在这里启动Activity时,还涉及到一个权限问题,由于一些手机厂商都有自己的自定义权限,这里我选择所有的权限让用户手动去开启(权限要动态校验)。到这一步已经差不多实现了,最后就是一些细节问题,滑动解锁以及沉浸式实现
监听亮屏和熄屏广播的代码网上都有,这里就不多说了,重点是权限适配的问题,我几乎找遍了全网,整理出下面这个工具类,直接上代码
权限适配
/**
* 锁屏显示权限工具类
*
* @author Sago
* @since 2024/12/7
*/
public class LockScreenPermissionUtil {
private static final String TAG = "PopPermissionUtil";
private static final int HW_OP_CODE_POPUP_BACKGROUND_WINDOW = 100000;
private static final int XM_OP_CODE_POPUP_BACKGROUND_WINDOW = 10021;
private static Context sContext;
private static boolean isInit = false;
public static void init(Context context) {
if (!isInit) {
sContext = context.getApplicationContext();
isInit = true;
}
}
/**
* 是否有后台弹出页面权限
*/
public static boolean hasPopupBackgroundPermission() {
if (isHuawei()) {
return checkHwPermission();
}
if (isXiaoMi()) {
return checkXmPermission();
}
if (isVivo()) {
return checkVivoPermission();
}
if (isOppo() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(sContext);
}
return true;
}
/**
* 是否有悬浮窗权限
*/
public static boolean haveSystemAlertWindow() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(sContext);
}
return true;
}
public static boolean isHuawei() {
return checkManufacturer("huawei");
}
public static boolean isXiaoMi() {
return checkManufacturer("xiaomi");
}
public static boolean isOppo() {
return checkManufacturer("oppo");
}
public static boolean isVivo() {
return checkManufacturer("vivo");
}
private static boolean checkManufacturer(String manufacturer) {
return manufacturer.equalsIgnoreCase(Build.MANUFACTURER);
}
/**
* 华为后台弹窗权限
*
* @return
*/
private static boolean checkHwPermission() {
Context context = sContext;
try {
Class<?> c = Class.forName("com.huawei.android.app.AppOpsManagerEx");
Method m = c.getDeclaredMethod(
"checkHwOpNoThrow",
AppOpsManager.class,
int.class,
int.class,
String.class
);
Object result = m.invoke(
c.newInstance(),
context.getSystemService(Context.APP_OPS_SERVICE),
HW_OP_CODE_POPUP_BACKGROUND_WINDOW,
Binder.getCallingUid(),
context.getPackageName()
);
Log.d(TAG, "LockScreenPermissionUtil checkHwPermission result: " + (AppOpsManager.MODE_ALLOWED == (int) result));
return AppOpsManager.MODE_ALLOWED == (int) result;
} catch (Exception e) {
// ignore
}
return false;
}
private static boolean checkXmPermission() {
// Context context = sContext;
// AppOpsManager ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// try {
// Method method = ops.getClass().getMethod(
// "checkOpNoThrow",
// int.class,
// int.class,
// String.class
//