From 145b3a5129e810dd22c1f626a28f99f2641d3a0c Mon Sep 17 00:00:00 2001 From: George Hodulik Date: Wed, 27 Mar 2019 11:18:43 -0700 Subject: [PATCH] Add AppPredictionServiceExists check. When the flag is set, Chooser will check if APS exists and use it for direct share ranking. Otherwise, Chooser defaults to just querying ShortcutManager itself. ResolverActivity will register an appPredictor with ui surface "resolver" and ChooserActivity will use ui surface "share". Later, we will add a similar behavior to use the AppPredictor to sort actvities by default (if it exists), otherwise use the old API ResolverRankerService. Test: atest frameworks/base/core/tests/coretests/src/com/android/internal/app Test: Added parameterized tests for case when there is no AppPredictionService. Bug:129014961 Change-Id: I6109aa8f2436cf0b3c3b4fbc36e571b2b00fe45c --- .../android/internal/app/ChooserActivity.java | 83 +++++++++++++------ .../internal/app/ChooserActivityTest.java | 35 +++++++- 2 files changed, 90 insertions(+), 28 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 4d4fe03977911..b8ac6dc96db91 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -24,6 +24,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; import android.app.prediction.AppPredictionContext; @@ -131,6 +132,7 @@ import java.util.List; public class ChooserActivity extends ResolverActivity { private static final String TAG = "ChooserActivity"; + /** * Boolean extra to change the following behavior: Normally, ChooserActivity finishes itself * in onStop when launched in a new task. If this extra is set to true, we do not finish @@ -141,7 +143,6 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; - /** * If {@link #USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS} and this is set to true, * {@link AppPredictionManager} will be queried for direct share targets. @@ -433,18 +434,8 @@ public class ChooserActivity extends ResolverActivity { .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - final IntentFilter filter = getTargetIntentFilter(); - Bundle extras = new Bundle(); - extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter); - AppPredictionManager appPredictionManager = - getSystemService(AppPredictionManager.class); - mAppPredictor = appPredictionManager.createAppPredictionSession( - new AppPredictionContext.Builder(this) - .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT) - .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE) - .setExtras(extras) - .build()); + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { mAppPredictorCallback = resultList -> { if (isFinishing() || isDestroyed()) { return; @@ -467,8 +458,10 @@ public class ChooserActivity extends ResolverActivity { appTarget.getPackageName(), appTarget.getClassName()))); } sendShareShortcutInfoList(shareShortcutInfos, driList); + sendShortcutManagerShareTargetResultCompleted(); }; - mAppPredictor.registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); + appPredictor + .registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); } mChooserRowLayer = getResources().getDrawable(R.drawable.chooser_row_layer_list, null); @@ -872,7 +865,7 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { + if (mAppPredictor != null) { mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback); mAppPredictor.destroy(); } @@ -1205,10 +1198,12 @@ public class ChooserActivity extends ResolverActivity { } private void queryDirectShareTargets(ChooserListAdapter adapter) { - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - mAppPredictor.requestPredictionUpdate(); + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { + appPredictor.requestPredictionUpdate(); return; } + // Default to just querying ShortcutManager if AppPredictor not present. final IntentFilter filter = getTargetIntentFilter(); if (filter == null) { return; @@ -1248,12 +1243,16 @@ public class ChooserActivity extends ResolverActivity { } if (resultMessageSent) { - final Message msg = Message.obtain(); - msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED; - mChooserHandler.sendMessage(msg); + sendShortcutManagerShareTargetResultCompleted(); } } + private void sendShortcutManagerShareTargetResultCompleted() { + final Message msg = Message.obtain(); + msg.what = SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED; + mChooserHandler.sendMessage(msg); + } + private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) { ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); Bundle extras = new Bundle(); @@ -1309,9 +1308,7 @@ public class ChooserActivity extends ResolverActivity { void updateModelAndChooserCounts(TargetInfo info) { if (info != null) { - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { - sendClickToAppPredictor(info); - } + sendClickToAppPredictor(info); final ResolveInfo ri = info.getResolveInfo(); Intent targetIntent = getTargetIntent(); if (ri != null && ri.activityInfo != null && targetIntent != null) { @@ -1332,6 +1329,10 @@ public class ChooserActivity extends ResolverActivity { } private void sendClickToAppPredictor(TargetInfo targetInfo) { + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor == null) { + return; + } if (!(targetInfo instanceof ChooserTargetInfo)) { return; } @@ -1345,8 +1346,9 @@ public class ChooserActivity extends ResolverActivity { if (shortcutId == null) { return; } - mAppPredictor.notifyAppTargetEvent( + appPredictor.notifyAppTargetEvent( new AppTargetEvent.Builder( + // TODO(b/124404997) Send full shortcut info, not just Id with AppTargetId. new AppTarget.Builder(new AppTargetId(shortcutId)) .setTarget(componentName.getPackageName(), getUser()) .setClassName(componentName.getClassName()) @@ -1356,6 +1358,34 @@ public class ChooserActivity extends ResolverActivity { .build()); } + @Nullable + private AppPredictor getAppPredictor() { + if (mAppPredictor == null + && getPackageManager().getAppPredictionServicePackageName() != null) { + final IntentFilter filter = getTargetIntentFilter(); + Bundle extras = new Bundle(); + extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter); + AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(this) + .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE) + .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT) + .setExtras(extras) + .build(); + AppPredictionManager appPredictionManager + = getSystemService(AppPredictionManager.class); + mAppPredictor = appPredictionManager.createAppPredictionSession(appPredictionContext); + } + return mAppPredictor; + } + + /** + * This will return an app predictor if it is enabled for direct share sorting + * and if one exists. Otherwise, it returns null. + */ + @Nullable + private AppPredictor getAppPredictorForDirectShareIfEnabled() { + return USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS ? getAppPredictor() : null; + } + void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); @@ -2014,7 +2044,8 @@ public class ChooserActivity extends ResolverActivity { } } - if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) { + if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS + || USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { if (DEBUG) { Log.d(TAG, "querying direct share targets from ShortcutManager"); } @@ -2242,7 +2273,7 @@ public class ChooserActivity extends ResolverActivity { return CALLER_TARGET_SCORE_BOOST; } - if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { + if (getAppPredictorForDirectShareIfEnabled() != null) { return SHORTCUT_TARGET_SCORE_BOOST; } 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 00b4a225c55f1..ac039dddb66c8 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -42,6 +42,7 @@ import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; @@ -53,7 +54,6 @@ import android.metrics.LogMaker; import android.net.Uri; import android.service.chooser.ChooserTarget; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; @@ -62,10 +62,14 @@ import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Function; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -75,21 +79,48 @@ import java.util.List; /** * Chooser activity instrumentation tests */ -@RunWith(AndroidJUnit4.class) +@RunWith(Parameterized.class) public class ChooserActivityTest { + private static final Function DEFAULT_PM = pm -> pm; + private static final Function NO_APP_PREDICTION_SERVICE_PM = + pm -> { + PackageManager mock = Mockito.spy(pm); + when(mock.getAppPredictionServicePackageName()).thenReturn(null); + return mock; + }; + + @Parameterized.Parameters + public static Collection packageManagers() { + return Arrays.asList(new Object[][] { + {0, "Default PackageManager", DEFAULT_PM}, + {1, "No App Prediction Service", NO_APP_PREDICTION_SERVICE_PM} + }); + } + private static final int CONTENT_PREVIEW_IMAGE = 1; private static final int CONTENT_PREVIEW_FILE = 2; private static final int CONTENT_PREVIEW_TEXT = 3; + private Function mPackageManagerOverride; + private int mTestNum; @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); + public ChooserActivityTest( + int testNum, + String testName, + Function packageManagerOverride) { + mPackageManagerOverride = packageManagerOverride; + mTestNum = testNum; + } + @Before public void cleanOverrideData() { sOverrides.reset(); + sOverrides.createPackageManager = mPackageManagerOverride; } @Test