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

Commit d761f5c3 authored by Jeremy Sim's avatar Jeremy Sim Committed by Android (Google) Code Review
Browse files

Merge "App Pairs: Implement Taskbar functionality" into main

parents 151751c0 2987d95b
Loading
Loading
Loading
Loading
+77 −45
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.NavigationMode;
@@ -127,6 +128,7 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;

import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

@@ -975,6 +977,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
    }

    protected void onTaskbarIconClicked(View view) {
        TaskbarUIController taskbarUIController = mControllers.uiController;
        RecentsView recents = taskbarUIController.getRecentsView();
        boolean shouldCloseAllOpenViews = true;
        Object tag = view.getTag();
        if (tag instanceof Task) {
@@ -982,46 +986,26 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
            ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
                    ActivityOptions.makeBasic());
            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
        } else if (tag instanceof FolderInfo) {
        } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_FOLDER) {
            // Tapping an expandable folder icon on Taskbar
            shouldCloseAllOpenViews = false;
            FolderIcon folderIcon = (FolderIcon) view;
            Folder folder = folderIcon.getFolder();

            folder.setPriorityOnFolderStateChangedListener(
                    new Folder.OnFolderStateChangedListener() {
                        @Override
                        public void onFolderStateChanged(int newState) {
                            if (newState == Folder.STATE_OPEN) {
                                setTaskbarWindowFocusableForIme(true);
                            } else if (newState == Folder.STATE_CLOSED) {
                                // Defer by a frame to ensure we're no longer fullscreen and thus
                                // won't jump.
                                getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
                                folder.setPriorityOnFolderStateChangedListener(null);
                            }
            expandFolder((FolderIcon) view);
        } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR) {
            // Tapping an app pair icon on Taskbar
            if (recents != null && recents.isSplitSelectionActive()) {
                // TODO (b/274835596): Implement "can't split with this" bounce animation
                Toast.makeText(this, "Unable to split with an app pair. Select another app.",
                        Toast.LENGTH_SHORT).show();
            } else {
                // Else launch the selected app pair
                launchFromTaskbarPreservingSplitIfVisible(recents, fi.contents);
                mControllers.uiController.onTaskbarIconLaunched(fi);
                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
            }
                    });

            setTaskbarWindowFullscreen(true);

            getDragLayer().post(() -> {
                folder.animateOpen();
                getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);

                folder.iterateOverItems((itemInfo, itemView) -> {
                    mControllers.taskbarViewController
                            .setClickAndLongClickListenersForIcon(itemView);
                    // To play haptic when dragging, like other Taskbar items do.
                    itemView.setHapticFeedbackEnabled(true);
                    return false;
                });
            });
        } else if (tag instanceof WorkspaceItemInfo) {
            // Tapping a launchable icon on Taskbar
            WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
            if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) {
                TaskbarUIController taskbarUIController = mControllers.uiController;
                RecentsView recents = taskbarUIController.getRecentsView();
                if (recents != null && recents.isSplitSelectionActive()) {
                    // If we are selecting a second app for split, launch the split tasks
                    taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
@@ -1049,7 +1033,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
                            getSystemService(LauncherApps.class)
                                    .startShortcut(packageName, id, null, null, info.user);
                        } else {
                            launchFromTaskbarPreservingSplitIfVisible(recents, info);
                            launchFromTaskbarPreservingSplitIfVisible(
                                    recents, Collections.singletonList(info));
                        }

                    } catch (NullPointerException
@@ -1082,14 +1067,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
        } else if (tag instanceof AppInfo) {
            // Tapping an item in AllApps
            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 {
                launchFromTaskbarPreservingSplitIfVisible(recents, info);
                launchFromTaskbarPreservingSplitIfVisible(recents, Collections.singletonList(info));
            }
            mControllers.uiController.onTaskbarIconLaunched(info);
            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
@@ -1111,17 +1094,22 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
     * (potentially breaking a split pair).
     */
    private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents,
            ItemInfo info) {
            List<? extends ItemInfo> itemInfos) {
        if (recents == null) {
            return;
        }

        boolean findExactPairMatch = itemInfos.size() == 2;
        // Convert the list of ItemInfo instances to a list of ComponentKeys
        List<ComponentKey> componentKeys =
                itemInfos.stream().map(ItemInfo::getComponentKey).toList();
        recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
                Collections.singletonList(info.getComponentKey()),
                componentKeys,
                findExactPairMatch,
                foundTasks -> {
                    @Nullable Task foundTask = foundTasks.get(0);
                    if (foundTask != null) {
                        TaskView foundTaskView =
                                recents.getTaskViewByTaskId(foundTask.key.id);
                        TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
                        if (foundTaskView != null
                                && foundTaskView.isVisibleToUser()) {
                            TestLogging.recordEvent(
@@ -1130,8 +1118,17 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
                            return;
                        }
                    }
                    startItemInfoActivity(info);
                });

                    if (findExactPairMatch) {
                        // We did not find the app pair we were looking for, so launch one.
                        recents.getSplitSelectController().getAppPairsController().launchAppPair(
                                (WorkspaceItemInfo) itemInfos.get(0),
                                (WorkspaceItemInfo) itemInfos.get(1));
                    } else {
                        startItemInfoActivity(itemInfos.get(0));
                    }
                }
        );
    }

    private void startItemInfoActivity(ItemInfo info) {
@@ -1153,6 +1150,41 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
        }
    }

    /** Expands a folder icon when it is clicked */
    private void expandFolder(FolderIcon folderIcon) {
        Folder folder = folderIcon.getFolder();

        folder.setPriorityOnFolderStateChangedListener(
                new Folder.OnFolderStateChangedListener() {
                    @Override
                    public void onFolderStateChanged(int newState) {
                        if (newState == Folder.STATE_OPEN) {
                            setTaskbarWindowFocusableForIme(true);
                        } else if (newState == Folder.STATE_CLOSED) {
                            // Defer by a frame to ensure we're no longer fullscreen and thus
                            // won't jump.
                            getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
                            folder.setPriorityOnFolderStateChangedListener(null);
                        }
                    }
                });

        setTaskbarWindowFullscreen(true);

        getDragLayer().post(() -> {
            folder.animateOpen();
            getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);

            folder.iterateOverItems((itemInfo, itemView) -> {
                mControllers.taskbarViewController
                        .setClickAndLongClickListenersForIcon(itemView);
                // To play haptic when dragging, like other Taskbar items do.
                itemView.setHapticFeedbackEnabled(true);
                return false;
            });
        });
    }

    /**
     * Returns whether the taskbar is currently visually stashed.
     */
