AndroID 固定屏幕功能
可能大家看到这个标题不知道是什么东西,我先说明下,androID6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能。
屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换。
一、设置固定屏幕
我们先来看systemUI/src/com/AndroID/systemUI/recents/ScreenPinningRequest.Java的代码,这段代码就是长按home键出现几个Activity,然后按了图钉的那个按钮。在这里直接调用了AMS的startLockTaskModeOnCurrent函数。
@OverrIDe public voID onClick(VIEw v) { if (v.getID() == R.ID.screen_pinning_ok_button || mRequestwindow == v) { try { ActivityManagerNative.getDefault().startLockTaskModeOnCurrent(); } catch (remoteexception e) {} } clearPrompt(); }
我们来看AMS的startLockTaskModeOnCurrent函数,先调用ActivityStackSupervisor的topRunningActivityLocked获取最前面的Activity,然后调用startLockTaskModeLocked函数,参数是TaskRecord。
public voID startLockTaskModeOnCurrent() throws remoteexception { enforceCallingPermission(androID.Manifest.permission.MANAGE_ACTIVITY_STACKS,"startLockTaskModeOnCurrent"); long IDent = Binder.clearCallingIDentity(); try { synchronized (this) { ActivityRecord r = mStackSupervisor.topRunningActivityLocked(); if (r != null) { startLockTaskModeLocked(r.task); } } } finally { Binder.restoreCallingIDentity(IDent); } }
我们再来看topRunningActivityLocked函数,先从mFocusedStack中获取最前面的Activity。如果没有再遍历所有的mStacks获取。
ActivityRecord topRunningActivityLocked() { final ActivityStack focusedStack = mFocusedStack; ActivityRecord r = focusedStack.topRunningActivityLocked(null); if (r != null) { return r; } // Return to the home stack. final ArrayList<ActivityStack> stacks = mHomeStack.mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (stack != focusedStack && isFrontStack(stack)) { r = stack.topRunningActivityLocked(null); if (r != null) { return r; } } } return null; }
在startLockTaskModeLocked函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,下面我们来看这个函数,我们的task不为null,第一次mlockTaskModeTasks为空,会发送一个LOCK_TASK_START_MSG消息
voID setLockTaskModeLocked(TaskRecord task,int lockTaskModeState,String reason,boolean andResume) { if (task == null) { // Take out of lock task mode if necessary final TaskRecord lockedTask = getLockedTaskLocked(); if (lockedTask != null) { removeLockedTaskLocked(lockedTask); if (!mlockTaskModeTasks.isEmpty()) { // There are locked tasks remaining,can only finish this task,not unlock it. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,"setLockTaskModeLocked: Tasks remaining,can't unlock"); lockedTask.performClearTaskLocked(); resumetopActivitIEsLocked(); return; } } if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,"setLockTaskModeLocked: No tasks to unlock. Callers=" + DeBUG.getCallers(4)); return; } // Should have already been checked,but do it again. if (task.mlockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,"setLockTaskModeLocked: Can't lock due to auth"); return; } if (isLockTaskModeViolation(task)) { Slog.e(TAG_LOCKTASK,"setLockTaskMode: Attempt to start an unauthorized lock task."); return; } if (mlockTaskModeTasks.isEmpty()) { // First locktask. final Message lockTaskMsg = Message.obtain(); lockTaskMsg.obj = task.intent.getComponent().getPackagename(); lockTaskMsg.arg1 = task.userID; lockTaskMsg.what = LOCK_TASK_START_MSG;//发送消息 lockTaskMsg.arg2 = lockTaskModeState; mHandler.sendMessage(lockTaskMsg); } // Add it or move it to the top. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,"setLockTaskModeLocked: Locking to " + task + " Callers=" + DeBUG.getCallers(4)); mlockTaskModeTasks.remove(task); mlockTaskModeTasks.add(task);//加入到mlockModeTasks中 if (task.mlockTaskUID == -1) { task.mlockTaskUID = task.effectiveUID; } if (andResume) { findTaskToMovetoFrontLocked(task,null,reason);//把task放最前面 resumetopActivitIEsLocked();//显示新的Activity } }
我们再来看消息处理,在消息处理中主要调用了WMS的disableKeyguard函数。
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); mlockTaskModeState = msg.arg2; if (getStatusbarService() != null) { int flags = 0; if (mlockTaskModeState == LOCK_TASK_MODE_LOCKED) { flags = StatusbarManager.disABLE_MASK & (~StatusbarManager.disABLE_BACK); } else if (mlockTaskModeState == LOCK_TASK_MODE_PINNED) { flags = StatusbarManager.disABLE_MASK & (~StatusbarManager.disABLE_BACK) & (~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;
二、固定屏幕后Activity启动流程
在固定屏幕后,如果我们启动其他TaskRecord的Activity是不能启动的,我们来看下这个原理。在startActivityUncheckedLocked函数中会调用isLockTaskModeViolation函数来判断是否进一步的Activity的启动流程,我们来看下这个函数,调用getLockedTaskLocked来看mlockTaskModeTasks(就是锁定屏幕的那些Task),如果当前的task就是当前正在固定屏幕的task,直接return false就是可以继续启动Activity的流程,而如果不是,我们需要看task的mlockTaskAuth变量。
boolean isLockTaskModeViolation(TaskRecord task,boolean isNewClearTask) { if (getLockedTaskLocked() == task && !isNewClearTask) { return false; } final int lockTaskAuth = task.mlockTaskAuth; switch (lockTaskAuth) { case LOCK_TASK_AUTH_DONT_LOCK: return !mlockTaskModeTasks.isEmpty(); case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: case LOCK_TASK_AUTH_LAUNCHABLE: case LOCK_TASK_AUTH_WHITEListED: return false; case LOCK_TASK_AUTH_PINNABLE: // Pinnable tasks can't be launched on top of locktask tasks. return !mlockTaskModeTasks.isEmpty(); default: Slog.w(TAG,"isLockTaskModeViolation: invalID lockTaskAuth value=" + lockTaskAuth); return true; } }
我们再来看TaskRecord的setLockedTaskAuth函数,在新建一个TaskRecord的时候会调用setIntent函数,而setIntent函数又是在TaskRecord的构造函数中调用的。我们来看这个函数mlockTaskAuth的值是根据mlockTaskMode来定的,而mlockTaskMode又是ActivityInfo传入的,这个值是在PKMS解析AndroIDManifest.xml的时候构造的,默认就是LOCK_TASK_LAUNCH_MODE_DEFAulT,而当没有白名单mlockTaskAuth最后就是LOCK_TASK_AUTH_PINNABLE。
voID setLockTaskAuth() { if (!mPrivileged && (mlockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS || mlockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { // Non-priv apps are not allowed to use always or never,fall back to default mlockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAulT; } switch (mlockTaskMode) { case LOCK_TASK_LAUNCH_MODE_DEFAulT: mlockTaskAuth = isLockTaskWhiteListedLocked() ? LOCK_TASK_AUTH_WHITEListED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: mlockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK; break; case LOCK_TASK_LAUNCH_MODE_ALWAYS: mlockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; break; case LOCK_TASK_LAUNCH_MODE_IF_WHITEListED: mlockTaskAuth = isLockTaskWhiteListedLocked() ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,"setLockTaskAuth: task=" + this + " mlockTaskAuth=" + lockTaskAuthToString()); }
我们再来看isLockTaskModeViolation函数如下代码,现在是task的mlockTaskAuth 是LOCK_TASK_AUTH_PINNABLE,而当前处于固定屏幕,所以mlockTaskModeTasks不为null,最后返回true。那Activity启动流程就不能走下去了,那就是代表启动普通的Activity会被阻止。
case LOCK_TASK_AUTH_PINNABLE: // Pinnable tasks can't be launched on top of locktask tasks. return !mlockTaskModeTasks.isEmpty();
三、取消固定屏幕
最后我们再来看看取消固定屏幕,取消屏幕会在Phonestatusbar中取消,但是一定是要有虚拟键,原生就是这么设定的。最后调用了AMS的stopLockTaskModeOnCurrent函数。这个函数主要是调用了stopLockTaskMode函数,这个函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,之前在固定屏幕时也是调用了这个函数,但是这里我们仔细看,其第一个参数为null。
public voID stopLockTaskMode() { final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked(); if (lockTask == null) { // Our work here is done. return; } final int callingUID = Binder.getCallingUID(); final int lockTaskUID = lockTask.mlockTaskUID; // Ensure the same caller for startLockTaskMode and stopLockTaskMode. // It is possible lockTaskMode was started by the system process because // androID:lockTaskMode is set to a locking value in the application manifest instead of // the app calling startLockTaskMode. In this case {@link TaskRecord.mlockTaskUID} will // be 0,so we compare the callingUID to the {@link TaskRecord.effectiveUID} instead. if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED && callingUID != lockTaskUID && (lockTaskUID != 0 || (lockTaskUID == 0 && callingUID != lockTask.effectiveUID))) { throw new SecurityException("InvalID uID,expected " + lockTaskUID + " callingUID=" + callingUID + " effectiveUID=" + lockTask.effectiveUID); } long IDent = Binder.clearCallingIDentity(); try { Log.d(TAG,"stopLockTaskMode"); // Stop lock task synchronized (this) { mStackSupervisor.setLockTaskModeLocked(null,ActivityManager.LOCK_TASK_MODE_NONE,"stopLockTask",true); } } finally { Binder.restoreCallingIDentity(IDent); } }
我们来看下这个函数,如果为空,现在调用getLockedTaskLocked获取当前固定屏幕的TaskRecord,然后调用removeLockedTaskLocked去除这个TaskRecord,如果还不为null,调用resumetopActivitIEsLocked启动下个Activity(一般也就是下个屏幕锁定的TaskRecord的Activity)。
如果为空了,直接返回。但是在我们下次启动普通的Activity的时候就恢复正常了,因为mlockTaskModeTasks已经为空了。
voID setLockTaskModeLocked(TaskRecord task,"setLockTaskModeLocked: No tasks to unlock. Callers=" + DeBUG.getCallers(4)); return; }
四、没有虚拟键如何取消屏幕固定
前面说过如果没有虚拟键就不能取消屏幕固定了,我们说下几种方式
1.使用am命令 am task lock stop可以调用am的stopLockTaskMode函数
2.另一种我们可以在Activity.java中修改代码,比较长按返回键调用AMS的stopLockTaskMode方法,下面就是实现,Activity本身提供了stopLockTask就是调用了AMS的stopLockTaskMode方法
public boolean onKeyLongPress(int keyCode,KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { stopLockTask(); } return false; }
3.直接在Settings中对这项进行置灰处理
在SecuritySettings会读取security_settings_misc.xml文件然后加入相关perference,这其中就会有如下是屏幕固定相关的
<PreferenceScreen androID:key="screen_pinning_settings" androID:title="@string/screen_pinning_Title" androID:summary="@string/switch_off_text" androID:fragment="com.androID.settings.ScreenPinningSettings"/>
我们可以在SecuritySettings读取该文件之后,调用WMS的hasNavigationbar来看有没有虚拟键(没有虚拟按键到时候不能取消屏幕固定),如果没有直接把Settings中这项置灰。
// Append the rest of the settings addPreferencesFromresource(R.xml.security_settings_misc); IWindowManager windowManager = WindowManagerGlobal.getwindowManagerService(); try { boolean is_screen_pining = windowManager.hasNavigationbar(); root.findPreference(KEY_SCREEN_PINNING).setEnabled(is_screen_pining); } catch(remoteexception e) { Log.e("SecuritySettings","get window service remoteexception."); }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
总结以上是内存溢出为你收集整理的Android6.0 固定屏幕功能实现方法及实例全部内容,希望文章能够帮你解决Android6.0 固定屏幕功能实现方法及实例所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)