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

Commit 4f5b6016 authored by Jakob Schneider's avatar Jakob Schneider
Browse files

Version-2: Prioritize the session-provided icon & label for archived apps...

Version-2: Prioritize the session-provided icon & label for archived apps during unarchival in the iconCache.

* Also ensures that apps are sorted based on their actual name, so that
  they don't jump around when "Pending.." switches to "Downloading.."
* In case of faillure during unarchival, icons shown are reverted to that of PM supplied ones.

New UI: http://recall/-/gMbThhDGagWFqnJTbQCqSz/fPuzxUuU7cGXCNdygMkXAB

Test: atest CacheDataUpdatedTaskTest.java and locally verified.
Bug: 319495216
Flag: ACONFIG com.android.launcher3.enable_support_for_archiving TRUNKFOOD
Change-Id: I6410482706af900e273fdc6f7cf0b0692442364c
parent e35d5d8c
Loading
Loading
Loading
Loading
+25 −3
Original line number Diff line number Diff line
@@ -441,16 +441,34 @@ public class LauncherModel implements InstallSessionTracker.Callback {
            @Override
            public void execute(@NonNull final LauncherAppState app,
                    @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
                IconCache iconCache = app.getIconCache();
                final IntSet removedIds = new IntSet();
                HashSet<WorkspaceItemInfo> archivedItemsToCacheRefresh = new HashSet<>();
                HashSet<String> archivedPackagesToCacheRefresh = new HashSet<>();
                synchronized (dataModel) {
                    for (ItemInfo info : dataModel.itemsIdMap) {
                        if (info instanceof WorkspaceItemInfo
                                && ((WorkspaceItemInfo) info).hasPromiseIconUi()
                                && user.equals(info.user)
                                && info.getIntent() != null
                                && TextUtils.equals(packageName, info.getIntent().getPackage())) {
                                && info.getIntent() != null) {
                            if (TextUtils.equals(packageName, info.getIntent().getPackage())) {
                                removedIds.add(info.id);
                            }
                            if (((WorkspaceItemInfo) info).isArchived()) {
                                WorkspaceItemInfo workspaceItem = (WorkspaceItemInfo) info;
                                // Remove package cache icon for archived app in case of a session
                                // failure.
                                mApp.getIconCache().removeIconsForPkg(packageName, user);
                                // Refresh icons on the workspace for archived apps.
                                iconCache.getTitleAndIcon(workspaceItem,
                                        workspaceItem.usingLowResIcon());
                                archivedPackagesToCacheRefresh.add(packageName);
                                archivedItemsToCacheRefresh.add(workspaceItem);
                            }
                        }
                    }
                    if (!archivedPackagesToCacheRefresh.isEmpty()) {
                        apps.updateIconsAndLabels(archivedPackagesToCacheRefresh, user);
                    }
                }

@@ -459,6 +477,10 @@ public class LauncherModel implements InstallSessionTracker.Callback {
                            ItemInfoMatcher.ofItemIds(removedIds),
                            "removed because install session failed");
                }
                if (!archivedItemsToCacheRefresh.isEmpty()) {
                    bindUpdatedWorkspaceItems(archivedItemsToCacheRefresh.stream().toList());
                    bindApplicationsIfNeeded();
                }
            }
        });
    }
