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

Commit 1a98a9d8 authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Fix app pair launches with certain apps

This CL changes the way app pairs are saved. Previously, we saved the app pair using WorkspaceItemInfos directly from Recents, which caused certain apps to fail on launch because they weren't being launched with the correct Intent.

Now, we look up a proper launchable WorkspaceItemInfo from the AllAppsStore and save that to the app pair, which should fix the launch issues.

Bug: 323112914
Bug: 323110399
Test: Can launch Slides, Play Store, and Calendar when part of an app pair.
Flag: ACONFIG com.android.wm.shell.enable_app_pairs TEAMFOOD
Change-Id: I475afa1a237c50ffb0ffaf85af43912a803b8011
parent 1d6f76f8
Loading
Loading
Loading
Loading
+79 −5
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.quickstep.util;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;


import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -30,6 +31,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.isPersisten


import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.Pair;
@@ -42,10 +44,12 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -93,14 +97,38 @@ public class AppPairsController {
    }
    }


    /**
    /**
     * Creates a new app pair ItemInfo and adds it to the workspace
     * Creates a new app pair ItemInfo and adds it to the workspace.
     * <br>
     * We create WorkspaceItemInfos to save onto the app pair in the following way:
     * <br> 1. We verify that the ComponentKey from our Recents tile corresponds to a real
     * launchable app in the app store.
     * <br> 2. If it doesn't, we search for the underlying launchable app via package name, and use
     * that instead.
     * <br> 3. If that fails, we re-use the existing WorkspaceItemInfo by cloning it and replacing
     * its intent with one from PackageManager.
     * <br> 4. If everything fails, we just use the WorkspaceItemInfo as is, with its existing
     * intent. This is not preferred, but will still work in most cases (notably it will not work
     * well on trampoline apps).
     */
     */
    public void saveAppPair(GroupedTaskView gtv) {
    public void saveAppPair(GroupedTaskView gtv) {
        TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
        TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
        WorkspaceItemInfo app1 = attributes[0].getItemInfo().clone();
        WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
        WorkspaceItemInfo app2 = attributes[1].getItemInfo().clone();
        WorkspaceItemInfo recentsInfo2 = attributes[0].getItemInfo();
        app1.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
        WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
        app2.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
        WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());

        // If app lookup fails, use the WorkspaceItemInfo that we have, but try to override default
        // intent with one from PackageManager.
        if (app1 == null) {
            Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo1.title
                    + " failed. Falling back to the WorkspaceItemInfo from Recents.");
            app1 = convertRecentsItemToAppItem(recentsInfo1);
        }
        if (app2 == null) {
            Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo2.title
                    + " failed. Falling back to the WorkspaceItemInfo from Recents.");
            app2 = convertRecentsItemToAppItem(recentsInfo2);
        }


        @PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
        @PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
        if (!isPersistentSnapPosition(snapPosition)) {
        if (!isPersistentSnapPosition(snapPosition)) {
@@ -188,6 +216,52 @@ public class AppPairsController {
        );
        );
    }
    }


    /**
     * Creates a new launchable WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION by looking the
     * ComponentKey up in the AllAppsStore. If no app is found, attempts a lookup by package
     * instead. If that lookup fails, returns null.
     */
    @Nullable
    private WorkspaceItemInfo lookupLaunchableItem(@Nullable ComponentKey key) {
        if (key == null) {
            return null;
        }

        AllAppsStore appsStore = Launcher.getLauncher(mContext).getAppsView().getAppsStore();

        // Lookup by ComponentKey
        AppInfo appInfo = appsStore.getApp(key);
        if (appInfo == null) {
            // Lookup by package
            appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
        }

        return appInfo != null ? appInfo.makeWorkspaceItem(mContext) : null;
    }

    /**
     * Converts a WorkspaceItemInfo of itemType=ITEM_TYPE_TASK (from a Recents task) to a new
     * WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION.
     */
    private WorkspaceItemInfo convertRecentsItemToAppItem(WorkspaceItemInfo recentsItem) {
        if (recentsItem.itemType != LauncherSettings.Favorites.ITEM_TYPE_TASK) {
            Log.w(TAG, "Expected ItemInfo of type ITEM_TYPE_TASK, but received "
                    + recentsItem.itemType);
        }

        WorkspaceItemInfo launchableItem = recentsItem.clone();
        PackageManager p = mContext.getPackageManager();
        Intent launchIntent = p.getLaunchIntentForPackage(recentsItem.getTargetPackage());
        Log.w(TAG, "Initial intent from Recents: " + launchableItem.intent + "\n"
                + "Intent from PackageManager: " + launchIntent);
        if (launchIntent != null) {
            // If lookup from PackageManager fails, just use the existing intent
            launchableItem.intent = launchIntent;
        }
        launchableItem.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
        return launchableItem;
    }

    /**
    /**
     * Handles the complicated logic for how to animate an app pair entrance when already inside an
     * Handles the complicated logic for how to animate an app pair entrance when already inside an
     * app or app pair.
     * app or app pair.
+12 −1
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -138,12 +139,22 @@ public class AllAppsStore<T extends Context & ActivityContext> {
    /**
    /**
     * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise
     * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise
     * null.
     * null.
     *
     * Uses {@link AppInfo#COMPONENT_KEY_COMPARATOR} as a default comparator.
     */
     */
    @Nullable
    @Nullable
    public AppInfo getApp(ComponentKey key) {
    public AppInfo getApp(ComponentKey key) {
        return getApp(key, COMPONENT_KEY_COMPARATOR);
    }

    /**
     * Generic version of {@link #getApp(ComponentKey)} that allows comparator to be specified.
     */
    @Nullable
    public AppInfo getApp(ComponentKey key, Comparator<AppInfo> comparator) {
        mTempInfo.componentName = key.componentName;
        mTempInfo.componentName = key.componentName;
        mTempInfo.user = key.user;
        mTempInfo.user = key.user;
        int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
        int index = Arrays.binarySearch(mApps, mTempInfo, comparator);
        return index < 0 ? null : mApps[index];
        return index < 0 ? null : mApps[index];
    }
    }


+3 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,9 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
        return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
        return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
    };
    };


    public static final Comparator<AppInfo> PACKAGE_KEY_COMPARATOR = Comparator.comparingInt(
            (AppInfo a) -> a.user.hashCode()).thenComparing(ItemInfo::getTargetPackage);

    /**
    /**
     * The intent used to start the application.
     * The intent used to start the application.
     */
     */