当前位置: 首页 > article >正文

Android U 分屏——SystemUI侧处理

WMShell相关的dump命令

手机分屏启动应用后运行命令:adb shell dumpsys activity service SystemUIService WMShell
我们可以找到其中分屏的部分,如下图所示:
在这里插入图片描述

分屏的组成

简图

分屏是由上分屏(SideStage)、下分屏(MainStage)以及分割线组成。这里我们主要关注分屏的Stage部分,如下图所示:
在这里插入图片描述
我们这里上分屏是电话,下分屏是短信。
通过adb shell dumpsys activity containers命令可以看层级结构,这里我们看看上下分屏指的是什么在这里插入图片描述
这里Task=331其实就是分屏的RootTask,通过wct.reorder(mRootTaskInfo.token, true);(在system_server进程中)设置的,使其显示到最前面。其下面挂着Task=332(MainStage,下分屏)Task=333(SideStage,上分屏),这段代码也就是为了把这两个task下面挂上对应应用的task,即Task=333(SideStage,上分屏)下面挂着应用task=335(电话Task)Task=332(MainStage,下分屏)下面挂着应用task=334(短信Task)
总之,我们需要分清楚分屏的task和应用的task,不要弄混淆。

注:在android T(13) 中,上分屏为MainStage,下分屏为SideStage

stage的创建

在StageCoordinator构造方法中创建了MainStageSideStage
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
            ShellTaskOrganizer taskOrganizer, DisplayController displayController,
            DisplayImeController displayImeController,
            DisplayInsetsController displayInsetsController, Transitions transitions,
            TransactionPool transactionPool,
            IconProvider iconProvider, ShellExecutor mainExecutor,
            Optional<RecentTasksController> recentTasks) {
        ......
        mMainStage = new MainStage(
                mContext,
                mTaskOrganizer,
                mDisplayId,
                mMainStageListener,
                mSyncQueue,
                mSurfaceSession,
                iconProvider);
        mSideStage = new SideStage(
                mContext,
                mTaskOrganizer,
                mDisplayId,
                mSideStageListener,
                mSyncQueue,
                mSurfaceSession,
                iconProvider);
        ......
}

MainStage
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java

class MainStage extends StageTaskListener {
    ......
    MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
            SurfaceSession surfaceSession, IconProvider iconProvider) {
        super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
                iconProvider);
    }

SideStage
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java

class SideStage extends StageTaskListener {
    ......
    SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
            SurfaceSession surfaceSession, IconProvider iconProvider) {
        super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
                iconProvider);
    }

这里我们可以看到MainStageSideStage的构造方法都调用其父类构造方法,而他们的父类都是StageTaskListener,所以我们只需要关注StageTaskListener构造方法即可。
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java

    StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
            SurfaceSession surfaceSession, IconProvider iconProvider) {
        mContext = context;
        mCallbacks = callbacks;
        mSyncQueue = syncQueue;
        mSurfaceSession = surfaceSession;
        mIconProvider = iconProvider;
        taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
    }

这里我们可以看到最关键的创建方法就是createRootTask,传递了当前Display(displayId)、当前窗口模式(WINDOWING_MODE_MULTI_WINDOW)和当前Stage(this)。
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java

    /**
     * Creates a persistent root task in WM for a particular windowing-mode.
     * @param displayId The display to create the root task on.
     * @param windowingMode Windowing mode to put the root task in.
     * @param listener The listener to get the created task callback.
     */
    public void createRootTask(int displayId, int windowingMode, TaskListener listener) {
        createRootTask(displayId, windowingMode, listener, false /* removeWithTaskOrganizer */);
    }

    /**
     * Creates a persistent root task in WM for a particular windowing-mode.
     * @param displayId The display to create the root task on.
     * @param windowingMode Windowing mode to put the root task in.
     * @param listener The listener to get the created task callback.
     * @param removeWithTaskOrganizer True if this task should be removed when organizer destroyed.
     */
    public void createRootTask(int displayId, int windowingMode, TaskListener listener,
            boolean removeWithTaskOrganizer) {
        ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s" ,
                displayId, windowingMode, listener.toString());
        final IBinder cookie = new Binder();
        setPendingLaunchCookieListener(cookie, listener);
        super.createRootTask(displayId, windowingMode, cookie, removeWithTaskOrganizer);
    }

