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

Commit 93fc0f3a authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Allow user to select second split app from Taskbar

This patch makes it so that (when we enable Taskbar in Overview) users will be able to select their second app for splitscreen by tapping the Taskbar icon, or the icon in AllApps.

This was done by performing a check to SplitSelectStateController when a taskbar icon is hit. If we are currently in split select mode, Taskbar/AllApps icons will no longer launch their respective fullscreen apps, but instead confirm the split attempt. The confirmed app will either be an already-running instance of the app, or a fresh instance of the app (if none is currently running). The split confirmation function is located in TaskbarUIController, where it is accessible to both LauncherTaskbarUIController (for 1P Launcher) and FallbackTaskbarUIController (for 3P launchers).

Also cleans up ~2 lines of unused code from the old splitscreen instructions toast.

Outstanding issues:
- When selecting a second app from within AllApps, the AllApps shade does not animate away in a satisfying way
- When selecting a second app and launching a fresh instance of that app, the animation appears to come from the wrong location
- Intent + Intent splitting does not currently work
- If the selected app is already running with multiple instances, it picks the oldest instance. Ideally, the newest instance is preferred.

Bug: 251747761
Test: Manual testing with Taskbar in Overview flag enabled
Change-Id: I302dc092740bb880f9134ba8e2e587c4f2c29d01
parent 345ef9ff
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -40,8 +40,7 @@ public class FallbackTaskbarUIController extends TaskbarUIController {
                    animateToRecentsState(toState);

                    // Handle tapping on live tile.
                    RecentsView recentsView = mRecentsActivity.getOverviewPanel();
                    recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
                    getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT
                            ? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
                }
            };
@@ -85,4 +84,9 @@ public class FallbackTaskbarUIController extends TaskbarUIController {
            anim.start();
        }
    }

    @Override
    public RecentsView getRecentsView() {
        return mRecentsActivity.getOverviewPanel();
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.views.RecentsView;

import java.io.PrintWriter;
import java.util.Arrays;
@@ -393,4 +394,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {

        mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
    }

    @Override
    public RecentsView getRecentsView() {
        return mLauncher.getOverviewPanel();
    }
}
+55 −32
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -132,6 +133,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
    private final boolean mIsUserSetupComplete;
    private final boolean mIsNavBarForceVisible;
    private final boolean mIsNavBarKidsMode;

    private boolean mIsDestroyed = false;
    // The flag to know if the window is excluded from magnification region computation.
    private boolean mIsExcludeFromMagnificationRegion = false;
@@ -755,6 +757,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
            if (info.isDisabled()) {
                ItemClickHandler.handleDisabledItemClicked(info, this);
            } else {
                TaskbarUIController taskbarUIController = mControllers.uiController;
                RecentsView recents = taskbarUIController.getRecentsView();
                if (recents != null
                        && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
                    // If we are selecting a second app for split, launch the split tasks
                    taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
                } else {
                    // Else launch the selected task
                    Intent intent = new Intent(info.getIntent())
                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    try {
@@ -781,16 +791,29 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
                        }

                        mControllers.uiController.onTaskbarIconLaunched(info);
                    mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
                } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
                    } catch (NullPointerException
                            | ActivityNotFoundException
                            | SecurityException e) {
                        Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
                                .show();
                        Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
                    }
                }
                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
            }
        } else if (tag instanceof AppInfo) {
            AppInfo info = (AppInfo) tag;
            TaskbarUIController taskbarUIController = mControllers.uiController;
            RecentsView recents = taskbarUIController.getRecentsView();
            if (recents != null
                    && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
                // If we are selecting a second app for split, launch the split tasks
                taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
            } else {
                // Else launch the selected task
                startItemInfoActivity((AppInfo) tag);
                mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
            }
            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
        } else {
            Log.e(TAG, "Unknown type clicked: " + tag);
+39 −0
Original line number Diff line number Diff line
@@ -15,13 +15,18 @@
 */
package com.android.launcher3.taskbar;

import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;

import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;

import java.io.PrintWriter;
import java.util.stream.Stream;
@@ -128,4 +133,38 @@ public class TaskbarUIController {
                prefix,
                getClass().getSimpleName()));
    }

    /**
     * Returns RecentsView. Overwritten in LauncherTaskbarUIController and
     * FallbackTaskbarUIController with Launcher-specific implementations. Returns null for other
     * UI controllers (like DesktopTaskbarUIController) that don't have a RecentsView.
     */
    public @Nullable RecentsView getRecentsView() {
        return null;
    }

    /**
     * Uses the clicked Taskbar icon to launch a second app for splitscreen.
     */
    public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
        RecentsView recents = getRecentsView();
        TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
        if (foundTaskView != null) {
            recents.confirmSplitSelect(
                    foundTaskView,
                    foundTaskView.getTask(),
                    foundTaskView.getIconView().getDrawable(),
                    foundTaskView.getThumbnail(),
                    foundTaskView.getThumbnail().getThumbnail(),
                    /* intent */ null);
        } else {
            recents.confirmSplitSelect(
                    /* containerTaskView */ null,
                    /* task */ null,
                    new BitmapDrawable(info.bitmap.icon),
                    startingView,
                    /* thumbnail */ null,
                    intent);
        }
    }
}
+47 −14
Original line number Diff line number Diff line
@@ -74,9 +74,12 @@ import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -661,8 +664,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
    private TaskView mSecondSplitHiddenView;
    @Nullable
    private SplitBounds mSplitBoundsConfig;
    private final Toast mSplitToast = Toast.makeText(getContext(),
            R.string.toast_split_select_app, Toast.LENGTH_SHORT);
    private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
            R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);