+11 −3
Original line number Diff line number Diff line
@@ -43,9 +43,7 @@ public class AppInfoComparator implements Comparator<AppInfo> {
    @Override
    public int compare(AppInfo a, AppInfo b) {
        // Order by the title in the current locale
        int result = mLabelComparator.compare(
                a.title == null ? "" : a.title.toString(),
                b.title == null ? "" : b.title.toString());
        int result = mLabelComparator.compare(getSortingTitle(a), getSortingTitle(b));
        if (result != 0) {
            return result;
        }
@@ -64,4 +62,14 @@ public class AppInfoComparator implements Comparator<AppInfo> {
            return aUserSerial.compareTo(bUserSerial);
        }
    }

    private String getSortingTitle(AppInfo info) {
        if (info.appTitle != null) {
            return info.appTitle.toString();
        }
        if (info.title != null) {
            return info.title.toString();
        }
        return "";
    }
}
+54 −3
Original line number Diff line number Diff line
@@ -219,7 +219,19 @@ public class IconCache extends BaseIconCache {
        CacheEntry entry = cacheLocked(application.componentName,
                application.user, () -> null, mLauncherActivityInfoCachingLogic,
                false, application.usingLowResIcon());
        if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
        if (entry.bitmap == null || isDefaultIcon(entry.bitmap, application.user)) {
            return;
        }

        boolean preferPackageIcon = application.isArchived();
        if (preferPackageIcon) {
            String packageName = application.getTargetPackage();
            CacheEntry packageEntry =
                    cacheLocked(new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
                            application.user, () -> null, mLauncherActivityInfoCachingLogic,
                            false, application.usingLowResIcon());
            applyPackageEntry(packageEntry, application, entry);
        } else {
            applyCacheEntry(entry, application);
        }
    }
@@ -227,10 +239,14 @@ public class IconCache extends BaseIconCache {
    /**
     * Fill in {@param info} with the icon and label for {@param activityInfo}
     */
    @SuppressWarnings("NewApi")
    public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
            LauncherActivityInfo activityInfo, boolean useLowResIcon) {
        boolean isAppArchived = Utilities.enableSupportForArchiving() && activityInfo != null
                && activityInfo.getActivityInfo().isArchived;
        // If we already have activity info, no need to use package icon
        getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
        getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon,
                isAppArchived);
    }

    /**
@@ -309,7 +325,7 @@ public class IconCache extends BaseIconCache {
        } else {
            Intent intent = info.getIntent();
            getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
                    true, useLowResIcon);
                    true, useLowResIcon, info.isArchived());
        }
    }

@@ -333,6 +349,28 @@ public class IconCache extends BaseIconCache {
        applyCacheEntry(entry, infoInOut);
    }

    /**
     * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
     */
    public synchronized void getTitleAndIcon(
            @NonNull ItemInfoWithIcon infoInOut,
            @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
            boolean usePkgIcon, boolean useLowResIcon, boolean preferPackageEntry) {
        CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
                activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
                useLowResIcon);
        if (preferPackageEntry) {
            String packageName = infoInOut.getTargetPackage();
            CacheEntry packageEntry = cacheLocked(
                    new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
                    infoInOut.user, activityInfoProvider, mLauncherActivityInfoCachingLogic,
                    usePkgIcon, useLowResIcon);
            applyPackageEntry(packageEntry, infoInOut, entry);
        } else {
            applyCacheEntry(entry, infoInOut);
        }
    }

    /**
     * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
     *
@@ -551,6 +589,19 @@ public class IconCache extends BaseIconCache {
        }
    }

    protected void applyPackageEntry(@NonNull final CacheEntry packageEntry,
            @NonNull final ItemInfoWithIcon info, @NonNull final CacheEntry fallbackEntry) {
        info.title = Utilities.trim(packageEntry.title);
        info.appTitle = Utilities.trim(fallbackEntry.title);
        info.contentDescription = packageEntry.contentDescription;
        info.bitmap = packageEntry.bitmap;
        if (packageEntry.bitmap == null) {
            // TODO: entry.bitmap can never be null, so this should not happen at all.
            Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
            info.bitmap = getDefaultIcon(info.user);
        }
    }

    public Drawable getFullResIcon(LauncherActivityInfo info) {
        return mIconProvider.getIcon(info, mIconDpi);
    }
+7 −0
Original line number Diff line number Diff line
@@ -159,6 +159,13 @@ public class ItemInfo {
    @Nullable
    public CharSequence title;

    /**
     * Optionally set: The appTitle might e.g. be different if {@code title} is used to
     * display progress (e.g. Downloading..).
     */
    @Nullable
    public CharSequence appTitle;

    /**
     * Content description of the item.
     */
+76 −2
Original line number Diff line number Diff line
@@ -2,21 +2,33 @@ package com.android.launcher3.model;

import static android.os.Process.myUserHandle;

import static androidx.test.InstrumentationRegistry.getInstrumentation;

