diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java index 21bc479c22..92432a836b 100644 --- a/src/com/android/launcher3/ExtendedEditText.java +++ b/src/com/android/launcher3/ExtendedEditText.java @@ -26,7 +26,6 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.views.ActivityContext; @@ -99,18 +98,6 @@ public class ExtendedEditText extends EditText { } } - /** - * Sets whether EditText background should be visible - * @param maxAlpha defines the maximum alpha the background should animates to - */ - public void setBackgroundVisibility(boolean visible, float maxAlpha) {} - - /** - * Returns whether a visible background is set on EditText - */ - public boolean getBackgroundVisibility() { - return getBackground() != null; - } public void showKeyboard() { mShowImeAfterFirstLayout = !showSoftInput(); @@ -150,9 +137,6 @@ public class ExtendedEditText extends EditText { if (!TextUtils.isEmpty(getText())) { setText(""); } - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - return; - } if (isFocused()) { View nextFocus = focusSearch(View.FOCUS_DOWN); if (nextFocus != null) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ed9f0449b9..85dd3b3cc6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -87,6 +87,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; +import android.os.Trace; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; @@ -280,6 +281,11 @@ public class Launcher extends StatefulActivity implements Launche private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375; + private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame"; + private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps"; + public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0; + public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1; + private Configuration mOldConfig; @Thunk @@ -366,7 +372,15 @@ public class Launcher extends StatefulActivity implements Launche private LauncherState mPrevLauncherState; @Override + @TargetApi(Build.VERSION_CODES.S) protected void onCreate(Bundle savedInstanceState) { + // Only use a hard-coded cookie since we only want to trace this once. + if (Utilities.ATLEAST_S) { + Trace.beginAsyncSection( + DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE); + Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, + DISPLAY_ALL_APPS_TRACE_COOKIE); + } Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT, TraceHelper.FLAG_UI_EVENT); if (DEBUG_STRICT_MODE) { @@ -2584,6 +2598,7 @@ public class Launcher extends StatefulActivity implements Launche } @Override + @TargetApi(Build.VERSION_CODES.S) public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) { mSynchronouslyBoundPages = boundPages; mPagesToBindSynchronously = new IntSet(); @@ -2606,6 +2621,10 @@ public class Launcher extends StatefulActivity implements Launche executor.onLoadAnimationCompleted(); } executor.attachTo(this); + if (Utilities.ATLEAST_S) { + Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME, + DISPLAY_WORKSPACE_TRACE_COOKIE); + } } /** @@ -2669,9 +2688,14 @@ public class Launcher extends StatefulActivity implements Launche * Implementation of the method from LauncherModel.Callbacks. */ @Override + @TargetApi(Build.VERSION_CODES.S) public void bindAllApplications(AppInfo[] apps, int flags) { mAppsView.getAppsStore().setApps(apps, flags); PopupContainerWithArrow.dismissInvalidPopup(this); + if (Utilities.ATLEAST_S) { + Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, + DISPLAY_ALL_APPS_TRACE_COOKIE); + } } /** diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index e779ee80ea..ee5f7e4a71 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -59,7 +59,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Insettable; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; @@ -624,6 +623,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo for (int i = 0; i < mAH.length; i++) { mAH[i].padding.top = padding; mAH[i].applyPadding(); + if (mAH[i].recyclerView != null) { + mAH[i].recyclerView.scrollToTop(); + } } mHeaderTop = mHeader.getTop(); } @@ -639,6 +641,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo public void onClearSearchResult() { mIsSearching = false; + mHeader.setCollapsed(false); rebindAdapters(); getActiveRecyclerView().scrollToTop(); } @@ -814,14 +817,13 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo invalidateHeader(); } if (mSearchUiManager.getEditText() != null) { - ExtendedEditText editText = mSearchUiManager.getEditText(); - boolean bgVisible = editText.getBackgroundVisibility(); + boolean bgVisible = mSearchUiManager.getBackgroundVisibility(); if (scrolledOffset == 0 && !mIsSearching) { bgVisible = true; } else if (scrolledOffset > mHeaderThreshold) { bgVisible = false; } - editText.setBackgroundVisibility(bgVisible, 1 - prog); + mSearchUiManager.setBackgroundVisibility(bgVisible, 1 - prog); } } diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java index 9bf60433ff..6ff2132286 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java @@ -47,6 +47,8 @@ public interface FloatingHeaderRow { /** * Scrolls the content vertically. + * @param scroll scrolled distance in pixels for active recyclerview. + * @param isScrolledOut bool to determine if row is scrolled out of view */ void setVerticalScroll(int scroll, boolean isScrolledOut); diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java index 924a392389..7478b53d55 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -48,6 +48,19 @@ public interface SearchUiManager { @Nullable ExtendedEditText getEditText(); + /** + * Sets whether EditText background should be visible + * @param maxAlpha defines the maximum alpha the background should animates to + */ + default void setBackgroundVisibility(boolean visible, float maxAlpha) {} + + /** + * Returns whether a visible background is set on EditText + */ + default boolean getBackgroundVisibility() { + return false; + } + /** * sets highlight result's title */ diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index f091262c35..382f7a75cb 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -147,6 +147,11 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag( "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace"); + public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag( + "ENABLE_BULK_WORKSPACE_ICON_LOADING", + false, + "Enable loading workspace icons in bulk."); + // Keep as DeviceFlag for remote disable in emergency. public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag( "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions"); diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 1a468aeb88..60d6e830c6 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -19,6 +19,8 @@ package com.android.launcher3.icons; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static java.util.stream.Collectors.groupingBy; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,10 +32,15 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ShortcutInfo; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; import android.graphics.drawable.Drawable; import android.os.Process; +import android.os.Trace; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; @@ -47,6 +54,7 @@ import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.icons.cache.CachingLogic; import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.IconRequestInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -56,8 +64,13 @@ import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.stream.Stream; /** * Cache of application icons. Icons can be made from any thread. @@ -306,6 +319,87 @@ public class IconCache extends BaseIconCache { applyCacheEntry(entry, infoInOut); } + /** + * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles. + * + * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query. + * @param user UserHandle all the given iconRequestInfos share + * @param useLowResIcons whether we should exclude the icon column from the sql results. + */ + private Cursor createBulkQueryCursor( + List> iconRequestInfos, UserHandle user, boolean useLowResIcons) + throws SQLiteException { + String[] queryParams = Stream.concat( + iconRequestInfos.stream() + .map(r -> r.itemInfo.getTargetComponent()) + .filter(Objects::nonNull) + .distinct() + .map(ComponentName::flattenToString), + Stream.of(Long.toString(getSerialNumberForUser(user)))).toArray(String[]::new); + String componentNameQuery = TextUtils.join( + ",", Collections.nCopies(queryParams.length - 1, "?")); + + return mIconDb.query( + useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES, + IconDB.COLUMN_COMPONENT + + " IN ( " + componentNameQuery + " )" + + " AND " + IconDB.COLUMN_USER + " = ?", + queryParams); + } + + /** + * Load and fill icons requested in iconRequestInfos using a single bulk sql query. + */ + public synchronized void getTitlesAndIconsInBulk( + List> iconRequestInfos) { + Map, List>> iconLoadSubsectionsMap = + iconRequestInfos.stream() + .collect(groupingBy(iconRequest -> + Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon))); + + Trace.beginSection("loadIconsInBulk"); + iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> { + Map>> duplicateIconRequestsMap = + filteredList.stream() + .collect(groupingBy(iconRequest -> + iconRequest.itemInfo.getTargetComponent())); + + Trace.beginSection("loadIconSubsectionInBulk"); + try (Cursor c = createBulkQueryCursor( + filteredList, + /* user = */ sectionKey.first, + /* useLowResIcons = */ sectionKey.second)) { + int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT); + while (c.moveToNext()) { + ComponentName cn = ComponentName.unflattenFromString( + c.getString(componentNameColumnIndex)); + List> duplicateIconRequests = + duplicateIconRequestsMap.get(cn); + + if (cn != null) { + CacheEntry entry = cacheLocked( + cn, + /* user = */ sectionKey.first, + () -> duplicateIconRequests.get(0).launcherActivityInfo, + mLauncherActivityInfoCachingLogic, + c, + /* usePackageIcon= */ false, + /* useLowResIcons = */ sectionKey.second); + + for (IconRequestInfo iconRequest : duplicateIconRequests) { + applyCacheEntry(entry, iconRequest.itemInfo); + } + } + } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + } finally { + Trace.endSection(); + } + }); + Trace.endSection(); + } + /** * Fill in {@param infoInOut} with the corresponding icon and label. diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java index e2df16d45a..0fc4c2d716 100644 --- a/src/com/android/launcher3/model/DeviceGridState.java +++ b/src/com/android/launcher3/model/DeviceGridState.java @@ -119,12 +119,23 @@ public class DeviceGridState { } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DeviceGridState that = (DeviceGridState) o; - return mNumHotseat == that.mNumHotseat - && deviceTypeCompatible(mDeviceType, that.mDeviceType) - && Objects.equals(mGridSizeString, that.mGridSizeString); + public String toString() { + return "DeviceGridState{" + + "mGridSizeString='" + mGridSizeString + '\'' + + ", mNumHotseat=" + mNumHotseat + + ", mDeviceType=" + mDeviceType + + '}'; + } + + /** + * Returns true if the database from another DeviceGridState can be loaded into the current + * DeviceGridState without migration, or false otherwise. + */ + public boolean isCompatible(DeviceGridState other) { + if (this == other) return true; + if (other == null) return false; + return mNumHotseat == other.mNumHotseat + && deviceTypeCompatible(mDeviceType, other.mDeviceType) + && Objects.equals(mGridSizeString, other.mGridSizeString); } } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java index e7d0749e9e..fc86cf83f6 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java @@ -103,7 +103,13 @@ public class GridSizeMigrationTaskV2 { * Check given a new IDP, if migration is necessary. */ public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) { - return !new DeviceGridState(idp).equals(new DeviceGridState(context)); + DeviceGridState idpGridState = new DeviceGridState(idp); + DeviceGridState contextGridState = new DeviceGridState(context); + boolean needsToMigrate = !idpGridState.isCompatible(contextGridState); + // TODO: Revert this change after b/200010396 is fixed + Log.d(TAG, "Migration is needed. idpGridState: " + idpGridState + + ", contextGridState: " + contextGridState); + return needsToMigrate; } /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */ diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 7e3bceee1d..8a5a9bf327 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -16,13 +16,10 @@ package com.android.launcher3.model; -import static android.graphics.BitmapFactory.decodeByteArray; - import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.Intent; -import android.content.Intent.ShortcutIconResource; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -45,11 +42,10 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.IconCache; -import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.IconRequestInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; @@ -184,32 +180,21 @@ public class LoaderCursor extends CursorWrapper { * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource. */ protected boolean loadIcon(WorkspaceItemInfo info) { - try (LauncherIcons li = LauncherIcons.obtain(mContext)) { - if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - String packageName = getString(iconPackageIndex); - String resourceName = getString(iconResourceIndex); - if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) { - info.iconResource = new ShortcutIconResource(); - info.iconResource.packageName = packageName; - info.iconResource.resourceName = resourceName; - BitmapInfo iconInfo = li.createIconBitmap(info.iconResource); - if (iconInfo != null) { - info.bitmap = iconInfo; - return true; - } - } - } + return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext); + } - // Failed to load from resource, try loading from DB. - byte[] data = getBlob(iconIndex); - try { - info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length)); - return true; - } catch (Exception e) { - Log.e(TAG, "Failed to decode byte array for info " + info, e); - return false; - } - } + public IconRequestInfo createIconRequestInfo( + WorkspaceItemInfo wai, boolean useLowResIcon) { + String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + ? getString(iconPackageIndex) : null; + String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + ? getString(iconResourceIndex) : null; + byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + || restoreFlag != 0 + ? getBlob(iconIndex) : null; + + return new IconRequestInfo<>( + wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon); } /** @@ -262,6 +247,11 @@ public class LoaderCursor extends CursorWrapper { */ public WorkspaceItemInfo getAppShortcutInfo( Intent intent, boolean allowMissingTarget, boolean useLowResIcon) { + return getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, true); + } + + public WorkspaceItemInfo getAppShortcutInfo( + Intent intent, boolean allowMissingTarget, boolean useLowResIcon, boolean loadIcon) { if (user == null) { Log.d(TAG, "Null user found in getShortcutInfo"); return null; @@ -288,9 +278,11 @@ public class LoaderCursor extends CursorWrapper { info.user = user; info.intent = newIntent; - mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon); - if (mIconCache.isDefaultIcon(info.bitmap, user)) { - loadIcon(info); + if (loadIcon) { + mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon); + if (mIconCache.isDefaultIcon(info.bitmap, user)) { + loadIcon(info); + } } if (mActivityInfo != null) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index f4a0eb89bd..124960679b 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -43,6 +43,7 @@ import android.content.pm.ShortcutInfo; import android.graphics.Point; import android.net.Uri; import android.os.Bundle; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -71,6 +72,7 @@ import com.android.launcher3.icons.cache.IconCacheUpdateHandler; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.IconRequestInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.LauncherAppWidgetInfo; @@ -197,7 +199,12 @@ public class LoaderTask implements Runnable { TimingLogger logger = new TimingLogger(TAG, "run"); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List allShortcuts = new ArrayList<>(); - loadWorkspace(allShortcuts); + Trace.beginSection("LoadWorkspace"); + try { + loadWorkspace(allShortcuts); + } finally { + Trace.endSection(); + } logASplit(logger, "loadWorkspace"); // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. @@ -225,7 +232,13 @@ public class LoaderTask implements Runnable { verifyNotStopped(); // second step - List allActivityList = loadAllApps(); + Trace.beginSection("LoadAllApps"); + List allActivityList; + try { + allActivityList = loadAllApps(); + } finally { + Trace.endSection(); + } logASplit(logger, "loadAllApps"); verifyNotStopped(); @@ -408,6 +421,7 @@ public class LoaderTask implements Runnable { LauncherAppWidgetProviderInfo widgetProviderInfo; Intent intent; String targetPkg; + List> iconRequestInfos = new ArrayList<>(); while (!mStopped && c.moveToNext()) { try { @@ -530,7 +544,10 @@ public class LoaderTask implements Runnable { } else if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { info = c.getAppShortcutInfo( - intent, allowMissingTarget, useLowResIcon); + intent, + allowMissingTarget, + useLowResIcon, + !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()); } else if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { @@ -582,6 +599,8 @@ public class LoaderTask implements Runnable { } if (info != null) { + iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon)); + c.applyCommonProperties(info); info.intent = intent; @@ -799,6 +818,21 @@ public class LoaderTask implements Runnable { Log.e(TAG, "Desktop items loading interrupted", e); } } + if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) { + Trace.beginSection("LoadWorkspaceIconsInBulk"); + try { + mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); + for (IconRequestInfo iconRequestInfo : + iconRequestInfos) { + WorkspaceItemInfo wai = iconRequestInfo.itemInfo; + if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { + iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); + } + } + } finally { + Trace.endSection(); + } + } } finally { IOUtils.closeSilently(c); } diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java new file mode 100644 index 0000000000..2f566f6fc0 --- /dev/null +++ b/src/com/android/launcher3/model/data/IconRequestInfo.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model.data; + +import static android.graphics.BitmapFactory.decodeByteArray; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.LauncherActivityInfo; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.icons.LauncherIcons; + +/** + * Class representing one request for an icon to be queried in a sql database. + * + * @param ItemInfoWithIcon subclass whose title and icon can be loaded and filled by an sql + * query. + */ +public class IconRequestInfo { + + private static final String TAG = "IconRequestInfo"; + + @NonNull public final T itemInfo; + @Nullable public final LauncherActivityInfo launcherActivityInfo; + @Nullable public final String packageName; + @Nullable public final String resourceName; + @Nullable public final byte[] iconBlob; + public final boolean useLowResIcon; + + public IconRequestInfo( + @NonNull T itemInfo, + @Nullable LauncherActivityInfo launcherActivityInfo, + @Nullable String packageName, + @Nullable String resourceName, + @Nullable byte[] iconBlob, + boolean useLowResIcon) { + this.itemInfo = itemInfo; + this.launcherActivityInfo = launcherActivityInfo; + this.packageName = packageName; + this.resourceName = resourceName; + this.iconBlob = iconBlob; + this.useLowResIcon = useLowResIcon; + } + + /** Loads */ + public boolean loadWorkspaceIcon(Context context) { + if (!(itemInfo instanceof WorkspaceItemInfo)) { + throw new IllegalStateException( + "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo); + } + + try (LauncherIcons li = LauncherIcons.obtain(context)) { + WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo; + if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { + if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) { + info.iconResource = new Intent.ShortcutIconResource(); + info.iconResource.packageName = packageName; + info.iconResource.resourceName = resourceName; + BitmapInfo iconInfo = li.createIconBitmap(info.iconResource); + if (iconInfo != null) { + info.bitmap = iconInfo; + return true; + } + } + } + + // Failed to load from resource, try loading from DB. + try { + if (iconBlob == null) { + return false; + } + info.bitmap = li.createIconBitmap(decodeByteArray( + iconBlob, 0, iconBlob.length)); + return true; + } catch (Exception e) { + Log.e(TAG, "Failed to decode byte array for info " + info, e); + return false; + } + } + } +} diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 1a6ce8ccae..cd0c7f2240 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -153,8 +153,6 @@ public abstract class AbstractLauncherUiTest { public static String dumpHprofData() { String result; if (sDumpWasGenerated) { - Log.d("b/195319692", "dump has already been generated by another test", - new Exception()); result = "dump has already been generated by another test"; } else { try { @@ -169,7 +167,6 @@ public abstract class AbstractLauncherUiTest { "am dumpheap " + device.getLauncherPackageName() + " " + fileName); } sDumpWasGenerated = true; - Log.d("b/195319692", "sDumpWasGenerated := true", new Exception()); result = "memory dump filename: " + fileName; } catch (Throwable e) { Log.e(TAG, "dumpHprofData failed", e);