summaryrefslogtreecommitdiff
path: root/java/src/com
diff options
context:
space:
mode:
author Andrey Yepin <ayepin@google.com> 2024-10-07 15:43:44 -0700
committer Andrey Yepin <ayepin@google.com> 2024-10-08 15:45:57 -0700
commitde09e2d769cbffdc3ce206e5bd28aec85ddaf635 (patch)
tree3ab4c3cb3941f8e2761a3d77209bf6c9df9114db /java/src/com
parentf6400db571602fbbb3c5fa88276c3e5ed40792da (diff)
CachingTargetDataLoader to cache bitmaps and not drawables
A preparation refactoring. Make ChachingTargetDataLoader to cache icon bitmaps instead of the end drawables. Drawables carry state and the same drawable can be used in two places in the target grid (i.e. in the ranked targets row and in the all-target grid) yet the new hover effect needs to be provided independently for each position. Bug: 295175912 Test: manual basic functinality testing including Shareousel selection change. Test: atest IntentResolver-tests-unit Flag: EXEMPT refactoring Change-Id: I1c5e6333310e0984e39e22cab8cf162f2f31d6e8 Change-Id: I4a3b5dff8fcfcffe7742be8ea3bd348351332c9e
Diffstat (limited to 'java/src/com')
-rw-r--r--java/src/com/android/intentresolver/ChooserTargetActionsDialogFragment.java3
-rw-r--r--java/src/com/android/intentresolver/TargetPresentationGetter.java11
-rw-r--r--java/src/com/android/intentresolver/icons/BaseLoadIconTask.java17
-rw-r--r--java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt33
-rw-r--r--java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt21
-rw-r--r--java/src/com/android/intentresolver/icons/LoadDirectShareIconTask.java20
-rw-r--r--java/src/com/android/intentresolver/icons/LoadIconTask.java19
-rw-r--r--java/src/com/android/intentresolver/icons/TargetDataLoader.kt10
-rw-r--r--java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt6
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)
}