• Android 9 画中画实现流程


    Activity.java

    
        /**
         * Puts the activity in picture-in-picture mode if possible in the current system state. The
         * set parameters in {@param params} will be combined with the parameters from prior calls to
         * {@link #setPictureInPictureParams(PictureInPictureParams)}.
         *
         * The system may disallow entering picture-in-picture in various cases, including when the
         * activity is not visible, if the screen is locked or if the user has an activity pinned.
         *
         * @see android.R.attr#supportsPictureInPicture
         * @see PictureInPictureParams
         *
         * @param params non-null parameters to be combined with previously set parameters when entering
         * picture-in-picture.
         *
         * @return true if the system successfully put this activity into picture-in-picture mode or was
         * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device
         * does not support picture-in-picture, return false.
         */
        public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
            try {
                if (!deviceSupportsPictureInPictureMode()) {
                    return false;
                }
                if (params == null) {
                    throw new IllegalArgumentException("Expected non-null picture-in-picture params");
                }
                if (!mCanEnterPictureInPicture) {
                    throw new IllegalStateException("Activity must be resumed to enter"
                            + " picture-in-picture");
                }
                return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, params);//1
            } catch (RemoteException e) {
                return false;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    注释 1 getDefault 得到 IActivityManager 对象

        /**
         * Retrieve the system's default/global activity manager.
         *
         * @deprecated use ActivityManager.getService instead.
         */
        static public IActivityManager getDefault() {
            return ActivityManager.getService();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    顺着 IActivityManager.aidl 该方法传递给 ActivityManagerService
    boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
    ActivityManagerService.java

    @Override
    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized(this) {
                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
                        "enterPictureInPictureMode", token, params);
    
                // If the activity is already in picture in picture mode, then just return early
                if (isInPictureInPictureMode(r)) {
                    return true;
                }
    
                // Activity supports picture-in-picture, now check that we can enter PiP at this
                // point, if it is
                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
                        false /* beforeStopping */)) {
                    return false;
                }
    
                final Runnable enterPipRunnable = () -> { //1
                    // Only update the saved args from the args that are set
                    r.pictureInPictureArgs.copyOnlySet(params);
                    final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                    final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
                    // Adjust the source bounds by the insets for the transition down
                    final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
                    mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
                            "enterPictureInPictureMode"); //2
                    final PinnedActivityStack stack = r.getStack();
                    stack.setPictureInPictureAspectRatio(aspectRatio);
                    stack.setPictureInPictureActions(actions);
                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
                            r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
                    logPictureInPictureArgs(params);
                };
    
                if (isKeyguardLocked()) {
                    // If the keyguard is showing or occluded, then try and dismiss it before
                    // entering picture-in-picture (this will prompt the user to authenticate if the
                    // device is currently locked).
                    try {
                        dismissKeyguard(token, new KeyguardDismissCallback() {
                            @Override
                            public void onDismissSucceeded() throws RemoteException {
                                mHandler.post(enterPipRunnable);
                            }
                        }, null /* message */);
                    } catch (RemoteException e) {
                        // Local call
                    }
                } else {
                    // Enter picture in picture immediately otherwise
                    enterPipRunnable.run(); //3
                }
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    注释 1 创建了一个 Runnable ,该 Runnable 会在注释 3 被执行,其中核心代码是注释 2

     mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
                            "enterPictureInPictureMode"); //2
                   
    
    • 1
    • 2
    • 3

    ActivityStackSupervisor.java

    void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
            String reason) {
    
        mWindowManager.deferSurfaceLayout();
    
        final ActivityDisplay display = r.getStack().getDisplay();
        PinnedActivityStack stack = display.getPinnedStack();
    
        // This will clear the pinned stack by moving an existing task to the full screen stack,
        // ensuring only one task is present.
        if (stack != null) {
            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
        }
    
        // Need to make sure the pinned stack exist so we can resize it below...
        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); // 1
    
        // Calculate the target bounds here before the task is reparented back into pinned windowing
        // mode (which will reset the saved bounds)
        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
    
        try {
            final TaskRecord task = r.getTask();
            // Resize the pinned stack to match the current size of the task the activity we are
            // going to be moving is currently contained in. We do this to have the right starting
            // animation bounds for the pinned stack to the desired bounds the caller wants.
            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
    
            if (task.mActivities.size() == 1) {
                // Defer resume until below, and do not schedule PiP changes until we animate below
                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
                        false /* schedulePictureInPictureModeChange */, reason); // 2
            } else {
                // There are multiple activities in the task and moving the top activity should
                // reveal/leave the other activities in their original task.
    
                // Currently, we don't support reparenting activities across tasks in two different
                // stacks, so instead, just create a new task in the same stack, reparent the
                // activity into that task, and then reparent the whole task to the new stack. This
                // ensures that all the necessary work to migrate states in the old and new stacks
                // is also done.
                final TaskRecord newTask = task.getStack().createTaskRecord(
                        getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
    
                // Defer resume until below, and do not schedule PiP changes until we animate below
                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
            }
    
            // Reset the state that indicates it can enter PiP while pausing after we've moved it
            // to the pinned stack
            r.supportsEnterPipOnTaskSwitch = false;
        } finally {
            mWindowManager.continueSurfaceLayout();
        }
    
        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
                true /* fromFullscreen */); //3
    
        // Update the visibility of all activities after the they have been reparented to the new
        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
        // drawn signal is scheduled after the bounds animation start call on the bounds animator
        // thread.
        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); //5
        resumeFocusedStackTopActivityLocked();
    
        mService.mTaskChangeNotificationController.notifyActivityPinned(r);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    注释 1 创建画中画模式的 stack,注释 2 将 Activity 的 Task 放到新建的画中画 stack 中,注释 3 执行变化动画,注释 4 用来显示该 task

    进入画中画模式后,新的 activity 样式定义在 DecorView.java,当该 Window 进入画中画后
    PhoneWindow.java

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
        if (mDecor != null) {
            mDecor.updatePictureInPictureOutlineProvider(isInPictureInPictureMode);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    DecorView.java

    
    /**
     * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
     * an opaque shadow even if the window background is completely transparent. This only applies
     * to activities that are currently the task root.
     */
    public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
        if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
            return;
        }
    
        if (isInPictureInPictureMode) {
            final Window.WindowControllerCallback callback =
                    mWindow.getWindowControllerCallback();
            if (callback != null && callback.isTaskRoot()) {
                // Call super implementation directly as we don't want to save the PIP outline
                // provider to be restored
                super.setOutlineProvider(PIP_OUTLINE_PROVIDER); // 1
            }
        } else {
            // Restore the previous outline provider
            if (getOutlineProvider() != mLastOutlineProvider) {
                setOutlineProvider(mLastOutlineProvider);
            }
        }
        mIsInPictureInPictureMode = isInPictureInPictureMode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注释 1 给 DecorView 设置了一个外轮廓,外轮廓定义如下
    DecorView.java

    // This is used to workaround an issue where the PiP shadow can be transparent if the window
    // background is transparent
    private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
        @Override
        public void getOutline(View view, Outline outline) {
            outline.setRect(0, 0, view.getWidth(), view.getHeight());
            outline.setAlpha(1f);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    【排序算法】计数排序(C语言)
    codeforces:Codeforces Round #821 (Div. 2) 【思维 + 贪心分贡献的dp】
    C# Interlocked 类
    export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH的作用
    c++生成随机数(猜数字游戏)
    进程和线程和协程补充
    2024年数字化经济与智慧金融国际会议(ICDESF 2024)
    golang报错fatal error: all goroutines are asleep - deadlock
    Unity --- 场景/场景管理类 与 异步场景加载法
    【Java】运算符以及JShell脚本工具
  • 原文地址:https://blog.csdn.net/lzs781/article/details/127926781