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

Commit 51beb146 authored by Jerry Chang's avatar Jerry Chang
Browse files

Migrate recents.splitPrimaryTask to WM Shell library

There were some legacy ways to trigger split screen through
Recents.splitPrimaryTask. Decouples the API from Recents and migrates it
to WM Shell library.

Removes SplitScreen.onDockedTopTask and SplitScreen.onRecentsDrawn
because we have SplitScreenTaskOrg to instantiate new deivider bar and
resize primary/secondary split after entering split screen mode.

Fix: 169056724
Test: atest SystemUITests
Test: atest WMShellUnitTests
Test: manual trigger splitPrimaryTask api and check
Change-Id: I68979e2e32c7ff36d35ae451c81dcf09e2dc5ef6
parent b0424a35
Loading
Loading
Loading
Loading
+0 −28
Original line number Diff line number Diff line
@@ -1319,34 +1319,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                mBackground.getRight(), mBackground.getBottom(), Op.UNION);
    }

    void onDockedTopTask() {
        mState.animateAfterRecentsDrawn = true;
        startDragging(false /* animate */, false /* touching */);
        updateDockSide();
        mEntranceAnimationRunning = true;

        resizeStackSurfaces(calculatePositionForInsetBounds(),
                mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
                mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
                null /* transaction */);
    }

    void onRecentsDrawn() {
        updateDockSide();
        final int position = calculatePositionForInsetBounds();
        if (mState.animateAfterRecentsDrawn) {
            mState.animateAfterRecentsDrawn = false;

            mHandler.post(() -> {
                // Delay switching resizing mode because this might cause jank in recents animation
                // that's longer than this animation.
                stopDragging(position, getSnapAlgorithm().getMiddleTarget(),
                        mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
                        200 /* endDelay */);
            });
        }
    }

    void onUndockingTask() {
        int dockSide = mSplitLayout.getPrimarySplitSide();
        if (inSplitMode()) {
+9 −11
Original line number Diff line number Diff line
@@ -48,14 +48,6 @@ public interface SplitScreen {
    /** Switch to minimized state if appropriate. */
    void setMinimized(boolean minimized);

    /**
     * Workaround for b/62528361, at the time recents has drawn, it may happen before a
     * configuration change to the Divider, and internally, the event will be posted to the
     * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
     * register the event handler here and proxy the event to the current DividerView.
     */
    void onRecentsDrawn();

    /** Called when there's an activity forced resizable. */
    void onActivityForcedResizable(String packageName, int taskId, int reason);

@@ -68,9 +60,6 @@ public interface SplitScreen {
    /** Called when there's a task undocking. */
    void onUndockingTask();

    /** Called when top task docked. */
    void onDockedTopTask();

    /** Called when app transition finished. */
    void onAppTransitionFinished();

@@ -85,4 +74,13 @@ public interface SplitScreen {

    /** @return the container token for the secondary split root task. */
    WindowContainerToken getSecondaryRoot();

    /**
     * Splits the primary task if feasible, this is to preserve legacy way to toggle split screen.
     * Like triggering split screen through long pressing recents app button or through
     * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN}.
     *
     * @return {@code true} if it successes to split the primary task.
     */
    boolean splitPrimaryTask();
}
+59 −34
Original line number Diff line number Diff line
@@ -16,19 +16,25 @@

package com.android.wm.shell.splitscreen;

import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;

import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -47,6 +53,7 @@ import com.android.wm.shell.common.TransactionPool;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

@@ -57,7 +64,7 @@ public class SplitScreenController implements SplitScreen,
        DisplayController.OnDisplaysChangedListener {
    static final boolean DEBUG = false;

    private static final String TAG = "Divider";
    private static final String TAG = "SplitScreenCtrl";
    private static final int DEFAULT_APP_TRANSITION_DURATION = 336;

    private final Context mContext;
@@ -157,12 +164,12 @@ public class SplitScreenController implements SplitScreen,
        // Don't initialize the divider or anything until we get the default display.
    }

    /** Returns {@code true} if split screen is supported on the device. */
    @Override
    public boolean isSplitScreenSupported() {
        return mSplits.isSplitScreenSupported();
    }

    /** Called when keyguard showing state changed. */
    @Override
    public void onKeyguardVisibilityChanged(boolean showing) {
        if (!isSplitActive() || mView == null) {
            return;
@@ -229,21 +236,22 @@ public class SplitScreenController implements SplitScreen,
        mHandler.post(task);
    }

    /** Returns {@link DividerView}. */
    @Override
    public DividerView getDividerView() {
        return mView;
    }

    /** Returns {@code true} if one of the split screen is in minimized mode. */
    @Override
    public boolean isMinimized() {
        return mMinimized;
    }

    @Override
    public boolean isHomeStackResizable() {
        return mHomeStackResizable;
    }

    /** Returns {@code true} if the divider is visible. */
    @Override
    public boolean isDividerVisible() {
        return mView != null && mView.getVisibility() == View.VISIBLE;
    }
@@ -328,7 +336,7 @@ public class SplitScreenController implements SplitScreen,
        }
    }

    /** Switch to minimized state if appropriate. */
    @Override
    public void setMinimized(final boolean minimized) {
        if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
        mHandler.post(() -> {
@@ -392,48 +400,29 @@ public class SplitScreenController implements SplitScreen,
        mWindowManager.setTouchable(!mAdjustedForIme);
    }

    /**
     * Workaround for b/62528361, at the time recents has drawn, it may happen before a
     * configuration change to the Divider, and internally, the event will be posted to the
     * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
     * register the event handler here and proxy the event to the current DividerView.
     */
    public void onRecentsDrawn() {
        if (mView != null) {
            mView.onRecentsDrawn();
        }
    }

    /** Called when there's an activity forced resizable. */
    @Override
    public void onActivityForcedResizable(String packageName, int taskId, int reason) {
        mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
    }

    /** Called when there's an activity dismissing split screen. */
    @Override
    public void onActivityDismissingSplitScreen() {
        mForcedResizableController.activityDismissingSplitScreen();
    }

    /** Called when there's an activity launch on secondary display failed. */
    @Override
    public void onActivityLaunchOnSecondaryDisplayFailed() {
        mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
    }

    /** Called when there's a task undocking. */
    @Override
    public void onUndockingTask() {
        if (mView != null) {
            mView.onUndockingTask();
        }
    }

    /** Called when top task docked. */
    public void onDockedTopTask() {
        if (mView != null) {
            mView.onDockedTopTask();
        }
    }

    /** Called when app transition finished. */
    @Override
    public void onAppTransitionFinished() {
        if (mView == null) {
            return;
@@ -441,7 +430,7 @@ public class SplitScreenController implements SplitScreen,
        mForcedResizableController.onAppTransitionFinished();
    }

    /** Dumps current status of Split Screen. */
    @Override
    public void dump(PrintWriter pw) {
        pw.print("  mVisible="); pw.println(mVisible);
        pw.print("  mMinimized="); pw.println(mMinimized);
@@ -458,7 +447,7 @@ public class SplitScreenController implements SplitScreen,
        return (long) (transitionDuration * transitionScale);
    }

    /** Registers listener that gets called whenever the existence of the divider changes. */
    @Override
    public void registerInSplitScreenListener(Consumer<Boolean> listener) {
        listener.accept(isDividerVisible());
        synchronized (mDockedStackExistsListeners) {
@@ -473,6 +462,42 @@ public class SplitScreenController implements SplitScreen,
        }
    }

    @Override
    public boolean splitPrimaryTask() {
        try {
            if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
                    || isSplitActive()) {
                return false;
            }

            // Try fetching the top running task.
            final List<RunningTaskInfo> runningTasks =
                    ActivityTaskManager.getService().getTasks(1 /* maxNum */);
            if (runningTasks == null || runningTasks.isEmpty()) {
                return false;
            }
            // Note: The set of running tasks from the system is ordered by recency.
            final RunningTaskInfo topRunningTask = runningTasks.get(0);

            final int activityType = topRunningTask.configuration.windowConfiguration
                    .getActivityType();
            if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
                return false;
            }

            if (!topRunningTask.supportsSplitScreenMultiWindow) {
                Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
                        Toast.LENGTH_SHORT).show();
                return false;
            }

            return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(
                    topRunningTask.taskId, true /* onTop */);
        } catch (RemoteException e) {
            return false;
        }
    }

    /** Notifies the bounds of split screen changed. */
    void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
        synchronized (mBoundsChangedListeners) {
@@ -532,7 +557,7 @@ public class SplitScreenController implements SplitScreen,
        return mWindowManagerProxy;
    }

    /** @return the container token for the secondary split root task. */
    @Override
    public WindowContainerToken getSecondaryRoot() {
        if (mSplits == null || mSplits.mSecondary == null) {
            return null;
+0 −13
Original line number Diff line number Diff line
@@ -372,19 +372,6 @@ public class ActivityManagerWrapper {
        }
    }

    /**
     * Moves an already resumed task to the side of the screen to initiate split screen.
     */
    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
            Rect initialBounds) {
        try {
            return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
                    true /* onTop */);
        } catch (RemoteException e) {
            return false;
        }
    }

    /**
     * Registers a task stack listener with the system.
     * This should be called on the main thread.
+1 −55
Original line number Diff line number Diff line
@@ -16,30 +16,17 @@

package com.android.systemui.recents;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.trust.TrustManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import android.widget.Toast;

import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.wm.shell.splitscreen.SplitScreen;

import java.util.Optional;

@@ -56,7 +43,6 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
    private final static String TAG = "OverviewProxyRecentsImpl";
    @Nullable
    private final Lazy<StatusBar> mStatusBarLazy;
    private final Optional<SplitScreen> mSplitScreenOptional;

    private Context mContext;
    private Handler mHandler;
@@ -65,10 +51,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {

    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    @Inject
    public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
            Optional<SplitScreen> splitScreenOptional) {
    public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
        mStatusBarLazy = statusBarLazy.orElse(null);
        mSplitScreenOptional = splitScreenOptional;
    }

    @Override
@@ -140,42 +124,4 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
            // Do nothing
        }
    }

    @Override
    public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
            int metricsDockAction) {
        Point realSize = new Point();
        if (initialBounds == null) {
            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
                    .getRealSize(realSize);
            initialBounds = new Rect(0, 0, realSize.x, realSize.y);
        }

        ActivityManager.RunningTaskInfo runningTask =
                ActivityManagerWrapper.getInstance().getRunningTask();
        final int activityType = runningTask != null
                ? runningTask.configuration.windowConfiguration.getActivityType()
                : ACTIVITY_TYPE_UNDEFINED;
        boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
        boolean isRunningTaskInHomeOrRecentsStack =
                activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
        if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
            if (runningTask.supportsSplitScreenMultiWindow) {
                if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
                        runningTask.id, stackCreateMode, initialBounds)) {
                    mSplitScreenOptional.ifPresent(splitScreen -> {
                        splitScreen.onDockedTopTask();
                        // The overview service is handling split screen, so just skip the wait
                        // for the first draw and notify the divider to start animating now
                        splitScreen.onRecentsDrawn();
                    });
                    return true;
                }
            } else {
                Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
                        Toast.LENGTH_SHORT).show();
            }
        }
        return false;
    }
}
Loading