DO NOT MERGE sysui: remove shelf

Bug: 26742568
Change-Id: I36bd013c4adb62b76b6f2789aa490c1484a535d7
This commit is contained in:
Xiaohui Chen
2016-01-26 11:21:35 -08:00
parent 5f1c64ef76
commit e9dab0dade
4 changed files with 0 additions and 2031 deletions

View File

@@ -1,113 +0,0 @@
/*
* Copyright (C) 2015 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.systemui.statusbar.phone;
import android.app.AppGlobals;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.Slog;
import android.widget.ImageView;
/**
* Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
* is hidden if the activity isn't recognized or if there is no icon.
*/
class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> {
private final static String TAG = "GetActivityIconTask";
private final PackageManager mPackageManager;
// The ImageView that will receive the icon.
private final ImageView mImageView;
public GetActivityIconTask(PackageManager packageManager, ImageView imageView) {
mPackageManager = packageManager;
mImageView = imageView;
}
@Override
protected Drawable doInBackground(AppButtonData... params) {
if (params.length != 1) {
throw new IllegalArgumentException("Expected one parameter");
}
AppButtonData buttonData = params[0];
AppInfo appInfo = buttonData.appInfo;
try {
IPackageManager mPM = AppGlobals.getPackageManager();
ActivityInfo ai = mPM.getActivityInfo(
appInfo.getComponentName(),
0,
appInfo.getUser().getIdentifier());
if (ai == null) {
Slog.w(TAG, "Icon not found for " + appInfo);
return null;
}
Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
Drawable badgedIcon =
mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
if (NavigationBarApps.DEBUG) {
// Draw pinned indicator and number of running tasks.
Bitmap bitmap = Bitmap.createBitmap(
badgedIcon.getIntrinsicWidth(),
badgedIcon.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
badgedIcon.setBounds(
0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight());
badgedIcon.draw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
if (buttonData.pinned) {
paint.setColor(Color.WHITE);
canvas.drawCircle(10, 10, 10, paint);
}
if (buttonData.tasks != null && buttonData.tasks.size() > 0) {
paint.setColor(Color.BLACK);
canvas.drawCircle(60, 30, 30, paint);
paint.setColor(Color.WHITE);
paint.setTextSize(50);
paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint);
}
badgedIcon = new BitmapDrawable(null, bitmap);
}
return badgedIcon;
} catch (RemoteException e) {
Slog.w(TAG, "Icon not found for " + appInfo, e);
return null;
}
}
@Override
protected void onPostExecute(Drawable icon) {
mImageView.setImageDrawable(icon);
}
}

View File