@@ -1212,6 +1213,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        return null;
    }

    /**
     * Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
     * no match.
     */
    @Nullable
    public TaskView getTaskViewByComponentName(ComponentName componentName) {
        for (int i = 0; i < getTaskViewCount(); i++) {
            TaskView taskView = requireTaskViewAt(i);
            if (taskView.getTask().key.sourceComponent.equals(componentName)) {
                return taskView;
            }
        }
        return null;
    }

    public void setOverviewStateEnabled(boolean enabled) {
        mOverviewStateEnabled = enabled;
        updateTaskStackListenerState();
@@ -4230,24 +4246,39 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
     * Confirms the selection of the next split task. The extra data is passed through because the
     * user may be selecting a subtask in a group.
     *
     * @param containerTaskView If our second selected app is currently running in Recents, this is
     *                          the "container" TaskView from Recents. If we are starting a fresh
     *                          instance of the app from an Intent, this will be null.
     * @param task The Task corresponding to our second selected app. If we are starting a fresh
     *             instance of the app from an Intent, this will be null.
     * @param drawable The Drawable corresponding to our second selected app's icon.
     * @param secondView The View representing the current space on the screen where the second app
     *                   is (either the ThumbnailView or the tapped icon).
     * @param intent If we are launching a fresh instance of the app, this is the Intent for it. If
     *               the second app is already running in Recents, this will be null.
     * @return true if waiting for confirmation of second app or if split animations are running,
     *          false otherwise
     */
    public boolean confirmSplitSelect(TaskView containerTaskView, Task task, IconView iconView,
            TaskThumbnailView thumbnailView) {
    public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
            View secondView, @Nullable Bitmap thumbnail, Intent intent) {
        if (canLaunchFullscreenTask()) {
            return false;
        }
        if (mSplitSelectStateController.isBothSplitAppsConfirmed()) {
            return true;
        }
        mSplitToast.cancel();
        // Second task is selected either as an already-running Task or an Intent
        if (task != null) {
            if (!task.isDockable) {
            // Task not split screen supported
                // Task does not support split screen
                mSplitUnsupportedToast.show();
                return true;
            }
            mSplitSelectStateController.setSecondTask(task);
        } else {
            mSplitSelectStateController.setSecondTask(intent);
        }

        RectF secondTaskStartingBounds = new RectF();
        Rect secondTaskEndingBounds = new Rect();
        // TODO(194414938) starting bounds seem slightly off, investigate
@@ -4274,9 +4305,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
                false /* fadeWithThumbnail */, true /* isStagedTask */);

        safeRemoveDragLayerView(mSecondFloatingTaskView);
        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
                thumbnailView, thumbnailView.getThumbnail(),
                iconView.getDrawable(), secondTaskStartingBounds);

        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, secondView,
                thumbnail, drawable, secondTaskStartingBounds);
        mSecondFloatingTaskView.setAlpha(1);
        mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
@@ -4292,7 +4323,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        });

        mSecondSplitHiddenView = containerTaskView;
        if (mSecondSplitHiddenView != null) {
            mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
        }

        InteractionJankMonitorWrapper.begin(this,
                InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
Loading