Merge changes from topic "launcher-loading" into sc-v2-dev
* changes: Improve workspace loading times. Add tracing to help in launcher load time profiling.
This commit is contained in:
committed by
Android (Google) Code Review
commit
ca1ee7a161
@@ -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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 <T extends ItemInfoWithIcon> Cursor createBulkQueryCursor(
|
||||
List<IconRequestInfo<T>> 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 <T extends ItemInfoWithIcon> void getTitlesAndIconsInBulk(
|
||||
List<IconRequestInfo<T>> iconRequestInfos) {
|
||||
Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
|
||||
iconRequestInfos.stream()
|
||||
.collect(groupingBy(iconRequest ->
|
||||
Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
|
||||
|
||||
Trace.beginSection("loadIconsInBulk");
|
||||
iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
|
||||
Map<ComponentName, List<IconRequestInfo<T>>> 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<IconRequestInfo<T>> 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<T> 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.
|
||||
|
||||
@@ -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<WorkspaceItemInfo> 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) {
|
||||
|
||||
@@ -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<ShortcutInfo> 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<LauncherActivityInfo> allActivityList = loadAllApps();
|
||||
Trace.beginSection("LoadAllApps");
|
||||
List<LauncherActivityInfo> 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<IconRequestInfo<WorkspaceItemInfo>> 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<WorkspaceItemInfo> iconRequestInfo :
|
||||
iconRequestInfos) {
|
||||
WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
|
||||
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
|
||||
iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Trace.endSection();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeSilently(c);
|
||||
}
|
||||
|
||||
@@ -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 <T> ItemInfoWithIcon subclass whose title and icon can be loaded and filled by an sql
|
||||
* query.
|
||||
*/
|
||||
public class IconRequestInfo<T extends ItemInfoWithIcon> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user