@@ -1,364 +0,0 @@
/*
* Copyright (C) 2015 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.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.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Data model and controller for app icons appearing in the navigation bar. The data is stored on
* disk in SharedPreferences. Each icon has a separate pref entry consisting of a flattened
* ComponentName.
*/
class NavigationBarAppsModel {
public interface OnAppsChangedListener {
void onPinnedAppsChanged();
}
public class ResolvedApp {
Intent launchIntent;
ResolveInfo ri;
}
private final static String TAG = "NavigationBarAppsModel";
// Default number of apps to load initially.
private final static int NUM_INITIAL_APPS = 4;
// Preferences file name.
private final static String SHARED_PREFERENCES_NAME = "com.android.systemui.navbarapps";
// Preference name for the version of the other preferences.
private final static String VERSION_PREF = "version";
// Current version number for preferences.
private final static int CURRENT_VERSION = 3;
// Preference name for the number of app icons.
private final static String APP_COUNT_PREF = "app_count";
// Preference name prefix for each app's info. The actual pref has an integer appended to it.
private final static String APP_PREF_PREFIX = "app_";
// User serial number prefix for each app's info. The actual pref has an integer appended to it.
private final static String APP_USER_PREFIX = "app_user_";
// Character separating current user serial number from the user-specific part of a pref.
// Example "22|app_user_2" - when logged as user with serial 22, we'll use this pref for the
// user serial of the third app of the logged-in user.
private final static char USER_SEPARATOR = '|';
private final Context mContext;
private final UserManager mUserManager;
private final SharedPreferences mPrefs;
// Apps are represented as an ordered list of app infos.
private List<AppInfo> mApps = new ArrayList<AppInfo>();
private List<OnAppsChangedListener> mOnAppsChangedListeners =
new ArrayList<OnAppsChangedListener>();
// Id of the current user.
private int mCurrentUserId = -1;
// Serial number of the current user.
private long mCurrentUserSerialNumber = -1;
public NavigationBarAppsModel(Context context) {
mContext = context;
mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
int version = mPrefs.getInt(VERSION_PREF, -1);
if (version != CURRENT_VERSION) {
// Since the data format changed, clean everything.
SharedPreferences.Editor edit = mPrefs.edit();
edit.clear();
edit.putInt(VERSION_PREF, CURRENT_VERSION);
edit.apply();
}
}
@VisibleForTesting
protected IPackageManager getPackageManager() {
return AppGlobals.getPackageManager();
}
// Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
public ResolvedApp resolveApp(AppInfo appInfo) {
ComponentName component = appInfo.getComponentName();
int appUserId = appInfo.getUser().getIdentifier();
if (mCurrentUserId != appUserId) {
// Check if app user is a profile of current user and the app user is enabled.
UserInfo appUserInfo = mUserManager.getUserInfo(appUserId);
UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId);
if (appUserInfo == null || currentUserInfo == null
|| appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
|| appUserInfo.profileGroupId != currentUserInfo.profileGroupId
|| !appUserInfo.isEnabled()) {
Slog.e(TAG, "User " + appUserId +
" is is not a profile of the current user, or is disabled.");
return null;
}
}
// 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) {
Slog.e(TAG, "Activity " + component + " is not installed.");
return null;
}
if (!info.exported) {
Slog.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
return null;
}
} catch (RemoteException e) {
Slog.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) {
ResolveInfo ri = apps.get(i);
ActivityInfo activityInfo = ri.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);
ResolvedApp resolvedApp = new ResolvedApp();
resolvedApp.launchIntent = launchIntent;
resolvedApp.ri = ri;
return resolvedApp;
}
}
Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
return null;
}
public void addOnAppsChangedListener(OnAppsChangedListener listener) {
mOnAppsChangedListeners.add(listener);
}
public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
mOnAppsChangedListeners.remove(listener);
}
/**
* Reinitializes the model for a new user.
*/
public void setCurrentUser(int userId) {
mCurrentUserId = userId;
mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(new UserHandle(userId));
mApps.clear();
int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
if (appCount >= 0) {
loadAppsFromPrefs(appCount);
} else {
// We switched to this user for the first time ever. This is a good opportunity to clean
// prefs for users deleted in the past.
removePrefsForDeletedUsers();
addDefaultApps();
}
}
/**
* Removes prefs for users that don't exist on the device.
*/
private void removePrefsForDeletedUsers() {
// Build a set of string representations of serial numbers of the device users.
final List<UserInfo> users = mUserManager.getUsers();
final int userCount = users.size();
final Set<String> userSerials = new HashSet<String> ();
for (int i = 0; i < userCount; ++i) {
userSerials.add(Long.toString(users.get(i).serialNumber));
}
// Walk though all prefs and delete ones which user is not in the string set.
final Map<String, ?> allPrefs = mPrefs.getAll();
final SharedPreferences.Editor edit = mPrefs.edit();
for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
final String key = pref.getKey();
if (key.equals(VERSION_PREF)) continue;
final int userSeparatorPos = key.indexOf(USER_SEPARATOR);
if (userSeparatorPos < 0) {
// Removing anomalous pref with no user.
edit.remove(key);
continue;
}
final String prefUserSerial = key.substring(0, userSeparatorPos);
if (!userSerials.contains(prefUserSerial)) {
// Removes pref for a not existing user.
edit.remove(key);
continue;
}
}
edit.apply();
}
/** Returns the list of apps. */
public List<AppInfo> getApps() {
return mApps;
}
/** Sets the list of apps and saves it. */
public void setApps(List<AppInfo> apps) {
mApps = apps;
savePrefs();
int size = mOnAppsChangedListeners.size();
for (int i = 0; i < size; ++i) {
mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
}
}
/** Saves the current model to disk. */
private void savePrefs() {
SharedPreferences.Editor edit = mPrefs.edit();
int appCount = mApps.size();
edit.putInt(userPrefixed(APP_COUNT_PREF), appCount);
for (int i = 0; i < appCount; i++) {
final AppInfo appInfo = mApps.get(i);
String componentNameString = appInfo.getComponentName().flattenToString();
edit.putString(prefNameForApp(i), componentNameString);
long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser());
edit.putLong(prefUserForApp(i), userSerialNumber);
}
// Start an asynchronous disk write.
edit.apply();
}
/** Loads AppInfo from prefs. Returns null if something is wrong. */
private AppInfo loadAppFromPrefs(int index) {
String prefValue = mPrefs.getString(prefNameForApp(index), null);
if (prefValue == null) {
Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index));
return null;
}
ComponentName componentName = ComponentName.unflattenFromString(prefValue);
if (componentName == null) {
Slog.w(TAG, "Invalid component name " + prefValue);
return null;
}
long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1);
if (userSerialNumber == -1) {
Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index));
return null;
}
UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
if (appUser == null) {
Slog.w(TAG, "No user for serial " + userSerialNumber);
return null;
}
AppInfo appInfo = new AppInfo(componentName, appUser);
if (resolveApp(appInfo) == null) {
return null;
}
return appInfo;
}
/** Loads the list of apps from SharedPreferences. */
private void loadAppsFromPrefs(int appCount) {
for (int i = 0; i < appCount; i++) {
AppInfo appInfo = loadAppFromPrefs(i);
if (appInfo != null) {
mApps.add(appInfo);
}
}
if (appCount != mApps.size()) savePrefs();
}
/** Adds the first few apps from the owner profile. Used for demo purposes. */
private void addDefaultApps() {
// Get a list of all app activities.
final Intent queryIntent = new Intent(Intent.ACTION_MAIN, null);
queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(
queryIntent, 0 /* flags */, mCurrentUserId);
final int appCount = apps.size();
for (int i = 0; i < NUM_INITIAL_APPS && i < appCount; i++) {
ResolveInfo ri = apps.get(i);
ComponentName componentName = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId)));
}
savePrefs();
}
/** Returns a pref prefixed with the serial number of the current user. */
private String userPrefixed(String pref) {
return Long.toString(mCurrentUserSerialNumber) + USER_SEPARATOR + pref;
}
/** Returns the pref name for the app at a given index. */
private String prefNameForApp(int index) {
return userPrefixed(APP_PREF_PREFIX + Integer.toString(index));
}
/** Returns the pref name for the app's user at a given index. */
private String prefUserForApp(int index) {
return userPrefixed(APP_USER_PREFIX + Integer.toString(index));
}
}