+2 −0
Original line number Diff line number Diff line
@@ -216,6 +216,7 @@ public class TaskbarUIController {

        recentsView.getSplitSelectController().findLastActiveTasksAndRunCallback(
                Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()),
                false /* findExactPairMatch */,
                foundTasks -> {
                    @Nullable Task foundTask = foundTasks.get(0);
                    splitSelectSource.alreadyRunningTaskId = foundTask == null
@@ -234,6 +235,7 @@ public class TaskbarUIController {
        RecentsView recents = getRecentsView();
        recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
                Collections.singletonList(info.getComponentKey()),
                false /* findExactPairMatch */,
                foundTasks -> {
                    @Nullable Task foundTask = foundTasks.get(0);
                    if (foundTask != null) {
+26 −10
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;

import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
@@ -47,6 +49,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.FolderInfo;
@@ -307,12 +310,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar

            // Replace any Hotseat views with the appropriate type if it's not already that type.
            final int expectedLayoutResId;
            boolean isFolder = false;
            boolean isCollection = false;
            if (hotseatItemInfo.isPredictedItem()) {
                expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
            } else if (hotseatItemInfo instanceof FolderInfo) {
                expectedLayoutResId = R.layout.folder_icon;
                isFolder = true;
            } else if (hotseatItemInfo instanceof FolderInfo fi) {
                expectedLayoutResId = fi.itemType == ITEM_TYPE_APP_PAIR
                        ? R.layout.app_pair_icon
                        : R.layout.folder_icon;
                isCollection = true;
            } else {
                expectedLayoutResId = R.layout.taskbar_app_icon;
            }
@@ -323,7 +328,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar

                // see if the view can be reused
                if ((hotseatView.getSourceLayoutResId() != expectedLayoutResId)
                        || (isFolder && (hotseatView.getTag() != hotseatItemInfo))) {
                        || (isCollection && (hotseatView.getTag() != hotseatItemInfo))) {
                    // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation,
                    // so if the info changes we need to reinflate. This should only happen if a new
                    // folder is dragged to the position that another folder previously existed.
@@ -336,12 +341,23 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
            }

            if (hotseatView == null) {
                if (isFolder) {
                if (isCollection) {
                    FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                    FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
                            mActivityContext, this, folderInfo);
                    folderIcon.setTextVisible(false);
                    hotseatView = folderIcon;
                    switch (hotseatItemInfo.itemType) {
                        case ITEM_TYPE_FOLDER:
                            hotseatView = FolderIcon.inflateFolderAndIcon(
                                    expectedLayoutResId, mActivityContext, this, folderInfo);
                            ((FolderIcon) hotseatView).setTextVisible(false);
                            break;
                        case ITEM_TYPE_APP_PAIR:
                            hotseatView = AppPairIcon.inflateIcon(
                                    expectedLayoutResId, mActivityContext, this, folderInfo);
                            ((AppPairIcon) hotseatView).setTextVisible(false);
                            break;
                        default:
                            throw new IllegalStateException(
                                    "Unexpected item type: " + hotseatItemInfo.itemType);
                    }
                } else {
                    hotseatView = inflate(expectedLayoutResId);
                }
+1 −0
Original line number Diff line number Diff line
@@ -642,6 +642,7 @@ public class QuickstepLauncher extends Launcher {
        // using that.
        mSplitSelectStateController.findLastActiveTasksAndRunCallback(
                Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()),
                false /* findExactPairMatch */,
                foundTasks -> {
                    @Nullable Task foundTask = foundTasks.get(0);
                    boolean taskWasFound = foundTask != null;
+3 −1
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ public class AppPairsController {
        ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user);
        mSplitSelectStateController.findLastActiveTasksAndRunCallback(
                Arrays.asList(app1Key, app2Key),
                false /* findExactPairMatch */,
                foundTasks -> {
                    @Nullable Task foundTask1 = foundTasks.get(0);
                    Intent task1Intent;
@@ -153,7 +154,8 @@ public class AppPairsController {

                    mSplitSelectStateController.launchSplitTasks(
                            AppPairsController.convertRankToSnapPosition(app1.rank));
                });
                }
        );
    }

    /**
Loading