Android SplitScreen

Android P

Posted by LXG on September 15, 2020

代码

从Recent启动分屏模式

recent_split_screen

Launcher

packages/apps/Launcher3/quickstep/src/com/android/quickstep/TaskSystemShortcut.java


public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {

    public static class SplitScreen extends TaskSystemShortcut {


        @Override
        public View.OnClickListener getOnClickListener(
                BaseDraggingActivity activity, TaskView taskView) {

            -------------------------------------------------------------------------------------------

                // 启动分屏
                if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
                        ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft))) {
                    ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
                    try {
                        sysUiProxy.onSplitScreenInvoked();
                    } catch (RemoteException e) {
                        Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
                        return;
                    }

                    -------------------------------------------------------------------

                }

        }
    }

}

public abstract class ActivityOptionsCompat {

    /**
     * @return ActivityOptions for starting a task in split screen.
     */
    public static ActivityOptions makeSplitScreenOptions(boolean dockTopLeft) {
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
        options.setSplitScreenCreateMode(dockTopLeft
                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
        return options;
    }

}

SystemServer


public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {


    @Override
    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
                "startActivityFromRecents()");

        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (this) {
                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
                        safeOptions);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

}


public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
        RecentTasks.Callbacks {

    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
            SafeActivityOptions options) {
        TaskRecord task = null;
        final String callingPackage;
        final Intent intent;
        final int userId;
        int activityType = ACTIVITY_TYPE_UNDEFINED;
        int windowingMode = WINDOWING_MODE_UNDEFINED;       // WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
        final ActivityOptions activityOptions = options != null
                ? options.getOptions(this)
                : null;
        if (activityOptions != null) {
            activityType = activityOptions.getLaunchActivityType();
            windowingMode = activityOptions.getLaunchWindowingMode();
        }
        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
            throw new IllegalArgumentException("startActivityFromRecents: Task "
                    + taskId + " can't be launch in the home/recents stack.");
        }

        mWindowManager.deferSurfaceLayout();
        try {
            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                mWindowManager.setDockedStackCreateState(
                        activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);

                // Defer updating the stack in which recents is until the app transition is done, to
                // not run into issues where we still need to draw the task in recents but the
                // docked stack is already created.
                deferUpdateRecentsHomeStackBounds();
                mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
            }

            task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
                    activityOptions, ON_TOP);

            -----------------------------------------------------------------------------------

            callingPackage = task.mCallingPackage;
            intent = task.intent;
            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
            userId = task.userId;
            return mService.getActivityStartController().startActivityInPackage(
                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
                    null, 0, 0, options, userId, task, "startActivityFromRecents",
                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
        } finally {
            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
                // If we are launching the task in the docked stack, put it into resizing mode so
                // the window renders full-screen with the background filling the void. Also only
                // call this at the end to make sure that tasks exists on the window manager side.
                setResizingDuringAnimation(task);

                final ActivityDisplay display = task.getStack().getDisplay();
                final ActivityStack topSecondaryStack =
                        display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);

                // 如果第二窗口显示的为Home则将Home移动到主焦点界面
                if (topSecondaryStack.isActivityTypeHome()) {
                    // If the home activity if the top split-screen secondary stack, then the
                    // primary split-screen stack is in the minimized mode which means it can't
                    // receive input keys, so we should move the focused app to the home app so that
                    // window manager can correctly calculate the focus window that can receive
                    // input keys.
                    moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");

                    // Immediately update the minimized docked stack mode, the upcoming animation
                    // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
                    // will do the animation to the target bounds
                    mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
                }
            }
            mWindowManager.continueSurfaceLayout();
        }
    }

}


public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    public void checkSplitScreenMinimizedChanged(boolean animate) {
        synchronized (mWindowMap) {
            final DisplayContent displayContent = getDefaultDisplayContentLocked();
            displayContent.getDockedDividerController().checkMinimizeChanged(animate);
        }
    }

}

public class DockedStackDividerController {

    DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
        mService = service;
        mDisplayContent = displayContent;
        final Context context = service.mContext;
        mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                context, android.R.interpolator.fast_out_slow_in);
        loadDimens();
    }

    private void loadDimens() {
        final Context context = mService.mContext;
        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_thickness);
        mDividerInsets = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_insets);
        mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
                DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());

        // 启动分屏后的最小化宽度
        mTaskHeightInMinimizedMode = getTaskHeightInMinimizedMode();
        initSnapAlgorithmForRotations();
    }

    void checkMinimizeChanged(boolean animate) {
        
        ------------------------------------------------------------

        setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate);

    }

}

SystemUI


public class Divider extends SystemUI {

    @Override
    public void start() {
        mWindowManager = new DividerWindowManager(mContext);
        update(mContext.getResources().getConfiguration());
        putComponent(Divider.class, this);
        mDockDividerVisibilityListener = new DockDividerVisibilityListener();
        SystemServicesProxy ssp = Recents.getSystemServices();
        ssp.registerDockedStackListener(mDockDividerVisibilityListener);
        mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
        EventBus.getDefault().register(this);
    }

    // 分割线
    private void addDivider(Configuration configuration) {
        mView = (DividerView)
                LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
        mView.injectDependencies(mWindowManager, mDividerState);
        mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
        mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
        final int size = mContext.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_thickness);
        final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
        final int width = landscape ? size : MATCH_PARENT;
        final int height = landscape ? MATCH_PARENT : size;
        mWindowManager.add(mView, width, height);
    }

}

setMinimizedDockedStack Trace


// SystemServer trace

system_process D/LXG: updateMinimizedDockedStack: java.lang.Throwable
        at com.android.server.wm.DockedStackDividerController.notifyDockedStackMinimizedChanged(DockedStackDividerController.java:554)
        at com.android.server.wm.DockedStackDividerController.setMinimizedDockedStack(DockedStackDividerController.java:763)
        at com.android.server.wm.DockedStackDividerController.checkMinimizeChanged(DockedStackDividerController.java:737)
        at com.android.server.wm.WindowManagerService.checkSplitScreenMinimizedChanged(WindowManagerService.java:2823)
        at com.android.server.am.ActivityStackSupervisor.startActivityFromRecents(ActivityStackSupervisor.java:4952)
        at com.android.server.am.ActivityManagerService.startActivityFromRecents(ActivityManagerService.java:5652)
        at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2357)
        at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3320)
        at android.os.Binder.execTransact(Binder.java:731)

// SystemUI trace

com.android.systemui D/LXG: updateMinimizedDockedStack: java.lang.Throwable
        at com.android.systemui.stackdivider.Divider.updateMinimizedDockedStack(Divider.java:130)
        at com.android.systemui.stackdivider.Divider.access$800(Divider.java:41)
        at com.android.systemui.stackdivider.Divider$DockDividerVisibilityListener.onDockedStackMinimizedChanged(Divider.java:198)
        at android.view.IDockedStackListener$Stub.onTransact(IDockedStackListener.java:77)
        at android.os.Binder.execTransact(Binder.java:731)

com.android.systemui D/LXG: resizeStack: java.lang.Throwable
        at com.android.systemui.stackdivider.DividerView.resizeStack(DividerView.java:1004)
        at com.android.systemui.stackdivider.DividerView.resizeStack(DividerView.java:999)
        at com.android.systemui.stackdivider.DividerView.setMinimizedDockStack(DividerView.java:798)
        at com.android.systemui.stackdivider.Divider$2.run(Divider.java:139)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6746)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)