设置了removeWithTaskOrganizer参数为false,继续传递参数调用到其父类TaskOrganizercreateRootTask方法
代码路径:frameworks/base/core/java/android/window/TaskOrganizer.java

    /**
     * Creates a persistent root task in WM for a particular windowing-mode.
     * @param displayId The display to create the root task on.
     * @param windowingMode Windowing mode to put the root task in.
     * @param launchCookie Launch cookie to associate with the task so that is can be identified
     *                     when the {@link ITaskOrganizer#onTaskAppeared} callback is called.
     * @param removeWithTaskOrganizer True if this task should be removed when organizer destroyed.
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
    public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie,
            boolean removeWithTaskOrganizer) {
        try {
            mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie,
                    removeWithTaskOrganizer);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

这里mTaskOrganizerControllerITaskOrganizerController对象,通过跨进程到system_server侧创建Task,其实现方法在TaskOrganizerController中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskOrganizerController.java

    @Override
    public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie,
            boolean removeWithTaskOrganizer) {
        enforceTaskPermission("createRootTask()");
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                //获取当前DisplayContent
                DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
                if (display == null) {
                    ProtoLog.e(WM_DEBUG_WINDOW_ORGANIZER,
                            "createRootTask unknown displayId=%d", displayId);
                    return;
                }

                createRootTask(display, windowingMode, launchCookie, removeWithTaskOrganizer);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @VisibleForTesting
    Task createRootTask(DisplayContent display, int windowingMode, @Nullable IBinder launchCookie) {
        return createRootTask(display, windowingMode, launchCookie,
                false /* removeWithTaskOrganizer */);
    }

    Task createRootTask(DisplayContent display, int windowingMode, @Nullable IBinder launchCookie,
            boolean removeWithTaskOrganizer) {
        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
                display.mDisplayId, windowingMode);
        // We want to defer the task appear signal until the task is fully created and attached to
        // to the hierarchy so that the complete starting configuration is in the task info we send
        // over to the organizer.
        //创建Task
        final Task task = new Task.Builder(mService)
                .setWindowingMode(windowingMode)
                .setIntent(new Intent())
                .setCreatedByOrganizer(true)
                .setDeferTaskAppear(true)
                .setLaunchCookie(launchCookie)
                .setParent(display.getDefaultTaskDisplayArea())
                .setRemoveWithTaskOrganizer(removeWithTaskOrganizer)
                .build();
        task.setDeferTaskAppear(false /* deferTaskAppear */);
        return task;
    }

这个方法很简单,就是获取了当前DisplayContent,然后创建了Task。这里还设置setCreatedByOrganizer(true),表示是通过TaskOrganizer的方式创建的。

分屏流程中的关键方法

设置分屏task

以前面设置分屏task方法为例

        //设置分屏Options
        addActivityOptions(options1, mSideStage);
        //添加启动分屏task(system_server进程)
        wct.startTask(taskId1, options1);

mSideStageSideStage对象,MainStageSideStage,他们都是继承StageTaskListener
并且其后会调用WindowContainerTransactionstartTask方法启动分屏的task,这里是在system_server进程中进行的。

设置分屏Options

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

    private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
        if (launchTarget != null) {
            //设置sideStage的WindowContainerToken,也就是上分屏的task的token
            //设置mainStage的WindowContainerToken,也就是下分屏的task的token
            opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, launchTarget.mRootTaskInfo.token);
        }
        // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
        // will be canceled.
        //允许其使用pendingInetent方式启动
        opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
        opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
    }

launchTarget.mRootTaskInfo.tokenWindowContainerToken对象,这里就是设置上分屏的task的token(sideStage的WindowContainerToken)到传递进来的Bundle对象中。

后续在system_server侧会通过ActivityOptions构造方法设置sideStage的WindowContainerToken
代码路径:frameworks/base/core/java/android/app/ActivityOptions.java

    public ActivityOptions(Bundle opts) {
        super(opts);
        ......
        mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, android.window.WindowContainerToken.class);
        ......
    }

在ActivityOptions构造方法中,会取出之前存放的WindowContainerToken对象赋值给mLaunchRootTask。
即把sideStage的WindowContainerToken设置为mLaunchRootTask,mainStage也是同理。

添加启动分屏应用task(system_server进程)

代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java

    /**
     * Starts a task by id. The task is expected to already exist (eg. as a recent task).
     * @param taskId Id of task to start.
     * @param options bundle containing ActivityOptions for the task's top activity.
     * @hide
     */
    @NonNull
    public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
        mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
        return this;
    }

