精华内容
下载资源
问答
  • 可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能屏幕固定开启后,屏幕只能固定...
  • android6.0 固定屏幕功能

    千次阅读 2016-09-19 10:38:32
    可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能屏幕固定开启后,屏幕只能固定...

    可能大家看到这个标题不知道是什么东西,我先说明下,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, 0, 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, 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;
            }


    四、没有虚拟键如何取消屏幕固定

    前面说过如果没有虚拟键就不能取消屏幕固定了,我们说下几种方式

    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在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能屏幕固定开启后,屏幕只能固定...
  •  一、设置中开启屏幕固定:  此功能在设置-安全中开启,不清楚以往的版本中是否支持就有已经有了此功能,但是Android4.4设置中到时没有发现此项。在Android 5.0发现了此项设置。刚一看到此项设置,就心想:“这...

    转载自 https://blog.csdn.net/u013656135/article/details/49741659

     一、设置中开启屏幕固定:  

        此功能在设置-安全中开启,不清楚以往的版本中是否支持就有已经有了此功能,但是Android4.4设置中到时没有发现此项。在Android 5.0发现了此项设置。刚一看到此项设置,就心想:“这是什么鬼!”。设置中的代码在SecuritySettings.java和ScreenPinningSettings.java中,代码量不多,Preference XML文件是security_settings_misc.xml:                

    
     
    1. if (Settings.System.getInt(getContentResolver(),

    2. Settings.System.LOCK_TO_APP_ENABLED, 0) != 0) {

    3. root.findPreference(KEY_SCREEN_PINNING).setSummary(

    4. getResources().getString(R.string.switch_on_text));

    5. }

        看到上面代码后,到\android5.1\frameworks\base\core\java\android\provider\Settings.java找到了LOCK_TO_APP_ENABLED,然后就发现这货被hide了,意思就说,在独立应用中是不能去设置此项的值的:    

    
     
    1. /**

    2. * Whether lock-to-app will be triggered by long-press on recents.

    3. * @hide

    4. */

    5. public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled";

        之后,本想查看下这货是怎么写进数据库的,纵所周知,provider settings里面的东西一般都会写进数据库,而settings.db的文件是这里被创建的:            

    \android5.1\frameworks\base\packages\SettingsProvider\src\com\android\providers\settings\DatabaseHelper.java

         按照介个意思,我想应该是会在这里写进数据库啊,然后就在DatabaseHelper.java搜索LOCK_TO_APP_ENABLED,但是没有找到,只能说,它不是在这里写进数据库的,无奈之下,再度查看ScreenPinningSettings.java中的相关代码:      

    
     
    1. private void setLockToAppEnabled(boolean isEnabled) {

    2. Settings.System.putInt(getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,

    3. isEnabled ? 1 : 0);

    4. }

          Settings.System.putInt()方法有如下描述:      

    
     
    1. /**

    2. * Convenience function for updating a single settings value as an

    3. * integer. This will either create a new entry in the table if the

    4. * given name does not exist, or modify the value of the existing row

    5. * with that name. Note that internally setting values are always

    6. * stored as strings, so this function converts the given value to a

    7. * string before storing it.

    8. *

    9. * @param cr The ContentResolver to access.

    10. * @param name The name of the setting to modify.

    11. * @param value The new value for the setting.

    12. * @return true if the value was set, false on database errors

    13. */

    14. public static boolean putInt(ContentResolver cr, String name, int value) {

    15. return putIntForUser(cr, name, value, UserHandle.myUserId());

    16. }

         由此推断,settings.db数据库system table中本没有lock_to_app_enabled此项,而在开启screen pinning后,会向此表中写入lock_to_app_enabled的数据:    

         settings.db 在手机中的位置:/data/data/com.android.providers.settings/database/settings.db (需要root)。

        

        二、屏幕固定开启后视图的显示:

               在Android5.1 -Recents分析 中曾提到过screen pinning。从代码上看,screen pinning和 Recents绑定到了一块,效果图大致是这样的:

                 (图1)

        意思就说,在显示Recents的时候,如果screen pinning在设置中已开启,那么在Recents 视图中最上面的app 缩略图的右下角会有个图标。点击图标以后会出现如下提示界面:

          (图2)

        此时点击“知道了”就会固定到Recents中显示的对应应用界面。通过Android5.1 -Recents分析 可知图1中的提示图标是在TaskView,其ID为lock_to_app_fab。既然响应点击事件,就可以在TaskView.java中直接找到onClick()方法:  

    
     
    1. @Override

    2. public void onClick(final View v) {

    3. final TaskView tv = this;

    4. final boolean delayViewClick = (v != this) && (v != mActionButtonView);

    5. if (delayViewClick) {

    6. // We purposely post the handler delayed to allow for the touch feedback to draw

    7. postDelayed(new Runnable() {

    8. @Override

    9. public void run() {

    10. if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {

    11. if (mCb != null) {

    12. mCb.onTaskViewAppIconClicked(tv);

    13. }

    14. } else if (v == mHeaderView.mDismissButton) {

    15. dismissTask();

    16. }

    17. }

    18. }, 125);

    19. } else {

    20. if (v == mActionButtonView) {

    21. // Reset the translation of the action button before we animate it out

    22. mActionButtonView.setTranslationZ(0f);

    23. }

    24. if (mCb != null) {

    25. mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView));

    26. }

    27. }

    28. }

             其中mActionButtonView就是响应点击事件的view。图2显示的view的布局为:screen_pinning_request_text_area.xml,其中Button ID:screen_pinning_ok_button就是图2中显示的“知道了”。这部分view 在ScreenPinningRequest.java中被inflate。  

    
     
    1. private void inflateView(boolean isLandscape) {

    2. // We only want this landscape orientation on <600dp, so rather than handle

    3. // resource overlay for -land and -sw600dp-land, just inflate this

    4. // other view for this single case.

    5. mLayout = (ViewGroup) View.inflate(getContext(), isLandscape

    6. ? R.layout.screen_pinning_request_land_phone : R.layout.screen_pinning_request,

    7. null);

    8. // Catch touches so they don't trigger cancel/activate, like outside does.

    9. mLayout.setClickable(true);

    10. ...

    11. ...

    12. }

      inflate视图,但是图2中中view是如何显示出来的呢?源码中是通过callback一层层的回调来实现的,前面提到过图1中的view是在TaskView中,TaskView有内部接口,在响应了view的onClick方法以后会调用TaskView类内部的callback:          

    
     
    1. if (mCb != null) {

    2. mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView));

    3. }

      而TaskStackView视图包含TaskView视图,并实现了TaskView内部的callback,并在此调用自己的callback:   

    
     
    1. @Override

    2. public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {

    3. // Cancel any doze triggers

    4. mUIDozeTrigger.stopDozing();

    5. if (mCb != null) {

    6. mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask);

    7. }

    8. }

      而RecentsView视图又包含TaskStackView视图,并实现TaskStackView的接口,RecentsView在此调用自己callback(onScreenPinningRequest):  

    
     
    1. @Override

    2. public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,

    3. final TaskStack stack, final Task task, final boolean lockToTask) {

    4. // Notify any callbacks of the launching of a new task

    5. if (mCb != null) {

    6. mCb.onTaskViewClicked();

    7. }

    8. ...

    9. if (lockToTask) {

    10. animStartedListener = new ActivityOptions.OnAnimationStartedListener() {

    11. boolean mTriggered = false;

    12. @Override

    13. public void onAnimationStarted() {

    14. if (!mTriggered) {

    15. postDelayed(new Runnable() {

    16. @Override

    17. public void run() {

    18. mCb.onScreenPinningRequest();

    19. }

    20. }, 350);

    21. mTriggered = true;

    22. }

    23. }

    24. };

    25. }

    26. ...

    27.  
    28. }

      到这里,callback回调还没有完,RecentsView的 RecentsViewCallbacks 接口被RecentsActivity实现: 

    
     
    1. @Override

    2. public void onScreenPinningRequest() {

    3. if (mStatusBar != null) {

    4. mStatusBar.showScreenPinningRequest(false);

    5. }

    6. }

        直到这里callback回调才算基本结束,mStatusBar是PhoneStatusBar类的实例对象,其showScreenPinningRequest方法:    

    
     
    1. public void showScreenPinningRequest(boolean allowCancel) {

    2. mScreenPinningRequest.showPrompt(allowCancel);

    3. }

        ScreenPinningRequest.java的showPrompt()方法:    

    
     
    1. public void showPrompt(boolean allowCancel) {

    2. clearPrompt();

    3. mRequestWindow = new RequestWindowView(mContext, allowCancel);

    4. mRequestWindow.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    5. // show the confirmation

    6. WindowManager.LayoutParams lp = getWindowLayoutParams();

    7. mWindowManager.addView(mRequestWindow, lp);

    8. }

        到这里,图2中的视图在响应了图1视图中的onClick事件以后就显示出来了。

     

    三、屏幕固定实现的功能:

        经过上面的分析可知,最终响应Button-screen_pinning_ok_button来实现屏幕固定的功能,代码自然在ScreenPinningRequest.java中:   

    
     
    1. @Override

    2. public void onClick(View v) {

    3. if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {

    4. try {

    5. ActivityManagerNative.getDefault().startLockTaskModeOnCurrent();

    6. } catch (RemoteException e) {}

    7. }

    8. clearPrompt();

    9. }

        其中ActivityManagerNative.getDefault() 相当于 ActivityManagerService,所以直接在ActivityManagerService.java中查找startLockTaskModeOnCurrent()方法:   

    
     
    1. @Override

    2. public void startLockTaskModeOnCurrent() throws RemoteException {

    3. enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,

    4. "startLockTaskModeOnCurrent");

    5. long ident = Binder.clearCallingIdentity();

    6. try {

    7. ActivityRecord r = null;

    8. synchronized (this) {

    9. r = mStackSupervisor.topRunningActivityLocked();

    10. }

    11. startLockTaskMode(r.task);

    12. } finally {

    13. Binder.restoreCallingIdentity(ident);

    14. }

    15. }

    16. void startLockTaskMode(TaskRecord task) {

    17. final String pkg;

    18. synchronized (this) {

    19. pkg = task.intent.getComponent().getPackageName();

    20. }

    21. boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;

    22. if (!isSystemInitiated && !isLockTaskAuthorized(pkg)) {

    23. StatusBarManagerInternal statusBarManager = LocalServices.getService(

    24. StatusBarManagerInternal.class);

    25. if (statusBarManager != null) {

    26. statusBarManager.showScreenPinningRequest();

    27. }

    28. return;

    29. }

    30. long ident = Binder.clearCallingIdentity();

    31. try {

    32. synchronized (this) {

    33. // Since we lost lock on task, make sure it is still there.

    34. task = mStackSupervisor.anyTaskForIdLocked(task.taskId);

    35. if (task != null) {

    36. if (!isSystemInitiated

    37. && ((mStackSupervisor.getFocusedStack() == null)

    38. || (task != mStackSupervisor.getFocusedStack().topTask()))) {

    39. throw new IllegalArgumentException("Invalid task, not in foreground");

    40. }

    41. mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated,

    42. "startLockTask");

    43. }

    44. }

    45. } finally {

    46. Binder.restoreCallingIdentity(ident);

    47. }

    48. }

        从代码中可看出,此功能的实现都管理activity 的stack 和 task。锁住stack 和 task 不让新的进来,就达到屏幕固定的目的,因为在这种情况下,不能为其他的activity准备stack和task。而取消此模式,有其对应的方法: 

    
     
    1. @Override

    2. public void stopLockTaskModeOnCurrent() throws RemoteException {

    3. enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,

    4. "stopLockTaskModeOnCurrent");

    5. long ident = Binder.clearCallingIdentity();

    6. try {

    7. stopLockTaskMode();

    8. } finally {

    9. Binder.restoreCallingIdentity(ident);

    10. }

    11. }

     

    四、在独立应用中屏幕固定模式又会怎样:

        首先,此功能是否支持在第三方应用里面实现呢?也许会考虑使用ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)

        来看看是否有相关的方法,但是ActivityManager.java中的相关接口都是hide的,不能被第三方应用使用:    

    
     
    1. /**

    2. * @hide

    3. */

    4. public void startLockTaskMode(int taskId) {

    5. try {

    6. ActivityManagerNative.getDefault().startLockTaskMode(taskId);

    7. } catch (RemoteException e) {

    8. }

    9. }

    10. /**

    11. * @hide

    12. */

    13. public void stopLockTaskMode() {

    14. try {

    15. ActivityManagerNative.getDefault().stopLockTaskMode();

    16. } catch (RemoteException e) {

    17. }

    18. }

        虽然ActivityManager类不让使用,但是Activity.java中却提供了相关方法(需要API>=21):  

    
     
    1. public void startLockTask() {

    2. try {

    3. ActivityManagerNative.getDefault().startLockTaskMode(mToken);

    4. } catch (RemoteException e) {

    5. }

    6. }

    7.  
    8. public void stopLockTask() {

    9. try {

    10. ActivityManagerNative.getDefault().stopLockTaskMode();

    11. } catch (RemoteException e) {

    12. }

    13. }

        于是在模拟器上测试了一下:

        

        这么看来,此功能算是支持第三方应用开启,并且还提供了一个放来来判断系统是否处于此模式:     

    
     
    1. /**

    2. * Return whether currently in lock task mode. When in this mode

    3. * no new tasks can be created or switched to.

    4. *

    5. * @see Activity#startLockTask()

    6. */

    7. public boolean isInLockTaskMode() {

    8. try {

    9. return ActivityManagerNative.getDefault().isInLockTaskMode();

    10. } catch (RemoteException e) {

    11. return false;

    12. }

    13. }

     

        而这也带来了思考的问题,当在设置中开启屏幕固定功能,并在Recents上固定某个应用的界面,那么这个应用的界面在onResume的时候是否需要使用isInLockTaskMode来做判断,从而做相应的处理? 这个就要看情况而定吧,我用自己前面瞎写的手电筒应用做测试,如果开启此模式,手电筒会出问题,这个跟我实现手电筒的代码有关系。

        问题又来了,在第三方应用中开启屏幕固定功能,提示界面又是如何显示出来的呢?这个就要回到前面提到的PhoneStatusBar.java,前面在Recents界面固定某个应用的界面是RecentsActivity中实现RecentsView内部接口并调用了PhoneStatusBar类中的showScreenPinningRequest(boolean allowCancel)方法。但是PhoneStatusBar类中还重写了一个父类的方法showScreenPinningRequest()。应用独立开启屏幕固定功能就会调用此方法:  

    
     
    1. @Override

    2. public void showScreenPinningRequest() {

    3. if (mKeyguardMonitor.isShowing()) {

    4. // Don't allow apps to trigger this from keyguard.

    5. return;

    6. }

    7. // Show screen pinning request, since this comes from an app, show 'no thanks', button.

    8. showScreenPinningRequest(true);

    9. }

    展开全文
  • 想问下这个用的是什么办法 为什么把网页下载下来后就么有这个功能了?! 用谷歌手机版打开 [图片说明](https://img-ask.csdn.net/upload/201607/16/1468650182_932788.png)
  • 我平时工作的时候,使用敬业签这款便签软件来辅助自己记录一天当中比较重要的事情,为了让自己能够随时看到电脑屏幕上记录的便签内容,敬业签的软件界面就有窗口置顶功能,无论当前打开的是什么文件夹或什么办公文档...

    便签是一种比较使用的辅助软件,使用者可以把自己平时工作、学习和生活中需要做的待办事项以及各类计划安排记录在便签软件中。便签置顶功能可以让某条便签内容固定在顶端,无论如何调整都会保持它的优先级,可以把便签内容固定置顶的软件是哪款?

    我平时工作的时候,使用敬业签这款便签软件来辅助自己记录一天当中比较重要的事情,为了让自己能够随时看到电脑屏幕上记录的便签内容,敬业签的软件界面就有窗口置顶功能,无论当前打开的是什么文件夹或什么办公文档,都可以让敬业签悬浮在指定的位置。

    在这里插入图片描述

    除了软件框体可以置顶悬浮之外,敬业签里面所记录的每条便签内容都可以设置为置顶,将便签内容设置为置顶之后,无论选择哪种排序模式,这条便签内容都会处于最上面,可以快速找到它,不需要的时候,也可以取消便签内容置顶。

    在这里插入图片描述

    为了方便用户的使用,在Windows电脑端将某条便签设置为置顶效果后,这个置顶效果会自动同步至其他操作端,这是因为敬业签有比较人性化的多端同步功能,支持苹果手机端、安卓手机端、iPad端、Windows电脑端和web网页端。有了多端同步的功能,用户就可以通过多个端口登录同一账号,查看、编辑和储存自己的敬业签内容,通过不同设备相互之间的良好协作来提高自己的工作效率。

    展开全文
  • Bootstrap 包含了一个响应式的、移动设备优先的、不固定的网格系统,可以随着设备或视口大小的增加而适当地扩展到 12 列。它包含了用于简单的布局选项的预定义类,也包含了用于生成更多语义布局的功能强大的混合类 ...
    Bootstrap 包含了一个响应式的、移动设备优先的、不固定的网格系统,可以随着设备或视口大小的增加而适当地扩展到 12 列。它包含了用于简单的布局选项的预定义类,也包含了用于生成更多语义布局的功能强大的混合类
    
    1. 响应式网格系统随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列。
    展开全文
  • 事实上,作为一款屏幕取色软件,目前我们并没有发现其他的类似软件有这个功能的,但是,我们加上了这个功能,为什么呢?因为我们考虑,各位设计师都设计业的行家里手,各位的个性自然与众不同的。我们的软件可以...
  • 由于公司需求,需要做一个实时...然后自己就尝试了各种方法,什么定时器、绝对定位、固定定位、计算屏幕高度再定位等等,都没有作用,键盘出来后还是会遮挡住。最后只能上网查找答案,找了半天,终于找到了答案,这里要
  • 就像一个电视机以及其播放的电影(这个比喻不太恰当,我强行比喻的),电视机用于放映电影的屏幕(控件的展示部分)是固定位置固定大小的,然而电影的世界(控件的内容部分)无限大的,我们只能展示这个无限的...
  • 关于word 2007的功能

    2013-08-26 18:38:00
    什么是功能区呢?好吧,我承认我也说不清楚,上图吧,如下图: 看到了吧,在图片中除了编辑区,占用高度最大的区域,而且高度是固定的。 我的屏幕分辨率只有1024×768,又或者我正在投影仪上展示word文档,这个...
  • 在网上找到的方法,很简单右击桌面,在桌面选项框中,找到自定义桌面,web选项框中,把锁定桌面项目和当前主页项目勾去掉...可能在某些工作中需要常访问一个网页,而把它固定在桌面上这个作用吧。我找到的链接...
  •  1、在屏幕顶部固定一个BottomView,XML布局最好使用RelativeLayout(底部的BottomView并不 ListView的footView,这个和footView独立的,想想为什么?)  2、然后自定义ListView控件,监听onTouchEvent事件,...
  • 但在3D中,摄像机个很重要的概念,因为如果直接在设备上显示标准几何模型的坐标,可能屏幕什么都看不到,因为模型坐标的范围可能不与设备坐标范围相对应,显示器本身个平面固定的二维矩形区域,
  • Snipaste for Mac一个简单但功能强大的剪切工具,允许您将屏幕截图固定屏幕上,下载并启动应用程序,按F1开始剪切,然后按F3将其粘贴为浮动窗口。您还可以将剪贴板中的文本或颜色信息转换为浮动图像窗口。不论您...
  • 获取访问Web App点击访问 fleet.diablohu.comWeb App针对手机与平板设备进行了优化适配,可用移动平台浏览器访问后选择将该Web App固定到主屏幕上,以便日后使用。获取客户端版本百度网盘 | OneDrive支持的操作系统...
  • 如果你作为设计师通过额外的付出创造了一个功能性流动布局,为什么不更进一步使其兼容所有分辨率,而不是局限于大多数屏幕。你可以使用一些技巧创造一种意想不到的适应性布局,这种布局在不断改变屏幕分辨率情况...
  • 网络技术与应用

    2016-10-27 16:26:00
    工作站(Workstation)一种高档的微型计算机,通常配有高分辨率的大屏幕显示器及容量很大的内存储器和外部存储器,并且具有较强的信息处理功能和高性能的图形、图像处理功能什么是服务器?服务器(Server)指具有...
  • Android 音乐APP 播放音乐前言正文① 修改布局 前言   作为音乐APP的主要功能,我放到了第三篇文章,因为播放音乐的功能并没有看上去那么简单...首先播放布局不随页面滚动,一直固定屏幕的底部,其次播放的进度
  • 屏幕快照-在Chrome中对整个页面进行屏幕快照有点困难,因为没有内部功能可以执行此操作。 有多种解决方法,并且Blipshot包含其中一种的实现。 screenshotter [.DOM] .js库(2个文件)设计为可重用,如果要制作全屏...
  • 干嘛用的没人有每人的需求,本人也正好由于公司正在做的业务须要,想做个照片展示的功能(当然得符合响应式了)。而这个照片展示必须符合下面几点功能:1。用三张图片占满屏幕的一行;2,长宽比固定(临时做成...
  • 字符集

    2020-07-12 21:34:38
    我们在计算机屏幕上看到的实体化的文字,而在计算机存储介质中存放的实际二进制的比特(byte)流。简单来说字符集就规定了某个文字对应的二进制数值存放方式(编码)和某串二进制数值代表了哪个文字(解码)的转换...
  • 2.定位则可以让盒子自由的在某个盒子内移动位置或者固定屏幕中某个位置,并且可以压住其他盒子 1.2定位组成 定位:将盒子定在某一位置,所以定位也在摆放盒子,按照定位的方式移动盒子。 定位=定位模式+边...
  • 最近我们APP 改版,首页风格大变,布局什么的都得改动,新的功能呢 ,就是用到了 RecyclerView ,因为首页 显示的数据就比较少,固定的五条数据 也就是五个 item, 本身首页 东西也比较多,需要滑动 两个屏幕...
  • Live Capture截图/截屏

    2011-10-01 19:40:44
    * 什么是LiveCapture? LiveCapture用户友好丰富特针的截屏和屏幕录像软件, 适合软件开发者、图形设计者以及普通用户 它不但有多种模式的截屏,还集多种功于一身,如:取色器,放大镜,调色板,屏幕标尺,坐标轴...
  • 项目中,网页页脚的js实现方式

    千次阅读 2018-04-14 09:45:02
    不出现纵滚动条时,固定在窗口的最最下面网上也有许多用CSS布局来实现页脚的,但是任然不够灵活,可能页面稍微变化下大小、比例、屏幕变化下分辨率什么的,这个CSS的布局可能就不起作用了,js才万能的~简单介绍下...
  • 最近终于实现了这个功能,可以带着我的小新pro13随地远程实验室的服务器了,爽。 为什么不用teamviewer ? 延迟大,极有可能被检测为商用然后收费,最要命的,如果服务器没有插显示屏,teamviewer不能显示的。 ...

空空如也

空空如也

1 2 3 4 5
收藏数 100
精华内容 40
关键字:

屏幕固定是什么功能