我们的云帆机器人(上面运行的安卓程序)有一个线下场景是商场,由于商场人多,总会遇到一些用户在我们的app里乱点,然后会跳出程序进入到系统设置的一些界面,这样很不友好。

比如程序中有一些需要输入文字的地方,弹出了输入法,有的用户就去故意点输入法的设置,结果就能进入到安卓的系统设置,商场的用户用的是我们机器人程序而不是手机,并且机器人上本来就屏蔽了多任务和返回等虚拟按键,结果无法返回原来的程序。

一种解决方式是自己在程序里去实现一个中文的输入法,但这代价也太大了。

另外一种方式就是使用安卓的Kiosk模式。这个模式直译的话是贩售亭,但实际上的意思是屏幕固定功能,也就是我们想要将用户看到的屏幕固定到我们的app中的意思。

介绍url https://www.sureshjoshi.com/mobile/android-kiosk-mode-without-root/

源码展示区:https://github.com/sureshjoshi/android-kiosk-example

中文介绍 https://juejin.im/entry/578f873dd342d30058e99c51

只看代码可能不太明白,于是我对着代码自己重新写了一个demo。其中遇到的问题如下:

1.写好了后运行代码,并不能锁住,主要原因是没有admin的权限,这个需要到设置--系统安全--设备管理 中找到这个程序并选中才可以。

2.另外就是有了admin权限后还是不能锁定,debug后发现是判断isDeviceOwnerApp的时候为false,这个是因为一个系统只能有一个OwnerApp,需要使用adb命令设置对应的recevier。命令是:

adb shell dpm set-device-owner com.honghe.screenlocktest/.AdminReceiver

3.注意这个功能只能安卓5.1之后可用

 

代码如下

MainActivity

 1 package com.honghe.screenlocktest;
 2 
 3 import android.app.admin.DevicePolicyManager;
 4 import android.content.ComponentName;
 5 import android.content.Context;
 6 import android.os.Build;
 7 import android.support.annotation.RequiresApi;
 8 import android.support.v7.app.AppCompatActivity;
 9 import android.os.Bundle;
10 import android.util.Log;
11 import android.view.View;
12 
13 import java.io.BufferedReader;
14 import java.io.DataOutputStream;
15 import java.io.InputStream;
16 import java.io.InputStreamReader;
17 
18 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
19 public class MainActivity extends AppCompatActivity {
20     private static final String TAG = MainActivity.class.getName();
21     private DevicePolicyManager dpm;
22     private boolean inKioskMode;
23     private ComponentName deviceAdmin;
24     private Process process = null;
25     private DataOutputStream os = null;
26 
27     @Override
28     protected void onCreate(Bundle savedInstanceState) {
29         super.onCreate(savedInstanceState);
30         setContentView(R.layout.activity_main);
31         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
32             @Override
33             public void onClick(View v) {
34                 lockScreen();
35             }
36         });
37         findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
38             @Override
39             public void onClick(View v) {
40                 dislockScreen();
41             }
42         });
43     }
44 
45 
46     private boolean doLockScreen() {
47         if (dpm.isLockTaskPermitted(this.getPackageName())) {
48             Log.i("yunji.HotelAPP", "start lock screen");
49             startLockTask();
50             inKioskMode = true;
51             Log.i("yunji.HotelAPP", "lock screen success");
52             return true;
53         }
54         Log.w("yunji.HotelAPP", "cannot lock screen");
55         return false;
56     }
57 
58     private void lockScreen() {
59         try {
60             if (!inKioskMode) {
61                 dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
62                 deviceAdmin = new ComponentName(this, AdminReceiver.class);
63                 Log.e(TAG, "isAdminActive: " + dpm.isAdminActive(deviceAdmin) + "\tisDeviceOwnerApp: " + dpm.isDeviceOwnerApp(getPackageName()));
64                 if (dpm.isDeviceOwnerApp(getPackageName())) {
65                     //如果这里失效,请使用adb shell命令设置deviceOwnerAPP为当前app $ adb shell dpm set-device-owner com.honghe.screenlocktest/.AdminReceiver
66                     //参考 https://juejin.im/entry/578f873dd342d30058e99c51
67                     dpm.setLockTaskPackages(deviceAdmin,
68                             new String[]{getPackageName()});
69                     Log.e(TAG, "setLockTaskPackages: ");
70                 }
71                 doLockScreen();
72             }
73         } catch (Exception e) {
74             Log.e("yunji.HotelAPP", "Exception: " + e);
75         }
76     }
77 
78     private void dislockScreen() {
79 
80         try {
81             if (inKioskMode) {
82                 stopLockTask();
83                 inKioskMode = false;
84             }
85         } catch (Exception e) {
86             Log.e("yunji.HotelAPP", "Exception: " + e);
87         }
88     }
89 }

