Android Kiosk 模式

又是用户的需求,实现Kiosk模式,让设备只运行他想要的程序。之前做过一种是自动隐藏底部所有按键,可以看我之前的博客隐藏底部按键

但是这种方式还是有缺陷,虽然也能实现想要的模式,总感觉,很不满意。这次想要实现Android原本就有的Kiosk模式。

在Android中有两种模式对我们非常有用

  1. DeviceAdmin设备管理

  2. DeviceOwner设备所有者

当然还有一种模式ProfileOwner(配置文件所有者),这种模式看了一下资料说国内不好用,暂时不管,因为今天讲的也用不上。

先说设备管理模式

Android 2.2通过提供Android设备管理API引入了对企业应用程序的支持。设备管理API提供了系统级的设备管理功能。这些api允许您创建安全感知的应用程序,这些应用程序在企业设置中非常有用,在企业设置中,IT专业人员需要对员工设备进行丰富的控制。例如,内置的Android电子邮件应用程序利用了新的api来改进Exchange支持。通过电子邮件应用程序,Exchange管理员可以跨设备强制执行密码策略——包括字母数字密码或数字pin。管理员还可以远程擦除(即恢复出厂默认值)丢失或被盗的手机。Exchange用户可以同步他们的电子邮件和日历数据。  ——来自有道翻译官方Android文档。哈哈哈哈哈哈哈

代码实现如下

在AndroidManifest.xml文件中添加

  <receiver
            android:name=".AdminReceiver"
            android:label="@string/app_name"
            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" />
            </intent-filter>
        </receiver>

上面的receiver的name就是我们要创建的类名

public class AdminReceiver extends DeviceAdminReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        LogUtil.d("设备管理=onReceive");
    }

    @Override
    public CharSequence onDisableRequested(Context context, Intent intent) {
        LogUtil.d("设备管理=onDisableRequested");
        return super.onDisableRequested(context, intent);
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
        super.onDisabled(context, intent);
        LogUtil.d("设备管理=onDisabled");
    }

    @Override
    public void onEnabled(Context context, Intent intent) {
        super.onEnabled(context, intent);
        LogUtil.d("设备管理=onEnabled");
        //可以在此处进行跳转,但是不好处理其他的逻辑,暂时保留,后面会说其他跳转
        Intent itn = new Intent(context,
                MainActivity.class);
        itn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(itn);
    }
}

其中还有一个xml文件,声明元数据中使用的安全策略。元数据提供特定于设备管理员的附加信息,由DeviceAdminInfo类解析

device_admin示例:

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 指定设备超级管理员所拥有的权限 -->
    <uses-policies>

        <!--停用相机-->
        <disable-camera/>
        <!--锁屏时禁用某些功能-->
        <disable-keyguard-features/>
        <!--设置存储设备加密-->
        <encrypted-storage/>
        <!--设置锁定屏幕密码的有效期-->
        <expire-password/>
        <!--锁定屏幕-->
        <force-lock/>
        <!--设置密码规则-->
        <limit-password/>
        <!--更改屏幕解锁密码-->
        <reset-password/>
       <!-- &lt;!&ndash;设置设备全局代理&ndash;&gt;
        <set-global-proxy/>-->
        <!--监控屏幕解锁尝试次数-->
        <watch-login/>
        <!--恢复出厂设置-->
        <wipe-data/>
    </uses-policies>
</device-admin>

当然你也可以创建一个空的<device-admin>文件。不需要设置管理的权限

好了,需要的文件已经准备好了,现在是开始触发该模式的方式了

先通过代码判断我们的包名app是否已经处在设备管理者模式状态下

 private DevicePolicyManager dpm;
 private ComponentName componentName;



dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 

componentName = new ComponentName(context, AdminReceiver.class);



dpm.isAdminActive(componentName);//判断是否已经是设备管理者模式了,返回boolean。

当我们进入闪屏页时,进行判断。

if(isAdminActive()){
    startMain();//跳转主页面的逻辑
}else {
    satrtOpenDevicer();//主动询问设备管理者权限的逻辑
}
public void satrtOpenDevicer() {
        ComponentName componentName = new ComponentName(this, AdminReceiver.class);
        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
        intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "请求设备管理权限...");
        /*
         * 不能直接startActivity  因为可能在激活的时候用户点击了取消,这时候CheckBox状态是勾选的,但是实际是没激活的,
         */
        startActivityForResult(intent, DEVICECOMPONENTCODE);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.e("jsb_kiosk","结果"+requestCode);
        if(requestCode == DEVICECOMPONENTCODE){
            
            startMain();
        }
    }

这个时候当我们处于没有设备管理权限时进入app都会进行询问权限,界面如下:

好了处于设备模式后,就该处理DeviceOwner设备所有者

在这里查看大神写的比较完整的介绍

Android 提供了比较完善的设备所有者api,但是我发现调用的那些api效果完全没有作用,

ComponentName deviceAdmin = new ComponentName(this, AdminReceiver.class);
mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
if (!mDpm.isAdminActive(deviceAdmin)) {
            Toast.makeText(this, "没有设备管理权限", Toast.LENGTH_SHORT).show();
        }

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (mDpm.isDeviceOwnerApp(getPackageName())) {
                mDpm.setLockTaskPackages(deviceAdmin, new String[]{getPackageName()});
            } else {
           
                Log.i("jsb_","没有专属模式");

            }
        }

上面代码没有效果。我们通过mDpm.isDeviceOwnerApp去判断当前是否处于设备所有者的是没有效果的。

但是通过adb shell处理设备确实可以进入Kiosk模式,

命令adb shell dpm set-device-owner 包名/包名.AdminReceiver   (跟你创建的AdminReceiver地址一样)设置DeviceOwner;
命令adb shell dpm remove-active-admin 包名/包名.AdminReceiver   退出DeviceOwner;

那么我们只要通过代码实现该操作命令就可以了

private void execShell(String cmd){
        try{
            java.lang.Process p = Runtime.getRuntime().exec("sh");
            OutputStream outputStream = p.getOutputStream();
            DataOutputStream dataOutputStream=new DataOutputStream(outputStream);
            dataOutputStream.writeBytes(cmd);
            dataOutputStream.flush();
            dataOutputStream.close();
            outputStream.close();
        } catch(Throwable t)  {
            t.printStackTrace();
        }
    }

调用:

execShell("adb shell dpm set-device-owner "+getPackageName()+"/"+AdminReceiver.class.getName());

至于在什么时候调用该命令,就看个人需要,我是在闪屏页获取设备管理者后直接调用的

 @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.e("jsb_kiosk","结果"+requestCode);
        if(requestCode == DEVICECOMPONENTCODE){
            execShell("adb shell dpm set-device-owner "+getPackageName()+"/"+AdminReceiver.class.getName());
            startMain();
        }
    }

好了通过上面的操作,我们已经完全进入DeviceOwner设备所有者模式了

但是并没有完事,还差最后一步,真正的完成Kiosk模式

调用下面方法

private void enableKioskMode(boolean enabled) {
        try {
            if (enabled) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    if (mDpm.isLockTaskPermitted(this.getPackageName())) {
                        startLockTask();
                        mIsKioskEnabled = true;
                    } else {
                        //没有进入该模式,没有权限
                    }
                }
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    stopLockTask();
                }
                mIsKioskEnabled = false;
            }
        } catch (Exception e) {
            // TODO: Log and handle appropriately
        }
    }

最后看效果吧

点击进入改模式

ok,感觉很不错~~今天就到这里了,有空我上传一下demo,下班了。

猜你喜欢

转载自blog.csdn.net/qq_33796069/article/details/93632417