前言
从 Android 8.0(API 级别 26)开始,Android 允许以画中画 (PiP) 模式启动 activity。画中画是一种特殊类型的多窗口模式,最常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。
一. 画中画创建
Activity调用系统api:ActivityTaskManagerService#enterPictureInPictureMode() 开始:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
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
| boolean enterPictureInPictureMode(@NonNull ActivityRecord r, PictureInPictureParams params) { if (r.inPinnedWindowingMode()) { return true; } if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", false )) { return false; } final Runnable enterPipRunnable = () -> { synchronized (mGlobalLock) { if (r.getParent() == null) { Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); return; } r.setPictureInPictureParams(params); mRootWindowContainer.moveActivityToPinnedRootTask(r, null , "enterPictureInPictureMode"); final Task task = r.getTask(); if (task.getPausingActivity() == r) { task.schedulePauseActivity(r, false , false , "auto-pip"); } } }; if (r.isKeyguardLocked()) { mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() { @Override public void onDismissSucceeded() { mH.post(enterPipRunnable); } }, null ); } else { enterPipRunnable.run(); } return true; }
|
如果activity现在处于画中画模式,则为 return true ;如果无法进入画中画模式,则为 return false 。
App 端 binder 回调到 ATMS 的方法,如果有 keyguard ,先 dismiss keyguard 再进入pip。
接着根据上述代码看 RootWindowContainer#moveActivityToPinnedRootTask():
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason) { mService.deferWindowLayout(); final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); try { final Task task = r.getTask(); final TransitionController transitionController = task.mTransitionController; Transition newTransition = null; if (transitionController.isCollecting()) { transitionController.setReady(task, false ); } else if (transitionController.getTransitionPlayer() != null) { newTransition = transitionController.createTransition(TRANSIT_PIP); } final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); if (rootPinnedTask != null) { transitionController.collect(rootPinnedTask); removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); } r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); final TaskFragment organizedTf = r.getOrganizedTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; final Task rootTask; if (singleActivity) { rootTask = task; rootTask.maybeApplyLastRecentsAnimationTransaction(); } else { rootTask = new Task.Builder(mService) .setActivityType(r.getActivityType()) .setOnTop(true) .setActivityInfo(r.info) .setParent(taskDisplayArea) .setIntent(r.intent) .setDeferTaskAppear(true) .setHasBeenVisible(true) .build(); r.setLastParentBeforePip(launchIntoPipHostActivity); rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds); rootTask.setBounds(task.getBounds()); if (task.mLastRecentsAnimationTransaction != null) { rootTask.setLastRecentsAnimationTransaction( task.mLastRecentsAnimationTransaction, task.mLastRecentsAnimationOverlay); task.clearLastRecentsAnimationTransaction(false ); } if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1 && organizedTf.getTopNonFinishingActivity() == r) { organizedTf.mClearedTaskFragmentForPip = true; } r.reparent(rootTask, MAX_VALUE, reason); rootTask.maybeApplyLastRecentsAnimationTransaction(); final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.containsTransitRequest( TRANSIT_TO_BACK)) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } } final int intermediateWindowingMode = rootTask.getWindowingMode(); if (rootTask.getParent() != taskDisplayArea) { rootTask.reparent(taskDisplayArea, true ); } if (newTransition != null) { transitionController.requestStartTransition(newTransition, rootTask, null , null ); } transitionController.collect(rootTask); r.setWindowingMode(intermediateWindowingMode); r.mWaitForEnteringPinnedMode = true; rootTask.forAllTaskFragments(tf -> { if (!tf.isOrganizedTaskFragment()) { return; } tf.resetAdjacentTaskFragment(); if (tf.getTopNonFinishingActivity() != null) { tf.updateRequestedOverrideConfiguration(EMPTY); } }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { mWindowManager.mTaskSnapshotController.recordTaskSnapshot( task, false ); rootTask.setBounds(r.getOptions().getLaunchBounds()); } rootTask.setDeferTaskAppear(false); r.supportsEnterPipOnTaskSwitch = false; if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip && organizedTf.isTaskVisibleRequested()) { mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent( organizedTf); } } finally { mService.continueWindowLayout(); } ensureActivitiesVisible(null, 0, false ); resumeFocusedTasksTopActivities(); notifyActivityPipModeChanged(r.getTask(), r); }
|
二. 退出PIP模式
当 PIP 状态改变,将会在 ActivityTaskManagerService#onPictureInPictureStateChanged() 收到回调:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
1 2 3 4 5 6 7 8 9 10 11
| @Override public void onPictureInPictureStateChanged(PictureInPictureUiState pipState) { enforceTaskPermission("onPictureInPictureStateChanged"); final Task rootPinnedTask = mRootWindowContainer.getDefaultTaskDisplayArea() .getRootPinnedTask(); if (rootPinnedTask != null && rootPinnedTask.getTopMostActivity() != null) { mWindowManager.mAtmService.mActivityClientController.onPictureInPictureStateChanged( rootPinnedTask.getTopMostActivity(), pipState); } }
|
ActivityClientController#onPictureInPictureStateChanged() :
frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void onPictureInPictureStateChanged(@NonNull ActivityRecord r, PictureInPictureUiState pipState) { if (!r.inPinnedWindowingMode()) { throw new IllegalStateException("Activity is not in PIP mode"); } try { final ClientTransaction transaction = ClientTransaction.obtain( r.app.getThread(), r.token); transaction.addCallback(PipStateTransactionItem.obtain(pipState)); mService.getLifecycleManager().scheduleTransaction(transaction); } catch (Exception e) { Slog.w(TAG, "Failed to send pip state transaction item: " + r.intent.getComponent(), e); } }
|
本文链接:
http://longzhiye.top/2024/02/14/2024-02-14/