1. 程式人生 > >在Android上啟用Kiosk模式

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