summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-05-06 19:38:43 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-05-06 19:38:43 +0000
commit2f1c0975679a342121489fa8ce8aa60a5782941c (patch)
tree0f40d0a5be370837e7c7494cbb9d6e439cb20b12
parent2d4d3fe6fb73f5ee28f510fbfe1db2ac44c5d780 (diff)
parent729b5dd9fae069c1c34eff2d655620d6b7455c4e (diff)
Merge "Do not animate target icons and labels after payload selection change." into main
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java27
-rw-r--r--java/src/com/android/intentresolver/ChooserListAdapter.java22
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java2
-rw-r--r--java/src/com/android/intentresolver/ResolverListAdapter.java15
-rw-r--r--java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java1
-rw-r--r--java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java2
-rw-r--r--java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt76
-rw-r--r--java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt5
-rw-r--r--java/src/com/android/intentresolver/icons/LabelInfo.kt2
-rw-r--r--java/src/com/android/intentresolver/icons/TargetDataLoader.kt4
-rw-r--r--java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt6
-rw-r--r--tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java5
-rw-r--r--tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt2
13 files changed, 136 insertions, 33 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index 4608f37b..0e519726 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -109,6 +109,7 @@ import com.android.intentresolver.emptystate.NoAppsAvailableEmptyStateProvider;
import com.android.intentresolver.emptystate.NoCrossProfileEmptyStateProvider;
import com.android.intentresolver.emptystate.WorkProfilePausedEmptyStateProvider;
import com.android.intentresolver.grid.ChooserGridAdapter;
+import com.android.intentresolver.icons.Caching;
import com.android.intentresolver.icons.TargetDataLoader;
import com.android.intentresolver.inject.Background;
import com.android.intentresolver.logging.EventLog;
@@ -166,6 +167,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -256,7 +258,11 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Inject @AppPredictionAvailable public boolean mAppPredictionAvailable;
@Inject @ImageEditor public Optional<ComponentName> mImageEditor;
@Inject @NearbyShare public Optional<ComponentName> mNearbyShare;
- @Inject public TargetDataLoader mTargetDataLoader;
+ protected TargetDataLoader mTargetDataLoader;
+ @Inject public Provider<TargetDataLoader> mTargetDataLoaderProvider;
+ @Inject
+ @Caching
+ public Provider<TargetDataLoader> mCachingTargetDataLoaderProvider;
@Inject public DevicePolicyResources mDevicePolicyResources;
@Inject public ProfilePagerResources mProfilePagerResources;
@Inject public PackageManager mPackageManager;
@@ -331,6 +337,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
+ mTargetDataLoader = mChooserServiceFeatureFlags.chooserPayloadToggling()
+ ? mCachingTargetDataLoaderProvider.get()
+ : mTargetDataLoaderProvider.get();
+
setTheme(R.style.Theme_DeviceDefault_Chooser);
// Initializer is invoked when this function returns, via Lifecycle.
@@ -767,6 +777,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
mRequest.getInitialIntents(),
mMaxTargetsPerRow);
mChooserMultiProfilePagerAdapter.setCurrentPage(currentPage);
+ for (int i = 0, count = mChooserMultiProfilePagerAdapter.getItemCount(); i < count; i++) {
+ mChooserMultiProfilePagerAdapter.getPageAdapterForIndex(i)
+ .getListAdapter().setAnimateItems(false);
+ }
if (mPersonalPackageMonitor != null) {
mPersonalPackageMonitor.unregister();
}
@@ -1375,17 +1389,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
return context.getSharedPreferences(PINNED_SHARED_PREFS_NAME, MODE_PRIVATE);
}
- protected ChooserMultiProfilePagerAdapter createMultiProfilePagerAdapter() {
- return createMultiProfilePagerAdapter(
- /* context = */ this,
- mProfilePagerResources,
- mViewModel.getRequest().getValue(),
- mProfiles,
- mProfileAvailability,
- mRequest.getInitialIntents(),
- mMaxTargetsPerRow);
- }
-
private ChooserMultiProfilePagerAdapter createMultiProfilePagerAdapter(
Context context,
ProfilePagerResources profilePagerResources,
diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java
index 29b5698b..8b848e55 100644
--- a/java/src/com/android/intentresolver/ChooserListAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserListAdapter.java
@@ -153,6 +153,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
};
+ private boolean mAnimateItems = true;
+
public ChooserListAdapter(
Context context,
List<Intent> payloadIntents,
@@ -308,6 +310,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
+ public void setAnimateItems(boolean animateItems) {
+ mAnimateItems = animateItems;
+ }
+
@Override
public void handlePackagesChanged() {
if (mPackageChangeCallback != null) {
@@ -371,18 +377,15 @@ public class ChooserListAdapter extends ResolverListAdapter {
final CharSequence displayLabel = Objects.requireNonNullElse(info.getDisplayLabel(), "");
final CharSequence extendedInfo = Objects.requireNonNullElse(info.getExtendedInfo(), "");
holder.bindLabel(displayLabel, extendedInfo);
- if (!TextUtils.isEmpty(displayLabel)) {
+ if (mAnimateItems && !TextUtils.isEmpty(displayLabel)) {
mAnimationTracker.animateLabel(holder.text, info);
}
- if (!TextUtils.isEmpty(extendedInfo) && holder.text2.getVisibility() == View.VISIBLE) {
+ if (mAnimateItems
+ && !TextUtils.isEmpty(extendedInfo)
+ && holder.text2.getVisibility() == View.VISIBLE) {
mAnimationTracker.animateLabel(holder.text2, info);
}
- holder.bindIcon(info);
- if (info.hasDisplayIcon()) {
- mAnimationTracker.animateIcon(holder.icon, info);
- }
-
if (info.isSelectableTargetInfo()) {
// direct share targets should append the application name for a better readout
DisplayResolveInfo rInfo = info.getDisplayResolveInfo();
@@ -418,6 +421,11 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
+ holder.bindIcon(info);
+ if (mAnimateItems && info.hasDisplayIcon()) {
+ mAnimationTracker.animateIcon(holder.icon, info);
+ }
+
if (info.isPlaceHolderTargetInfo()) {
bindPlaceholder(holder);
}
diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java
index e79cb2d1..a402fc72 100644
--- a/java/src/com/android/intentresolver/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/ResolverActivity.java
@@ -1357,7 +1357,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// Load the icon asynchronously
ImageView icon = findViewById(com.android.internal.R.id.icon);
- targetDataLoader.loadAppTargetIcon(
+ targetDataLoader.getOrLoadAppTargetIcon(
otherProfileResolveInfo,
inactiveAdapter.getUserHandle(),
(drawable) -> {
diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java
index 2a8fcfa4..5fd37d43 100644
--- a/java/src/com/android/intentresolver/ResolverListAdapter.java
+++ b/java/src/com/android/intentresolver/ResolverListAdapter.java
@@ -739,26 +739,31 @@ public class ResolverListAdapter extends BaseAdapter {
holder.bindLabel("", "");
loadLabel(dri);
}
- holder.bindIcon(info);
if (!dri.hasDisplayIcon()) {
loadIcon(dri);
}
+ holder.bindIcon(info);
}
}
protected final void loadIcon(DisplayResolveInfo info) {
if (mRequestedIcons.add(info)) {
- mTargetDataLoader.loadAppTargetIcon(
+ Drawable icon = mTargetDataLoader.getOrLoadAppTargetIcon(
info,
getUserHandle(),
- (drawable) -> onIconLoaded(info, drawable));
+ (drawable) -> {
+ onIconLoaded(info, drawable);
+ notifyDataSetChanged();
+ });
+ if (icon != null) {
+ onIconLoaded(info, icon);
+ }
}
}
private void onIconLoaded(DisplayResolveInfo displayResolveInfo, Drawable drawable) {
if (!displayResolveInfo.hasDisplayIcon()) {
displayResolveInfo.getDisplayIconHolder().setDisplayIcon(drawable);
- notifyDataSetChanged();
}
}
@@ -822,7 +827,7 @@ public class ResolverListAdapter extends BaseAdapter {
public void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
final DisplayResolveInfo iconInfo = getFilteredItem();
if (iconInfo != null) {
- mTargetDataLoader.loadAppTargetIcon(
+ mTargetDataLoader.getOrLoadAppTargetIcon(
iconInfo, getUserHandle(), iconView::setImageDrawable);
}
}
diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
index 536f11ce..5e44c53e 100644
--- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
+++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
@@ -196,6 +196,7 @@ public class DisplayResolveInfo implements TargetInfo {
}
@Override
+ @NonNull
public ComponentName getResolvedComponentName() {
return new ComponentName(mResolveInfo.activityInfo.packageName,
mResolveInfo.activityInfo.name);
diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
index 4fe28384..95cb443e 100644
--- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
+++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
@@ -123,6 +124,7 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo {
}
@Override
+ @NonNull
public ComponentName getResolvedComponentName() {
if (hasSelected()) {
return mTargetInfos.get(mSelected).getResolvedComponentName();
diff --git a/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt
new file mode 100644
index 00000000..b3054231
--- /dev/null
+++ b/java/src/com/android/intentresolver/icons/CachingTargetDataLoader.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 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.intentresolver.icons
+
+import android.content.ComponentName
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import androidx.collection.LruCache
+import com.android.intentresolver.chooser.DisplayResolveInfo
+import com.android.intentresolver.chooser.SelectableTargetInfo
+import java.util.function.Consumer
+import javax.annotation.concurrent.GuardedBy
+import javax.inject.Qualifier
+
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.BINARY) annotation class Caching
+
+private typealias IconCache = LruCache<ComponentName, Drawable>
+
+class CachingTargetDataLoader(
+ private val targetDataLoader: TargetDataLoader,
+ private val cacheSize: Int = 100,
+) : TargetDataLoader() {
+ @GuardedBy("self") private val perProfileIconCache = HashMap<UserHandle, IconCache>()
+
+ override fun getOrLoadAppTargetIcon(
+ info: DisplayResolveInfo,
+ userHandle: UserHandle,
+ callback: Consumer<Drawable>
+ ): Drawable? {
+ val cacheKey = info.toCacheKey()
+ return getCachedAppIcon(cacheKey, userHandle)
+ ?: targetDataLoader.getOrLoadAppTargetIcon(info, userHandle) { drawable ->
+ getProfileIconCache(userHandle).put(cacheKey, drawable)
+ callback.accept(drawable)
+ }
+ }
+
+ override fun loadDirectShareIcon(
+ info: SelectableTargetInfo,
+ userHandle: UserHandle,
+ callback: Consumer<Drawable>
+ ) = targetDataLoader.loadDirectShareIcon(info, userHandle, callback)
+
+ override fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) =
+ targetDataLoader.loadLabel(info, callback)
+
+ override fun getOrLoadLabel(info: DisplayResolveInfo) = targetDataLoader.getOrLoadLabel(info)
+
+ private fun getCachedAppIcon(component: ComponentName, userHandle: UserHandle): Drawable? =
+ getProfileIconCache(userHandle)[component]
+
+ private fun getProfileIconCache(userHandle: UserHandle): IconCache =
+ synchronized(perProfileIconCache) {
+ perProfileIconCache.getOrPut(userHandle) { IconCache(cacheSize) }
+ }
+
+ private fun DisplayResolveInfo.toCacheKey() =
+ ComponentName(
+ resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name,
+ )
+}
diff --git a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt
index 054fbe71..1a724d73 100644
--- a/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt
+++ b/java/src/com/android/intentresolver/icons/DefaultTargetDataLoader.kt
@@ -62,11 +62,11 @@ class DefaultTargetDataLoader(
)
}
- override fun loadAppTargetIcon(
+ override fun getOrLoadAppTargetIcon(
info: DisplayResolveInfo,
userHandle: UserHandle,
callback: Consumer<Drawable>,
- ) {
+ ): Drawable? {
val taskId = nextTaskId.getAndIncrement()
LoadIconTask(context, info, userHandle, presentationFactory) { result ->
removeTask(taskId)
@@ -74,6 +74,7 @@ class DefaultTargetDataLoader(
}
.also { addTask(taskId, it) }
.executeOnExecutor(executor)
+ return null
}
override fun loadDirectShareIcon(
diff --git a/java/src/com/android/intentresolver/icons/LabelInfo.kt b/java/src/com/android/intentresolver/icons/LabelInfo.kt
index a9c4cd77..4b60d607 100644
--- a/java/src/com/android/intentresolver/icons/LabelInfo.kt
+++ b/java/src/com/android/intentresolver/icons/LabelInfo.kt
@@ -16,4 +16,4 @@
package com.android.intentresolver.icons
-class LabelInfo(val label: CharSequence?, val subLabel: CharSequence?)
+data class LabelInfo(val label: CharSequence?, val subLabel: CharSequence?)
diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt
index 07c62177..7789df44 100644
--- a/java/src/com/android/intentresolver/icons/TargetDataLoader.kt
+++ b/java/src/com/android/intentresolver/icons/TargetDataLoader.kt
@@ -25,11 +25,11 @@ import java.util.function.Consumer
/** A target data loader contract. Added to support testing. */
abstract class TargetDataLoader {
/** Load an app target icon */
- abstract fun loadAppTargetIcon(
+ abstract fun getOrLoadAppTargetIcon(
info: DisplayResolveInfo,
userHandle: UserHandle,
callback: Consumer<Drawable>,
- )
+ ): Drawable?
/** Load a shortcut icon */
abstract fun loadDirectShareIcon(
diff --git a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt
index 32c040b8..9c0acb11 100644
--- a/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt
+++ b/java/src/com/android/intentresolver/icons/TargetDataLoaderModule.kt
@@ -35,4 +35,10 @@ object TargetDataLoaderModule {
@ActivityContext context: Context,
@ActivityOwned lifecycle: Lifecycle,
): TargetDataLoader = DefaultTargetDataLoader(context, lifecycle, isAudioCaptureDevice = false)
+
+ @Provides
+ @ActivityScoped
+ @Caching
+ fun cachingTargetDataLoader(targetDataLoader: TargetDataLoader): TargetDataLoader =
+ CachingTargetDataLoader(targetDataLoader)
}
diff --git a/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java b/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java
index 30858c8e..b46d8bc3 100644
--- a/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java
+++ b/tests/activity/src/com/android/intentresolver/ResolverWrapperActivity.java
@@ -171,11 +171,12 @@ public class ResolverWrapperActivity extends ResolverActivity {
}
@Override
- public void loadAppTargetIcon(
+ @Nullable
+ public Drawable getOrLoadAppTargetIcon(
@NonNull DisplayResolveInfo info,
@NonNull UserHandle userHandle,
@NonNull Consumer<Drawable> callback) {
- mTargetDataLoader.loadAppTargetIcon(info, userHandle, callback);
+ return mTargetDataLoader.getOrLoadAppTargetIcon(info, userHandle, callback);
}
@Override
diff --git a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
index bbe8a29e..5ac4f2b0 100644
--- a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
@@ -140,7 +140,7 @@ class ChooserListAdapterTest {
testSubject.onBindView(view, targetInfo, 0)
- verify(mTargetDataLoader, times(1)).loadAppTargetIcon(any(), any(), any())
+ verify(mTargetDataLoader, times(1)).getOrLoadAppTargetIcon(any(), any(), any())
}
@Test