通过应用taskId来启动应用task,此时只是将该应用task以及之前设置的options设置到层级结构树中,尚未提交事务,真正的添加在后续通过SplitTransitions.startEnterTransition(涉及RemoteTransition的情况,我们这里从多任务启动分屏就是这种情况)或者SyncTransactionQueue(涉及RemoteAnimationAdapter的情况)提交事务到系统侧才会真正的启动。

设置分屏位置

setSideStagePosition(splitPosition, wct);
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

    void setSideStagePosition(@SplitPosition int sideStagePosition,
            @Nullable WindowContainerTransaction wct) {
        //传递参数updateBounds为true
        setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
    }

    private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds,
            @Nullable WindowContainerTransaction wct) {
        //mSideStagePosition默认为SPLIT_POSITION_BOTTOM_OR_RIGHT,下分屏位置
        //mSideStagePosition与传递过来的sideStagePosition相同,则不修改
        if (mSideStagePosition == sideStagePosition) return;
        //不同时,将mSideStagePosition赋值为传递过来的sideStagePosition
        mSideStagePosition = sideStagePosition;
        sendOnStagePositionChanged();
        //mSideStageListener.mVisible判断分屏的可见性
        //updateBounds传递了true
        if (mSideStageListener.mVisible && updateBounds) {
            if (wct == null) {
                // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
                //如果WindowContainerTransaction为空(这里一般不为空)
                //这个方法会创建一个WindowContainerTransaction对象
                //然后再调用updateWindowBounds方法
                onLayoutSizeChanged(mSplitLayout);
            } else {
                //更新窗口bounds,后续会讲
                updateWindowBounds(mSplitLayout, wct);
                //没有实际意义
                sendOnBoundsChanged();
            }
        }
    }

mSideStagePosition

这个方法主要就是SideStage的分屏位置进行设置

private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;

其中mSideStagePosition从代码中可以发现默认值为SPLIT_POSITION_BOTTOM_OR_RIGHT,即值为1

之前桌面流程中传递过来的值是0,即sideStagePosition值为0,在上分屏显示。

mSideStageListener.mVisible分屏可见性

  1. 多任务中进入分屏时,会在shell动画流程中最后调用StageCoordinator.finishEnterSplitScreen去调用StageCoordinator.setSplitsVisible设置分屏可见性为true。
  2. HOME键退出分屏时,会在远程动画流程中(涉及APP的切换)最后调用通过StageCoordinator.onRecentsInSplitAnimationFinish去调用StageCoordinator.setSplitsVisible设置分屏可见性为false。
  3. 返回键退出分屏时,会在shell动画流程中最后调用StageCoordinator.prepareDismissAnimation去调用StageCoordinator.setSplitsVisible设置分屏可见性为false。

不管是哪种方式,最终都会通过StageCoordinator.setSplitsVisible设置分屏可见性。

onLayoutSizeChanged(mSplitLayout);

在多任务启动分屏流程中,WindowContainerTransaction对象不会为空,因此不会走到该流程。
这个方法主要是创建一个WindowContainerTransaction对象,并调用updateWindowBounds方法更新bounds,在没有更新bounds的情况下清除一些状态。
具体见分屏分割线相关 (留坑,尚未更新)

updateWindowBounds(mSplitLayout, wct);

见后文【更新分屏task的bound】

sendOnBoundsChanged();

这个方法本地验证注释过,没有发现什么影响。这里把这段代码上库时的注释放出来,仅供参考。

Adds real unfold animation for split-screen tasks when
doing the Shell unfold transition.
The approach is similar to full-screen tasks:
we animate the surfaces using shell transition
only when unfolding, when folding we are doing
it in the old way (by directly accessing
the surfaces from TaskOrganizer).

Refactored the previous fullscreen/splitscreen unfold
controllers flow to have one controller where we can
register diferrent 'animators'. This controller listens
to all task events in the shell task organizer.

简单分析下这个方法:

    private void sendOnBoundsChanged() {
        if (mSplitLayout == null) return;
        for (int i = mListeners.size() - 1; i >= 0; --i) {
            mListeners.get(i).onSplitBoundsChanged(mSplitLayout.getRootBounds(),
                    getMainStageBounds(), getSideStageBounds());
        }
    }

