diff options
author | Song Hu <songhu@google.com> | 2020-02-19 06:17:58 -0800 |
---|---|---|
committer | Song Hu <songhu@google.com> | 2020-02-19 18:39:06 -0800 |
commit | ba132e4f26930cfc40f0773a768d7611bb03106d (patch) | |
tree | 9b24233406fea3a71caea3bb63d813e852b18067 /services/people | |
parent | abe60fe37695641bc595ff5fc52ccf3ebda23dc0 (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')
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; } } } |