今天一大早(雾)其实我起床已经是中午了,刘公子问我怎么实现 Android 一键锁屏?我心想这不就是 Google 一下你就得到的事吗?这代码估计都烂大街了吧。不过又仔细想想,难道是 Android 日常弃用 API 导致不行?

讲一下我这里了解到的两种,一种是直接控制屏幕熄灭(休眠),还有一种是控制手机进入锁定状态(锁屏)。

(休眠)直接控制屏幕熄灭,实现出来的效果就是打电话时靠近手机将屏幕进入熄屏状态。

(锁屏)控制手机进入锁屏,这个就是一键锁屏实现的功能了。

据我了解到,要实现第一种的话之前是可以通过 PowerManager 的 goToSleep 实现,遗憾的是目前 Google 已经将该方法弃用了,不仅仅是该方法被弃用。就连 android.permission.DEVICE_POWER 获取电源管理的相关方法也直接被删掉了。虽然了解到貌似通过将 APP 加入系统应用能获取该权限,不过一般普通用户根本操作不来所以我这边只能放弃(有想法的小伙伴可以深入了解了解)

其实打开 PowerManager 的源码就能看到其实 goToSleep 方法还在,所以还有一种方法就是反射,不过该操作也是需要 root 权限,普通用户一般没得…(贴一下代码吧)

public void goToSleep(Context context) {
        PowerManager powerManager= (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        try {
            powerManager.getClass().getMethod("goToSleep", new Class[]{long.class}).invoke(powerManager, SystemClock.uptimeMillis());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
}

还有一个就是通过 PowerManager.WakeLock 来实现。(反正在我手机上运行后没效果,我不知道啥原因…)

不过貌似通话时靠近手机将屏幕进入熄屏状态就是通过该方法实现的,估计还有其他我目前还没了解到的操作吧,不过我觉得该方案应该还是可行的,毕竟这么多软件的通话功能也实现了不是?

PowerManager powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
// 创建WakeLock
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP, "TestWakeLock");
// 获取
if (!wakeLock.isHeld()) {
    //wakeLock.acquire(5000);
    wakeLock.acquire();
}
// 释放
if (wakeLock.isHeld()) {
    wakeLock.release();
}

所以折腾了很久实际上我并未实现 (休眠)直接控制屏幕熄灭这个方案,不过将我踩的坑和一些思路分享出来,希望能帮到部分小伙伴吧。

接下来就是实现(锁屏)部分了。目前主流的方案就是通过 Android DeviceAdmin 设备管理权限,当然除了该方案还有 通过执行 adb shell input keyevent 26 实现(虽然确实是有点搞了,不过也确实是一个思路,哈哈哈哈哈哈)

如果是通过 adb 的方案的话,那就又涉及其他技术了,如:Android 运行 adb 高级权限命令,android 免 root 运行 adb高级权限命令等有趣的技术点。

【转载】android 免 root 运行 adb 高级权限命令,例如修改手机设置等

通过 adb 去执行的话,实用性不高且较为繁琐,好像还需要用户使用 adb 进行操作,对于普通用户来说就有点技术门槛了,所以我就直接跳过该方案

所以讲了这么久现在其实才真正进入正题。

利用 Android DeviceAdmin 设备管理权限实现锁屏

Android 通过提供 Android Device Administration API 来支持企业应用。该 Device Administration API 在系统级别提供设备管理功能。借助这些 API,您能够创建在企业环境中非常实用的注重安全性的应用;在企业环境中,IT 专业人员需要有力地对员工设备进行控制。例如,内置的 Android 电子邮件应用利用这些 API 改善了对 Exchange 的支持。通过电子邮件应用,Exchange 管理员可以跨设备强制执行密码政策(包括字母数字密码或数字 PIN 码)。管理员还可以远程清除(即恢复出厂默认设置)丢失或被盗手机上的数据。Exchange 用户可以同步他们的电子邮件和日历数据。

官方文档:https://developer.android.com/guide/topics/admin/device-admin?hl=zh-cn

可用用来干嘛呢?包括但不限于:控制手机锁定,清除手机数据,修改手机密码…

一句话就是国内的几乎全部手机找回功能中的实现基本是基于该功能。然后该功能也在企业设备管理中起到很大作用。

我们今天只是简单用到里面的控制手机锁屏。

首先创建一个广播(DeviceManger)继承至 DeviceAdminReceiver

public class DeviceManger extends DeviceAdminReceiver {

    @Override
    public void onEnabled(Context context, Intent intent) {
        super.onEnabled(context, intent);
        Toast.makeText(context, "已获取设备管理者权限", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
        super.onDisabled(context, intent);
        Toast.makeText(context, "已取消设备管理者权限", Toast.LENGTH_SHORT).show();
    }
}

创建好 DeviceManger 之后我们到 AndroidManifest.xml 的 application 中注册它

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.zmide.lockscreen">

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission
        android:name="android.permission.DEVICE_POWER"
        tools:ignore="ProtectedPermissions" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.LockScreen">

        ……

        <receiver
            android:name=".DeviceManger"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <meta-data
                android:name="android.app.device_admin"
                android:resource="@xml/device_admin" />

            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
                <action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

这里我们将会用到的 android.permission.WAKE_LOCK 权限也一并给上去了。

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DEVICE_POWER" tools:ignore="ProtectedPermissions" />

接下来我们创建一个 res/xml/device_admin.xml 文件,将 @xml/device_admin 实现一下

<?xml version="1.0" encoding="UTF-8"?>
<device-admin>
    <uses-policies>

        <!--一键锁屏-->
        <force-lock />

    </uses-policies>
</device-admin>

没错,这里面的话就是添加设备管理的权限了,除了 <force-lock /> 锁屏权限外还可以添加需要的一些如删除设备全部数据等权限,由于我们这里并不需要那些权限所以这一个就够了

到这里基本的 DeviceManger 广播就实现了。现在我们就开始使用这个广播来获取授权,并调用锁屏代码。

首先就是判断应用是否获取了管理权限

if (devicePolicyManager.isAdminActive(new ComponentName(this, DeviceManger.class))) {
   // TODO: 这里实现已获取权限的代码
} else {
   // TODO: 这里实现未获取权限的代码
}

然后就是获取设备管理权限的代码,启动广播

Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);

intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, new ComponentName(getApplication(), DeviceManger.class));

startActivityForResult(intent, 1);

接下来就是注销获取设备管理权限的代码,因为如果应用获取了设备管理权限就不能直接卸载,需要先取消应用的设备管理权限

DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);

devicePolicyManager.removeActiveAdmin(new ComponentName(getApplication(), DeviceManger.class));

最后就是调用 lockNow() 方法进行锁屏

DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);

devicePolicyManager.lockNow();

最终实现的 Activity 代码我已经上传到 Github :https://github.com/PBK-B/android-Lockscreen-demo/blob/master/app/src/main/java/com/zmide/lockscreen/MainActivity.java

示例项目地址:https://github.com/PBK-B/android-Lockscreen-demo