在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>