import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static com.android.launcher3.util.TestUtil.DUMMY_CLASS_NAME;
import static com.android.launcher3.util.TestUtil.DUMMY_PACKAGE;
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.uiautomator.UiDevice;

import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.BitmapInfo;
@@ -26,12 +38,15 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TestUtil;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -43,9 +58,18 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class CacheDataUpdatedTaskTest {

    @Rule
    public final CheckFlagsRule mCheckFlagsRule =
            DeviceFlagsValueProvider.createCheckFlagsRule();

    private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
    private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";

    private static final String ARCHIVED_PACKAGE = DUMMY_PACKAGE;
    private static final String ARCHIVED_CLASS_NAME = DUMMY_CLASS_NAME;
    private static final String ARCHIVED_TITLE = "Aardwolf";


    private LauncherModelHelper mModelHelper;
    private Context mContext;

@@ -57,6 +81,7 @@ public class CacheDataUpdatedTaskTest {
        mContext = mModelHelper.sandboxContext;
        mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1);
        mModelHelper.createInstallerSession(PENDING_APP_2);
        TestUtil.installDummyApp();

        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
                .atHotseat(1).putFolder("MyFolder")
@@ -73,14 +98,22 @@ public class CacheDataUpdatedTaskTest {
                .addApp(PENDING_APP_2, TEST_ACTIVITY)   // 8
                .addApp(PENDING_APP_2, TEST_ACTIVITY2)  // 9
                .addApp(PENDING_APP_2, TEST_ACTIVITY3)  // 10

                // Dummy Test Package
                .addApp(ARCHIVED_PACKAGE, ARCHIVED_CLASS_NAME) // 11
                .build();
        mModelHelper.setupDefaultLayoutProvider(builder);
        mModelHelper.loadModelSync();
        assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
        assertEquals(11, mModelHelper.getBgDataModel().itemsIdMap.size());

        UiDevice device = UiDevice.getInstance(getInstrumentation());
        assertThat(device.executeShellCommand(String.format("pm archive %s", ARCHIVED_PACKAGE)))
                .isEqualTo("Success\n");
    }

    @After
    public void tearDown() {
    public void tearDown() throws IOException {
        TestUtil.uninstallDummyApp();
        mModelHelper.destroy();
    }

@@ -138,6 +171,47 @@ public class CacheDataUpdatedTaskTest {
        });
    }

    @Test
    @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
    public void testSessionUpdate_archivedApps_sessionInfoPrioritized() {
        // Run on model executor so that no other task runs in the middle.
        runOnExecutorSync(MODEL_EXECUTOR, () -> {
            // Clear all icons from apps list so that its easy to check what was updated
            allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
            int mSession2 = mModelHelper.createInstallerSession(ARCHIVED_PACKAGE);
            mModelHelper.getModel().enqueueModelUpdateTask(
                    newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, ARCHIVED_PACKAGE));
            List<Integer> pendingArchivedAppIds = List.of(11);
            // Mark the app items as archived.
            allItems().forEach(wi -> {
                if (pendingArchivedAppIds.contains(wi.id)) {
                    wi.runtimeStatusFlags |= FLAG_ARCHIVED;
                }
            });
            // Before cache is updated with sessionInfo, confirm the title.
            for (WorkspaceItemInfo info : allItems()) {
                if (pendingArchivedAppIds.contains(info.id)) {
                    assertEquals(info.title, ARCHIVED_TITLE);
                }
            }

            // Update the cache with session details.
            LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
                    new PackageUserKey(ARCHIVED_PACKAGE, myUserHandle()),
                    mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession2));

            // Trigger a refresh for workspace itemInfo objects.
            mModelHelper.getModel().enqueueModelUpdateTask(
                    newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, ARCHIVED_PACKAGE));
            // Verify the new title from session is applied to the iconInfo.
            for (WorkspaceItemInfo info : allItems()) {
                if (pendingArchivedAppIds.contains(info.id)) {
                    assertEquals(info.title, ARCHIVED_PACKAGE);
                }
            }
        });
    }

    private void verifyUpdate(int... idsUpdated) {
        IntSet updates = IntSet.wrap(idsUpdated);
        for (WorkspaceItemInfo info : allItems()) {