From 15a4eef126161ee09961d3687c094f5a91ee2133 Mon Sep 17 00:00:00 2001 From: Joshua Trask Date: Tue, 5 Dec 2023 18:30:29 +0000 Subject: Pull package change handling into pager-adapter This *somewhat* cleans up a convoluted flow so that the "per-profile" concerns / references to "inactive profiles" get consolidated into MultiProfilePagerAdapter, where they'll be audited and generalized in the n-tab work. This has no side effects (beyond those noted for the parent change). These changes are as prototyped in ag/25335069 and described in go/chooser-ntab-refactoring. See below for a "by-snapshot" breakdown of the incremental changes composed in this CL. Snapshot 1: Move the main implementation from ResolverActivity to delegate through MultiProfilePagerAdapter, the component that owned most of the data responsibilities anyways. Note our `-activity` tests provide coverage for this basic flow (e.g., in manual experimentation, the tests would fail if I had the relocated method return the wrong value). Snapshot 2: Move ChooserActivity's customized package-change consideration (i.e., calling `notifyDataSetChanged()` prior to invoking the pager-adapter package-change handling steps) into `ChooserMultiProfilePagerAdapter`. This makes the "driver" code in the activities more generic in advance of moving more responsibilities to the page-adapter. Snapshot 3: Rebase on parent change, equivalent to snapshots 4 and 5 in ag/25335069. (Snapshot 4: update CL description after rebase) Snapshot 5: Move "per-profile" handling of refreshing all adapters into the MultiProfilePagerAdapter, which has a better model of the complete set of adapters that might need to be refreshed. (From ag/25335069 "snapshot 6") Bug: 310211468 Test: `IntentResolver-tests-{unit,activity,integration}`. See notes ^ Change-Id: I77981794345de23d52b4ca6b57ec974c60871dff --- .../android/intentresolver/v2/ChooserActivity.java | 11 +--- .../v2/ChooserMultiProfilePagerAdapter.java | 8 +++ .../v2/MultiProfilePagerAdapter.java | 60 ++++++++++++++++++++++ .../intentresolver/v2/ResolverActivity.java | 36 +++---------- 4 files changed, 75 insertions(+), 40 deletions(-) (limited to 'java') diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java index 6c961bb8..b8b82d66 100644 --- a/java/src/com/android/intentresolver/v2/ChooserActivity.java +++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java @@ -594,10 +594,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements // Refresh pinned items mPinnedSharedPrefs = getPinnedSharedPrefs(this); if (listAdapter == null) { - mChooserMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged(); - if (mChooserMultiProfilePagerAdapter.getCount() > 1) { - mChooserMultiProfilePagerAdapter.getInactiveListAdapter().handlePackagesChanged(); - } + mChooserMultiProfilePagerAdapter.refreshPackagesInAllTabs(); } else { listAdapter.handlePackagesChanged(); } @@ -1519,12 +1516,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements return PROFILE_PERSONAL; } - @Override // ResolverListCommunicator - public void onHandlePackagesChanged(ResolverListAdapter listAdapter) { - mChooserMultiProfilePagerAdapter.getActiveListAdapter().notifyDataSetChanged(); - super.onHandlePackagesChanged(listAdapter); - } - @Override protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildComplete) { setupScrollListener(); diff --git a/java/src/com/android/intentresolver/v2/ChooserMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/v2/ChooserMultiProfilePagerAdapter.java index 87b3b201..b9ee9622 100644 --- a/java/src/com/android/intentresolver/v2/ChooserMultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/v2/ChooserMultiProfilePagerAdapter.java @@ -153,6 +153,14 @@ public class ChooserMultiProfilePagerAdapter extends MultiProfilePagerAdapter< return rootView; } + @Override + public boolean onHandlePackagesChanged( + ChooserListAdapter listAdapter, boolean waitingToEnableWorkProfile) { + // TODO: why do we need to do the extra `notifyDataSetChanged()` in (only) the Chooser case? + getActiveListAdapter().notifyDataSetChanged(); + return super.onHandlePackagesChanged(listAdapter, waitingToEnableWorkProfile); + } + @Override protected final boolean rebuildTab(ChooserListAdapter listAdapter, boolean doPostProcessing) { if (doPostProcessing) { diff --git a/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java index a600d4ad..6d1870bf 100644 --- a/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java @@ -353,6 +353,66 @@ public class MultiProfilePagerAdapter< return getListViewForIndex(1 - getCurrentPage()); } + private boolean anyAdapterHasItems() { + for (int i = 0; i < mItems.size(); ++i) { + ListAdapterT listAdapter = mListAdapterExtractor.apply(getAdapterForIndex(i)); + if (listAdapter.getCount() > 0) { + return true; + } + } + return false; + } + + public void refreshPackagesInAllTabs() { + // TODO: handle all inactive profiles; for now we can only have at most one. It's unclear if + // this legacy logic really requires the active tab to be rebuilt first, or if we could just + // iterate over the tabs in arbitrary order. + getActiveListAdapter().handlePackagesChanged(); + if (getCount() > 1) { + getInactiveListAdapter().handlePackagesChanged(); + } + } + + /** + * Notify that there has been a package change which could potentially modify the set of targets + * that should be shown in the specified {@code listAdapter}. This may result in + * "rebuilding" the target list for that adapter. + * + * @param listAdapter an adapter that may need to be updated after the package-change event. + * @param waitingToEnableWorkProfile whether we've turned on the work profile, but haven't yet + * seen an {@code ACTION_USER_UNLOCKED} broadcast. In this case we skip the rebuild of any + * work-profile adapter because we wouldn't expect meaningful results -- but another rebuild + * will be prompted when we eventually get the broadcast. + * + * @return whether we're able to proceed with a Sharesheet session after processing this + * package-change event. If false, we were able to rebuild the targets but determined that there + * aren't any we could present in the UI without the app looking broken, so we should just quit. + */ + public boolean onHandlePackagesChanged( + ListAdapterT listAdapter, boolean waitingToEnableWorkProfile) { + if (listAdapter == getActiveListAdapter()) { + if (listAdapter.getUserHandle().equals(mWorkProfileUserHandle) + && waitingToEnableWorkProfile) { + // We have just turned on the work profile and entered the passcode to start it, + // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no + // point in reloading the list now, since the work profile user is still turning on. + return true; + } + + boolean listRebuilt = rebuildActiveTab(true); + if (listRebuilt) { + listAdapter.notifyDataSetChanged(); + } + + // TODO: shouldn't we check that the inactive tabs are built before declaring that we + // have to quit for lack of items? + return anyAdapterHasItems(); + } else { + clearInactiveProfileCache(); + return true; + } + } + /** * Rebuilds the tab that is currently visible to the user. *

