Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e481b384 authored by Jerry Chang's avatar Jerry Chang Committed by Android (Google) Code Review
Browse files

Merge "Support launching a shortcut and a task to split screen" into tm-qpr-dev

parents 8d8e064d 24158716
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.RemoteAnimationAdapter;
@@ -89,7 +90,7 @@ interface ISplitScreen {
            float splitRatio, in RemoteAnimationAdapter adapter) = 11;

    /**
     * Start a pair of intent and task using legacy transition system.
     * Starts a pair of intent and task using legacy transition system.
     */
    oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
            in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
@@ -108,4 +109,11 @@ interface ISplitScreen {
     * does not expect split to currently be running.
     */
    RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14;

    /**
     * Starts a pair of shortcut and task using legacy transition system.
     */
    oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
            in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
            in RemoteAnimationAdapter adapter) = 15;
}
+12 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
@@ -787,6 +788,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
                                    sidePosition, splitRatio, adapter));
        }

        @Override
        public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
                int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
                @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
            executeRemoteCallWithTaskPermission(mController,
                    "startShortcutAndTaskWithLegacyTransition", (controller) ->
                            controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
                                    shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
                                    splitRatio, adapter));
        }

        @Override
        public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
                int sideTaskId, @Nullable Bundle sideOptions,
