Merge changes I41e08b86,I7760f37d into qt-qpr1-dev
am: 30e495d0c4
Change-Id: Iae876b13f07a0bfe55baff58ca672d93a0f461e3
This commit is contained in:
@@ -24,6 +24,7 @@ import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
@@ -729,7 +730,13 @@ public class ChooserActivity extends ResolverActivity {
|
||||
/**
|
||||
* Returns true if app prediction service is defined and the component exists on device.
|
||||
*/
|
||||
private boolean isAppPredictionServiceAvailable() {
|
||||
@VisibleForTesting
|
||||
public boolean isAppPredictionServiceAvailable() {
|
||||
if (getPackageManager().getAppPredictionServicePackageName() == null) {
|
||||
// Default AppPredictionService is not defined.
|
||||
return false;
|
||||
}
|
||||
|
||||
final String appPredictionServiceName =
|
||||
getString(R.string.config_defaultAppPredictionService);
|
||||
if (appPredictionServiceName == null) {
|
||||
@@ -1584,25 +1591,19 @@ public class ChooserActivity extends ResolverActivity {
|
||||
// ShareShortcutInfos directly.
|
||||
boolean resultMessageSent = false;
|
||||
for (int i = 0; i < driList.size(); i++) {
|
||||
List<ChooserTarget> chooserTargets = new ArrayList<>();
|
||||
List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
|
||||
for (int j = 0; j < resultList.size(); j++) {
|
||||
if (driList.get(i).getResolvedComponentName().equals(
|
||||
resultList.get(j).getTargetComponent())) {
|
||||
ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
|
||||
// Incoming results are ordered but without a score. Create a score
|
||||
// based on the index in order to be sorted appropriately when joined
|
||||
// with legacy direct share api results.
|
||||
float score = Math.max(1.0f - (0.05f * j), 0.0f);
|
||||
ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score);
|
||||
chooserTargets.add(chooserTarget);
|
||||
if (mDirectShareAppTargetCache != null && appTargets != null) {
|
||||
mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
|
||||
}
|
||||
matchingShortcuts.add(resultList.get(j));
|
||||
}
|
||||
}
|
||||
if (chooserTargets.isEmpty()) {
|
||||
if (matchingShortcuts.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<ChooserTarget> chooserTargets = convertToChooserTarget(
|
||||
matchingShortcuts, resultList, appTargets, shortcutType);
|
||||
|
||||
final Message msg = Message.obtain();
|
||||
msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
|
||||
msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
|
||||
@@ -1640,23 +1641,69 @@ public class ChooserActivity extends ResolverActivity {
|
||||
return false;
|
||||
}
|
||||
|
||||
private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut,
|
||||
float score) {
|
||||
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
|
||||
return new ChooserTarget(
|
||||
// The name of this target.
|
||||
shortcutInfo.getShortLabel(),
|
||||
// Don't load the icon until it is selected to be shown
|
||||
null,
|
||||
// The ranking score for this target (0.0-1.0); the system will omit items with low
|
||||
// scores when there are too many Direct Share items.
|
||||
score,
|
||||
// The name of the component to be launched if this target is chosen.
|
||||
shareShortcut.getTargetComponent().clone(),
|
||||
// The extra values here will be merged into the Intent when this target is chosen.
|
||||
extras);
|
||||
/**
|
||||
* Converts a list of ShareShortcutInfos to ChooserTargets.
|
||||
* @param matchingShortcuts List of shortcuts, all from the same package, that match the current
|
||||
* share intent filter.
|
||||
* @param allShortcuts List of all the shortcuts from all the packages on the device that are
|
||||
* returned for the current sharing action.
|
||||
* @param allAppTargets List of AppTargets. Null if the results are not from prediction service.
|
||||
* @param shortcutType One of the values TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER or
|
||||
* TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
|
||||
* @return A list of ChooserTargets sorted by score in descending order.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
public List<ChooserTarget> convertToChooserTarget(
|
||||
@NonNull List<ShortcutManager.ShareShortcutInfo> matchingShortcuts,
|
||||
@NonNull List<ShortcutManager.ShareShortcutInfo> allShortcuts,
|
||||
@Nullable List<AppTarget> allAppTargets, @ShareTargetType int shortcutType) {
|
||||
// A set of distinct scores for the matched shortcuts. We use index of a rank in the sorted
|
||||
// list instead of the actual rank value when converting a rank to a score.
|
||||
List<Integer> scoreList = new ArrayList<>();
|
||||
if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
|
||||
for (int i = 0; i < matchingShortcuts.size(); i++) {
|
||||
int shortcutRank = matchingShortcuts.get(i).getShortcutInfo().getRank();
|
||||
if (!scoreList.contains(shortcutRank)) {
|
||||
scoreList.add(shortcutRank);
|
||||
}
|
||||
}
|
||||
Collections.sort(scoreList);
|
||||
}
|
||||
|
||||
List<ChooserTarget> chooserTargetList = new ArrayList<>(matchingShortcuts.size());
|
||||
for (int i = 0; i < matchingShortcuts.size(); i++) {
|
||||
ShortcutInfo shortcutInfo = matchingShortcuts.get(i).getShortcutInfo();
|
||||
int indexInAllShortcuts = allShortcuts.indexOf(matchingShortcuts.get(i));
|
||||
|
||||
float score;
|
||||
if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
|
||||
// Incoming results are ordered. Create a score based on index in the original list.
|
||||
score = Math.max(1.0f - (0.01f * indexInAllShortcuts), 0.0f);
|
||||
} else {
|
||||
// Create a score based on the rank of the shortcut.
|
||||
int rankIndex = scoreList.indexOf(shortcutInfo.getRank());
|
||||
score = Math.max(1.0f - (0.01f * rankIndex), 0.0f);
|
||||
}
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
|
||||
ChooserTarget chooserTarget = new ChooserTarget(shortcutInfo.getShortLabel(),
|
||||
null, // Icon will be loaded later if this target is selected to be shown.
|
||||
score, matchingShortcuts.get(i).getTargetComponent().clone(), extras);
|
||||
|
||||
chooserTargetList.add(chooserTarget);
|
||||
if (mDirectShareAppTargetCache != null && allAppTargets != null) {
|
||||
mDirectShareAppTargetCache.put(chooserTarget,
|
||||
allAppTargets.get(indexInAllShortcuts));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort ChooserTargets by score in descending order
|
||||
Comparator<ChooserTarget> byScore =
|
||||
(ChooserTarget a, ChooserTarget b) -> -Float.compare(a.getScore(), b.getScore());
|
||||
Collections.sort(chooserTargetList, byScore);
|
||||
return chooserTargetList;
|
||||
}
|
||||
|
||||
private String convertServiceName(String packageName, String serviceName) {
|
||||
@@ -1748,8 +1795,7 @@ public class ChooserActivity extends ResolverActivity {
|
||||
if (!mIsAppPredictorComponentAvailable) {
|
||||
return null;
|
||||
}
|
||||
if (mAppPredictor == null
|
||||
&& getPackageManager().getAppPredictionServicePackageName() != null) {
|
||||
if (mAppPredictor == null) {
|
||||
final IntentFilter filter = getTargetIntentFilter();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
|
||||
|
||||
@@ -36,6 +36,7 @@ import static org.hamcrest.CoreMatchers.is;
|
||||
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.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -51,6 +52,8 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager.ShareShortcutInfo;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
@@ -807,6 +810,108 @@ public class ChooserActivityTest {
|
||||
is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
|
||||
}
|
||||
|
||||
/**
|
||||
* The case when AppPrediction service is not defined in PackageManager is already covered
|
||||
* as a test parameter {@link ChooserActivityTest#packageManagers}. This test is checking the
|
||||
* case when the prediction service is defined but the component is not available on the device.
|
||||
*/
|
||||
@Test
|
||||
public void testIsAppPredictionServiceAvailable() {
|
||||
Intent sendIntent = createSendTextIntent();
|
||||
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
|
||||
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||
Mockito.anyBoolean(),
|
||||
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
|
||||
|
||||
final ChooserWrapperActivity activity = mActivityRule
|
||||
.launchActivity(Intent.createChooser(sendIntent, null));
|
||||
waitForIdle();
|
||||
|
||||
if (activity.getPackageManager().getAppPredictionServicePackageName() == null) {
|
||||
assertThat(activity.isAppPredictionServiceAvailable(), is(false));
|
||||
} else {
|
||||
assertThat(activity.isAppPredictionServiceAvailable(), is(true));
|
||||
|
||||
sOverrides.resources = Mockito.spy(activity.getResources());
|
||||
when(sOverrides.resources.getString(R.string.config_defaultAppPredictionService))
|
||||
.thenReturn("ComponentNameThatDoesNotExist");
|
||||
|
||||
assertThat(activity.isAppPredictionServiceAvailable(), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToChooserTarget_predictionService() {
|
||||
Intent sendIntent = createSendTextIntent();
|
||||
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
|
||||
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||
Mockito.anyBoolean(),
|
||||
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
|
||||
|
||||
final ChooserWrapperActivity activity = mActivityRule
|
||||
.launchActivity(Intent.createChooser(sendIntent, null));
|
||||
waitForIdle();
|
||||
|
||||
List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
|
||||
|
||||
int[] expectedOrderAllShortcuts = {0, 1, 2, 3};
|
||||
float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f};
|
||||
|
||||
List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
|
||||
null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
|
||||
assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
|
||||
expectedOrderAllShortcuts, expectedScoreAllShortcuts);
|
||||
|
||||
List<ShareShortcutInfo> subset = new ArrayList<>();
|
||||
subset.add(shortcuts.get(1));
|
||||
subset.add(shortcuts.get(2));
|
||||
subset.add(shortcuts.get(3));
|
||||
|
||||
int[] expectedOrderSubset = {1, 2, 3};
|
||||
float[] expectedScoreSubset = {0.99f, 0.98f, 0.97f};
|
||||
|
||||
chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
|
||||
TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
|
||||
assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
|
||||
expectedOrderSubset, expectedScoreSubset);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToChooserTarget_shortcutManager() {
|
||||
Intent sendIntent = createSendTextIntent();
|
||||
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
|
||||
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
|
||||
Mockito.anyBoolean(),
|
||||
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
|
||||
|
||||
final ChooserWrapperActivity activity = mActivityRule
|
||||
.launchActivity(Intent.createChooser(sendIntent, null));
|
||||
waitForIdle();
|
||||
|
||||
List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
|
||||
|
||||
int[] expectedOrderAllShortcuts = {2, 0, 3, 1};
|
||||
float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f};
|
||||
|
||||
List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
|
||||
null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
|
||||
assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
|
||||
expectedOrderAllShortcuts, expectedScoreAllShortcuts);
|
||||
|
||||
List<ShareShortcutInfo> subset = new ArrayList<>();
|
||||
subset.add(shortcuts.get(1));
|
||||
subset.add(shortcuts.get(2));
|
||||
subset.add(shortcuts.get(3));
|
||||
|
||||
int[] expectedOrderSubset = {2, 3, 1};
|
||||
float[] expectedScoreSubset = {1.0f, 0.99f, 0.98f};
|
||||
|
||||
chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
|
||||
TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
|
||||
assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
|
||||
expectedOrderSubset, expectedScoreSubset);
|
||||
}
|
||||
|
||||
// This test is too long and too slow and should not be taken as an example for future tests.
|
||||
@Test
|
||||
public void testDirectTargetSelectionLogging() throws InterruptedException {
|
||||
@@ -1103,4 +1208,43 @@ public class ChooserActivityTest {
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private List<ShareShortcutInfo> createShortcuts(Context context) {
|
||||
Intent testIntent = new Intent("TestIntent");
|
||||
|
||||
List<ShareShortcutInfo> shortcuts = new ArrayList<>();
|
||||
shortcuts.add(new ShareShortcutInfo(
|
||||
new ShortcutInfo.Builder(context, "shortcut1")
|
||||
.setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2
|
||||
new ComponentName("package1", "class1")));
|
||||
shortcuts.add(new ShareShortcutInfo(
|
||||
new ShortcutInfo.Builder(context, "shortcut2")
|
||||
.setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3
|
||||
new ComponentName("package2", "class2")));
|
||||
shortcuts.add(new ShareShortcutInfo(
|
||||
new ShortcutInfo.Builder(context, "shortcut3")
|
||||
.setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0
|
||||
new ComponentName("package3", "class3")));
|
||||
shortcuts.add(new ShareShortcutInfo(
|
||||
new ShortcutInfo.Builder(context, "shortcut4")
|
||||
.setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2
|
||||
new ComponentName("package4", "class4")));
|
||||
|
||||
return shortcuts;
|
||||
}
|
||||
|
||||
private void assertCorrectShortcutToChooserTargetConversion(List<ShareShortcutInfo> shortcuts,
|
||||
List<ChooserTarget> chooserTargets, int[] expectedOrder, float[] expectedScores) {
|
||||
assertEquals(expectedOrder.length, chooserTargets.size());
|
||||
for (int i = 0; i < chooserTargets.size(); i++) {
|
||||
ChooserTarget ct = chooserTargets.get(i);
|
||||
ShortcutInfo si = shortcuts.get(expectedOrder[i]).getShortcutInfo();
|
||||
ComponentName cn = shortcuts.get(expectedOrder[i]).getTargetComponent();
|
||||
|
||||
assertEquals(si.getId(), ct.getIntentExtras().getString(Intent.EXTRA_SHORTCUT_ID));
|
||||
assertEquals(si.getShortLabel(), ct.getTitle());
|
||||
assertThat(Math.abs(expectedScores[i] - ct.getScore()) < 0.000001, is(true));
|
||||
assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
@@ -84,6 +85,14 @@ public class ChooserWrapperActivity extends ChooserActivity {
|
||||
return super.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (sOverrides.resources != null) {
|
||||
return sOverrides.resources;
|
||||
}
|
||||
return super.getResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap loadThumbnail(Uri uri, Size size) {
|
||||
if (sOverrides.previewThumbnail != null) {
|
||||
@@ -145,6 +154,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
|
||||
public Bitmap previewThumbnail;
|
||||
public MetricsLogger metricsLogger;
|
||||
public int alternateProfileSetting;
|
||||
public Resources resources;
|
||||
|
||||
public void reset() {
|
||||
onSafelyStartCallback = null;
|
||||
@@ -157,6 +167,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
|
||||
resolverListController = mock(ResolverListController.class);
|
||||
metricsLogger = mock(MetricsLogger.class);
|
||||
alternateProfileSetting = 0;
|
||||
resources = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user