1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.icons; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.LauncherActivityInfo; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.graphics.drawable.Drawable; 27 import android.os.Handler; 28 import android.os.Process; 29 import android.os.UserHandle; 30 import android.util.Log; 31 32 import com.android.launcher3.AppInfo; 33 import com.android.launcher3.IconProvider; 34 import com.android.launcher3.InvariantDeviceProfile; 35 import com.android.launcher3.ItemInfoWithIcon; 36 import com.android.launcher3.LauncherFiles; 37 import com.android.launcher3.LauncherModel; 38 import com.android.launcher3.MainThreadExecutor; 39 import com.android.launcher3.WorkspaceItemInfo; 40 import com.android.launcher3.Utilities; 41 import com.android.launcher3.compat.LauncherAppsCompat; 42 import com.android.launcher3.compat.UserManagerCompat; 43 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic; 44 import com.android.launcher3.icons.cache.BaseIconCache; 45 import com.android.launcher3.icons.cache.CachingLogic; 46 import com.android.launcher3.icons.cache.HandlerRunnable; 47 import com.android.launcher3.model.PackageItemInfo; 48 import com.android.launcher3.util.InstantAppResolver; 49 import com.android.launcher3.util.Preconditions; 50 51 import java.util.function.Supplier; 52 53 import androidx.annotation.NonNull; 54 55 /** 56 * Cache of application icons. Icons can be made from any thread. 57 */ 58 public class IconCache extends BaseIconCache { 59 60 private static final String TAG = "Launcher.IconCache"; 61 62 private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); 63 64 private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic; 65 private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic; 66 67 private final LauncherAppsCompat mLauncherApps; 68 private final UserManagerCompat mUserManager; 69 private final InstantAppResolver mInstantAppResolver; 70 private final IconProvider mIconProvider; 71 72 private int mPendingIconRequestCount = 0; 73 IconCache(Context context, InvariantDeviceProfile inv)74 public IconCache(Context context, InvariantDeviceProfile inv) { 75 super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(), 76 inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */); 77 mComponentWithLabelCachingLogic = new ComponentCachingLogic(context); 78 mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this); 79 mLauncherApps = LauncherAppsCompat.getInstance(mContext); 80 mUserManager = UserManagerCompat.getInstance(mContext); 81 mInstantAppResolver = InstantAppResolver.newInstance(mContext); 82 mIconProvider = IconProvider.newInstance(context); 83 } 84 85 @Override getSerialNumberForUser(UserHandle user)86 protected long getSerialNumberForUser(UserHandle user) { 87 return mUserManager.getSerialNumberForUser(user); 88 } 89 90 @Override isInstantApp(ApplicationInfo info)91 protected boolean isInstantApp(ApplicationInfo info) { 92 return mInstantAppResolver.isInstantApp(info); 93 } 94 95 @Override getIconFactory()96 protected BaseIconFactory getIconFactory() { 97 return LauncherIcons.obtain(mContext); 98 } 99 100 /** 101 * Updates the entries related to the given package in memory and persistent DB. 102 */ updateIconsForPkg(String packageName, UserHandle user)103 public synchronized void updateIconsForPkg(String packageName, UserHandle user) { 104 removeIconsForPkg(packageName, user); 105 try { 106 PackageInfo info = mPackageManager.getPackageInfo(packageName, 107 PackageManager.GET_UNINSTALLED_PACKAGES); 108 long userSerial = mUserManager.getSerialNumberForUser(user); 109 for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) { 110 addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial, 111 false /*replace existing*/); 112 } 113 } catch (NameNotFoundException e) { 114 Log.d(TAG, "Package not found", e); 115 } 116 } 117 118 /** 119 * Fetches high-res icon for the provided ItemInfo and updates the caller when done. 120 * @return a request ID that can be used to cancel the request. 121 */ updateIconInBackground(final ItemInfoUpdateReceiver caller, final ItemInfoWithIcon info)122 public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller, 123 final ItemInfoWithIcon info) { 124 Preconditions.assertUIThread(); 125 if (mPendingIconRequestCount <= 0) { 126 LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND); 127 } 128 mPendingIconRequestCount ++; 129 130 IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) { 131 @Override 132 public void run() { 133 if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { 134 getTitleAndIcon(info, false); 135 } else if (info instanceof PackageItemInfo) { 136 getTitleAndIconForApp((PackageItemInfo) info, false); 137 } 138 mMainThreadExecutor.execute(() -> { 139 caller.reapplyItemInfo(info); 140 onEnd(); 141 }); 142 } 143 }; 144 Utilities.postAsyncCallback(mWorkerHandler, request); 145 return request; 146 } 147 onIconRequestEnd()148 private void onIconRequestEnd() { 149 mPendingIconRequestCount --; 150 if (mPendingIconRequestCount <= 0) { 151 LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND); 152 } 153 } 154 155 /** 156 * Updates {@param application} only if a valid entry is found. 157 */ updateTitleAndIcon(AppInfo application)158 public synchronized void updateTitleAndIcon(AppInfo application) { 159 CacheEntry entry = cacheLocked(application.componentName, 160 application.user, () -> null, mLauncherActivityInfoCachingLogic, 161 false, application.usingLowResIcon()); 162 if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) { 163 applyCacheEntry(entry, application); 164 } 165 } 166 167 /** 168 * Fill in {@param info} with the icon and label for {@param activityInfo} 169 */ getTitleAndIcon(ItemInfoWithIcon info, LauncherActivityInfo activityInfo, boolean useLowResIcon)170 public synchronized void getTitleAndIcon(ItemInfoWithIcon info, 171 LauncherActivityInfo activityInfo, boolean useLowResIcon) { 172 // If we already have activity info, no need to use package icon 173 getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon); 174 } 175 176 /** 177 * Fill in {@param info} with the icon and label. If the 178 * corresponding activity is not found, it reverts to the package icon. 179 */ getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon)180 public synchronized void getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon) { 181 // null info means not installed, but if we have a component from the intent then 182 // we should still look in the cache for restored app icons. 183 if (info.getTargetComponent() == null) { 184 info.applyFrom(getDefaultIcon(info.user)); 185 info.title = ""; 186 info.contentDescription = ""; 187 } else { 188 Intent intent = info.getIntent(); 189 getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user), 190 true, useLowResIcon); 191 } 192 } 193 getTitleNoCache(ComponentWithLabel info)194 public synchronized String getTitleNoCache(ComponentWithLabel info) { 195 CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info, 196 mComponentWithLabelCachingLogic, false /* usePackageIcon */, 197 true /* useLowResIcon */, false /* addToMemCache */); 198 return Utilities.trim(entry.title); 199 } 200 201 /** 202 * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info} 203 */ getTitleAndIcon( @onNull ItemInfoWithIcon infoInOut, @NonNull Supplier<LauncherActivityInfo> activityInfoProvider, boolean usePkgIcon, boolean useLowResIcon)204 private synchronized void getTitleAndIcon( 205 @NonNull ItemInfoWithIcon infoInOut, 206 @NonNull Supplier<LauncherActivityInfo> activityInfoProvider, 207 boolean usePkgIcon, boolean useLowResIcon) { 208 CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user, 209 activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon, useLowResIcon); 210 applyCacheEntry(entry, infoInOut); 211 } 212 213 214 /** 215 * Fill in {@param infoInOut} with the corresponding icon and label. 216 */ getTitleAndIconForApp( PackageItemInfo infoInOut, boolean useLowResIcon)217 public synchronized void getTitleAndIconForApp( 218 PackageItemInfo infoInOut, boolean useLowResIcon) { 219 CacheEntry entry = getEntryForPackageLocked( 220 infoInOut.packageName, infoInOut.user, useLowResIcon); 221 applyCacheEntry(entry, infoInOut); 222 } 223 applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info)224 protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) { 225 info.title = Utilities.trim(entry.title); 226 info.contentDescription = entry.contentDescription; 227 info.applyFrom((entry.icon == null) ? getDefaultIcon(info.user) : entry); 228 } 229 getFullResIcon(LauncherActivityInfo info)230 public Drawable getFullResIcon(LauncherActivityInfo info) { 231 return getFullResIcon(info, true); 232 } 233 getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable)234 public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) { 235 return mIconProvider.getIcon(info, mIconDpi, flattenDrawable); 236 } 237 238 @Override getIconSystemState(String packageName)239 protected String getIconSystemState(String packageName) { 240 return mIconProvider.getSystemStateForPackage(mSystemState, packageName); 241 } 242 243 public static abstract class IconLoadRequest extends HandlerRunnable { IconLoadRequest(Handler handler, Runnable endRunnable)244 IconLoadRequest(Handler handler, Runnable endRunnable) { 245 super(handler, endRunnable); 246 } 247 } 248 249 /** 250 * Interface for receiving itemInfo with high-res icon. 251 */ 252 public interface ItemInfoUpdateReceiver { 253 reapplyItemInfo(ItemInfoWithIcon info)254 void reapplyItemInfo(ItemInfoWithIcon info); 255 } 256 } 257