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:
Danning Chen
2020-01-30 16:04:01 -08:00
parent 25e3a1adb4
commit be2c1552d2
9 changed files with 435 additions and 205 deletions

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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();
}
}