View File

@@ -1,431 +0,0 @@
/*
* Copyright (C) 2015 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.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;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
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;
private NavigationBarAppsModel mModel;
@Override
protected void setUp() throws Exception {
super.setUp();
// Mockito setup boilerplate.
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
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);
when(context.getSharedPreferences(
"com.android.systemui.navbarapps", Context.MODE_PRIVATE)).thenReturn(mMockPrefs);
when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
when(context.getPackageManager()).thenReturn(mMockPackageManager);
setContext(context);
when(mMockUserManager.getUsers()).thenReturn(new ArrayList<UserInfo>());
// Assume the version pref is present and equal to the current version.
when(mMockPrefs.getInt("version", -1)).thenReturn(3);
when(mMockPrefs.edit()).thenReturn(mMockEdit);
when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(222L);
when(mMockUserManager.getSerialNumberForUser(new UserHandle(4))).thenReturn(444L);
when(mMockUserManager.getSerialNumberForUser(new UserHandle(5))).thenReturn(555L);
when(mMockUserManager.getUserForSerialNumber(222L)).thenReturn(new UserHandle(2));
when(mMockUserManager.getUserForSerialNumber(444L)).thenReturn(new UserHandle(4));
when(mMockUserManager.getUserForSerialNumber(555L)).thenReturn(new UserHandle(5));
UserInfo ui2 = new UserInfo();
ui2.profileGroupId = 999;
UserInfo ui4 = new UserInfo();
ui4.profileGroupId = 999;
UserInfo ui5 = new UserInfo();
ui5.profileGroupId = 999;
when(mMockUserManager.getUserInfo(2)).thenReturn(ui2);
when(mMockUserManager.getUserInfo(4)).thenReturn(ui4);
when(mMockUserManager.getUserInfo(5)).thenReturn(ui5);
mModel = new NavigationBarAppsModel(context) {
@Override
protected IPackageManager getPackageManager() {
return mMockIPackageManager;
}
};
}
/** Tests resolveApp(). */
public void testResolveApp() {
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));
mModel.setCurrentUser(3);
// Unlauncheable (for various reasons) apps.
assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
mModel.setCurrentUser(4);
assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
mModel.setCurrentUser(5);
assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
mModel.setCurrentUser(6);
assertEquals(null, mModel.resolveApp(
new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
// A launcheable app.
mModel.setCurrentUser(7);
NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp(
new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
assertNotNull(resolvedApp);
Intent intent = resolvedApp.launchIntent;
assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
assertEquals("package4", intent.getPackage());
assertEquals(ri1, resolvedApp.ri);
}
/** Initializes the model from SharedPreferences for a few app activites. */
private void initializeModelFromPrefs() {
// Assume several apps are stored.
when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
when(mMockPrefs.getString("222|app_0", null)).thenReturn("package1/class1");
when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(444L);
when(mMockPrefs.getString("222|app_1", null)).thenReturn("package2/class2");
when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(555L);
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);
}
/** Tests initializing the model from SharedPreferences. */
public void testInitializeFromPrefs() {
initializeModelFromPrefs();
List<AppInfo> apps = mModel.getApps();
assertEquals(2, apps.size());
assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
assertEquals(new UserHandle(4), apps.get(0).getUser());
assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
assertEquals(new UserHandle(5), apps.get(1).getUser());
}
/** Tests initializing the model when the SharedPreferences aren't available. */
public void testInitializeDefaultApps() {
// Assume the user's app count pref isn't available.
when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
// Assume some installed activities.
ActivityInfo ai1 = new ActivityInfo();
ai1.packageName = "package1";
ai1.name = "class1";
ActivityInfo ai2 = new ActivityInfo();
ai2.packageName = "package2";
ai2.name = "class2";
ResolveInfo ri1 = new ResolveInfo();
ri1.activityInfo = ai1;
ResolveInfo ri2 = new ResolveInfo();
ri2.activityInfo = ai2;
when(mMockPackageManager
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), eq(2)))
.thenReturn(Arrays.asList(ri1, ri2));
// Setting the user should load the installed activities.
mModel.setCurrentUser(2);
List<AppInfo> apps = mModel.getApps();
assertEquals(2, apps.size());
assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
assertEquals(new UserHandle(2), apps.get(0).getUser());
assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
assertEquals(new UserHandle(2), apps.get(1).getUser());
InOrder order = inOrder(mMockEdit);
order.verify(mMockEdit).apply();
order.verify(mMockEdit).putInt("222|app_count", 2);
order.verify(mMockEdit).putString("222|app_0", "package1/class1");
order.verify(mMockEdit).putLong("222|app_user_0", 222L);
order.verify(mMockEdit).putString("222|app_1", "package2/class2");
order.verify(mMockEdit).putLong("222|app_user_1", 222L);
order.verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests initializing the model if one of the prefs is missing. */
public void testInitializeWithMissingPref() {
// Assume two apps are nominally stored.
when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
// But assume one pref is missing.
when(mMockPrefs.getString("222|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);
List<AppInfo> apps = mModel.getApps();
assertEquals(1, apps.size());
assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
assertEquals(new UserHandle(5), apps.get(0).getUser());
InOrder order = inOrder(mMockEdit);
order.verify(mMockEdit).putInt("222|app_count", 1);
order.verify(mMockEdit).putString("222|app_0", "package0/class0");
order.verify(mMockEdit).putLong("222|app_user_0", 555L);
order.verify(mMockEdit).apply();
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("222|app_count", -1)).thenReturn(2);
when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
when(mMockPrefs.getString("222|app_1", null)).thenReturn("package1/class1");
when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(444L);
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);
List<AppInfo> apps = mModel.getApps();
assertEquals(1, apps.size());
assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
assertEquals(new UserHandle(5), apps.get(0).getUser());
// Once an unlauncheable app is detected, the model should save all apps excluding the
// unlauncheable one.
verify(mMockEdit).putInt("222|app_count", 1);
verify(mMockEdit).putString("222|app_0", "package0/class0");
verify(mMockEdit).putLong("222|app_user_0", 555L);
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests saving the model to SharedPreferences. */
public void testSavePrefs() {
initializeModelFromPrefs();
mModel.setApps(mModel.getApps());
verify(mMockEdit).putInt("222|app_count", 2);
verify(mMockEdit).putString("222|app_0", "package1/class1");
verify(mMockEdit).putLong("222|app_user_0", 444L);
verify(mMockEdit).putString("222|app_1", "package2/class2");
verify(mMockEdit).putLong("222|app_user_1", 555L);
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests cleaning all prefs on a version change. */
public void testVersionChange() {
// Assume the version pref changed.
when(mMockPrefs.getInt("version", -1)).thenReturn(1);
new NavigationBarAppsModel(getContext());
verify(mMockEdit).clear();
verify(mMockEdit).putInt("version", 3);
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests cleaning prefs for deleted users. */
public void testCleaningDeletedUsers() {
// Users on the device.
final UserInfo user1 = new UserInfo(11, "", 0);
user1.serialNumber = 1111;
final UserInfo user2 = new UserInfo(13, "", 0);
user2.serialNumber = 1313;
when(mMockUserManager.getUsers()).thenReturn(Arrays.asList(user1, user2));
when(mMockPrefs.edit()).
thenReturn(mMockEdit).
thenReturn(mock(SharedPreferences.Editor.class));
// Assume the user's app count pref isn't available. This will trigger clearing deleted
// users' prefs.
when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
final Map allPrefs = new HashMap<String, Object>();
allPrefs.put("version", null);
allPrefs.put("some_strange_pref", null);
allPrefs.put("", null);
allPrefs.put("|", null);
allPrefs.put("1313|app_count", null);
allPrefs.put("1212|app_count", null);
when(mMockPrefs.getAll()).thenReturn(allPrefs);
// Setting the user should remove prefs for deleted users.
mModel.setCurrentUser(2);
verify(mMockEdit).remove("some_strange_pref");
verify(mMockEdit).remove("");
verify(mMockEdit).remove("|");
verify(mMockEdit).remove("1212|app_count");
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests the apps-changed listener. */
public void testAppsChangedListeners() {
NavigationBarAppsModel.OnAppsChangedListener listener =
mock(NavigationBarAppsModel.OnAppsChangedListener.class);
mModel.addOnAppsChangedListener(listener);
mModel.setApps(new ArrayList<AppInfo>());
verify(listener).onPinnedAppsChanged();
verifyNoMoreInteractions(listener);
mModel.removeOnAppsChangedListener(listener);
mModel.setApps(new ArrayList<AppInfo>());
verifyNoMoreInteractions(listener);
}
}