2015年6月7日 星期日

Screen pinning

Reference:

https://developer.android.com/about/versions/android-5.0.html#ScreenPinning
http://blog.csdn.net/aaa111/article/details/43866981

Abrtraction:

打開Screen pinning後,通知欄和狀態欄會隱藏起來,Home key和Recent app key會失效,而且不允許啟動其它activity

How to open/close screen pinning:

手動:
從Settings -> Security -> Screen Pinning來釘選指定的app
同時長按 Back key和 Recent app key來關閉screen pinning

程式:
從app中呼叫startLockTask() 來釘選app。
若app不具有系統權限,則會跳出提示視窗,詢問使用者是否要執行Screen pinning
若app具有系統權限,可以直接呼叫setLockTaskPackages (ComponentName admin, String[] packages) ,省略詢問使用者的步驟
從app中呼叫stopLockTask()來關閉screen pinning

Source code:

Settings
.packages/app/Settings/src/com/android/settings/ScreenPinningSettings.java
    private void setLockToAppEnabled(boolean isEnabled) {
        Settings.System.putInt(getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,
                isEnabled ? 1 : 0);
    }

framework/base
.frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
void startLockTaskMode(TaskRecord task) {

.frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void setLockTaskModeLocked(TaskRecord task, boolean isLocked)

public void handleMessage(Message msg) {
                ......
                case LOCK_TASK_START_MSG: {
                    // When lock task starts, we disable the status bars.
                    try {
                        if (mLockTaskNotify == null) {
                            mLockTaskNotify = new LockTaskNotify(mService.mContext);
                        }
                        mLockTaskNotify.show(true);                  // Show toast message
                        mLockTaskIsLocked = msg.arg2 == 0;
                        if (getStatusBarService() != null) {
                            int flags =
                                    StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK;
                            if (!mLockTaskIsLocked) {
                                flags ^= StatusBarManager.DISABLE_HOME
                                        | StatusBarManager.DISABLE_RECENT;
                            }
                            getStatusBarService().disable(flags, mToken,
                                    mService.mContext.getPackageName());
                        }
                        mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
                        if (getDevicePolicyManager() != null) {
                            getDevicePolicyManager().notifyLockTaskModeChanged(true,
                                    (String)msg.obj, msg.arg1);
                        }
                    } catch (RemoteException ex) {
                        throw new RuntimeException(ex);
                    }
                } break;

                case LOCK_TASK_END_MSG: {
                    // When lock task ends, we enable the status bars.
                    try {
                        if (getStatusBarService() != null) {
                            getStatusBarService().disable(StatusBarManager.DISABLE_NONE, mToken,
                                    mService.mContext.getPackageName());
                        }
                        mWindowManager.reenableKeyguard(mToken);
                        if (getDevicePolicyManager() != null) {
                            getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
                                    msg.arg1);
                        }
                        if (mLockTaskNotify == null) {
                            mLockTaskNotify = new LockTaskNotify(mService.mContext);
                        }
                        mLockTaskNotify.show(false);
                        try {
                            boolean shouldLockKeyguard = Settings.System.getInt(
                                    mService.mContext.getContentResolver(),
                                    Settings.System.LOCK_TO_APP_EXIT_LOCKED) != 0;
                            if (!mLockTaskIsLocked && shouldLockKeyguard) {
                                mWindowManager.lockNow(null);
                                mWindowManager.dismissKeyguard();
                                new LockPatternUtils(mService.mContext)
                                        .requireCredentialEntry(UserHandle.USER_ALL);
                            }
                        } catch (SettingNotFoundException e) {
                            // No setting, don't lock.
                        }
                    } catch (RemoteException ex) {
                        throw new RuntimeException(ex);
                    }
                } break;

.frameworks/base/services/core/java/com/android/server/am/LockTaskNotify.java
    public void show(boolean starting) {
        int showString = R.string.lock_to_app_exit;
        if (starting) {
            showString = R.string.lock_to_app_start;
        }
        Toast.makeText(mContext, mContext.getString(showString), Toast.LENGTH_LONG).show();
    }

Related issue:

1. Toast messages are not displayed in screen pinning scenarios when accessed through other Users/Guest mode
[Solution1]
.frameworks/base/core/java/android/widget/Toast.java
             params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+            // Support multi-user
+            params.privateFlags =

                               WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;         }
 
[Solution2]
https://android.googlesource.com/platform/frameworks/base/+/a585268aff94b620b2c22e9ba3ae1163bb5dc8a8%5E%21/#F1
.frameworks/base/core/java/android/widget/Toast.java
     public int getYOffset() {
         return mTN.mY;
     }
+
+   /**
+   * Gets the LayoutParams for the Toast window.
+   * @hide
+   */
+   public WindowManager.LayoutParams getWindowParams() {
+       return mTN.mParams;
+ }


.frameworks/base/services/core/java/com/android/server/am/LockTaskNotify.java
 import android.content.Context;
import android.os.Handler;
import android.os.Message;
+import android.view.WindowManager; import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;

@@ -56,8 +57,7 @@

     if (mLastToast != null) {
         mLastToast.cancel();
     }
-   mLastToast = Toast.makeText(mContext, text, Toast.LENGTH_LONG);
-   mLastToast.show();

+   mLastToast = makeAllUserToastAndShow(text); }

public void show(boolean starting) {
@@ -65,7 +65,15 @@

     if (starting) {
         showString = R.string.lock_to_app_start;
     }
-    Toast.makeText(mContext, mContext.getString(showString), Toast.LENGTH_LONG).show();+   makeAllUserToastAndShow(mContext.getString(showString));
+   }
+
+   private Toast makeAllUserToastAndShow(String text) {
+   Toast toast = Toast.makeText(mContext, text, Toast.LENGTH_LONG);
+   toast.getWindowParams().privateFlags |=
+   WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+   toast.show();
+   return toast;
}







沒有留言: