diff options
author | 2024-10-09 18:24:58 +0000 | |
---|---|---|
committer | 2024-10-09 18:24:58 +0000 | |
commit | 102bf02d3a21cdf402e6b1de6f3cb0794ca9c34f (patch) | |
tree | 7b94ed3e6d0653ae90427d88f8089f4ec25f868a /java/src | |
parent | a8b34a512b4c29e6ebc0c03c10f1cae11171bcf1 (diff) | |
parent | de09e2d769cbffdc3ce206e5bd28aec85ddaf635 (diff) |
Merge "CachingTargetDataLoader to cache bitmaps and not drawables" into main
Diffstat (limited to 'java/src')
9 files changed, 71 insertions, 69 deletions
diff --git a/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java b/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java index ae80fad4..ff0bda01 100644 --- a/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java +++ b/java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -136,7 +137,7 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment final TargetPresentationGetter pg = getProvidingAppPresentationGetter(); title.setText(isShortcutTarget() ? mShortcutTitle : pg.getLabel()); - icon.setImageDrawable(pg.getIcon(mUserHandle)); + icon.setImageDrawable(new BitmapDrawable(getResources(), pg.getIconBitmap(mUserHandle))); rv.setAdapter(new VHAdapter(items)); return v; diff --git a/java/src/com/android/intentresolver/TargetPresentationGetter.java b/java/src/com/android/intentresolver/TargetPresentationGetter.java index 910c65c9..ac74366e 100644 --- a/java/src/com/android/intentresolver/TargetPresentationGetter.java +++ b/java/src/com/android/intentresolver/TargetPresentationGetter.java @@ -23,7 +23,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.text.TextUtils; @@ -77,7 +76,7 @@ public abstract class TargetPresentationGetter { @Nullable protected abstract String getAppLabelForSubstitutePermission(); - private Context mContext; + private final Context mContext; private final int mIconDpi; private final boolean mHasSubstitutePermission; private final ApplicationInfo mAppInfo; @@ -88,14 +87,6 @@ public abstract class TargetPresentationGetter { * Retrieve the image that should be displayed as the icon when this target is presented to the * specified {@code userHandle}. */ - public Drawable getIcon(UserHandle userHandle) { - return new BitmapDrawable(mContext.getResources(), getIconBitmap(userHandle)); - } - - /** - * Retrieve the image that should be displayed as the icon when this target is presented to the - * specified {@code userHandle}. - */ public Bitmap getIconBitmap(@Nullable UserHandle userHandle) { Drawable drawable = null; if (mHasSubstitutePermission) { diff --git a/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java b/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java index 2eceb89c..f09fcfc5 100644 --- a/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java +++ b/java/src/com/android/intentresolver/icons/BaseLoadIconTask.java @@ -17,34 +17,31 @@ package com.android.intentresolver.icons; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; import android.os.AsyncTask; -import com.android.intentresolver.R; +import androidx.annotation.Nullable; + import com.android.intentresolver.TargetPresentationGetter; import java.util.function.Consumer; -abstract class BaseLoadIconTask extends AsyncTask<Void, Void, Drawable> { +abstract class BaseLoadIconTask extends AsyncTask<Void, Void, Bitmap> { protected final Context mContext; protected final TargetPresentationGetter.Factory mPresentationFactory; - private final Consumer<Drawable> mCallback; + private final Consumer<Bitmap> mCallback; BaseLoadIconTask( Context context, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Consumer<Bitmap> callback) { mContext = context; mPresentationFactory = presentationFactory; mCallback = callback; } - protected final Drawable loadIconPlaceholder() { - return mContext.getDrawable(R.drawable.resolver_icon_placeholder); - } - @Override - protected final void onPostExecute(Drawable d) { + protected final void onPostExecute(@Nullable Bitmap d) { mCallback.accept(d); } } diff --git a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt index 8474b4c3..b0c26777 100644 --- a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt @@ -17,6 +17,9 @@ package com.android.intentresolver.icons import android.content.ComponentName +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.UserHandle import androidx.collection.LruCache @@ -28,23 +31,26 @@ import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.BINARY) annotation class Caching -private typealias IconCache = LruCache<String, Drawable> +private typealias IconCache = LruCache<String, Bitmap> class CachingTargetDataLoader( + private val context: Context, private val targetDataLoader: TargetDataLoader, private val cacheSize: Int = 100, -) : TargetDataLoader() { +) : TargetDataLoader { @GuardedBy("self") private val perProfileIconCache = HashMap<UserHandle, IconCache>() override fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, - callback: Consumer<Drawable> + callback: Consumer<Drawable>, ): Drawable? { val cacheKey = info.toCacheKey() - return getCachedAppIcon(cacheKey, userHandle) + return getCachedAppIcon(cacheKey, userHandle)?.let { BitmapDrawable(context.resources, it) } ?: targetDataLoader.getOrLoadAppTargetIcon(info, userHandle) { drawable -> - getProfileIconCache(userHandle).put(cacheKey, drawable) + (drawable as? BitmapDrawable)?.bitmap?.let { + getProfileIconCache(userHandle).put(cacheKey, it) + } callback.accept(drawable) } } @@ -52,13 +58,17 @@ class CachingTargetDataLoader( override fun getOrLoadDirectShareIcon( info: SelectableTargetInfo, userHandle: UserHandle, - callback: Consumer<Drawable> + callback: Consumer<Drawable>, ): Drawable? { val cacheKey = info.toCacheKey() - return cacheKey?.let { getCachedAppIcon(it, userHandle) } + return cacheKey + ?.let { getCachedAppIcon(it, userHandle) } + ?.let { BitmapDrawable(context.resources, it) } ?: targetDataLoader.getOrLoadDirectShareIcon(info, userHandle) { drawable -> if (cacheKey != null) { - getProfileIconCache(userHandle).put(cacheKey, drawable) + (drawable as? BitmapDrawable)?.bitmap?.let { + getProfileIconCache(userHandle).put(cacheKey, it) + } } callback.accept(drawable) } @@ -69,7 +79,7 @@ class CachingTargetDataLoader( override fun getOrLoadLabel(info: DisplayResolveInfo) = targetDataLoader.getOrLoadLabel(info) - private fun getCachedAppIcon(component: String, userHandle: UserHandle): Drawable? = + private fun getCachedAppIcon(component: String, userHandle: UserHandle): Bitmap? = getProfileIconCache(userHandle)[component] private fun getProfileIconCache(userHandle: UserHandle): IconCache = @@ -78,10 +88,7 @@ class CachingTargetDataLoader( } private fun DisplayResolveInfo.toCacheKey() = - ComponentName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name, - ) + ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) .flattenToString() private fun SelectableTargetInfo.toCacheKey(): String? = diff --git a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt index e7392f58..117c769d 100644 --- a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt @@ -18,6 +18,7 @@ package com.android.intentresolver.icons import android.app.ActivityManager import android.content.Context +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.AsyncTask import android.os.UserHandle @@ -26,6 +27,7 @@ import androidx.annotation.GuardedBy import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner +import com.android.intentresolver.R import com.android.intentresolver.TargetPresentationGetter import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.chooser.SelectableTargetInfo @@ -40,12 +42,12 @@ class DefaultTargetDataLoader( private val context: Context, private val lifecycle: Lifecycle, private val isAudioCaptureDevice: Boolean, -) : TargetDataLoader() { +) : TargetDataLoader { private val presentationFactory = TargetPresentationGetter.Factory( context, context.getSystemService(ActivityManager::class.java)?.launcherLargeIconDensity - ?: error("Unable to access ActivityManager") + ?: error("Unable to access ActivityManager"), ) private val nextTaskId = AtomicInteger(0) @GuardedBy("self") private val activeTasks = SparseArray<AsyncTask<*, *, *>>() @@ -68,9 +70,11 @@ class DefaultTargetDataLoader( callback: Consumer<Drawable>, ): Drawable? { val taskId = nextTaskId.getAndIncrement() - LoadIconTask(context, info, userHandle, presentationFactory) { result -> + LoadIconTask(context, info, presentationFactory) { bitmap -> removeTask(taskId) - callback.accept(result) + callback.accept( + bitmap?.let { BitmapDrawable(context.resources, it) } ?: loadIconPlaceholder() + ) } .also { addTask(taskId, it) } .executeOnExecutor(executor) @@ -87,9 +91,11 @@ class DefaultTargetDataLoader( context.createContextAsUser(userHandle, 0), info, presentationFactory, - ) { result -> + ) { bitmap -> removeTask(taskId) - callback.accept(result) + callback.accept( + bitmap?.let { BitmapDrawable(context.resources, it) } ?: loadIconPlaceholder() + ) } .also { addTask(taskId, it) } .executeOnExecutor(executor) @@ -123,6 +129,9 @@ class DefaultTargetDataLoader( synchronized(activeTasks) { activeTasks.remove(id) } } + private fun loadIconPlaceholder(): Drawable = + requireNotNull(context.getDrawable(R.drawable.resolver_icon_placeholder)) + private fun destroy() { synchronized(activeTasks) { for (i in 0 until activeTasks.size()) { diff --git a/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java b/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java index e2c0362d..641a0d6a 100644 --- a/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java +++ b/java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java @@ -23,7 +23,6 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Trace; @@ -50,19 +49,20 @@ class LoadDirectShareIconTask extends BaseLoadIconTask { Context context, SelectableTargetInfo targetInfo, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Consumer<Bitmap> callback) { super(context, presentationFactory, callback); mTargetInfo = targetInfo; } @Override - protected Drawable doInBackground(Void... voids) { - Drawable drawable = null; + @Nullable + protected Bitmap doInBackground(Void... voids) { + Bitmap iconBitmap = null; Trace.beginSection("shortcut-icon"); try { final Icon icon = mTargetInfo.getChooserTargetIcon(); if (icon == null || UriFilters.hasValidIcon(icon)) { - drawable = getChooserTargetIconDrawable( + iconBitmap = getChooserTargetIconBitmap( mContext, icon, mTargetInfo.getChooserTargetComponentName(), @@ -71,25 +71,21 @@ class LoadDirectShareIconTask extends BaseLoadIconTask { Log.e(TAG, "Failed to load shortcut icon for " + mTargetInfo.getChooserTargetComponentName() + "; no access"); } - if (drawable == null) { - drawable = loadIconPlaceholder(); - } } catch (Exception e) { Log.e( TAG, "Failed to load shortcut icon for " + mTargetInfo.getChooserTargetComponentName(), e); - drawable = loadIconPlaceholder(); } finally { Trace.endSection(); } - return drawable; + return iconBitmap; } @WorkerThread @Nullable - private Drawable getChooserTargetIconDrawable( + private Bitmap getChooserTargetIconBitmap( Context context, @Nullable Icon icon, ComponentName targetComponentName, @@ -129,6 +125,6 @@ class LoadDirectShareIconTask extends BaseLoadIconTask { Bitmap directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon); sif.recycle(); - return new BitmapDrawable(context.getResources(), directShareBadgedIcon); + return directShareBadgedIcon; } } diff --git a/java/src/com/android/intentresolver/icons/LoadIconTask.java b/java/src/com/android/intentresolver/icons/LoadIconTask.java index 75132208..4573fadf 100644 --- a/java/src/com/android/intentresolver/icons/LoadIconTask.java +++ b/java/src/com/android/intentresolver/icons/LoadIconTask.java @@ -19,11 +19,12 @@ package com.android.intentresolver.icons; import android.content.ComponentName; import android.content.Context; import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; import android.os.Trace; -import android.os.UserHandle; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.intentresolver.TargetPresentationGetter; import com.android.intentresolver.chooser.DisplayResolveInfo; @@ -32,38 +33,36 @@ import java.util.function.Consumer; class LoadIconTask extends BaseLoadIconTask { private static final String TAG = "IconTask"; protected final DisplayResolveInfo mDisplayResolveInfo; - private final UserHandle mUserHandle; private final ResolveInfo mResolveInfo; LoadIconTask( Context context, DisplayResolveInfo dri, - UserHandle userHandle, TargetPresentationGetter.Factory presentationFactory, - Consumer<Drawable> callback) { + Consumer<Bitmap> callback) { super(context, presentationFactory, callback); - mUserHandle = userHandle; mDisplayResolveInfo = dri; mResolveInfo = dri.getResolveInfo(); } @Override - protected Drawable doInBackground(Void... params) { + @Nullable + protected Bitmap doInBackground(Void... params) { Trace.beginSection("app-icon"); try { return loadIconForResolveInfo(mResolveInfo); } catch (Exception e) { ComponentName componentName = mDisplayResolveInfo.getResolvedComponentName(); Log.e(TAG, "Failed to load app icon for " + componentName, e); - return loadIconPlaceholder(); + return null; } finally { Trace.endSection(); } } - protected final Drawable loadIconForResolveInfo(ResolveInfo ri) { + protected final Bitmap loadIconForResolveInfo(ResolveInfo ri) { // Load icons based on userHandle from ResolveInfo. If in work profile/clone profile, icons // should be badged. - return mPresentationFactory.makePresentationGetter(ri).getIcon(ri.userHandle); + return mPresentationFactory.makePresentationGetter(ri).getIconBitmap(ri.userHandle); } } diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt index 935b527a..7cbd040e 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt @@ -23,24 +23,24 @@ import com.android.intentresolver.chooser.SelectableTargetInfo import java.util.function.Consumer /** A target data loader contract. Added to support testing. */ -abstract class TargetDataLoader { +interface TargetDataLoader { /** Load an app target icon */ - abstract fun getOrLoadAppTargetIcon( + fun getOrLoadAppTargetIcon( info: DisplayResolveInfo, userHandle: UserHandle, callback: Consumer<Drawable>, ): Drawable? /** Load a shortcut icon */ - abstract fun getOrLoadDirectShareIcon( + fun getOrLoadDirectShareIcon( info: SelectableTargetInfo, userHandle: UserHandle, callback: Consumer<Drawable>, ): Drawable? /** Load target label */ - abstract fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) + fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) /** Loads DisplayResolveInfo's display label synchronously, if needed */ - abstract fun getOrLoadLabel(info: DisplayResolveInfo) + fun getOrLoadLabel(info: DisplayResolveInfo) } diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt index 9c0acb11..86ebb9d9 100644 --- a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt +++ b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt @@ -39,6 +39,8 @@ object TargetDataLoaderModule { @Provides @ActivityScoped @Caching - fun cachingTargetDataLoader(targetDataLoader: TargetDataLoader): TargetDataLoader = - CachingTargetDataLoader(targetDataLoader) + fun cachingTargetDataLoader( + @ActivityContext context: Context, + targetDataLoader: TargetDataLoader, + ): TargetDataLoader = CachingTargetDataLoader(context, targetDataLoader) } |