在Android上启用Kiosk模式
我们的云帆机器人(上面运行的安卓程序)有一个线下场景是商场,由于商场人多,总会遇到一些用户在我们的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>