diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index fd8f66851d..551735e1a1 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -20,6 +20,8 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURC import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD; +import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_AVAILABLE; +import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_UNAVAILABLE; import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -326,6 +328,16 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } else if (UserCache.ACTION_PROFILE_ADDED.equals(action) || UserCache.ACTION_PROFILE_REMOVED.equals(action)) { forceReload(); + } else if (ACTION_PROFILE_AVAILABLE.equals(action) + || ACTION_PROFILE_UNAVAILABLE.equals(action)) { + /* + * This broadcast is only available when android.os.Flags.allowPrivateProfile() is set. + * For Work-profile this broadcast will be sent in addition to + * ACTION_MANAGED_PROFILE_AVAILABLE/UNAVAILABLE. + * So effectively, this if block only handles the non-work profile case. + */ + enqueueModelUpdateTask(new PackageUpdatedTask( + PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); } } diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 72c6cb8445..d822fec4cf 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -80,6 +80,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.pm.UserCache; import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Themes; @@ -203,7 +204,9 @@ public class ActivityAllAppsContainerView mWorkManager = new WorkProfileManager( mActivityContext.getSystemService(UserManager.class), - this, mActivityContext.getStatsLogManager()); + this, + mActivityContext.getStatsLogManager(), + UserCache.INSTANCE.get(mActivityContext)); mAH = Arrays.asList(null, null, null); mNavBarScrimPaint = new Paint(); mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext)); diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index 9f6e0fcd93..051cf50ccc 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -124,6 +124,9 @@ public class AllAppsStore { * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION + * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED + * @see + * com.android.launcher3.model.BgDataModel.Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED */ public boolean hasModelFlag(int mask) { return (mModelFlags & mask) != 0; diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java index ac0e5a43f6..61c3d3f76b 100644 --- a/src/com/android/launcher3/allapps/WorkProfileManager.java +++ b/src/com/android/launcher3/allapps/WorkProfileManager.java @@ -25,10 +25,10 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.os.Build; -import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -40,12 +40,14 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.recyclerview.widget.RecyclerView; +import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.pm.UserCache; import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip; import java.lang.annotation.Retention; @@ -84,16 +86,19 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP private WorkModeSwitch mWorkModeSwitch; + private final UserCache mUserCache; + @WorkProfileState private int mCurrentState; public WorkProfileManager( UserManager userManager, ActivityAllAppsContainerView allApps, - StatsLogManager statsLogManager) { + StatsLogManager statsLogManager, UserCache userCache) { mUserManager = userManager; mAllApps = allApps; - mMatcher = mAllApps.mPersonalMatcher.negate(); mStatsLogManager = statsLogManager; + mUserCache = userCache; + mMatcher = info -> info != null && mUserCache.getUserInfo(info.user).isWork(); } /** @@ -103,11 +108,11 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP public void setWorkProfileEnabled(boolean enabled) { updateCurrentState(STATE_TRANSITION); UI_HELPER_EXECUTOR.post(() -> { - for (UserHandle userProfile : mUserManager.getUserProfiles()) { - if (Process.myUserHandle().equals(userProfile)) { - continue; + for (UserHandle userProfile : mUserCache.getUserProfiles()) { + if (mUserCache.getUserInfo(userProfile).isWork()) { + mUserManager.requestQuietModeEnabled(!enabled, userProfile); + break; } - mUserManager.requestQuietModeEnabled(!enabled, userProfile); } }); } @@ -131,7 +136,13 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP * Requests work profile state from {@link AllAppsStore} and updates work profile related views */ public void reset() { - boolean isEnabled = !mAllApps.getAppsStore().hasModelFlag(FLAG_QUIET_MODE_ENABLED); + int quietModeFlag; + if (Flags.enablePrivateSpace()) { + quietModeFlag = FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; + } else { + quietModeFlag = FLAG_QUIET_MODE_ENABLED; + } + boolean isEnabled = !mAllApps.getAppsStore().hasModelFlag(quietModeFlag); updateCurrentState(isEnabled ? STATE_ENABLED : STATE_DISABLED); if (mWorkModeSwitch != null) { // reset the position of the button and clear IME insets. diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index 8f85bfbd3d..190eb78d2a 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -79,6 +79,8 @@ public class AllAppsList { * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION * @see Callbacks#FLAG_QUIET_MODE_ENABLED * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION + * @see Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED + * @see Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED */ private int mFlags; diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 54ecc00c59..7f0f683091 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -477,6 +477,10 @@ public class BgDataModel { int FLAG_QUIET_MODE_ENABLED = 1 << 1; // If launcher can change quiet mode int FLAG_QUIET_MODE_CHANGE_PERMISSION = 1 << 2; + // If quiet mode is enabled for work profile user + int FLAG_WORK_PROFILE_QUIET_MODE_ENABLED = 1 << 3; + // If quiet mode is enabled for private profile user + int FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED = 1 << 4; /** * Returns an IntSet of page ids to bind first, synchronously if possible diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 1ab0355bb8..f4ce3607f7 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -23,8 +23,10 @@ import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL; import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; @@ -57,8 +59,10 @@ import android.util.LongSparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Flags; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; @@ -143,7 +147,7 @@ public class LoaderTask implements Runnable { private final InstallSessionHelper mSessionHelper; private final IconCache mIconCache; - private final UserManagerState mUserManagerState = new UserManagerState(); + private final UserManagerState mUserManagerState; protected final Map mWidgetProvidersMap = new ArrayMap<>(); private Map mShortcutKeyToPinnedShortcuts; @@ -156,6 +160,13 @@ public class LoaderTask implements Runnable { public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) { + this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState()); + } + + @VisibleForTesting + LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, + ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder, + UserManagerState userManagerState) { mApp = app; mBgAllAppsList = bgAllAppsList; mBgDataModel = bgModel; @@ -164,9 +175,10 @@ public class LoaderTask implements Runnable { mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class); mUserManager = mApp.getContext().getSystemService(UserManager.class); - mUserCache = UserCache.INSTANCE.get(mApp.getContext()); + mUserCache = UserCache.getInstance(mApp.getContext()); mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext()); mIconCache = mApp.getIconCache(); + mUserManagerState = userManagerState; } protected synchronized void waitForIdle() { @@ -973,6 +985,8 @@ public class LoaderTask implements Runnable { mBgAllAppsList.clear(); List> iconRequestInfos = new ArrayList<>(); + boolean isWorkProfileQuiet = false; + boolean isPrivateProfileQuiet = false; for (UserHandle user : profiles) { // Query for the set of apps final List apps = mLauncherApps.getActivityList(null, user); @@ -982,6 +996,14 @@ public class LoaderTask implements Runnable { return allActivityList; } boolean quietMode = mUserManagerState.isUserQuiet(user); + + if (Flags.enablePrivateSpace()) { + if (mUserCache.getUserInfo(user).isWork()) { + isWorkProfileQuiet = quietMode; + } else if (mUserCache.getUserInfo(user).isPrivate()) { + isPrivateProfileQuiet = quietMode; + } + } // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfo app = apps.get(i); @@ -1023,8 +1045,13 @@ public class LoaderTask implements Runnable { Trace.endSection(); } - mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, - mUserManagerState.isAnyProfileQuietModeEnabled()); + if (Flags.enablePrivateSpace()) { + mBgAllAppsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isWorkProfileQuiet); + mBgAllAppsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isPrivateProfileQuiet); + } else { + mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, + mUserManagerState.isAnyProfileQuietModeEnabled()); + } mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION, hasShortcutsPermission(mApp.getContext())); mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION, diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 37a717145a..9a0a6eb2cc 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -15,7 +15,9 @@ */ package com.android.launcher3.model; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON; @@ -31,6 +33,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import com.android.launcher3.Flags; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; @@ -169,14 +172,24 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { break; case OP_USER_AVAILABILITY_CHANGE: { UserManagerState ums = new UserManagerState(); - ums.init(UserCache.INSTANCE.get(context), - context.getSystemService(UserManager.class)); + UserManager userManager = context.getSystemService(UserManager.class); + ums.init(UserCache.INSTANCE.get(context), userManager); + boolean isUserQuiet = ums.isUserQuiet(mUser); flagOp = FlagOp.NO_OP.setFlag( - WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER, ums.isUserQuiet(mUser)); + WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER, isUserQuiet); appsList.updateDisabledFlags(matcher, flagOp); - // We are not synchronizing here, as int operations are atomic - appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled()); + if (Flags.enablePrivateSpace()) { + UserCache userCache = UserCache.INSTANCE.get(context); + if (userCache.getUserInfo(mUser).isWork()) { + appsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isUserQuiet); + } else if (userCache.getUserInfo(mUser).isPrivate()) { + appsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isUserQuiet); + } + } else { + // We are not synchronizing here, as int operations are atomic + appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled()); + } break; } default: diff --git a/src/com/android/launcher3/model/UserManagerState.java b/src/com/android/launcher3/model/UserManagerState.java index 97a5905f0a..720f08e256 100644 --- a/src/com/android/launcher3/model/UserManagerState.java +++ b/src/com/android/launcher3/model/UserManagerState.java @@ -61,6 +61,9 @@ public class UserManagerState { /** * Returns true if any user profile has quiet mode enabled. + *

+ * Do not use this for determining if a specific profile has quiet mode enabled, as their can + * be more than one profile in quiet mode. */ public boolean isAnyProfileQuietModeEnabled() { for (int i = mQuietUsersHashCodeMap.size() - 1; i >= 0; i--) { diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java index e2b12861c4..4661fd4498 100644 --- a/src/com/android/launcher3/pm/UserCache.java +++ b/src/com/android/launcher3/pm/UserCache.java @@ -55,10 +55,18 @@ public class UserCache implements SafeCloseable { ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; public static final String ACTION_PROFILE_LOCKED = ATLEAST_U ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; + public static final String ACTION_PROFILE_AVAILABLE = "android.intent.action.PROFILE_AVAILABLE"; + public static final String ACTION_PROFILE_UNAVAILABLE = + "android.intent.action.PROFILE_UNAVAILABLE"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(UserCache::new); + /** Returns an instance of UserCache bound to the context provided. */ + public static UserCache getInstance(Context context) { + return INSTANCE.get(context); + } + private final List> mUserEventListeners = new ArrayList<>(); private final SimpleBroadcastReceiver mUserChangeReceiver = new SimpleBroadcastReceiver(this::onUsersChanged); @@ -87,7 +95,9 @@ public class UserCache implements SafeCloseable { ACTION_PROFILE_ADDED, ACTION_PROFILE_REMOVED, ACTION_PROFILE_UNLOCKED, - ACTION_PROFILE_LOCKED); + ACTION_PROFILE_LOCKED, + ACTION_PROFILE_AVAILABLE, + ACTION_PROFILE_UNAVAILABLE); updateCache(); } diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 78116ae080..af77d03f57 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -101,6 +101,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet // the table can display. private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f; + private final UserCache mUserCache; private final UserManagerState mUserManagerState = new UserManagerState(); private final UserHandle mCurrentUser = Process.myUserHandle(); private final Predicate mPrimaryWidgetsFilter = @@ -192,6 +193,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet ? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height) : 0; + mUserCache = UserCache.INSTANCE.get(context); mUserManagerState.init(UserCache.INSTANCE.get(context), context.getSystemService(UserManager.class)); } @@ -311,7 +313,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) { mNoWidgetsView.setText(R.string.no_search_results); } else if (adapterHolder.mAdapterType == AdapterHolder.WORK - && mUserManagerState.isAnyProfileQuietModeEnabled() + && mUserCache.getUserProfiles().stream() + .filter(userHandle -> mUserCache.getUserInfo(userHandle).isWork()) + .anyMatch(mUserManagerState::isUserQuiet) && mActivityContext.getStringCache() != null) { mNoWidgetsView.setText(mActivityContext.getStringCache().workProfilePausedTitle); } else { diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt index 036f2d8fdc..def27b8897 100644 --- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt +++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt @@ -1,10 +1,12 @@ package com.android.launcher3.model -import android.appwidget.AppWidgetManager +import android.content.Context import android.os.UserHandle +import android.platform.test.flag.junit.SetFlagsRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.Flags import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherModel @@ -12,19 +14,25 @@ import com.android.launcher3.LauncherModel.LoaderTransaction import com.android.launcher3.icons.IconCache import com.android.launcher3.icons.cache.CachingLogic import com.android.launcher3.icons.cache.IconCacheUpdateHandler +import com.android.launcher3.pm.UserCache import com.android.launcher3.util.Executors.MODEL_EXECUTOR import com.android.launcher3.util.LooperIdleLock +import com.android.launcher3.util.UserIconInfo +import com.android.launcher3.util.rule.StaticMockitoRule import com.google.common.truth.Truth import java.util.concurrent.CountDownLatch import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.Spy private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql" @@ -40,7 +48,13 @@ class LoaderTaskTest { @Mock private lateinit var iconCache: IconCache @Mock private lateinit var idleLock: LooperIdleLock @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler - @Mock private lateinit var appWidgetManager: AppWidgetManager + @Mock private lateinit var userCache: UserCache + + @Spy private var userManagerState: UserManagerState? = UserManagerState() + + @get:Rule(order = 0) val staticMockitoRule = StaticMockitoRule(UserCache::class.java) + @get:Rule(order = 1) + val setFlagsRule = SetFlagsRule().apply { initAllFlagsToReleaseConfigDefault() } @Before fun setup() { @@ -63,8 +77,7 @@ class LoaderTaskTest { `when`(launcherBinder.newIdleLock(any(LoaderTask::class.java))).thenReturn(idleLock) `when`(idleLock.awaitLocked(1000)).thenReturn(false) `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler) - `when`(appWidgetManager.getInstalledProvidersForProfile(any(UserHandle::class.java))) - .thenReturn(emptyList()) + `when`(UserCache.getInstance(any(Context::class.java))).thenReturn(userCache) } @Test @@ -95,6 +108,48 @@ class LoaderTaskTest { verify(modelDelegate).modelLoadComplete() verify(transaction).commit() } + + @Test + fun setsQuietModeFlagCorrectlyForWorkProfile() = + with(BgDataModel()) { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE) + val MAIN_HANDLE = UserHandle.of(0) + val mockUserHandles = arrayListOf(MAIN_HANDLE) + `when`(userCache.userProfiles).thenReturn(mockUserHandles) + `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true) + `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1)) + + LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState) + .runSyncOnBackgroundThread() + + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList, Mockito.never()) + .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + } + + @Test + fun setsQuietModeFlagCorrectlyForPrivateProfile() = + with(BgDataModel()) { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE) + val MAIN_HANDLE = UserHandle.of(0) + val mockUserHandles = arrayListOf(MAIN_HANDLE) + `when`(userCache.userProfiles).thenReturn(mockUserHandles) + `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true) + `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3)) + + LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState) + .runSyncOnBackgroundThread() + + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList, Mockito.never()) + .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + } } private fun LoaderTask.runSyncOnBackgroundThread() { diff --git a/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java b/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java index ba416ae8c5..3411fc11db 100644 --- a/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java +++ b/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java @@ -18,14 +18,19 @@ package com.android.launcher3.ui; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -34,15 +39,20 @@ import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.WorkProfileManager; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.ActivityContextWrapper; +import com.android.launcher3.util.UserIconInfo; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.Arrays; + @SmallTest @RunWith(AndroidJUnit4.class) public class ActivityAllAppsContainerViewTest { @@ -50,19 +60,38 @@ public class ActivityAllAppsContainerViewTest { private static final UserHandle WORK_HANDLE = new UserHandle(13); @Mock private StatsLogManager mStatsLogManager; + @Mock + private UserCache mUserCache; + @Mock + private UserManager mUserManager; private AppInfo[] mWorkAppInfo; private ActivityAllAppsContainerView mActivityAllAppsContainerView; private WorkProfileManager mWorkManager; + private Context mContext; + + @Rule public final SetFlagsRule mSetFlagsRule = getFlagsRule(); + + private SetFlagsRule getFlagsRule() { + SetFlagsRule flagsRule = new SetFlagsRule(); + flagsRule.initAllFlagsToReleaseConfigDefault(); + return flagsRule; + } @Before public void setUp() { MockitoAnnotations.initMocks(this); - Context context = new ActivityContextWrapper(getApplicationContext()); - mActivityAllAppsContainerView = new ActivityAllAppsContainerView(context); - mWorkManager = new WorkProfileManager(context.getSystemService(UserManager.class), - mActivityAllAppsContainerView, mStatsLogManager); + mContext = new ActivityContextWrapper(getApplicationContext()); + mActivityAllAppsContainerView = new ActivityAllAppsContainerView(mContext); + when(mUserCache.getUserProfiles()) + .thenReturn(Arrays.asList(Process.myUserHandle(), WORK_HANDLE)); + when(mUserCache.getUserInfo(Process.myUserHandle())) + .thenReturn(new UserIconInfo(Process.myUserHandle(), 0)); + when(mUserCache.getUserInfo(WORK_HANDLE)) + .thenReturn(new UserIconInfo(WORK_HANDLE, 1)); + mWorkManager = new WorkProfileManager(mUserManager, mActivityAllAppsContainerView, + mStatsLogManager, mUserCache); mActivityAllAppsContainerView.setWorkManager(mWorkManager); - ComponentName componentName = new ComponentName(context, + ComponentName componentName = new ComponentName(mContext, "com.android.launcher3.tests.Activity" + "Gmail"); AppInfo gmailWorkAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent()); mWorkAppInfo = new AppInfo[]{gmailWorkAppInfo}; @@ -85,4 +114,22 @@ public class ActivityAllAppsContainerViewTest { assertThat(mActivityAllAppsContainerView.shouldShowTabs()).isEqualTo(true); } + + @Test + public void testWorkProfileEnabled_requestQuietModeCalledCorrectly() throws Exception { + /* Setup */ + when(mUserManager.requestQuietModeEnabled(false, WORK_HANDLE)) + .thenReturn(true); + + /* Execution */ + mWorkManager.setWorkProfileEnabled(true); + + /* Assertion */ + awaitTasksCompleted(); + Mockito.verify(mUserManager).requestQuietModeEnabled(false, WORK_HANDLE); + } + + private static void awaitTasksCompleted() throws Exception { + UI_HELPER_EXECUTOR.submit(() -> null).get(); + } }