Returns {@code true} if rebuild has completed. diff --git a/java/src/com/android/intentresolver/v2/ResolverActivity.java b/java/src/com/android/intentresolver/v2/ResolverActivity.java index a7f2047d..dec68649 100644 --- a/java/src/com/android/intentresolver/v2/ResolverActivity.java +++ b/java/src/com/android/intentresolver/v2/ResolverActivity.java @@ -945,29 +945,12 @@ public class ResolverActivity extends FragmentActivity implements } @Override // ResolverListCommunicator - public void onHandlePackagesChanged(ResolverListAdapter listAdapter) { - if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) { - if (listAdapter.getUserHandle().equals( - requireAnnotatedUserHandles().workProfileUserHandle) - && mLogic.getWorkProfileAvailabilityManager().isWaitingToEnableWorkProfile()) { - // We have just turned on the work profile and entered the pass code to start it, - // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no - // point in reloading the list now, since the work profile user is still - // turning on. - return; - } - boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true); - if (listRebuilt) { - ResolverListAdapter activeListAdapter = - mMultiProfilePagerAdapter.getActiveListAdapter(); - activeListAdapter.notifyDataSetChanged(); - if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) { - // We no longer have any items... just finish the activity. - finish(); - } - } - } else { - mMultiProfilePagerAdapter.clearInactiveProfileCache(); + public final void onHandlePackagesChanged(ResolverListAdapter listAdapter) { + if (!mMultiProfilePagerAdapter.onHandlePackagesChanged( + listAdapter, + mLogic.getWorkProfileAvailabilityManager().isWaitingToEnableWorkProfile())) { + // We no longer have any items... just finish the activity. + finish(); } } @@ -2105,13 +2088,6 @@ public class ResolverActivity extends FragmentActivity implements mRetainInOnStop = retainInOnStop; } - private boolean inactiveListAdapterHasItems() { - if (!shouldShowTabs()) { - return false; - } - return mMultiProfilePagerAdapter.getInactiveListAdapter().getCount() > 0; - } - final class ItemClickListener implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { @Override -- cgit v1.2.3-59-g8ed1b