Merge "Checking for unlauncheable apps."
This commit is contained in:
committed by
Android (Google) Code Review
commit
16ac7c3b1f
@@ -20,7 +20,6 @@ import android.animation.LayoutTransition;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.AppGlobals;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
@@ -29,13 +28,10 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.AttributeSet;
|
||||
@@ -49,6 +45,7 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.util.List;
|
||||
@@ -75,6 +72,8 @@ class NavigationBarApps extends LinearLayout {
|
||||
private final PackageManager mPackageManager;
|
||||
private final UserManager mUserManager;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final AppPackageMonitor mAppPackageMonitor;
|
||||
|
||||
|
||||
// This view has two roles:
|
||||
// 1) If the drag started outside the pinned apps list, it is a placeholder icon with a null
|
||||
@@ -106,6 +105,7 @@ class NavigationBarApps extends LinearLayout {
|
||||
mPackageManager = context.getPackageManager();
|
||||
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
mAppPackageMonitor = new AppPackageMonitor();
|
||||
|
||||
// Dragging an icon removes and adds back the dragged icon. Use the layout transitions to
|
||||
// trigger animation. By default all transitions animate, so turn off the unneeded ones.
|
||||
@@ -121,6 +121,76 @@ class NavigationBarApps extends LinearLayout {
|
||||
setLayoutTransition(transition);
|
||||
}
|
||||
|
||||
// Monitor that catches events like "app uninstalled".
|
||||
private class AppPackageMonitor extends PackageMonitor {
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, int uid) {
|
||||
postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
|
||||
super.onPackageRemoved(packageName, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageModified(String packageName) {
|
||||
postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
|
||||
super.onPackageModified(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesAvailable(String[] packages) {
|
||||
if (isReplacing()) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
|
||||
for (String packageName : packages) {
|
||||
postRemoveIfUnlauncheable(packageName, user);
|
||||
}
|
||||
}
|
||||
super.onPackagesAvailable(packages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesUnavailable(String[] packages) {
|
||||
if (!isReplacing()) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
|
||||
for (String packageName : packages) {
|
||||
postRemoveIfUnlauncheable(packageName, user);
|
||||
}
|
||||
}
|
||||
super.onPackagesUnavailable(packages);
|
||||
}
|
||||
}
|
||||
|
||||
private void postRemoveIfUnlauncheable(final String packageName, final UserHandle user) {
|
||||
// This method doesn't necessarily get called in the main thread. Redirect the call into
|
||||
// the main thread.
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isAttachedToWindow()) return;
|
||||
removeIfUnlauncheable(packageName, user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeIfUnlauncheable(String packageName, UserHandle user) {
|
||||
long appUserSerialNumber = mUserManager.getSerialNumberForUser(user);
|
||||
|
||||
// Remove icons for all apps that match a package that perhaps became unlauncheable.
|
||||
for(int i = sAppsModel.getAppCount() - 1; i >= 0; --i) {
|
||||
AppInfo appInfo = sAppsModel.getApp(i);
|
||||
if (appInfo.getUserSerialNumber() != appUserSerialNumber) continue;
|
||||
|
||||
ComponentName appComponentName = appInfo.getComponentName();
|
||||
if (!appComponentName.getPackageName().equals(packageName)) continue;
|
||||
|
||||
if (sAppsModel.buildAppLaunchIntent(appComponentName, user) != null) continue;
|
||||
|
||||
removeViewAt(i);
|
||||
sAppsModel.removeApp(i);
|
||||
sAppsModel.savePrefs();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@@ -145,12 +215,15 @@ class NavigationBarApps extends LinearLayout {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_USER_SWITCHED);
|
||||
mContext.registerReceiver(mBroadcastReceiver, filter);
|
||||
|
||||
mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mContext.unregisterReceiver(mBroadcastReceiver);
|
||||
mAppPackageMonitor.unregister();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -470,7 +543,6 @@ class NavigationBarApps extends LinearLayout {
|
||||
ComponentName component = appInfo.getComponentName();
|
||||
|
||||
long appUserSerialNumber = appInfo.getUserSerialNumber();
|
||||
|
||||
UserHandle appUser = mUserManager.getUserForSerialNumber(appUserSerialNumber);
|
||||
if (appUser == null) {
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
@@ -478,7 +550,12 @@ class NavigationBarApps extends LinearLayout {
|
||||
" because its user doesn't exist.");
|
||||
return;
|
||||
}
|
||||
int appUserId = appUser.getIdentifier();
|
||||
|
||||
Intent launchIntent = sAppsModel.buildAppLaunchIntent(component, appUser);
|
||||
if (launchIntent == null) {
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Play a scale-up animation while launching the activity.
|
||||
// TODO: Consider playing a different animation, or no animation, if the activity is
|
||||
@@ -489,54 +566,9 @@ class NavigationBarApps extends LinearLayout {
|
||||
ActivityOptions opts =
|
||||
ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight());
|
||||
Bundle optsBundle = opts.toBundle();
|
||||
|
||||
// Launch the activity. This code is based on LauncherAppsService.startActivityAsUser code.
|
||||
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
|
||||
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
launchIntent.setSourceBounds(sourceBounds);
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
launchIntent.setPackage(component.getPackageName());
|
||||
|
||||
IPackageManager pm = AppGlobals.getPackageManager();
|
||||
try {
|
||||
ActivityInfo info = pm.getActivityInfo(component, 0, appUserId);
|
||||
if (info == null) {
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Can't start activity " + component + " because it's not installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.exported) {
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Can't start activity " + component + " because it doesn't have 'exported' attribute.");
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Failed to get activity info for " + component, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the component actually has Intent.CATEGORY_LAUCNCHER
|
||||
// as calling startActivityAsUser ignores the category and just
|
||||
// resolves based on the component if present.
|
||||
List<ResolveInfo> apps = getContext().getPackageManager().queryIntentActivitiesAsUser(launchIntent,
|
||||
0 /* flags */, appUserId);
|
||||
final int size = apps.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
ActivityInfo activityInfo = apps.get(i).activityInfo;
|
||||
if (activityInfo.packageName.equals(component.getPackageName()) &&
|
||||
activityInfo.name.equals(component.getClassName())) {
|
||||
// Found an activity with category launcher that matches
|
||||
// this component so ok to launch.
|
||||
launchIntent.setComponent(component);
|
||||
mContext.startActivityAsUser(launchIntent, optsBundle, appUser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Attempt to launch activity without category Intent.CATEGORY_LAUNCHER " + component);
|
||||
mContext.startActivityAsUser(launchIntent, optsBundle, appUser);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,16 +16,23 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import android.app.AppGlobals;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -94,6 +101,58 @@ class NavigationBarAppsModel {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected IPackageManager getPackageManager() {
|
||||
return AppGlobals.getPackageManager();
|
||||
}
|
||||
|
||||
// Returns a launch intent for a given component, or null if the component is unlauncheable.
|
||||
public Intent buildAppLaunchIntent(ComponentName component, UserHandle appUser) {
|
||||
int appUserId = appUser.getIdentifier();
|
||||
|
||||
// This code is based on LauncherAppsService.startActivityAsUser code.
|
||||
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
|
||||
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
launchIntent.setPackage(component.getPackageName());
|
||||
|
||||
try {
|
||||
ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId);
|
||||
if (info == null) {
|
||||
Log.e(TAG, "Activity " + component + " is not installed.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!info.exported) {
|
||||
Log.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
|
||||
return null;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to get activity info for " + component, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that the component actually has Intent.CATEGORY_LAUNCHER
|
||||
// as calling startActivityAsUser ignores the category and just
|
||||
// resolves based on the component if present.
|
||||
List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent,
|
||||
0 /* flags */, appUserId);
|
||||
final int size = apps.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
ActivityInfo activityInfo = apps.get(i).activityInfo;
|
||||
if (activityInfo.packageName.equals(component.getPackageName()) &&
|
||||
activityInfo.name.equals(component.getClassName())) {
|
||||
// Found an activity with category launcher that matches
|
||||
// this component so ok to launch.
|
||||
launchIntent.setComponent(component);
|
||||
return launchIntent;
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitializes the model for a new user.
|
||||
*/
|
||||
@@ -199,6 +258,10 @@ class NavigationBarAppsModel {
|
||||
|
||||
/** Loads the list of apps from SharedPreferences. */
|
||||
private void loadAppsFromPrefs() {
|
||||
UserManager mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
|
||||
boolean hadUnlauncheableApps = false;
|
||||
|
||||
int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
|
||||
for (int i = 0; i < appCount; i++) {
|
||||
String prefValue = mPrefs.getString(prefNameForApp(i), null);
|
||||
@@ -214,8 +277,15 @@ class NavigationBarAppsModel {
|
||||
// Couldn't find the saved state. Just skip this item.
|
||||
continue;
|
||||
}
|
||||
mApps.add(new AppInfo(componentName, userSerialNumber));
|
||||
UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
|
||||
if (appUser != null && buildAppLaunchIntent(componentName, appUser) != null) {
|
||||
mApps.add(new AppInfo(componentName, userSerialNumber));
|
||||
} else {
|
||||
hadUnlauncheableApps = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hadUnlauncheableApps) savePrefs();
|
||||
}
|
||||
|
||||
/** Adds the first few apps from the owner profile. Used for demo purposes. */
|
||||
|
||||
@@ -16,21 +16,26 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import org.mockito.InOrder;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.test.AndroidTestCase;
|
||||
@@ -45,6 +50,7 @@ import java.util.Map;
|
||||
/** Tests for the data model for the navigation bar app icons. */
|
||||
public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
private PackageManager mMockPackageManager;
|
||||
private IPackageManager mMockIPackageManager;
|
||||
private SharedPreferences mMockPrefs;
|
||||
private SharedPreferences.Editor mMockEdit;
|
||||
private UserManager mMockUserManager;
|
||||
@@ -61,6 +67,7 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
|
||||
final Context context = mock(Context.class);
|
||||
mMockPackageManager = mock(PackageManager.class);
|
||||
mMockIPackageManager = mock(IPackageManager.class);
|
||||
mMockPrefs = mock(SharedPreferences.class);
|
||||
mMockEdit = mock(SharedPreferences.Editor.class);
|
||||
mMockUserManager = mock(UserManager.class);
|
||||
@@ -78,8 +85,71 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
when(mMockPrefs.edit()).thenReturn(mMockEdit);
|
||||
|
||||
when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(22L);
|
||||
when(mMockUserManager.getUserForSerialNumber(45L)).thenReturn(new UserHandle(4));
|
||||
when(mMockUserManager.getUserForSerialNumber(239L)).thenReturn(new UserHandle(5));
|
||||
|
||||
mModel = new NavigationBarAppsModel(context);
|
||||
mModel = new NavigationBarAppsModel(context) {
|
||||
@Override
|
||||
protected IPackageManager getPackageManager() {
|
||||
return mMockIPackageManager;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Tests buildAppLaunchIntent(). */
|
||||
public void testBuildAppLaunchIntent() {
|
||||
ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
|
||||
mockNonExportedActivityInfo.exported = false;
|
||||
ActivityInfo mockExportedActivityInfo = new ActivityInfo();
|
||||
mockExportedActivityInfo.exported = true;
|
||||
try {
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package1", "class1"), 0, 4)).
|
||||
thenReturn(mockNonExportedActivityInfo);
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package2", "class2"), 0, 5)).
|
||||
thenThrow(new RemoteException());
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package3", "class3"), 0, 6)).
|
||||
thenReturn(mockExportedActivityInfo);
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package4", "class4"), 0, 7)).
|
||||
thenReturn(mockExportedActivityInfo);
|
||||
} catch (RemoteException e) {
|
||||
fail("RemoteException can't happen in the test, but it happened.");
|
||||
}
|
||||
|
||||
// Assume some installed activities.
|
||||
ActivityInfo ai0 = new ActivityInfo();
|
||||
ai0.packageName = "package0";
|
||||
ai0.name = "class0";
|
||||
ActivityInfo ai1 = new ActivityInfo();
|
||||
ai1.packageName = "package4";
|
||||
ai1.name = "class4";
|
||||
ResolveInfo ri0 = new ResolveInfo();
|
||||
ri0.activityInfo = ai0;
|
||||
ResolveInfo ri1 = new ResolveInfo();
|
||||
ri1.activityInfo = ai1;
|
||||
when(mMockPackageManager
|
||||
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
|
||||
.thenReturn(Arrays.asList(ri0, ri1));
|
||||
|
||||
// Unlauncheable (for various reasons) apps.
|
||||
assertEquals(null, mModel.buildAppLaunchIntent(
|
||||
new ComponentName("package0", "class0"), new UserHandle(3)));
|
||||
assertEquals(null, mModel.buildAppLaunchIntent(
|
||||
new ComponentName("package1", "class1"), new UserHandle(4)));
|
||||
assertEquals(null, mModel.buildAppLaunchIntent(
|
||||
new ComponentName("package2", "class2"), new UserHandle(5)));
|
||||
assertEquals(null, mModel.buildAppLaunchIntent(
|
||||
new ComponentName("package3", "class3"), new UserHandle(6)));
|
||||
|
||||
// A launcheable app.
|
||||
Intent intent = mModel.buildAppLaunchIntent(
|
||||
new ComponentName("package4", "class4"), new UserHandle(7));
|
||||
assertNotNull(intent);
|
||||
assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
|
||||
assertEquals("package4", intent.getPackage());
|
||||
}
|
||||
|
||||
/** Initializes the model from SharedPreferences for a few app activites. */
|
||||
@@ -93,6 +163,39 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
when(mMockPrefs.getString("22|app_2", null)).thenReturn("package2/class2");
|
||||
when(mMockPrefs.getLong("22|app_user_2", -1)).thenReturn(239L);
|
||||
|
||||
ActivityInfo mockActivityInfo = new ActivityInfo();
|
||||
mockActivityInfo.exported = true;
|
||||
try {
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package1", "class1"), 0, 4)).thenReturn(mockActivityInfo);
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package2", "class2"), 0, 5)).thenReturn(mockActivityInfo);
|
||||
} catch (RemoteException e) {
|
||||
fail("RemoteException can't happen in the test, but it happened.");
|
||||
}
|
||||
|
||||
// Assume some installed activities.
|
||||
ActivityInfo ai0 = new ActivityInfo();
|
||||
ai0.packageName = "package0";
|
||||
ai0.name = "class0";
|
||||
ActivityInfo ai1 = new ActivityInfo();
|
||||
ai1.packageName = "package1";
|
||||
ai1.name = "class1";
|
||||
ActivityInfo ai2 = new ActivityInfo();
|
||||
ai2.packageName = "package2";
|
||||
ai2.name = "class2";
|
||||
ResolveInfo ri0 = new ResolveInfo();
|
||||
ri0.activityInfo = ai0;
|
||||
ResolveInfo ri1 = new ResolveInfo();
|
||||
ri1.activityInfo = ai1;
|
||||
ResolveInfo ri2 = new ResolveInfo();
|
||||
ri2.activityInfo = ai2;
|
||||
when(mMockPackageManager
|
||||
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
|
||||
.thenReturn(Arrays.asList(ri0, ri1, ri2));
|
||||
|
||||
mModel.setCurrentUser(2);
|
||||
}
|
||||
|
||||
@@ -133,6 +236,15 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
assertEquals(22L, mModel.getApp(0).getUserSerialNumber());
|
||||
assertEquals("package2/class2", mModel.getApp(1).getComponentName().flattenToString());
|
||||
assertEquals(22L, mModel.getApp(1).getUserSerialNumber());
|
||||
InOrder order = inOrder(mMockEdit);
|
||||
order.verify(mMockEdit).apply();
|
||||
order.verify(mMockEdit).putInt("22|app_count", 2);
|
||||
order.verify(mMockEdit).putString("22|app_0", "package1/class1");
|
||||
order.verify(mMockEdit).putLong("22|app_user_0", 22L);
|
||||
order.verify(mMockEdit).putString("22|app_1", "package2/class2");
|
||||
order.verify(mMockEdit).putLong("22|app_user_1", 22L);
|
||||
order.verify(mMockEdit).apply();
|
||||
verifyNoMoreInteractions(mMockEdit);
|
||||
}
|
||||
|
||||
/** Tests initializing the model if one of the prefs is missing. */
|
||||
@@ -145,11 +257,72 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
|
||||
// But assume one pref is missing.
|
||||
when(mMockPrefs.getString("22|app_1", null)).thenReturn(null);
|
||||
|
||||
ActivityInfo mockActivityInfo = new ActivityInfo();
|
||||
mockActivityInfo.exported = true;
|
||||
try {
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
|
||||
} catch (RemoteException e) {
|
||||
fail("RemoteException can't happen in the test, but it happened.");
|
||||
}
|
||||
|
||||
ActivityInfo ai0 = new ActivityInfo();
|
||||
ai0.packageName = "package0";
|
||||
ai0.name = "class0";
|
||||
ResolveInfo ri0 = new ResolveInfo();
|
||||
ri0.activityInfo = ai0;
|
||||
when(mMockPackageManager
|
||||
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
|
||||
.thenReturn(Arrays.asList(ri0));
|
||||
|
||||
// Initializing the model should load from prefs and skip the missing one.
|
||||
mModel.setCurrentUser(2);
|
||||
assertEquals(1, mModel.getAppCount());
|
||||
assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
|
||||
assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
|
||||
verifyNoMoreInteractions(mMockEdit);
|
||||
}
|
||||
|
||||
/** Tests initializing the model if one of the apps is unlauncheable. */
|
||||
public void testInitializeWithUnlauncheableApp() {
|
||||
// Assume two apps are nominally stored.
|
||||
when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(2);
|
||||
when(mMockPrefs.getString("22|app_0", null)).thenReturn("package0/class0");
|
||||
when(mMockPrefs.getLong("22|app_user_0", -1)).thenReturn(239L);
|
||||
when(mMockPrefs.getString("22|app_1", null)).thenReturn("package1/class1");
|
||||
when(mMockPrefs.getLong("22|app_user_1", -1)).thenReturn(45L);
|
||||
|
||||
ActivityInfo mockActivityInfo = new ActivityInfo();
|
||||
mockActivityInfo.exported = true;
|
||||
try {
|
||||
when(mMockIPackageManager.getActivityInfo(
|
||||
new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
|
||||
} catch (RemoteException e) {
|
||||
fail("RemoteException can't happen in the test, but it happened.");
|
||||
}
|
||||
|
||||
ActivityInfo ai0 = new ActivityInfo();
|
||||
ai0.packageName = "package0";
|
||||
ai0.name = "class0";
|
||||
ResolveInfo ri0 = new ResolveInfo();
|
||||
ri0.activityInfo = ai0;
|
||||
when(mMockPackageManager
|
||||
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
|
||||
.thenReturn(Arrays.asList(ri0));
|
||||
|
||||
// Initializing the model should load from prefs and skip the unlauncheable one.
|
||||
mModel.setCurrentUser(2);
|
||||
assertEquals(1, mModel.getAppCount());
|
||||
assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
|
||||
assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
|
||||
|
||||
// Once an unlauncheable app is detected, the model should save all apps excluding the
|
||||
// unlauncheable one.
|
||||
verify(mMockEdit).putInt("22|app_count", 1);
|
||||
verify(mMockEdit).putString("22|app_0", "package0/class0");
|
||||
verify(mMockEdit).putLong("22|app_user_0", 239L);
|
||||
verify(mMockEdit).apply();
|
||||
verifyNoMoreInteractions(mMockEdit);
|
||||
}
|
||||
|
||||
/** Tests saving the model to SharedPreferences. */
|
||||
|
||||
Reference in New Issue
Block a user