+84 −83
Original line number Diff line number Diff line
@@ -69,11 +69,11 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
@@ -85,7 +85,6 @@ import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -531,133 +530,135 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
            int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
            float splitRatio, RemoteAnimationAdapter adapter) {
        startWithLegacyTransition(mainTaskId, sideTaskId, null /* pendingIntent */,
                null /* fillInIntent */, mainOptions, sideOptions, sidePosition, splitRatio,
                adapter);
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        if (sideOptions == null) sideOptions = new Bundle();
        addActivityOptions(sideOptions, mSideStage);
        wct.startTask(sideTaskId, sideOptions);

        startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
    }

    /** Start an intent and a task ordered by {@code intentFirst}. */
    void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
            int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
        startWithLegacyTransition(taskId, INVALID_TASK_ID, pendingIntent, fillInIntent,
                mainOptions, sideOptions, sidePosition, splitRatio, adapter);
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        if (sideOptions == null) sideOptions = new Bundle();
        addActivityOptions(sideOptions, mSideStage);
        wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);

        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
    }

    private void startWithLegacyTransition(int mainTaskId, int sideTaskId,
            @Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
            @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
    void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
            int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
        final boolean withIntent = pendingIntent != null && fillInIntent != null;
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        if (sideOptions == null) sideOptions = new Bundle();
        addActivityOptions(sideOptions, mSideStage);
        wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);

        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
    }

    private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
            @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
            RemoteAnimationAdapter adapter) {
        // Init divider first to make divider leash for remote animation target.
        mSplitLayout.init();
        mSplitLayout.setDivideRatio(splitRatio);

        // Set false to avoid record new bounds with old task still on top;
        mShouldUpdateRecents = false;
        mIsDividerRemoteAnimating = true;
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
        prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
        prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
        // Need to add another wrapper here in shell so that we can inject the divider bar
        // and also manage the process elevation via setRunningRemote
        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {

        LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
            @Override
            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
                    RemoteAnimationTarget[] apps,
                    RemoteAnimationTarget[] wallpapers,
                    RemoteAnimationTarget[] nonApps,
                    final IRemoteAnimationFinishedCallback finishedCallback) {
                RemoteAnimationTarget[] augmentedNonApps =
                        new RemoteAnimationTarget[nonApps.length + 1];
                for (int i = 0; i < nonApps.length; ++i) {
                    augmentedNonApps[i] = nonApps[i];
                }
                augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                    IRemoteAnimationFinishedCallback finishedCallback,
                    SurfaceControl.Transaction t) {
                if (apps == null || apps.length == 0) {
                    onRemoteAnimationFinished(apps);
                    t.apply();
                    try {
                        adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
                    } catch (RemoteException e) {
                        Slog.e(TAG, "Error starting remote animation", e);
                    }
                    return;
                }

                // The surfaces of splitting tasks were placed with window bounds when preparing the
                // transition, so update divider surface separately.
                final RemoteAnimationTarget dividerTarget = getDividerBarLegacyTarget();
                mSplitLayout.getRefDividerBounds(mTempRect1);
                t.setLayer(dividerTarget.leash, Integer.MAX_VALUE)
                        .setPosition(dividerTarget.leash, mTempRect1.left, mTempRect1.top);
                setDividerVisibility(true, t);

                for (int i = 0; i < apps.length; ++i) {
                    if (apps[i].mode == MODE_OPENING) {
                        t.show(apps[i].leash);
                    }
                }
                t.apply();

                IRemoteAnimationFinishedCallback wrapCallback =
                        new IRemoteAnimationFinishedCallback.Stub() {
                            @Override
                            public void onAnimationFinished() throws RemoteException {
                                onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
                                onRemoteAnimationFinished(apps);
                                finishedCallback.onAnimationFinished();
                            }
                        };
                Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
                try {
                    adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
                            augmentedNonApps, wrapCallback);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error starting remote animation", e);
                }
            }

            @Override
            public void onAnimationCancelled(boolean isKeyguardOccluded) {
                onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
                try {
                    adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
                            ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
                                    dividerTarget), wrapCallback);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Error starting remote animation", e);
                }
            }
        };
        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
                wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());

        if (mainOptions == null) {
            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
        } else {
            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
            mainOptions = mainActivityOptions.toBundle();
        }

        sideOptions = sideOptions != null ? sideOptions : new Bundle();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        setSideStagePosition(sidePosition, wct);

        mSplitLayout.setDivideRatio(splitRatio);
        if (!mMainStage.isActive()) {
            // Build a request WCT that will launch both apps such that task 0 is on the main stage
            // while task 1 is on the side stage.
            mMainStage.activate(wct, false /* reparent */);
        }
        updateWindowBounds(mSplitLayout, wct);
        wct.reorder(mRootTaskInfo.token, true);
        wct.setForceTranslucent(mRootTaskInfo.token, false);

        // Make sure the launch options will put tasks in the corresponding split roots
        if (mainOptions == null) mainOptions = new Bundle();
        addActivityOptions(mainOptions, mMainStage);
        addActivityOptions(sideOptions, mSideStage);

        // Add task launch requests
        wct.startTask(mainTaskId, mainOptions);
        if (withIntent) {
            wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
        } else {
            wct.startTask(sideTaskId, sideOptions);
        }
        wct.merge(sideWct, true);

        mSyncQueue.queue(wct);
        mSyncQueue.runInSync(t -> {
            setDividerVisibility(true, t);
            updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
        });
        updateWindowBounds(mSplitLayout, wct);
        wct.reorder(mRootTaskInfo.token, true);
        wct.setForceTranslucent(mRootTaskInfo.token, false);

        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
    }

    private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
            WindowContainerTransaction evictWct) {
    private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
        mIsDividerRemoteAnimating = false;
        mShouldUpdateRecents = true;
        // If any stage has no child after animation finished, it means that split will display
        // nothing, such status will happen if task and intent is same app but not support
        // multi-instance, we should exit split and expand that app as full screen.
        if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
            mMainExecutor.execute(() ->
                    exitSplitScreen(mMainStage.getChildCount() == 0
        if (apps == null || apps.length == 0) return;

        // If any stage has no child after finished animation, that side of the split will display
        // nothing. This might happen if starting the same app on the both sides while not
        // supporting multi-instance. Exit the split screen and expand that app to full screen.
        if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
            mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
                    ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
        } else {
            mSyncQueue.queue(evictWct);
            return;
        }

        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
        mSyncQueue.queue(evictWct);
    }

    /**