diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index bcb32fb60f477..a47ad737c2aba 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -301,7 +301,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) { if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) { - activeListAdapter.postListReadyRunnable(doPostProcessing); + activeListAdapter.postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true); return false; } return activeListAdapter.rebuildList(doPostProcessing); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index b671fa74bf9ae..a84d9640c7f87 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2404,10 +2404,9 @@ public class ChooserActivity extends ResolverActivity implements public ChooserGridAdapter createChooserGridAdapter(Context context, List payloadIntents, Intent[] initialIntents, List rList, boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) { - ChooserListAdapter chooserListAdapter = new ChooserListAdapter(context, payloadIntents, - initialIntents, rList, - filterLastUsed, createListController(userHandle), useLayoutForBrowsables, - this, this); + ChooserListAdapter chooserListAdapter = createChooserListAdapter(context, payloadIntents, + initialIntents, rList, filterLastUsed, + useLayoutForBrowsables, createListController(userHandle)); AppPredictor.Callback appPredictorCallback = createAppPredictorCallback(chooserListAdapter); AppPredictor appPredictor = setupAppPredictorForUser(userHandle, appPredictorCallback); chooserListAdapter.setAppPredictor(appPredictor); @@ -2415,6 +2414,16 @@ public class ChooserActivity extends ResolverActivity implements return new ChooserGridAdapter(chooserListAdapter); } + @VisibleForTesting + public ChooserListAdapter createChooserListAdapter(Context context, + List payloadIntents, Intent[] initialIntents, List rList, + boolean filterLastUsed, boolean useLayoutForBrowsables, + ResolverListController resolverListController) { + return new ChooserListAdapter(context, payloadIntents, initialIntents, rList, + filterLastUsed, resolverListController, useLayoutForBrowsables, + this, this, context.getPackageManager()); + } + @VisibleForTesting protected ResolverListController createListController(UserHandle userHandle) { AppPredictor appPredictor = getAppPredictorForShareActivitiesIfEnabled(userHandle); diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java index 69a4927c9ab1d..73ee2950c481f 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -88,7 +88,7 @@ public class ChooserListAdapter extends ResolverListAdapter { private final int mMaxShortcutTargetsPerApp; private final ChooserListCommunicator mChooserListCommunicator; private final SelectableTargetInfo.SelectableTargetInfoCommunicator - mSelectableTargetInfoComunicator; + mSelectableTargetInfoCommunicator; private int mNumShortcutResults = 0; @@ -117,7 +117,8 @@ public class ChooserListAdapter extends ResolverListAdapter { boolean filterLastUsed, ResolverListController resolverListController, boolean useLayoutForBrowsables, ChooserListCommunicator chooserListCommunicator, - SelectableTargetInfo.SelectableTargetInfoCommunicator selectableTargetInfoComunicator) { + SelectableTargetInfo.SelectableTargetInfoCommunicator selectableTargetInfoCommunicator, + PackageManager packageManager) { // Don't send the initial intents through the shared ResolverActivity path, // we want to separate them into a different section. super(context, payloadIntents, null, rList, filterLastUsed, @@ -128,10 +129,9 @@ public class ChooserListAdapter extends ResolverListAdapter { mMaxShortcutTargetsPerApp = context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp); mChooserListCommunicator = chooserListCommunicator; - mSelectableTargetInfoComunicator = selectableTargetInfoComunicator; + mSelectableTargetInfoCommunicator = selectableTargetInfoCommunicator; if (initialIntents != null) { - final PackageManager pm = context.getPackageManager(); for (int i = 0; i < initialIntents.length; i++) { final Intent ii = initialIntents[i]; if (ii == null) { @@ -147,7 +147,7 @@ public class ChooserListAdapter extends ResolverListAdapter { final ComponentName cn = ii.getComponent(); if (cn != null) { try { - ai = pm.getActivityInfo(ii.getComponent(), 0); + ai = packageManager.getActivityInfo(ii.getComponent(), 0); ri = new ResolveInfo(); ri.activityInfo = ai; } catch (PackageManager.NameNotFoundException ignored) { @@ -155,7 +155,7 @@ public class ChooserListAdapter extends ResolverListAdapter { } } if (ai == null) { - ri = pm.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY); + ri = packageManager.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY); ai = ri != null ? ri.activityInfo : null; } if (ai == null) { @@ -455,7 +455,7 @@ public class ChooserListAdapter extends ResolverListAdapter { UserHandle userHandle = getUserHandle(); Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */); boolean isInserted = insertServiceTarget(new SelectableTargetInfo(contextAsUser, - origTarget, target, targetScore, mSelectableTargetInfoComunicator, + origTarget, target, targetScore, mSelectableTargetInfoCommunicator, (isShortcutResult ? directShareToShortcutInfos.get(target) : null))); if (isInserted && isShortcutResult) { @@ -507,7 +507,7 @@ public class ChooserListAdapter extends ResolverListAdapter { .map(target -> new SelectableTargetInfo( contextAsUser, origTarget, target, target.getScore(), - mSelectableTargetInfoComunicator, + mSelectableTargetInfoCommunicator, (isShortcutResult ? directShareToShortcutInfos.get(target) : null)) ) diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 4ba6ce4e04ce4..cf9b955333300 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1026,7 +1026,8 @@ public class ResolverActivity extends Activity implements } @Override // ResolverListCommunicator - public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) { + public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing, + boolean rebuildCompleted) { if (isAutolaunching()) { return; } @@ -1041,7 +1042,7 @@ public class ResolverActivity extends Activity implements } // showEmptyResolverListEmptyState can mark the tab as loaded, // which is a precondition for auto launching - if (maybeAutolaunchActivity()) { + if (rebuildCompleted && maybeAutolaunchActivity()) { return; } if (doPostProcessing) { diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 62bddb17db5f4..2fd938f452917 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -282,7 +282,7 @@ public class ResolverListAdapter extends BaseAdapter { } setPlaceholderCount(placeholderCount); createSortingTask(doPostProcessing).execute(currentResolveList); - postListReadyRunnable(doPostProcessing); + postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ false); return false; } else { processSortedList(currentResolveList, doPostProcessing); @@ -370,7 +370,7 @@ public class ResolverListAdapter extends BaseAdapter { } mResolverListCommunicator.sendVoiceChoicesIfNeeded(); - postListReadyRunnable(doPostProcessing); + postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true); mIsTabLoaded = true; } @@ -380,14 +380,15 @@ public class ResolverListAdapter extends BaseAdapter { * handler thread to update after the current task is finished. * @param doPostProcessing Whether to update the UI and load additional direct share targets * after the list has been rebuilt + * @param rebuildCompleted Whether the list has been completely rebuilt */ - void postListReadyRunnable(boolean doPostProcessing) { + void postListReadyRunnable(boolean doPostProcessing, boolean rebuildCompleted) { if (mPostListReadyRunnable == null) { mPostListReadyRunnable = new Runnable() { @Override public void run() { mResolverListCommunicator.onPostListReady(ResolverListAdapter.this, - doPostProcessing); + doPostProcessing, rebuildCompleted); mPostListReadyRunnable = null; } }; @@ -649,7 +650,8 @@ public class ResolverListAdapter extends BaseAdapter { Intent getReplacementIntent(ActivityInfo activityInfo, Intent defIntent); - void onPostListReady(ResolverListAdapter listAdapter, boolean updateUi); + void onPostListReady(ResolverListAdapter listAdapter, boolean updateUi, + boolean rebuildCompleted); void sendVoiceChoicesIfNeeded(); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index c52555b5ed326..edad5c40fccd8 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -34,6 +34,7 @@ import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_ import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static com.android.internal.app.MatcherUtils.first; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.hamcrest.CoreMatchers.is; @@ -41,6 +42,8 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -55,6 +58,8 @@ import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; @@ -302,7 +307,7 @@ public class ChooserActivityTest { waitForIdle(); UsageStatsManager usm = activity.getUsageStatsManager(); verify(sOverrides.resolverListController, times(1)) - .topK(Mockito.any(List.class), Mockito.anyInt()); + .topK(any(List.class), anyInt()); assertThat(activity.getIsSelected(), is(false)); sOverrides.onSafelyStartCallback = targetInfo -> { return true; @@ -312,7 +317,7 @@ public class ChooserActivityTest { .perform(click()); waitForIdle(); verify(sOverrides.resolverListController, times(1)) - .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString()); + .updateChooserCounts(Mockito.anyString(), anyInt(), Mockito.anyString()); verify(sOverrides.resolverListController, times(1)) .updateModel(toChoose.activityInfo.getComponentName()); assertThat(activity.getIsSelected(), is(true)); @@ -1750,6 +1755,54 @@ public class ChooserActivityTest { assertThat(chosen[0], is(personalResolvedComponentInfos.get(1).getResolveInfoAt(0))); } + @Test + public void testOneInitialIntent_noAutolaunch() { + List personalResolvedComponentInfos = + createResolvedComponentsForTest(1); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); + Intent chooserIntent = createChooserIntent(new Intent[] {new Intent("action.fake")}); + ResolveInfo[] chosen = new ResolveInfo[1]; + sOverrides.onSafelyStartCallback = targetInfo -> { + chosen[0] = targetInfo.getResolveInfo(); + return true; + }; + sOverrides.packageManager = mock(PackageManager.class); + ResolveInfo ri = createFakeResolveInfo(); + when(sOverrides.packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(ri); + waitForIdle(); + + mActivityRule.launchActivity(chooserIntent); + waitForIdle(); + + assertNull(chosen[0]); + } + + private Intent createChooserIntent(Intent[] initialIntents) { + Intent chooserIntent = new Intent(); + chooserIntent.setAction(Intent.ACTION_CHOOSER); + chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); + chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title"); + chooserIntent.putExtra(Intent.EXTRA_INTENT, createSendTextIntent()); + chooserIntent.setType("text/plain"); + if (initialIntents != null) { + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents); + } + return chooserIntent; + } + + private ResolveInfo createFakeResolveInfo() { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.name = "FakeActivityName"; + ri.activityInfo.packageName = "fake.package.name"; + ri.activityInfo.applicationInfo = new ApplicationInfo(); + ri.activityInfo.applicationInfo.packageName = "fake.package.name"; + return ri; + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 5b83f952b7454..071b2259e1f09 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -58,6 +58,18 @@ public class ChooserWrapperActivity extends ChooserActivity { return multiProfilePagerAdapter; } + @Override + public ChooserListAdapter createChooserListAdapter(Context context, List payloadIntents, + Intent[] initialIntents, List rList, boolean filterLastUsed, + boolean useLayoutForBrowsables, ResolverListController resolverListController) { + PackageManager packageManager = + sOverrides.packageManager == null ? context.getPackageManager() + : sOverrides.packageManager; + return new ChooserListAdapter(context, payloadIntents, initialIntents, rList, + filterLastUsed, resolverListController, useLayoutForBrowsables, + this, this, packageManager); + } + ChooserListAdapter getAdapter() { return mChooserMultiProfilePagerAdapter.getActiveListAdapter(); } @@ -217,6 +229,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public boolean hasCrossProfileIntents; public boolean isQuietModeEnabled; public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector; + public PackageManager packageManager; public void reset() { onSafelyStartCallback = null; @@ -235,6 +248,7 @@ public class ChooserWrapperActivity extends ChooserActivity { workProfileUserHandle = null; hasCrossProfileIntents = true; isQuietModeEnabled = false; + packageManager = null; multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() { @Override public boolean hasCrossProfileIntents(List intents, int sourceUserId,