从这个方法中我们可以看到,主要就是调用了onSplitBoundsChanged

	@ExternalThread
	public interface SplitScreen {
	    ......
	    interface SplitScreenListener {
	        default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {}
	        default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {}
	        default void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {}
	        default void onSplitVisibilityChanged(boolean visible) {}
	    }
	    ......
	}

onSplitBoundsChanged是SplitScreen.SplitScreenListener的接口方法,需要找到其实现在哪。
因此先来看看前面的mListeners里面存放是什么

	private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();

	void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
        if (mListeners.contains(listener)) return;
        mListeners.add(listener);
        sendStatusToListener(listener);
    }

存放的是注册的SplitScreen.SplitScreenListener对象,这里添加的listener指的就是ISplitScreenImpl中创建的对象,我们找到其中创建的对象。

    private static class ISplitScreenImpl extends ISplitScreen.Stub
            implements ExternalInterfaceBinder {
        private SplitScreenController mController;
        private final SingleInstanceRemoteListener<SplitScreenController,
                ISplitScreenListener> mListener;
        private final SplitScreen.SplitScreenListener mSplitScreenListener =
                new SplitScreen.SplitScreenListener() {
                    @Override
                    public void onStagePositionChanged(int stage, int position) {
                        mListener.call(l -> l.onStagePositionChanged(stage, position));
                    }

                    @Override
                    public void onTaskStageChanged(int taskId, int stage, boolean visible) {
                        mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
                    }
                };

        public ISplitScreenImpl(SplitScreenController controller) {
            mController = controller;
            mListener = new SingleInstanceRemoteListener<>(controller,
                    c -> c.registerSplitScreenListener(mSplitScreenListener),
                    c -> c.unregisterSplitScreenListener(mSplitScreenListener));
        }

        ......

        @Override
        public void registerSplitScreenListener(ISplitScreenListener listener) {
            executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
                    (controller) -> mListener.register(listener));
        }
        ......
}

这里(controller) -> mListener.register(listener));实际上就是调用的c -> c.registerSplitScreenListener(mSplitScreenListener)
也就是说在registerSplitScreenListener的实现中mListeners.add(listener);,其中的listener指的就是这里的mSplitScreenListener
但是我们可以看到mSplitScreenListener中并没有实现接口中的onSplitBoundsChanged方法,因此什么都没有做。
且本地验证SplitScreen接口中其他的实现也没有在设置分屏位置场景调用,感兴趣的可以研究补充。
具体流程不在赘述,附registerSplitScreenListener调用堆栈流程