activity_main.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     android:orientation="vertical"
 8     tools:context=".MainActivity">
 9 
10     <TextView
11         android:layout_width="wrap_content"
12         android:layout_height="wrap_content"
13         android:text="Hello World!"
14         app:layout_constraintBottom_toBottomOf="parent"
15         app:layout_constraintLeft_toLeftOf="parent"
16         app:layout_constraintRight_toRightOf="parent"
17         app:layout_constraintTop_toTopOf="parent" />
18 
19     <Button
20         android:id="@+id/button"
21         android:layout_width="wrap_content"
22         android:layout_height="wrap_content"
23         android:text="锁定" />
24 
25     <Button
26         android:id="@+id/button2"
27         android:layout_width="wrap_content"
28         android:layout_height="wrap_content"
29         android:text="解锁" />
30 
31     <EditText
32         android:id="@+id/editText"
33         android:layout_width="match_parent"
34         android:layout_height="wrap_content"
35         android:ems="10"
36         android:inputType="textPersonName"
37         android:text="Name" />
38 
39 </LinearLayout>

 

 

AdminReceiver.java

 1 package com.honghe.screenlocktest;
 2 import android.app.admin.DeviceAdminReceiver;
 3 import android.content.Context;
 4 import android.content.Intent;
 5 
 6 
 7 /**
 8  * Created by zkzhou on 7/15/16.
 9  */
10 public class AdminReceiver extends DeviceAdminReceiver {
11     @Override
12     public void onEnabled(Context context, Intent intent) {
13     }
14 
15     @Override
16     public CharSequence onDisableRequested(Context context, Intent intent) {
17         return "Warning: Device Admin is going to be disabled.";
18     }
19 
20     @Override
21     public void onDisabled(Context context, Intent intent) {
22     }
23 
24     @Override
25     public void onLockTaskModeEntering(Context context, Intent intent,
26                                        String pkg) {
27     }
28 
29     @Override
30     public void onLockTaskModeExiting(Context context, Intent intent) {
31     }
32 }

androidManifest.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.honghe.screenlocktest">
 4 
 5     <application
 6         android:allowBackup="true"
 7         android:icon="@mipmap/ic_launcher"
 8         android:label="@string/app_name"
 9         android:roundIcon="@mipmap/ic_launcher_round"
10         android:supportsRtl="true"
11         android:theme="@style/AppTheme">
12         <activity android:name=".MainActivity">
13             <intent-filter>
14                 <action android:name="android.intent.action.MAIN" />
15 
16                 <category android:name="android.intent.category.LAUNCHER" />
17             </intent-filter>
18         </activity>
19         <receiver
20             android:name=".AdminReceiver"
21             android:label="@string/app_name"
22             android:permission="android.permission.BIND_DEVICE_ADMIN">
23             <meta-data
24                 android:name="android.app.device_admin"
25                 android:resource="@xml/device_admin" />
26 
27             <intent-filter>
28                 <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
29             </intent-filter>
30         </receiver>
31     </application>
32 
33 
34 </manifest>

xml/device_admin.xml

 1 <device-admin xmlns:android="http://schemas.android.com/apk/res/android">
 2     <uses-policies>
 3         <force-lock/>
 4         <watch-login/>
 5         <disable-camera/>
 6         <disable-keyguard-features/>
 7         <encrypted-storage/>
 8         <expire-password/>
 9         <limit-password/>
10         <reset-password/>
11         <set-global-proxy/>
12         <wipe-data/>
13     </uses-policies>
14 </device-admin>