summaryrefslogtreecommitdiff
path: root/services/people
diff options
context:
space:
mode:
authorSong Hu <songhu@google.com>2020-02-19 06:17:58 -0800
committerSong Hu <songhu@google.com>2020-02-19 18:39:06 -0800
commitba132e4f26930cfc40f0773a768d7611bb03106d (patch)
tree9b24233406fea3a71caea3bb63d813e852b18067 /services/people
parentabe60fe37695641bc595ff5fc52ccf3ebda23dc0 (diff)
Implement sharesheet ranking in PeopleService. Ranking is only based on
recency of past sharing for the moment. Bug: 149822311 Test: atest com.android.server.people.data.DataManagerTest Test: atest com.android.server.people.data.ShareTargetPredictorTest Change-Id: I90d1b3f488b34fede7d577dc04832b976f4a1bbb
Diffstat (limited to 'services/people')
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java102
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java6
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java138
3 files changed, 156 insertions, 90 deletions
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 6b97c98b0029..da971da46350 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -51,7 +51,6 @@ import android.provider.Telephony.MmsSms;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -275,40 +274,35 @@ public class DataManager {
mContext.getPackageName(), intentFilter, callingUserId);
}
- /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
- public void reportAppTargetEvent(@NonNull AppTargetEvent event,
+ /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
+ public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@Nullable IntentFilter intentFilter) {
AppTarget appTarget = event.getTarget();
- ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null;
- if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
- return;
- }
- PackageData packageData = getPackage(appTarget.getPackageName(),
- appTarget.getUser().getIdentifier());
- if (packageData == null) {
+ if (appTarget == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
return;
}
+ UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
+ PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
+ String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
+ EventHistoryImpl eventHistory;
if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
- String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
- String shortcutId = shortcutInfo.getId();
- if (packageData.getConversationStore().getConversation(shortcutId) == null
- || TextUtils.isEmpty(mimeType)) {
+ // Direct share event
+ if (appTarget.getShortcutInfo() == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
- EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
- @Event.EventType int eventType;
- if (mimeType.startsWith("text/")) {
- eventType = Event.TYPE_SHARE_TEXT;
- } else if (mimeType.startsWith("image/")) {
- eventType = Event.TYPE_SHARE_IMAGE;
- } else if (mimeType.startsWith("video/")) {
- eventType = Event.TYPE_SHARE_VIDEO;
- } else {
- eventType = Event.TYPE_SHARE_OTHER;
+ String shortcutId = appTarget.getShortcutInfo().getId();
+ if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+ addOrUpdateConversationInfo(appTarget.getShortcutInfo());
}
- eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
+ } else {
+ // App share event
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_CLASS_BASED, appTarget.getClassName());
}
+ eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
}
/** Prunes the data for the specified user. */
@@ -335,6 +329,17 @@ public class DataManager {
});
}
+ private int mimeTypeToShareEventType(String mimeType) {
+ if (mimeType.startsWith("text/")) {
+ return Event.TYPE_SHARE_TEXT;
+ } else if (mimeType.startsWith("image/")) {
+ return Event.TYPE_SHARE_IMAGE;
+ } else if (mimeType.startsWith("video/")) {
+ return Event.TYPE_SHARE_VIDEO;
+ }
+ return Event.TYPE_SHARE_OTHER;
+ }
+
private void pruneUninstalledPackageData(@NonNull UserData userData) {
Set<String> installApps = new ArraySet<>();
mPackageManagerInternal.forEachInstalledPackage(
@@ -410,12 +415,13 @@ public class DataManager {
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
+ private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0;
+ }
+
@VisibleForTesting
@WorkerThread
- void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) {
- if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) {
- return;
- }
+ void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
UserData userData = getUnlockedUserData(shortcutInfo.getUserId());
if (userData == null) {
return;
@@ -431,24 +437,24 @@ public class DataManager {
builder.setShortcutId(shortcutInfo.getId());
builder.setLocusId(shortcutInfo.getLocusId());
builder.setShortcutFlags(shortcutInfo.getFlags());
-
- Person person = shortcutInfo.getPersons()[0];
- builder.setPersonImportant(person.isImportant());
- builder.setPersonBot(person.isBot());
- String contactUri = person.getUri();
- if (contactUri != null) {
- ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
- if (helper.query(contactUri)) {
- builder.setContactUri(helper.getContactUri());
- builder.setContactStarred(helper.isStarred());
- builder.setContactPhoneNumber(helper.getPhoneNumber());
+ builder.setContactUri(null);
+ builder.setContactPhoneNumber(null);
+ builder.setContactStarred(false);
+
+ if (shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0) {
+ Person person = shortcutInfo.getPersons()[0];
+ builder.setPersonImportant(person.isImportant());
+ builder.setPersonBot(person.isBot());
+ String contactUri = person.getUri();
+ if (contactUri != null) {
+ ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+ if (helper.query(contactUri)) {
+ builder.setContactUri(helper.getContactUri());
+ builder.setContactStarred(helper.isStarred());
+ builder.setContactPhoneNumber(helper.getPhoneNumber());
+ }
}
- } else {
- builder.setContactUri(null);
- builder.setContactPhoneNumber(null);
- builder.setContactStarred(false);
}
-
conversationStore.addOrUpdate(builder.build());
}
@@ -626,7 +632,9 @@ public class DataManager {
List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
/*shortcutIds=*/ null);
for (ShortcutInfo shortcut : shortcuts) {
- onShortcutAddedOrUpdated(shortcut);
+ if (isPersonShortcut(shortcut)) {
+ addOrUpdateConversationInfo(shortcut);
+ }
}
});
}
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 19cf8af5d66b..c89dadc3fbd6 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -73,6 +73,7 @@ public class AppTargetPredictor {
*/
@MainThread
public void onAppTargetEvent(AppTargetEvent event) {
+ mCallbackExecutor.execute(() -> reportAppTargetEvent(event));
}
/**
@@ -104,6 +105,11 @@ public class AppTargetPredictor {
return mUpdatePredictionsMethod;
}
+ /** To be overridden by the subclass to report app target event. */
+ @WorkerThread
+ void reportAppTargetEvent(AppTargetEvent event) {
+ }
+
/** To be overridden by the subclass to predict the targets. */
@WorkerThread
void predictTargets() {
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 90d821641149..8e5d75be12b7 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,7 +16,6 @@
package com.android.server.people.prediction;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -28,15 +27,18 @@ import android.app.prediction.AppTargetId;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.util.Range;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
import com.android.server.people.data.EventHistory;
import com.android.server.people.data.PackageData;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -52,89 +54,139 @@ class ShareTargetPredictor extends AppTargetPredictor {
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
}
- @MainThread
+ /** Reports chosen history of direct/app share targets. */
+ @WorkerThread
@Override
- public void onAppTargetEvent(AppTargetEvent event) {
- getDataManager().reportAppTargetEvent(event, mIntentFilter);
+ void reportAppTargetEvent(AppTargetEvent event) {
+ getDataManager().reportShareTargetEvent(event, mIntentFilter);
}
+ /** Provides prediction on direct share targets */
@WorkerThread
@Override
- protected void predictTargets() {
- List<ShareTarget> shareTargets = getShareTargets();
- // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
- List<AppTarget> appTargets = new ArrayList<>();
- for (ShareTarget shareTarget : shareTargets) {
-
- ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
- AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
- String shareTargetClassName =
- shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
- AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
- .setClassName(shareTargetClassName)
- .build();
- appTargets.add(appTarget);
- if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
- break;
- }
+ void predictTargets() {
+ List<ShareTarget> shareTargets = getDirectShareTargets();
+ rankTargets(shareTargets);
+ List<AppTarget> res = new ArrayList<>();
+ for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
+ shareTargets.size()); i++) {
+ res.add(shareTargets.get(i).getAppTarget());
}
- updatePredictions(appTargets);
+ updatePredictions(res);
}
- @VisibleForTesting
- List<ShareTarget> getShareTargets() {
+ /** Provides prediction on app share targets */
+ @WorkerThread
+ @Override
+ void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ List<ShareTarget> shareTargets = getAppShareTargets(targets);
+ rankTargets(shareTargets);
+ List<AppTarget> appTargetList = new ArrayList<>();
+ shareTargets.forEach(t -> appTargetList.add(t.getAppTarget()));
+ callback.accept(appTargetList);
+ }
+
+ private void rankTargets(List<ShareTarget> shareTargets) {
+ // Rank targets based on recency of sharing history only for the moment.
+ // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app.
+ Collections.sort(shareTargets, (t1, t2) -> {
+ if (t1.getEventHistory() == null) {
+ return 1;
+ }
+ if (t2.getEventHistory() == null) {
+ return -1;
+ }
+ Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ if (timeSlot1 == null) {
+ return 1;
+ } else if (timeSlot2 == null) {
+ return -1;
+ } else {
+ return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper());
+ }
+ });
+ }
+
+ private List<ShareTarget> getDirectShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ AppTarget appTarget = new AppTarget.Builder(
+ new AppTargetId(shortcutInfo.getId()),
+ shortcutInfo)
+ .setClassName(shareShortcut.getTargetComponent().getClassName())
+ .build();
String packageName = shortcutInfo.getPackage();
int userId = shortcutInfo.getUserId();
PackageData packageData = getDataManager().getPackage(packageName, userId);
- ConversationData conversationData = null;
+ ConversationInfo conversationInfo = null;
+ EventHistory eventHistory = null;
if (packageData != null) {
String shortcutId = shortcutInfo.getId();
- ConversationInfo conversationInfo =
- packageData.getConversationInfo(shortcutId);
-
+ conversationInfo = packageData.getConversationInfo(shortcutId);
if (conversationInfo != null) {
- EventHistory eventHistory = packageData.getEventHistory(shortcutId);
- conversationData = new ConversationData(
- packageName, userId, conversationInfo, eventHistory);
+ eventHistory = packageData.getEventHistory(shortcutId);
}
}
- shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+ shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo));
}
return shareTargets;
}
+ private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) {
+ List<ShareTarget> shareTargets = new ArrayList<>();
+ for (AppTarget target : targets) {
+ PackageData packageData = getDataManager().getPackage(target.getPackageName(),
+ target.getUser().getIdentifier());
+ shareTargets.add(new ShareTarget(target,
+ packageData == null ? null
+ : packageData.getClassLevelEventHistory(target.getClassName()), null));
+ }
+ return shareTargets;
+ }
+
@VisibleForTesting
static class ShareTarget {
@NonNull
- private final ShareShortcutInfo mShareShortcutInfo;
+ private final AppTarget mAppTarget;
@Nullable
- private final ConversationData mConversationData;
-
- private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
- @Nullable ConversationData conversationData) {
- mShareShortcutInfo = shareShortcutInfo;
- mConversationData = conversationData;
+ private final EventHistory mEventHistory;
+ @Nullable
+ private final ConversationInfo mConversationInfo;
+
+ private ShareTarget(@NonNull AppTarget appTarget,
+ @Nullable EventHistory eventHistory,
+ @Nullable ConversationInfo conversationInfo) {
+ mAppTarget = appTarget;
+ mEventHistory = eventHistory;
+ mConversationInfo = conversationInfo;
}
@NonNull
@VisibleForTesting
- ShareShortcutInfo getShareShortcutInfo() {
- return mShareShortcutInfo;
+ AppTarget getAppTarget() {
+ return mAppTarget;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ EventHistory getEventHistory() {
+ return mEventHistory;
}
@Nullable
@VisibleForTesting
- ConversationData getConversationData() {
- return mConversationData;
+ ConversationInfo getConversationInfo() {
+ return mConversationInfo;
}
}
}