Merge "Support CATEGORY_LEANBACK_LAUNCHER on tv devices." into pi-dev
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 3a0ae9f..093b1bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
@@ -59,6 +60,8 @@
import java.io.File;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -135,6 +138,38 @@
final BackgroundHandler mBackgroundHandler;
final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper());
+ /** Requests that the home app is loaded. */
+ public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0;
+
+ /** Requests that icons are loaded. */
+ public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1;
+
+ /** Requests that sizes are loaded. */
+ public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2;
+
+ /** Requests that launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3;
+
+ /** Requests that leanback launcher intents are resolved. */
+ public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4;
+
+ /**
+ * Flags to configure the session to request various types of info.
+ */
+ @IntDef(prefix = { "FLAG_SESSION_" }, value = {
+ FLAG_SESSION_REQUEST_HOME_APP,
+ FLAG_SESSION_REQUEST_ICONS,
+ FLAG_SESSION_REQUEST_SIZES,
+ FLAG_SESSION_REQUEST_LAUNCHER,
+ FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionFlags {}
+
+ public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+ FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
+ FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+
private ApplicationsState(Application app) {
mContext = app;
mPm = mContext.getPackageManager();
@@ -265,7 +300,8 @@
}
}
- private void clearEntries() {
+ @VisibleForTesting
+ void clearEntries() {
for (int i = 0; i < mEntriesMap.size(); i++) {
mEntriesMap.valueAt(i).clear();
}
@@ -346,7 +382,7 @@
if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
- if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) {
mBackgroundHandler.post(
() -> {
try {
@@ -595,6 +631,7 @@
}
public class Session implements LifecycleObserver {
+
final Callbacks mCallbacks;
boolean mResumed;
@@ -609,6 +646,7 @@
boolean mRebuildForeground;
private final boolean mHasLifecycle;
+ @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
@@ -620,6 +658,14 @@
}
}
+ public @SessionFlags int getSessionFlags() {
+ return mFlags;
+ }
+
+ public void setSessionFlags(@SessionFlags int flags) {
+ mFlags = flags;
+ }
+
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
@@ -833,10 +879,11 @@
private class BackgroundHandler extends Handler {
static final int MSG_REBUILD_LIST = 1;
static final int MSG_LOAD_ENTRIES = 2;
- static final int MSG_LOAD_ICONS = 3;
- static final int MSG_LOAD_SIZES = 4;
- static final int MSG_LOAD_LAUNCHER = 5;
- static final int MSG_LOAD_HOME_APP = 6;
+ static final int MSG_LOAD_HOME_APP = 3;
+ static final int MSG_LOAD_LAUNCHER = 4;
+ static final int MSG_LOAD_LEANBACK_LAUNCHER = 5;
+ static final int MSG_LOAD_ICONS = 6;
+ static final int MSG_LOAD_SIZES = 7;
boolean mRunning;
@@ -860,6 +907,8 @@
}
}
+ int flags = getCombinedSessionFlags(mSessions);
+
switch (msg.what) {
case MSG_REBUILD_LIST: {
} break;
@@ -889,8 +938,8 @@
// happens because of the way we generate the list in
// doResumeIfNeededLocked.
AppEntry entry = mEntriesMap.get(0).get(info.packageName);
- if (entry != null &&
- (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ if (entry != null && !hasFlag(entry.info.flags,
+ ApplicationInfo.FLAG_INSTALLED)) {
mEntriesMap.get(0).remove(info.packageName);
mAppEntries.remove(entry);
}
@@ -909,166 +958,206 @@
}
} break;
case MSG_LOAD_HOME_APP: {
- final List<ResolveInfo> homeActivities = new ArrayList<>();
- mPm.getHomeActivities(homeActivities);
- synchronized (mEntriesMap) {
- final int entryCount = mEntriesMap.size();
- for (int i = 0; i < entryCount; i++) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
- final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- for (ResolveInfo activity : homeActivities) {
- String packageName = activity.activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.isHomeApp = true;
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) {
+ final List<ResolveInfo> homeActivities = new ArrayList<>();
+ mPm.getHomeActivities(homeActivities);
+ synchronized (mEntriesMap) {
+ final int entryCount = mEntriesMap.size();
+ for (int i = 0; i < entryCount; i++) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock");
+ final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(
+ i);
+ for (ResolveInfo activity : homeActivities) {
+ String packageName = activity.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.isHomeApp = true;
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock");
}
}
sendEmptyMessage(MSG_LOAD_LAUNCHER);
- }
- break;
- case MSG_LOAD_LAUNCHER: {
- Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
- .addCategory(Intent.CATEGORY_LAUNCHER);
- for (int i = 0; i < mEntriesMap.size(); i++) {
- int userId = mEntriesMap.keyAt(i);
- // If we do not specify MATCH_DIRECT_BOOT_AWARE or
- // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
- // according to the user's lock state. When the user is locked, components
- // with ComponentInfo#directBootAware == false will be filtered. We should
- // explicitly include both direct boot aware and unaware components here.
- List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
- launchIntent,
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId
- );
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
- HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
- final int N = intents.size();
- for (int j = 0; j < N; j++) {
- String packageName = intents.get(j).activityInfo.packageName;
- AppEntry entry = userEntries.get(packageName);
- if (entry != null) {
- entry.hasLauncherEntry = true;
- } else {
- Log.w(TAG, "Cannot find pkg: " + packageName
- + " on user " + userId);
+ } break;
+ case MSG_LOAD_LAUNCHER:
+ case MSG_LOAD_LEANBACK_LAUNCHER: {
+ if ((msg.what == MSG_LOAD_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER))
+ || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER &&
+ hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) {
+
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+ launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER
+ ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER);
+ for (int i = 0; i < mEntriesMap.size(); i++) {
+ int userId = mEntriesMap.keyAt(i);
+ // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+ // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+ // according to the user's lock state. When the user is locked,
+ // components
+ // with ComponentInfo#directBootAware == false will be filtered. We should
+ // explicitly include both direct boot aware and unaware components here.
+ List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+ launchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId
+ );
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+ HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+ final int N = intents.size();
+ for (int j = 0; j < N; j++) {
+ ResolveInfo resolveInfo = intents.get(j);
+ String packageName = resolveInfo.activityInfo.packageName;
+ AppEntry entry = userEntries.get(packageName);
+ if (entry != null) {
+ entry.hasLauncherEntry = true;
+ entry.launcherEntryEnabled |=
+ resolveInfo.activityInfo.enabled;
+ } else {
+ Log.w(TAG, "Cannot find pkg: " + packageName
+ + " on user " + userId);
+ }
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
}
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+ }
+
+ if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
}
}
-
- if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+ if (msg.what == MSG_LOAD_LAUNCHER) {
+ sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER);
+ } else {
+ sendEmptyMessage(MSG_LOAD_ICONS);
}
- sendEmptyMessage(MSG_LOAD_ICONS);
} break;
case MSG_LOAD_ICONS: {
- int numDone = 0;
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
- for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
- AppEntry entry = mAppEntries.get(i);
- if (entry.icon == null || !entry.mounted) {
- synchronized (entry) {
- if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+ for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.icon == null || !entry.mounted) {
+ synchronized (entry) {
+ if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ numDone++;
+ }
+ }
+ }
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+ }
+ if (numDone > 0) {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+ }
+ }
+ if (numDone >= 2) {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ break;
+ }
+ }
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ } break;
+ case MSG_LOAD_SIZES: {
+ if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+ if (mCurComputingSizePkg != null) {
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: currently computing");
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ for (int i = 0; i < mAppEntries.size(); i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)
+ && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
+ if (entry.sizeLoadStart == 0 ||
+ (entry.sizeLoadStart < (now - 20 * 1000))) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
mMainHandler.sendMessage(m);
}
- numDone++;
+ entry.sizeLoadStart = now;
+ mCurComputingSizeUuid = entry.info.storageUuid;
+ mCurComputingSizePkg = entry.info.packageName;
+ mCurComputingSizeUserId = UserHandle.getUserId(
+ entry.info.uid);
+
+ mBackgroundHandler.post(() -> {
+ try {
+ final StorageStats stats =
+ mStats.queryStatsForPackage(
+ mCurComputingSizeUuid,
+ mCurComputingSizePkg,
+ UserHandle.of(
+ mCurComputingSizeUserId));
+ final PackageStats legacy = new PackageStats(
+ mCurComputingSizePkg,
+ mCurComputingSizeUserId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacy,
+ true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ });
}
+ if (DEBUG_LOCKING) Log.v(TAG,
+ "MSG_LOAD_SIZES releasing: now computing");
+ return;
}
}
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
- }
- if (numDone > 0) {
- if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
- }
- }
- if (numDone >= 2) {
- sendEmptyMessage(MSG_LOAD_ICONS);
- } else {
- sendEmptyMessage(MSG_LOAD_SIZES);
- }
- } break;
- case MSG_LOAD_SIZES: {
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
- if (mCurComputingSizePkg != null) {
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- for (int i=0; i<mAppEntries.size(); i++) {
- AppEntry entry = mAppEntries.get(i);
- if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
- && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
- if (entry.sizeLoadStart == 0 ||
- (entry.sizeLoadStart < (now-20*1000))) {
- if (!mRunning) {
- mRunning = true;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
- mMainHandler.sendMessage(m);
- }
- entry.sizeLoadStart = now;
- mCurComputingSizeUuid = entry.info.storageUuid;
- mCurComputingSizePkg = entry.info.packageName;
- mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-
- mBackgroundHandler.post(() -> {
- try {
- final StorageStats stats = mStats.queryStatsForPackage(
- mCurComputingSizeUuid, mCurComputingSizePkg,
- UserHandle.of(mCurComputingSizeUserId));
- final PackageStats legacy = new PackageStats(
- mCurComputingSizePkg, mCurComputingSizeUserId);
- legacy.codeSize = stats.getCodeBytes();
- legacy.dataSize = stats.getDataBytes();
- legacy.cacheSize = stats.getCacheBytes();
- try {
- mStatsObserver.onGetStatsCompleted(legacy, true);
- } catch (RemoteException ignored) {
- }
- } catch (NameNotFoundException | IOException e) {
- Log.w(TAG, "Failed to query stats: " + e);
- try {
- mStatsObserver.onGetStatsCompleted(null, false);
- } catch (RemoteException ignored) {
- }
- }
-
- });
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
- return;
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+ mRunning = false;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+ mMainHandler.sendMessage(m);
}
+ if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
- if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
- mRunning = false;
- Message m = mMainHandler.obtainMessage(
- MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
- mMainHandler.sendMessage(m);
- }
- if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
}
} break;
}
}
+ private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+ synchronized (mEntriesMap) {
+ int flags = 0;
+ for (Session session : sessions) {
+ flags |= session.mFlags;
+ }
+ return flags;
+ }
+ }
+
final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
if (!succeeded) {
@@ -1257,6 +1346,11 @@
public boolean hasLauncherEntry;
/**
+ * Whether the component that has a launcher intent filter is enabled.
+ */
+ public boolean launcherEntryEnabled;
+
+ /**
* Whether or not it's a Home app.
*/
public boolean isHomeApp;
@@ -1283,7 +1377,7 @@
// A location where extra info can be placed to be used by custom filters.
public Object extraInfo;
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public AppEntry(Context context, ApplicationInfo info, long id) {
apkFile = new File(info.sourceDir);
this.id = id;
@@ -1336,6 +1430,10 @@
}
}
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
+
/**
* Compare by label, then package name, then uid.
*/
@@ -1449,13 +1547,13 @@
public boolean filterApp(AppEntry entry) {
if (AppUtils.isInstant(entry.info)) {
return false;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
} else if (entry.hasLauncherEntry) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) {
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
return true;
}
return false;
@@ -1486,9 +1584,9 @@
@Override
public boolean filterApp(AppEntry entry) {
- if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
return true;
- } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
return true;
}
return false;
@@ -1547,7 +1645,7 @@
@Override
public boolean filterApp(AppEntry entry) {
return !AppUtils.isInstant(entry.info)
- && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ && hasFlag(entry.info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS);
}
};
@@ -1589,7 +1687,7 @@
// TODO: Update for the new game category.
boolean isGame;
synchronized (info.info) {
- isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+ isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME)
|| info.info.category == ApplicationInfo.CATEGORY_GAME;
}
return isGame;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
new file mode 100644
index 0000000..2dbabe0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2017 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.UserIdInt;
+import android.app.ApplicationPackageManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.ApplicationsState.Session;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+ ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
+ ApplicationsStateRoboTest.ShadowPackageManager.class})
+public class ApplicationsStateRoboTest {
+
+ private final static String HOME_PACKAGE_NAME = "com.android.home";
+ private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
+
+ /** Class under test */
+ private ApplicationsState mApplicationsState;
+
+ @Mock
+ private Callbacks mCallbacks;
+ @Captor
+ private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
+ @Mock
+ private StorageStatsManager mStorageStatsManager;
+
+ @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+ public static class ShadowIconDrawableFactory {
+
+ @Implementation
+ public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+ return new ColorDrawable(0);
+ }
+ }
+
+ @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+ public static class ShadowPackageManager extends
+ org.robolectric.shadows.ShadowApplicationPackageManager {
+
+ @Implementation
+ public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ outActivities.add(resolveInfo);
+ return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
+ }
+
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
+ @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+ List<ResolveInfo> resolveInfos = new ArrayList<>();
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
+ resolveInfo.activityInfo.enabled = true;
+ resolveInfo.filter = new IntentFilter();
+ resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
+ resolveInfos.add(resolveInfo);
+ return resolveInfos;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Robolectric does not know about the StorageStatsManager as a system service.
+ // Registering a mock of this service as a replacement.
+ ShadowContextImpl shadowContext = Shadow.extract(
+ RuntimeEnvironment.application.getBaseContext());
+ shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+ StorageStats storageStats = new StorageStats();
+ storageStats.codeBytes = 10;
+ storageStats.dataBytes = 20;
+ storageStats.cacheBytes = 30;
+ when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
+ anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
+
+ mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
+ mApplicationsState.clearEntries();
+ }
+
+ private ApplicationInfo createApplicationInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.sourceDir = "foo";
+ appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ appInfo.storageUuid = UUID.randomUUID();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+
+ private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+ AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
+ appEntry.label = "label";
+ appEntry.mounted = true;
+ return appEntry;
+ }
+
+ private void addApp(String packageName, int id) {
+ ApplicationInfo appInfo = createApplicationInfo(packageName);
+ AppEntry appEntry = createAppEntry(appInfo, id);
+ mApplicationsState.mAppEntries.add(appEntry);
+ mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry);
+ }
+
+ private void processAllMessages() {
+ Handler mainHandler = mApplicationsState.mMainHandler;
+ Handler bkgHandler = mApplicationsState.mBackgroundHandler;
+ ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
+ ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
+ shadowBkgLooper.idle();
+ shadowMainLooper.idle();
+ }
+
+ private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
+ for (AppEntry appEntry : appEntries) {
+ if (appEntry.id == id) {
+ return appEntry;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testDefaultSessionLoadsAll() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ addApp(LAUNCHABLE_PACKAGE_NAME,2);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(2);
+
+ for (AppEntry appEntry : appEntries) {
+ assertThat(appEntry.size).isGreaterThan(0L);
+ assertThat(appEntry.icon).isNotNull();
+ }
+
+ AppEntry homeEntry = findAppEntry(appEntries, 1);
+ assertThat(homeEntry.isHomeApp).isTrue();
+ assertThat(homeEntry.hasLauncherEntry).isFalse();
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 2);
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsIconsOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNotNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsSizesOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isGreaterThan(0L);
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsHomeOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
+ session.onResume();
+
+ addApp(HOME_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.hasLauncherEntry).isFalse();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isTrue();
+ session.onDestroy();
+ }
+
+ @Test
+ public void testCustomSessionLoadsLeanbackOnly() {
+ Session session = mApplicationsState.newSession(mCallbacks);
+ session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
+ session.onResume();
+
+ addApp(LAUNCHABLE_PACKAGE_NAME,1);
+ session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+ verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+ List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+ assertThat(appEntries.size()).isEqualTo(1);
+
+ AppEntry launchableEntry = findAppEntry(appEntries, 1);
+ assertThat(launchableEntry.icon).isNull();
+ assertThat(launchableEntry.size).isEqualTo(-1);
+ assertThat(launchableEntry.isHomeApp).isFalse();
+ assertThat(launchableEntry.hasLauncherEntry).isTrue();
+ assertThat(launchableEntry.launcherEntryEnabled).isTrue();
+ session.onDestroy();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index a3e1bc8..bbd3a53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
package com.android.settingslib.testutils.shadow;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.UserManager;
import org.robolectric.RuntimeEnvironment;
@@ -25,6 +26,9 @@
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
+import java.util.List;
+
@Implements(value = UserManager.class, inheritImplementationMethods = true)
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
@@ -49,6 +53,20 @@
return (UserManager) context.getSystemService(Context.USER_SERVICE);
}
+ @Implementation
+ public int[] getProfileIdsWithDisabled(int userId) {
+ return new int[] { 0 };
+ }
+
+ @Implementation
+ public List<UserInfo> getProfiles() {
+ UserInfo userInfo = new UserInfo();
+ userInfo.id = 0;
+ List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(userInfo);
+ return userInfos;
+ }
+
public static ShadowUserManager getShadow() {
return (ShadowUserManager) Shadow.extract(
RuntimeEnvironment.application.getSystemService(UserManager.class));