Adds annotations to Intent, and enables ChooserActivity to rank apps
according to annotations. Test: Unit tests and manul tests. More unit tests to be added. Change-Id: I3cbfd0cc10007290585fa3e7e3c92e1731f1f7b8
This commit is contained in:
@@ -8955,6 +8955,7 @@ package android.content {
|
||||
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
|
||||
field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
|
||||
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
|
||||
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
|
||||
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
|
||||
|
||||
@@ -9331,6 +9331,7 @@ package android.content {
|
||||
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
|
||||
field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
|
||||
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
|
||||
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
|
||||
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
|
||||
|
||||
@@ -8980,6 +8980,7 @@ package android.content {
|
||||
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
|
||||
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
|
||||
field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
|
||||
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
|
||||
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
|
||||
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
|
||||
|
||||
@@ -3842,6 +3842,52 @@ public class Intent implements Parcelable, Cloneable {
|
||||
public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER
|
||||
= "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
|
||||
|
||||
/**
|
||||
* An {@code ArrayList} of {@code String} annotations describing content for
|
||||
* {@link #ACTION_CHOOSER}.
|
||||
*
|
||||
* <p>If {@link #EXTRA_CONTENT_ANNOTATIONS} is present in an intent used to start a
|
||||
* {@link #ACTION_CHOOSER} activity, the first three annotations will be used to rank apps.</p>
|
||||
*
|
||||
* <p>Annotations should describe the major components or topics of the content. It is up to
|
||||
* apps initiating {@link #ACTION_CHOOSER} to learn and add annotations. Annotations should be
|
||||
* learned in advance, e.g., when creating or saving content, to avoid increasing latency to
|
||||
* start {@link #ACTION_CHOOSER}. Performance on customized annotations can suffer, if they are
|
||||
* rarely used for {@link #ACTION_CHOOSER} in the past 14 days. Therefore, it is recommended to
|
||||
* use the following annotations when applicable:</p>
|
||||
* <ul>
|
||||
* <li>"product": represents that the topic of the content is mainly about products, e.g.,
|
||||
* health & beauty, and office supplies.</li>
|
||||
* <li>"emotion": represents that the topic of the content is mainly about emotions, e.g.,
|
||||
* happy, and sad.</li>
|
||||
* <li>"person": represents that the topic of the content is mainly about persons, e.g.,
|
||||
* face, finger, standing, and walking.</li>
|
||||
* <li>"child": represents that the topic of the content is mainly about children, e.g.,
|
||||
* child, and baby.</li>
|
||||
* <li>"selfie": represents that the topic of the content is mainly about selfies.</li>
|
||||
* <li>"crowd": represents that the topic of the content is mainly about crowds.</li>
|
||||
* <li>"party": represents that the topic of the content is mainly about parties.</li>
|
||||
* <li>"animal": represent that the topic of the content is mainly about animals.</li>
|
||||
* <li>"plant": represents that the topic of the content is mainly about plants, e.g.,
|
||||
* flowers.</li>
|
||||
* <li>"vacation": represents that the topic of the content is mainly about vacations.</li>
|
||||
* <li>"fashion": represents that the topic of the content is mainly about fashion, e.g.
|
||||
* sunglasses, jewelry, handbags and clothing.</li>
|
||||
* <li>"material": represents that the topic of the content is mainly about materials, e.g.,
|
||||
* paper, and silk.</li>
|
||||
* <li>"vehicle": represents that the topic of the content is mainly about vehicles, like
|
||||
* cars, and boats.</li>
|
||||
* <li>"document": represents that the topic of the content is mainly about documents, e.g.
|
||||
* posters.</li>
|
||||
* <li>"design": represents that the topic of the content is mainly about design, e.g. arts
|
||||
* and designs of houses.</li>
|
||||
* <li>"holiday": represents that the topic of the content is mainly about holidays, e.g.,
|
||||
* Christmas and Thanksgiving.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String EXTRA_CONTENT_ANNOTATIONS
|
||||
= "android.intent.extra.CONTENT_ANNOTATIONS";
|
||||
|
||||
/**
|
||||
* A {@link ResultReceiver} used to return data back to the sender.
|
||||
*
|
||||
|
||||
@@ -418,7 +418,7 @@ public class ChooserActivity extends ResolverActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
updateChooserCounts(target);
|
||||
updateModelAndChooserCounts(target);
|
||||
return super.onTargetSelected(target, alwaysCheck);
|
||||
}
|
||||
|
||||
@@ -575,27 +575,18 @@ public class ChooserActivity extends ResolverActivity {
|
||||
// Do nothing. We'll send the voice stuff ourselves.
|
||||
}
|
||||
|
||||
void updateChooserCounts(TargetInfo info) {
|
||||
void updateModelAndChooserCounts(TargetInfo info) {
|
||||
if (info != null) {
|
||||
UsageStatsManager usageStatsManager =
|
||||
(UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
if (usageStatsManager == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Can not start UsageStatsManager");
|
||||
}
|
||||
return;
|
||||
}
|
||||
final ResolveInfo ri = info.getResolveInfo();
|
||||
Intent targetIntent = getTargetIntent();
|
||||
if (ri != null && ri.activityInfo != null && targetIntent != null) {
|
||||
usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
|
||||
targetIntent.getType(), null, targetIntent.getAction());
|
||||
if (mAdapter != null) {
|
||||
mAdapter.updateModel(info.getResolvedComponentName());
|
||||
mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
|
||||
targetIntent.getAction());
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
|
||||
Log.d(TAG, "Annotation to be updated is " + targetIntent.getType());
|
||||
Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
|
||||
}
|
||||
} else if(DEBUG) {
|
||||
@@ -618,7 +609,7 @@ public class ChooserActivity extends ResolverActivity {
|
||||
} else {
|
||||
TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
|
||||
if (super.onTargetSelected(clonedTarget, false)) {
|
||||
updateChooserCounts(clonedTarget);
|
||||
updateModelAndChooserCounts(clonedTarget);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1299,6 +1299,10 @@ public class ResolverActivity extends Activity {
|
||||
mResolverListController.updateModel(componentName);
|
||||
}
|
||||
|
||||
public void updateChooserCounts(String packageName, int userId, String action) {
|
||||
mResolverListController.updateChooserCounts(packageName, userId, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
|
||||
* to complete.
|
||||
|
||||
@@ -52,6 +52,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
|
||||
|
||||
// One week
|
||||
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7;
|
||||
|
||||
@@ -74,7 +76,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
private final long mSinceTime;
|
||||
private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
|
||||
private final String mReferrerPackage;
|
||||
public String mContentType;
|
||||
private String mContentType;
|
||||
private String[] mAnnotations;
|
||||
private String mAction;
|
||||
private LogisticRegressionAppRanker mRanker;
|
||||
|
||||
@@ -91,10 +94,26 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
|
||||
mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
|
||||
mContentType = intent.getType();
|
||||
getContentAnnotations(intent);
|
||||
mAction = intent.getAction();
|
||||
mRanker = new LogisticRegressionAppRanker(context);
|
||||
}
|
||||
|
||||
public void getContentAnnotations(Intent intent) {
|
||||
ArrayList<String> annotations = intent.getStringArrayListExtra(
|
||||
Intent.EXTRA_CONTENT_ANNOTATIONS);
|
||||
if (annotations != null) {
|
||||
int size = annotations.size();
|
||||
if (size > NUM_OF_TOP_ANNOTATIONS_TO_USE) {
|
||||
size = NUM_OF_TOP_ANNOTATIONS_TO_USE;
|
||||
}
|
||||
mAnnotations = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
mAnnotations[i] = annotations.get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void compute(List<ResolvedComponentInfo> targets) {
|
||||
mScoredTargets.clear();
|
||||
|
||||
@@ -132,12 +151,18 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
if (launched > mostLaunched) {
|
||||
mostLaunched = launched;
|
||||
}
|
||||
// TODO(kanlig): get and combine counts of categories.
|
||||
|
||||
int selected = 0;
|
||||
if (pkStats.mChooserCounts != null && mAction != null
|
||||
&& pkStats.mChooserCounts.get(mAction) != null) {
|
||||
selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
|
||||
if (mAnnotations != null) {
|
||||
final int size = mAnnotations.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
selected += pkStats.mChooserCounts.get(mAction)
|
||||
.getOrDefault(mAnnotations[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
if (mAction == null) {
|
||||
@@ -288,6 +313,12 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChooserCounts(String packageName, int userId, String action) {
|
||||
if (mUsm != null) {
|
||||
mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateModel(ComponentName componentName) {
|
||||
if (mScoredTargets == null || componentName == null ||
|
||||
!mScoredTargets.containsKey(componentName)) {
|
||||
|
||||
@@ -224,4 +224,10 @@ public class ResolverListController {
|
||||
mResolverComparator.updateModel(componentName);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChooserCounts(String packageName, int userId, String action) {
|
||||
if (mResolverComparator != null) {
|
||||
mResolverComparator.updateChooserCounts(packageName, userId, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class ChooserActivityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateChooserCountsAfterUserSelection() throws InterruptedException {
|
||||
public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
|
||||
Intent sendIntent = createSendImageIntent();
|
||||
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
|
||||
|
||||
@@ -142,19 +142,15 @@ public class ChooserActivityTest {
|
||||
sOverrides.onSafelyStartCallback = targetInfo -> {
|
||||
return true;
|
||||
};
|
||||
String action = sendIntent.getAction();
|
||||
String annotation = sendIntent.getType();
|
||||
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
|
||||
String packageName = toChoose.activityInfo.packageName;
|
||||
long toChooseCount = getCount(usm, packageName, action, annotation);
|
||||
onView(withText(toChoose.activityInfo.name))
|
||||
.perform(click());
|
||||
waitForIdle();
|
||||
verify(sOverrides.resolverListController, times(1))
|
||||
.updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString());
|
||||
verify(sOverrides.resolverListController, times(1))
|
||||
.updateModel(toChoose.activityInfo.getComponentName());
|
||||
assertThat(activity.getIsSelected(), is(true));
|
||||
long updatedCount = getCount(usm, packageName, action, annotation);
|
||||
assertThat(updatedCount, is(toChooseCount + 1l));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -188,7 +188,6 @@ class UserUsageStatsService {
|
||||
String[] annotations = event.mContentAnnotations;
|
||||
if (annotations != null) {
|
||||
for (String annotation : annotations) {
|
||||
// TODO(kanlig): update with confidences of annotations.
|
||||
stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user