diff options
4 files changed, 111 insertions, 1 deletions
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig index ae0b56e6f009..45a21beabd89 100644 --- a/core/java/android/service/chooser/flags.aconfig +++ b/core/java/android/service/chooser/flags.aconfig @@ -44,6 +44,16 @@ flag { } flag { + name: "notify_single_item_change_on_icon_load" + namespace: "intentresolver" + description: "ChooserGridAdapter to notify specific items change when the target icon is loaded (instead of all-item change)." + bug: "298193161" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "fix_resolver_memory_leak" is_exported: true namespace: "intentresolver" diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c009fc3b7e63..9bc6671bbc31 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -23,6 +23,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; import static android.content.ContentProvider.getUriWithoutUserId; import static android.content.ContentProvider.getUserIdFromUri; +import static android.service.chooser.Flags.notifySingleItemChangeOnIconLoad; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; @@ -163,9 +164,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -3212,6 +3215,8 @@ public class ChooserActivity extends ResolverActivity implements private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20; + private final Set<ViewHolderBase> mBoundViewHolders = new HashSet<>(); + ChooserGridAdapter(ChooserListAdapter wrappedAdapter) { super(); mChooserListAdapter = wrappedAdapter; @@ -3232,6 +3237,31 @@ public class ChooserActivity extends ResolverActivity implements notifyDataSetChanged(); } }); + if (notifySingleItemChangeOnIconLoad()) { + wrappedAdapter.setOnIconLoadedListener(this::onTargetIconLoaded); + } + } + + private void onTargetIconLoaded(DisplayResolveInfo info) { + for (ViewHolderBase holder : mBoundViewHolders) { + switch (holder.getViewType()) { + case VIEW_TYPE_NORMAL: + TargetInfo itemInfo = + mChooserListAdapter.getItem( + ((ItemViewHolder) holder).mListPosition); + if (info == itemInfo) { + notifyItemChanged(holder.getAdapterPosition()); + } + break; + case VIEW_TYPE_CALLER_AND_RANK: + ItemGroupViewHolder groupHolder = (ItemGroupViewHolder) holder; + if (suggestedAppsGroupContainsTarget(groupHolder, info)) { + notifyItemChanged(holder.getAdapterPosition()); + } + break; + } + + } } public void setFooterHeight(int height) { @@ -3382,6 +3412,9 @@ public class ChooserActivity extends ResolverActivity implements @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (notifySingleItemChangeOnIconLoad()) { + mBoundViewHolders.add((ViewHolderBase) holder); + } int viewType = ((ViewHolderBase) holder).getViewType(); switch (viewType) { case VIEW_TYPE_DIRECT_SHARE: @@ -3396,6 +3429,22 @@ public class ChooserActivity extends ResolverActivity implements } @Override + public void onViewRecycled(RecyclerView.ViewHolder holder) { + if (notifySingleItemChangeOnIconLoad()) { + mBoundViewHolders.remove((ViewHolderBase) holder); + } + super.onViewRecycled(holder); + } + + @Override + public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) { + if (notifySingleItemChangeOnIconLoad()) { + mBoundViewHolders.remove((ViewHolderBase) holder); + } + return super.onFailedToRecycleView(holder); + } + + @Override public int getItemViewType(int position) { int count; @@ -3604,6 +3653,33 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Checks whether the suggested apps group, {@code holder}, contains the target, + * {@code info}. + */ + private boolean suggestedAppsGroupContainsTarget( + ItemGroupViewHolder holder, DisplayResolveInfo info) { + + int position = holder.getAdapterPosition(); + int start = getListPosition(position); + int startType = getRowType(start); + + int columnCount = holder.getColumnCount(); + int end = start + columnCount - 1; + while (getRowType(end) != startType && end >= start) { + end--; + } + + for (int i = 0; i < columnCount; i++) { + if (start + i <= end) { + if (mChooserListAdapter.getItem(holder.getItemIndex(i)) == info) { + return true; + } + } + } + return false; + } + int getListPosition(int position) { position -= getSystemRowCount() + getProfileRowCount(); diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java index d38689c7505b..1b8c36db3908 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -16,9 +16,12 @@ package com.android.internal.app; +import static android.service.chooser.Flags.notifySingleItemChangeOnIconLoad; + import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; +import android.annotation.Nullable; import android.app.prediction.AppPredictor; import android.content.ComponentName; import android.content.Context; @@ -56,6 +59,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class ChooserListAdapter extends ResolverListAdapter { private static final String TAG = "ChooserListAdapter"; @@ -108,6 +112,9 @@ public class ChooserListAdapter extends ResolverListAdapter { // Represents the UserSpace in which the Initial Intents should be resolved. private final UserHandle mInitialIntentsUserSpace; + @Nullable + private Consumer<DisplayResolveInfo> mOnIconLoadedListener; + // For pinned direct share labels, if the text spans multiple lines, the TextView will consume // the full width, even if the characters actually take up less than that. Measure the actual // line widths and constrain the View's width based upon that so that the pin doesn't end up @@ -218,6 +225,10 @@ public class ChooserListAdapter extends ResolverListAdapter { true); } + public void setOnIconLoadedListener(Consumer<DisplayResolveInfo> onIconLoadedListener) { + mOnIconLoadedListener = onIconLoadedListener; + } + AppPredictor getAppPredictor() { return mAppPredictor; } @@ -329,6 +340,15 @@ public class ChooserListAdapter extends ResolverListAdapter { } } + @Override + protected void onIconLoaded(DisplayResolveInfo info) { + if (notifySingleItemChangeOnIconLoad() && mOnIconLoadedListener != null) { + mOnIconLoadedListener.accept(info); + } else { + notifyDataSetChanged(); + } + } + private void loadDirectShareIcon(SelectableTargetInfo info) { LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info); if (task == null) { diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 54c0e61fd5cd..4d9ce86096c7 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -680,6 +680,10 @@ public class ResolverListAdapter extends BaseAdapter { } } + protected void onIconLoaded(DisplayResolveInfo info) { + notifyDataSetChanged(); + } + private void loadLabel(DisplayResolveInfo info) { LoadLabelTask task = mLabelLoaders.get(info); if (task == null) { @@ -1004,7 +1008,7 @@ public class ResolverListAdapter extends BaseAdapter { mResolverListCommunicator.updateProfileViewButton(); } else if (!mDisplayResolveInfo.hasDisplayIcon()) { mDisplayResolveInfo.setDisplayIcon(d); - notifyDataSetChanged(); + onIconLoaded(mDisplayResolveInfo); } } } |