Create ShareSheetPredictor in People Service.
Change-Id: I93a14fb8172131c8bf8c9cf7d58d99fa13c80aa3 Test: Build and run on a test device and unit tests Bug: 146522621
This commit is contained in:
@@ -25,7 +25,7 @@ import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.people.data.DataManager;
|
||||
import com.android.server.people.prediction.ConversationPredictor;
|
||||
import com.android.server.people.prediction.AppTargetPredictor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,12 +34,12 @@ class SessionInfo {
|
||||
|
||||
private static final String TAG = "SessionInfo";
|
||||
|
||||
private final ConversationPredictor mConversationPredictor;
|
||||
private final AppTargetPredictor mAppTargetPredictor;
|
||||
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
|
||||
new RemoteCallbackList<>();
|
||||
|
||||
SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
|
||||
mConversationPredictor = new ConversationPredictor(predictionContext,
|
||||
mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
|
||||
this::updatePredictions, dataManager);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ class SessionInfo {
|
||||
mCallbacks.unregister(callback);
|
||||
}
|
||||
|
||||
ConversationPredictor getPredictor() {
|
||||
return mConversationPredictor;
|
||||
AppTargetPredictor getPredictor() {
|
||||
return mAppTargetPredictor;
|
||||
}
|
||||
|
||||
void onDestroy() {
|
||||
|
||||
@@ -59,7 +59,6 @@ import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.telephony.SmsApplication;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -199,6 +198,13 @@ public class DataManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the {@link PackageData} for the given package and user. */
|
||||
@Nullable
|
||||
public PackageData getPackage(@NonNull String packageName, @UserIdInt int userId) {
|
||||
UserData userData = getUnlockedUserData(userId);
|
||||
return userData != null ? userData.getPackageData(packageName) : null;
|
||||
}
|
||||
|
||||
/** Gets the {@link ShortcutInfo} for the given shortcut ID. */
|
||||
@Nullable
|
||||
public ShortcutInfo getShortcut(@NonNull String packageName, @UserIdInt int userId,
|
||||
@@ -212,20 +218,11 @@ public class DataManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversation {@link ShareShortcutInfo}s from all packages owned by the calling user
|
||||
* that match the specified {@link IntentFilter}.
|
||||
* Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
|
||||
* the specified {@link IntentFilter}.
|
||||
*/
|
||||
public List<ShareShortcutInfo> getConversationShareTargets(
|
||||
@NonNull IntentFilter intentFilter) {
|
||||
List<ShareShortcutInfo> shareShortcuts = mShortcutManager.getShareTargets(intentFilter);
|
||||
List<ShareShortcutInfo> result = new ArrayList<>();
|
||||
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
|
||||
ShortcutInfo si = shareShortcut.getShortcutInfo();
|
||||
if (getConversationInfo(si.getPackage(), si.getUserId(), si.getId()) != null) {
|
||||
result.add(shareShortcut);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
|
||||
return mShortcutManager.getShareTargets(intentFilter);
|
||||
}
|
||||
|
||||
/** Reports the {@link AppTargetEvent} from App Prediction Manager. */
|
||||
@@ -236,7 +233,7 @@ public class DataManager {
|
||||
if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
|
||||
return;
|
||||
}
|
||||
PackageData packageData = getPackageData(appTarget.getPackageName(),
|
||||
PackageData packageData = getPackage(appTarget.getPackageName(),
|
||||
appTarget.getUser().getIdentifier());
|
||||
if (packageData == null) {
|
||||
return;
|
||||
@@ -283,20 +280,6 @@ public class DataManager {
|
||||
return userData != null && userData.isUnlocked() ? userData : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PackageData getPackageData(@NonNull String packageName, int userId) {
|
||||
UserData userData = getUnlockedUserData(userId);
|
||||
return userData != null ? userData.getPackageData(packageName) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ConversationInfo getConversationInfo(@NonNull String packageName, @UserIdInt int userId,
|
||||
@NonNull String shortcutId) {
|
||||
PackageData packageData = getPackageData(packageName, userId);
|
||||
return packageData != null ? packageData.getConversationStore().getConversation(shortcutId)
|
||||
: null;
|
||||
}
|
||||
|
||||
private void updateDefaultDialer(@NonNull UserData userData) {
|
||||
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
|
||||
String defaultDialer = telecomManager != null
|
||||
@@ -318,7 +301,7 @@ public class DataManager {
|
||||
if (shortcutId == null) {
|
||||
return null;
|
||||
}
|
||||
PackageData packageData = getPackageData(sbn.getPackageName(),
|
||||
PackageData packageData = getPackage(sbn.getPackageName(),
|
||||
sbn.getUser().getIdentifier());
|
||||
if (packageData == null
|
||||
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
|
||||
@@ -382,7 +365,7 @@ public class DataManager {
|
||||
usageEvents.getNextEvent(e);
|
||||
|
||||
String packageName = e.getPackageName();
|
||||
PackageData packageData = getPackageData(packageName, userId);
|
||||
PackageData packageData = getPackage(packageName, userId);
|
||||
if (packageData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.people.data;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.LocusId;
|
||||
import android.text.TextUtils;
|
||||
@@ -67,6 +68,15 @@ public class PackageData {
|
||||
return getEventStore().getPackageEventHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
|
||||
* ConversationInfo} does not exist.
|
||||
*/
|
||||
@Nullable
|
||||
public ConversationInfo getConversationInfo(@NonNull String shortcutId) {
|
||||
return getConversationStore().getConversation(shortcutId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the combined {@link EventHistory} for a given shortcut ID. This returned {@link
|
||||
* EventHistory} has events of all types, no matter whether they're annotated with shortcut ID,
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.people.prediction;
|
||||
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.WorkerThread;
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.people.data.DataManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Predictor that predicts the {@link AppTarget} the user is most likely to open.
|
||||
*/
|
||||
public class AppTargetPredictor {
|
||||
|
||||
private static final String UI_SURFACE_SHARE = "share";
|
||||
|
||||
/** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
|
||||
public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
|
||||
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
|
||||
@NonNull DataManager dataManager) {
|
||||
if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
|
||||
return new ShareTargetPredictor(
|
||||
predictionContext, updatePredictionsMethod, dataManager);
|
||||
}
|
||||
return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
|
||||
}
|
||||
|
||||
private final AppPredictionContext mPredictionContext;
|
||||
private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
|
||||
private final DataManager mDataManager;
|
||||
private final ExecutorService mCallbackExecutor;
|
||||
|
||||
AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
|
||||
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
|
||||
@NonNull DataManager dataManager) {
|
||||
mPredictionContext = predictionContext;
|
||||
mUpdatePredictionsMethod = updatePredictionsMethod;
|
||||
mDataManager = dataManager;
|
||||
mCallbackExecutor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to indicate a target launch.
|
||||
*/
|
||||
@MainThread
|
||||
public void onAppTargetEvent(AppTargetEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to indicate a particular location has been shown to the user.
|
||||
*/
|
||||
@MainThread
|
||||
public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to request sorting of the provided targets based on the prediction
|
||||
* ranking.
|
||||
*/
|
||||
@MainThread
|
||||
public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
|
||||
mCallbackExecutor.execute(() -> sortTargets(targets, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to request target predictions.
|
||||
*/
|
||||
@MainThread
|
||||
public void onRequestPredictionUpdate() {
|
||||
mCallbackExecutor.execute(this::predictTargets);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
|
||||
return mUpdatePredictionsMethod;
|
||||
}
|
||||
|
||||
/** To be overridden by the subclass to predict the targets. */
|
||||
@WorkerThread
|
||||
void predictTargets() {
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by the subclass to sort the provided targets based on the prediction
|
||||
* ranking.
|
||||
*/
|
||||
@WorkerThread
|
||||
void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
|
||||
callback.accept(targets);
|
||||
}
|
||||
|
||||
AppPredictionContext getPredictionContext() {
|
||||
return mPredictionContext;
|
||||
}
|
||||
|
||||
DataManager getDataManager() {
|
||||
return mDataManager;
|
||||
}
|
||||
|
||||
void updatePredictions(List<AppTarget> targets) {
|
||||
mUpdatePredictionsMethod.accept(targets);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.people.prediction;
|
||||
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager.ShareShortcutInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.ChooserActivity;
|
||||
import com.android.server.people.data.DataManager;
|
||||
import com.android.server.people.data.EventHistory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Predictor that predicts the conversations or apps the user is most likely to open.
|
||||
*/
|
||||
public class ConversationPredictor {
|
||||
|
||||
private static final String UI_SURFACE_SHARE = "share";
|
||||
|
||||
private final AppPredictionContext mPredictionContext;
|
||||
private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
|
||||
private final DataManager mDataManager;
|
||||
private final ExecutorService mCallbackExecutor;
|
||||
@Nullable
|
||||
private final IntentFilter mIntentFilter;
|
||||
|
||||
public ConversationPredictor(@NonNull AppPredictionContext predictionContext,
|
||||
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
|
||||
@NonNull DataManager dataManager) {
|
||||
mPredictionContext = predictionContext;
|
||||
mUpdatePredictionsMethod = updatePredictionsMethod;
|
||||
mDataManager = dataManager;
|
||||
mCallbackExecutor = Executors.newSingleThreadExecutor();
|
||||
if (UI_SURFACE_SHARE.equals(mPredictionContext.getUiSurface())) {
|
||||
mIntentFilter = mPredictionContext.getExtras().getParcelable(
|
||||
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
|
||||
} else {
|
||||
mIntentFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to indicate a target launch.
|
||||
*/
|
||||
@MainThread
|
||||
public void onAppTargetEvent(AppTargetEvent event) {
|
||||
mDataManager.reportAppTargetEvent(event, mIntentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to indicate a particular location has been shown to the user.
|
||||
*/
|
||||
@MainThread
|
||||
public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {}
|
||||
|
||||
/**
|
||||
* Called by the client app to request sorting of the provided targets based on the prediction
|
||||
* ranking.
|
||||
*/
|
||||
@MainThread
|
||||
public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
|
||||
mCallbackExecutor.execute(() -> callback.accept(targets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client app to request target predictions.
|
||||
*/
|
||||
@MainThread
|
||||
public void onRequestPredictionUpdate() {
|
||||
// TODO: Re-route the call to different ranking classes for different surfaces.
|
||||
mCallbackExecutor.execute(() -> {
|
||||
List<AppTarget> targets = new ArrayList<>();
|
||||
if (mIntentFilter != null) {
|
||||
List<ShareShortcutInfo> shareShortcuts =
|
||||
mDataManager.getConversationShareTargets(mIntentFilter);
|
||||
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
|
||||
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
|
||||
AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
|
||||
String shareTargetClass = shareShortcut.getTargetComponent().getClassName();
|
||||
targets.add(new AppTarget.Builder(appTargetId, shortcutInfo)
|
||||
.setClassName(shareTargetClass)
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
List<ConversationData> conversationDataList = new ArrayList<>();
|
||||
mDataManager.forAllPackages(packageData ->
|
||||
packageData.forAllConversations(conversationInfo -> {
|
||||
EventHistory eventHistory = packageData.getEventHistory(
|
||||
conversationInfo.getShortcutId());
|
||||
ConversationData conversationData = new ConversationData(
|
||||
packageData.getPackageName(), packageData.getUserId(),
|
||||
conversationInfo, eventHistory);
|
||||
conversationDataList.add(conversationData);
|
||||
}));
|
||||
for (ConversationData conversationData : conversationDataList) {
|
||||
String shortcutId = conversationData.getConversationInfo().getShortcutId();
|
||||
ShortcutInfo shortcut = mDataManager.getShortcut(
|
||||
conversationData.getPackageName(), conversationData.getUserId(),
|
||||
shortcutId);
|
||||
if (shortcut != null) {
|
||||
AppTargetId appTargetId = new AppTargetId(shortcut.getId());
|
||||
targets.add(new AppTarget.Builder(appTargetId, shortcut).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
mUpdatePredictionsMethod.accept(targets);
|
||||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
|
||||
return mUpdatePredictionsMethod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.people.prediction;
|
||||
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.WorkerThread;
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager.ShareShortcutInfo;
|
||||
|
||||
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.EventHistory;
|
||||
import com.android.server.people.data.PackageData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class ShareTargetPredictor extends AppTargetPredictor {
|
||||
|
||||
private final IntentFilter mIntentFilter;
|
||||
|
||||
ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
|
||||
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
|
||||
@NonNull DataManager dataManager) {
|
||||
super(predictionContext, updatePredictionsMethod, dataManager);
|
||||
mIntentFilter = predictionContext.getExtras().getParcelable(
|
||||
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@Override
|
||||
public void onAppTargetEvent(AppTargetEvent event) {
|
||||
getDataManager().reportAppTargetEvent(event, mIntentFilter);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
updatePredictions(appTargets);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<ShareTarget> getShareTargets() {
|
||||
List<ShareTarget> shareTargets = new ArrayList<>();
|
||||
List<ShareShortcutInfo> shareShortcuts =
|
||||
getDataManager().getShareShortcuts(mIntentFilter);
|
||||
|
||||
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
|
||||
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
|
||||
String packageName = shortcutInfo.getPackage();
|
||||
int userId = shortcutInfo.getUserId();
|
||||
PackageData packageData = getDataManager().getPackage(packageName, userId);
|
||||
|
||||
ConversationData conversationData = null;
|
||||
if (packageData != null) {
|
||||
String shortcutId = shortcutInfo.getId();
|
||||
ConversationInfo conversationInfo =
|
||||
packageData.getConversationInfo(shortcutId);
|
||||
|
||||
if (conversationInfo != null) {
|
||||
EventHistory eventHistory = packageData.getEventHistory(shortcutId);
|
||||
conversationData = new ConversationData(
|
||||
packageName, userId, conversationInfo, eventHistory);
|
||||
}
|
||||
}
|
||||
shareTargets.add(new ShareTarget(shareShortcut, conversationData));
|
||||
}
|
||||
|
||||
return shareTargets;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class ShareTarget {
|
||||
|
||||
@NonNull
|
||||
private final ShareShortcutInfo mShareShortcutInfo;
|
||||
@Nullable
|
||||
private final ConversationData mConversationData;
|
||||
|
||||
private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
|
||||
@Nullable ConversationData conversationData) {
|
||||
mShareShortcutInfo = shareShortcutInfo;
|
||||
mConversationData = conversationData;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
ShareShortcutInfo getShareShortcutInfo() {
|
||||
return mShareShortcutInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
ConversationData getConversationData() {
|
||||
return mConversationData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import android.app.prediction.IPredictionCallback;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
@@ -76,6 +77,7 @@ public final class PeopleServiceTest {
|
||||
mPredictionContext = new AppPredictionContext.Builder(mContext)
|
||||
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
|
||||
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
|
||||
.setExtras(new Bundle())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,12 @@ import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.ShortcutManager.ShareShortcutInfo;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.ContentObserver;
|
||||
@@ -223,31 +221,6 @@ public final class DataManagerTest {
|
||||
eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetShareTargets() {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
ShortcutInfo shortcut1 =
|
||||
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson());
|
||||
ShareShortcutInfo shareShortcut1 =
|
||||
new ShareShortcutInfo(shortcut1, new ComponentName("pkg_1", "activity"));
|
||||
|
||||
ShortcutInfo shortcut2 =
|
||||
buildShortcutInfo("pkg_2", USER_ID_PRIMARY, "sc_2", buildPerson());
|
||||
ShareShortcutInfo shareShortcut2 =
|
||||
new ShareShortcutInfo(shortcut2, new ComponentName("pkg_2", "activity"));
|
||||
mDataManager.onShortcutAddedOrUpdated(shortcut2);
|
||||
|
||||
when(mShortcutManager.getShareTargets(any(IntentFilter.class)))
|
||||
.thenReturn(Arrays.asList(shareShortcut1, shareShortcut2));
|
||||
|
||||
List<ShareShortcutInfo> shareShortcuts =
|
||||
mDataManager.getConversationShareTargets(new IntentFilter());
|
||||
// Only "sc_2" is stored as a conversation.
|
||||
assertEquals(1, shareShortcuts.size());
|
||||
assertEquals("sc_2", shareShortcuts.get(0).getShortcutInfo().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
|
||||
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.people.prediction;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager.ShareShortcutInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.server.people.data.ConversationInfo;
|
||||
import com.android.server.people.data.DataManager;
|
||||
import com.android.server.people.data.EventHistory;
|
||||
import com.android.server.people.data.PackageData;
|
||||
import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public final class ShareTargetPredictorTest {
|
||||
|
||||
private static final String UI_SURFACE_SHARE = "share";
|
||||
private static final int NUM_PREDICTED_TARGETS = 5;
|
||||
private static final int USER_ID = 0;
|
||||
private static final String PACKAGE_1 = "pkg1";
|
||||
private static final String CLASS_1 = "cls1";
|
||||
private static final String PACKAGE_2 = "pkg2";
|
||||
private static final String CLASS_2 = "cls2";
|
||||
|
||||
@Mock private Context mContext;
|
||||
@Mock private DataManager mDataManager;
|
||||
@Mock private PackageData mPackageData1;
|
||||
@Mock private PackageData mPackageData2;
|
||||
|
||||
private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
|
||||
|
||||
private ShareTargetPredictor mPredictor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
|
||||
when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
|
||||
when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
|
||||
|
||||
AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
|
||||
.setUiSurface(UI_SURFACE_SHARE)
|
||||
.setPredictedTargetCount(NUM_PREDICTED_TARGETS)
|
||||
.setExtras(new Bundle())
|
||||
.build();
|
||||
mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetShareTargets() {
|
||||
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
|
||||
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
|
||||
mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
|
||||
mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
|
||||
|
||||
when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
|
||||
when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
|
||||
when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
|
||||
// "sc4" does not have a ConversationInfo.
|
||||
|
||||
when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
|
||||
when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
|
||||
|
||||
List<ShareTarget> shareTargets = mPredictor.getShareTargets();
|
||||
|
||||
assertEquals(4, shareTargets.size());
|
||||
|
||||
assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
|
||||
assertNotNull(shareTargets.get(0).getConversationData());
|
||||
|
||||
assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
|
||||
assertNotNull(shareTargets.get(1).getConversationData());
|
||||
|
||||
assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
|
||||
assertNotNull(shareTargets.get(2).getConversationData());
|
||||
|
||||
assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
|
||||
assertNull(shareTargets.get(3).getConversationData());
|
||||
}
|
||||
|
||||
private ShareShortcutInfo buildShareShortcut(
|
||||
String packageName, String className, String shortcutId) {
|
||||
ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
|
||||
ComponentName componentName = new ComponentName(packageName, className);
|
||||
return new ShareShortcutInfo(shortcutInfo, componentName);
|
||||
}
|
||||
|
||||
private ShortcutInfo buildShortcut(String packageName, String shortcutId) {
|
||||
Context mockContext = mock(Context.class);
|
||||
when(mockContext.getPackageName()).thenReturn(packageName);
|
||||
when(mockContext.getUserId()).thenReturn(USER_ID);
|
||||
when(mockContext.getUser()).thenReturn(UserHandle.of(USER_ID));
|
||||
ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, shortcutId)
|
||||
.setShortLabel(shortcutId)
|
||||
.setIntent(new Intent("TestIntent"));
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user