1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.people.prediction; 18 19 import static java.util.Collections.reverseOrder; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.UserIdInt; 24 import android.annotation.WorkerThread; 25 import android.app.prediction.AppPredictionContext; 26 import android.app.prediction.AppTarget; 27 import android.app.prediction.AppTargetEvent; 28 import android.app.prediction.AppTargetId; 29 import android.content.IntentFilter; 30 import android.content.pm.ShortcutInfo; 31 import android.content.pm.ShortcutManager.ShareShortcutInfo; 32 import android.util.Log; 33 import android.util.Slog; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.app.ChooserActivity; 37 import com.android.server.people.data.ConversationInfo; 38 import com.android.server.people.data.DataManager; 39 import com.android.server.people.data.EventHistory; 40 import com.android.server.people.data.PackageData; 41 42 import java.util.ArrayList; 43 import java.util.Collections; 44 import java.util.Comparator; 45 import java.util.List; 46 import java.util.function.Consumer; 47 48 /** 49 * Predictor that predicts the {@link AppTarget} the user is most likely to open on share sheet. 50 */ 51 class ShareTargetPredictor extends AppTargetPredictor { 52 53 private static final String TAG = "ShareTargetPredictor"; 54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 55 private final IntentFilter mIntentFilter; 56 ShareTargetPredictor(@onNull AppPredictionContext predictionContext, @NonNull Consumer<List<AppTarget>> updatePredictionsMethod, @NonNull DataManager dataManager, @UserIdInt int callingUserId)57 ShareTargetPredictor(@NonNull AppPredictionContext predictionContext, 58 @NonNull Consumer<List<AppTarget>> updatePredictionsMethod, 59 @NonNull DataManager dataManager, @UserIdInt int callingUserId) { 60 super(predictionContext, updatePredictionsMethod, dataManager, callingUserId); 61 mIntentFilter = predictionContext.getExtras().getParcelable( 62 ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY); 63 } 64 65 /** Reports chosen history of direct/app share targets. */ 66 @WorkerThread 67 @Override reportAppTargetEvent(AppTargetEvent event)68 void reportAppTargetEvent(AppTargetEvent event) { 69 if (DEBUG) { 70 Slog.d(TAG, "reportAppTargetEvent"); 71 } 72 if (mIntentFilter != null) { 73 getDataManager().reportShareTargetEvent(event, mIntentFilter); 74 } 75 } 76 77 /** Provides prediction on direct share targets */ 78 @WorkerThread 79 @Override predictTargets()80 void predictTargets() { 81 if (DEBUG) { 82 Slog.d(TAG, "predictTargets"); 83 } 84 if (mIntentFilter == null) { 85 updatePredictions(List.of()); 86 return; 87 } 88 List<ShareTarget> shareTargets = getDirectShareTargets(); 89 SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter), 90 System.currentTimeMillis()); 91 Collections.sort(shareTargets, 92 Comparator.comparing(ShareTarget::getScore, reverseOrder()) 93 .thenComparing(t -> t.getAppTarget().getRank())); 94 List<AppTarget> res = new ArrayList<>(); 95 for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(), 96 shareTargets.size()); i++) { 97 res.add(shareTargets.get(i).getAppTarget()); 98 } 99 updatePredictions(res); 100 } 101 102 /** Provides prediction on app share targets */ 103 @WorkerThread 104 @Override sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback)105 void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) { 106 if (DEBUG) { 107 Slog.d(TAG, "sortTargets"); 108 } 109 if (mIntentFilter == null) { 110 callback.accept(targets); 111 return; 112 } 113 List<ShareTarget> shareTargets = getAppShareTargets(targets); 114 SharesheetModelScorer.computeScoreForAppShare(shareTargets, 115 getShareEventType(mIntentFilter), getPredictionContext().getPredictedTargetCount(), 116 System.currentTimeMillis(), getDataManager(), 117 mCallingUserId); 118 Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore())); 119 List<AppTarget> appTargetList = new ArrayList<>(); 120 for (ShareTarget shareTarget : shareTargets) { 121 AppTarget appTarget = shareTarget.getAppTarget(); 122 appTargetList.add(new AppTarget.Builder(appTarget.getId(), appTarget.getPackageName(), 123 appTarget.getUser()) 124 .setClassName(appTarget.getClassName()) 125 .setRank(shareTarget.getScore() > 0 ? (int) (shareTarget.getScore() 126 * 1000) : 0) 127 .build()); 128 } 129 callback.accept(appTargetList); 130 } 131 getDirectShareTargets()132 private List<ShareTarget> getDirectShareTargets() { 133 List<ShareTarget> shareTargets = new ArrayList<>(); 134 List<ShareShortcutInfo> shareShortcuts = 135 getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId); 136 137 for (ShareShortcutInfo shareShortcut : shareShortcuts) { 138 ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); 139 AppTarget appTarget = new AppTarget.Builder( 140 new AppTargetId(shortcutInfo.getId()), 141 shortcutInfo) 142 .setClassName(shareShortcut.getTargetComponent().getClassName()) 143 .setRank(shortcutInfo.getRank()) 144 .build(); 145 String packageName = shortcutInfo.getPackage(); 146 int userId = shortcutInfo.getUserId(); 147 PackageData packageData = getDataManager().getPackage(packageName, userId); 148 149 ConversationInfo conversationInfo = null; 150 EventHistory eventHistory = null; 151 if (packageData != null) { 152 String shortcutId = shortcutInfo.getId(); 153 conversationInfo = packageData.getConversationInfo(shortcutId); 154 if (conversationInfo != null) { 155 eventHistory = packageData.getEventHistory(shortcutId); 156 } 157 } 158 shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo)); 159 } 160 161 return shareTargets; 162 } 163 getAppShareTargets(List<AppTarget> targets)164 private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) { 165 List<ShareTarget> shareTargets = new ArrayList<>(); 166 for (AppTarget target : targets) { 167 PackageData packageData = getDataManager().getPackage(target.getPackageName(), 168 target.getUser().getIdentifier()); 169 shareTargets.add(new ShareTarget(target, 170 packageData == null ? null 171 : packageData.getClassLevelEventHistory(target.getClassName()), null)); 172 } 173 return shareTargets; 174 } 175 getShareEventType(IntentFilter intentFilter)176 private int getShareEventType(IntentFilter intentFilter) { 177 String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; 178 return getDataManager().mimeTypeToShareEventType(mimeType); 179 } 180 181 @VisibleForTesting 182 static class ShareTarget { 183 184 @NonNull 185 private final AppTarget mAppTarget; 186 @Nullable 187 private final EventHistory mEventHistory; 188 @Nullable 189 private final ConversationInfo mConversationInfo; 190 private float mScore; 191 192 @VisibleForTesting ShareTarget(@onNull AppTarget appTarget, @Nullable EventHistory eventHistory, @Nullable ConversationInfo conversationInfo)193 ShareTarget(@NonNull AppTarget appTarget, 194 @Nullable EventHistory eventHistory, 195 @Nullable ConversationInfo conversationInfo) { 196 mAppTarget = appTarget; 197 mEventHistory = eventHistory; 198 mConversationInfo = conversationInfo; 199 mScore = 0f; 200 } 201 202 @NonNull 203 @VisibleForTesting getAppTarget()204 AppTarget getAppTarget() { 205 return mAppTarget; 206 } 207 208 @Nullable 209 @VisibleForTesting getEventHistory()210 EventHistory getEventHistory() { 211 return mEventHistory; 212 } 213 214 @Nullable 215 @VisibleForTesting getConversationInfo()216 ConversationInfo getConversationInfo() { 217 return mConversationInfo; 218 } 219 220 @VisibleForTesting getScore()221 float getScore() { 222 return mScore; 223 } 224 225 @VisibleForTesting setScore(float score)226 void setScore(float score) { 227 mScore = score; 228 } 229 } 230 } 231