registerSplitScreenListener: listener:com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl$1@ef57f97
registerSplitScreenListener: java.lang.Exception
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.StageCoordinator.registerSplitScreenListener(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:1662)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController.registerSplitScreenListener(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:451)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.lambda$new$0(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:1074)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.$r8$lambda$LTc1wMcZo9Of3RPyGCWtg6YiS5s(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl$$ExternalSyntheticLambda15.accept(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at com.android.wm.shell.common.SingleInstanceRemoteListener.register(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:97)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.lambda$registerSplitScreenListener$2(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:1091)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.$r8$lambda$3asGbaEmeTX8SI0BiI_eYjbgpQA(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl$$ExternalSyntheticLambda11.accept(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at com.android.wm.shell.common.ExecutorUtils.lambda$executeRemoteCallWithTaskPermission$1(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:60)
registerSplitScreenListener: 	at com.android.wm.shell.common.ExecutorUtils.$r8$lambda$s8eUOdyrqpqzzyFwAMGxO-MaCg4(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at com.android.wm.shell.common.ExecutorUtils$$ExternalSyntheticLambda1.run(go/retraceme e9084556ee5ba06aeb29383d2803776323e77195400e5ee36c8cd4861c083ef6:0)
registerSplitScreenListener: 	at android.os.Handler.handleCallback(Handler.java:958)
registerSplitScreenListener: 	at android.os.Handler.dispatchMessage(Handler.java:99)
registerSplitScreenListener: 	at android.os.Looper.loopOnce(Looper.java:205)
registerSplitScreenListener: 	at android.os.Looper.loop(Looper.java:294)
registerSplitScreenListener: 	at android.os.HandlerThread.run(HandlerThread.java:67)

设置分屏比例

mSplitLayout.setDivideRatio(splitRatio);

    /** Updates divide position and split bounds base on the ratio within root bounds. */
    public void setDivideRatio(float ratio) {
        final int position = isLandscape()
                ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
                : mRootBounds.top + (int) (mRootBounds.height() * ratio);
        final DividerSnapAlgorithm.SnapTarget snapTarget =
                mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
        setDividePosition(snapTarget.position, false /* applyLayoutChange */);
    }

这里会先根据ratio计算出一个位置position,但是这个position并不是直接的SnapTarget的position,需要把这个position传递到calculateNonDismissingSnapTarget方法计算出SnapTarget,然后在使用SnapTarget的position。
具体见分屏分割线相关 (留坑,尚未更新)

更新分屏task的bounds

updateWindowBounds(mSplitLayout, wct);

传递上下分屏task信息

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

    /**
     * Populates `wct` with operations that match the split windows to the current layout.
     * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
     *
     * @return true if stage bounds actually .
     */
    private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
        final StageTaskListener topLeftStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
        final StageTaskListener bottomRightStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
        return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
                bottomRightStage.mRootTaskInfo);
    }

这个方法传递了上下屏task信息后,要对这些task的bound进行修改。
注意:这里传递的是SideStage和MainStage这个两个上下分屏容器task信息,而非这个两个Stage下面的挂着的应用的task信息。
layout.applyTaskChanges方法传递了WindowContainerTransaction对象和上下分屏的task信息。

设置bounds

代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java

    public boolean applyTaskChanges(WindowContainerTransaction wct,
            ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
        boolean boundsChanged = false;
        if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) {
            //设置bounds
            wct.setBounds(task1.token, mBounds1);
            wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1));
            //记录新的bounds
            mWinBounds1.set(mBounds1);
            mWinToken1 = task1.token;
            boundsChanged = true;
        }
        if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) {
            //设置bounds
            wct.setBounds(task2.token, mBounds2);
            wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2));
            //记录新的bounds
            mWinBounds2.set(mBounds2);
            mWinToken2 = task2.token;
            boundsChanged = true;
        }
        return boundsChanged;
    }

这个方法主要就是设置新bounds,并把新的bounds记录到Rect对象中。
这里通过WindowContainerTransaction对象对bounds进行设置。

设置bounds的实现(system_server侧)

    /**
     * Resize a container.
     */
    @NonNull
    public WindowContainerTransaction setBounds(
            @NonNull WindowContainerToken container,@NonNull Rect bounds) {
        Change chg = getOrCreateChange(container.asBinder());
        chg.mConfiguration.windowConfiguration.setBounds(bounds);
        chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
        chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
        return this;
    }

    /**
     * Set the smallestScreenWidth of a container.
     */
    @NonNull
    public WindowContainerTransaction setSmallestScreenWidthDp(
            @NonNull WindowContainerToken container, int widthDp) {
        Change cfg = getOrCreateChange(container.asBinder());
        cfg.mConfiguration.smallestScreenWidthDp = widthDp;
        cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
        return this;
    }

这里就是把设置的bounds保存到Change对象中,后续提交WindowContainerTransaction后,在system_server侧便会进行真正的处理。


http://www.kler.cn/a/571286.html

相关文章:

  • 蓝桥杯---归并排序2(leetcode170)题解
  • 石基大商:OceanBase + Flink CDC,搭建连锁零售系统数据湖
  • CentOS7 安装Redis 6.2.6 详细教程
  • 队列的顺序结构—循环队列的判断条件(rear + 1) % MAXSIZE分析
  • flowable的使用
  • 使用Windbg分析dump文件定位软件异常的方法与操作步骤
  • 【Python 数据结构 5.栈】
  • day51 shell
  • 交叉编译 ARM 架构浏览器(补充)
  • 某金融租赁公司数据治理实践
  • R JSON 文件
  • Geotools中获取Shapefile的属性表格字符集编码的一种方法
  • VS2015 c++和cmake配置编程
  • 蓝桥杯复盘记录004(2023)
  • 19.8、C++11新特性有哪些⑧【基于范围的for循环】
  • 深入探索像ChatGPT这样的大语言模型-03-POST-Training:Reinforcement Learning
  • Lua脚本使用教学指南:与Spring Boot项目集成示例
  • ClickHouse深度解析:OLAP领域的性能怪兽
  • 【星云 Orbit • STM32F4】10. 在串口接收中断里即时解析数据头的程序框架
  • 测试是如何跟进和管理 bug