Merge "Rename LoaderResults to LauncherBinder." into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c269648..6031b49 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -364,7 +364,12 @@
      * Returns the height that taskbar will be touchable.
      */
     public int getTouchableHeight() {
-        return mIsStashed ? mStashedHeight : mUnstashedHeight;
+        int bottomMargin = 0;
+        if (DisplayController.isTransientTaskbar(mActivity)) {
+            bottomMargin = mActivity.getResources().getDimensionPixelSize(
+                    R.dimen.transient_taskbar_margin);
+        }
+        return mIsStashed ? mStashedHeight : (mUnstashedHeight + bottomMargin);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8409475..a2adbd7 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -323,6 +323,7 @@
     private final boolean mIsTransientTaskbar;
     // May be set to false when mIsTransientTaskbar is true.
     private boolean mCanSlowSwipeGoHome = true;
+    private boolean mHasReachedOverviewThreshold = false;
 
     @Nullable
     private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null;
@@ -765,6 +766,10 @@
 
     private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
         if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
+            if (isLikelyToStartNewTask && mIsTransientTaskbar) {
+                setDividerShown(false /* shown */, true /* immediate */);
+            }
+
             mIsLikelyToStartNewTask = isLikelyToStartNewTask;
             maybeUpdateRecentsAttachedState(animate);
         }
@@ -1669,7 +1674,9 @@
         mRecentsAnimationController.enableInputConsumer();
 
         // Start hiding the divider
-        setDividerShown(false /* shown */, true /* immediate */);
+        if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) {
+            setDividerShown(false /* shown */, true /* immediate */);
+        }
     }
 
     private void computeRecentsScrollIfInvisible() {
@@ -2305,6 +2312,10 @@
 
         // "Catch up" with the displacement at mTaskbarCatchUpThreshold.
         if (displacement < mTaskbarCatchUpThreshold) {
+            if (!mHasReachedOverviewThreshold) {
+                setDividerShown(false /* shown */, true /* immediate */);
+                mHasReachedOverviewThreshold = true;
+            }
             return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold,
                     mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL);
         }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 9e25555..f1c0f3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,7 +17,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.Intent.ACTION_USER_UNLOCKED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -50,10 +49,8 @@
 import android.graphics.Region;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.view.MotionEvent;
 
@@ -63,9 +60,9 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -109,15 +106,6 @@
     private final boolean mIsOneHandedModeSupported;
     private boolean mPipIsActive;
 
-    private boolean mIsUserUnlocked;
-    private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
-    private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
-        if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
-            mIsUserUnlocked = true;
-            notifyUserUnlocked();
-        }
-    });
-
     private int mGestureBlockingTaskId = -1;
     private @NonNull Region mExclusionRegion = new Region();
     private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -143,14 +131,6 @@
             runOnDestroy(mRotationTouchHelper::destroy);
         }
 
-        // Register for user unlocked if necessary
-        mIsUserUnlocked = context.getSystemService(UserManager.class)
-                .isUserUnlocked(Process.myUserHandle());
-        if (!mIsUserUnlocked) {
-            mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
-        }
-        runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
-
         // Register for exclusion updates
         mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
             @Override
@@ -310,39 +290,12 @@
     }
 
     /**
-     * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
-     * will be called back immediately.
-     */
-    public void runOnUserUnlocked(Runnable action) {
-        if (mIsUserUnlocked) {
-            action.run();
-        } else {
-            mUserUnlockedActions.add(action);
-        }
-    }
-
-    /**
-     * @return whether the user is unlocked.
-     */
-    public boolean isUserUnlocked() {
-        return mIsUserUnlocked;
-    }
-
-    /**
      * @return whether the user has completed setup wizard
      */
     public boolean isUserSetupComplete() {
         return mIsUserSetupComplete;
     }
 
-    private void notifyUserUnlocked() {
-        for (Runnable action : mUserUnlockedActions) {
-            action.run();
-        }
-        mUserUnlockedActions.clear();
-        mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
-    }
-
     /**
      * Sets the task id where gestures should be blocked
      */
@@ -585,7 +538,7 @@
         pw.println("  assistantAvailable=" + mAssistantAvailable);
         pw.println("  assistantDisabled="
                 + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
-        pw.println("  isUserUnlocked=" + mIsUserUnlocked);
+        pw.println("  isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
         pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
         pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
         pw.println("  deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5d17cc7..d19f124 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -87,6 +87,7 @@
 import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -406,8 +407,8 @@
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
 
         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
-        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
-        mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+        LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+        LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
         mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
 
         ProtoTracer.INSTANCE.get(this).add(this);
@@ -477,7 +478,7 @@
     }
 
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
-        if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+        if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
             // mode doesn't have gestures
             return;
@@ -520,7 +521,7 @@
 
     @UiThread
     private void onSystemUiFlagsChanged(int lastSysUIFlags) {
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
@@ -565,7 +566,7 @@
 
     @UiThread
     private void onAssistantVisibilityChanged() {
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
                     mDeviceState.getAssistantVisibility());
         }
@@ -575,7 +576,7 @@
     public void onDestroy() {
         Log.d(TAG, "Touch service destroyed: user=" + getUserId());
         sIsInitialized = false;
-        if (mDeviceState.isUserUnlocked()) {
+        if (LockedUserState.get(this).isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
             mOverviewComponentObserver.onDestroy();
         }
@@ -609,7 +610,7 @@
         TestLogging.recordMotionEvent(
                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
 
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
 
@@ -631,7 +632,8 @@
                 mGestureState = newGestureState;
                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
                 mUncheckedConsumer = mConsumer;
-            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+            } else if (LockedUserState.get(this).isUserUnlocked()
+                    && mDeviceState.isFullyGesturalNavMode()
                     && mDeviceState.canTriggerAssistantAction(event)) {
                 mGestureState = createGestureState(mGestureState);
                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
@@ -751,7 +753,7 @@
 
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             CompoundString reasonString = newCompoundString("device locked");
             InputConsumer consumer;
             if (canStartSystemGesture) {
@@ -800,7 +802,7 @@
 
             // If Taskbar is present, we listen for long press to unstash it.
             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
-            if (tac != null) {
+            if (tac != null && canStartSystemGesture) {
                 reasonString.append(NEWLINE_PREFIX)
                         .append(reasonPrefix)
                         .append(SUBSTRING_PREFIX)
@@ -1098,7 +1100,7 @@
     }
 
     private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
 
@@ -1130,7 +1132,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        if (!mDeviceState.isUserUnlocked()) {
+        if (!LockedUserState.get(this).isUserUnlocked()) {
             return;
         }
         final BaseActivityInterface activityInterface =
@@ -1171,7 +1173,7 @@
         } else {
             // Dump everything
             FeatureFlags.dump(pw);
-            if (mDeviceState.isUserUnlocked()) {
+            if (LockedUserState.get(this).isUserUnlocked()) {
                 PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
             }
             mDeviceState.dump(pw);
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b7e0105..64951ca 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -23,14 +23,14 @@
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -66,6 +66,8 @@
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
 
 /**
  * Class for handling clicks on workspace and all-apps items
@@ -156,32 +158,18 @@
 
     private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
             boolean downloadStarted) {
-        if (downloadStarted) {
-            // If the download has started, simply direct to the market app.
-            startMarketIntentForPackage(v, launcher, packageName);
-            return;
-        }
-        UserHandle user = v.getTag() instanceof ItemInfo
-                ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
-        new AlertDialog.Builder(launcher)
-                .setTitle(R.string.abandoned_promises_title)
-                .setMessage(R.string.abandoned_promise_explanation)
-                .setPositiveButton(R.string.abandoned_search,
-                        (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
-                .setNeutralButton(R.string.abandoned_clean_this,
-                        (d, i) -> launcher.getWorkspace()
-                                .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
-                                        Collections.singleton(packageName), user),
-                                        "user explicitly removes the promise app icon"))
-                .create().show();
-    }
-
-    private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
+        CompletableFuture<SessionInfo> siFuture;
         if (Utilities.ATLEAST_Q) {
-            SessionInfo sessionInfo = InstallSessionHelper.INSTANCE.get(launcher)
-                    .getActiveSessionInfo(item.user, packageName);
-            if (sessionInfo != null) {
+            siFuture = CompletableFuture.supplyAsync(() ->
+                    InstallSessionHelper.INSTANCE.get(launcher)
+                            .getActiveSessionInfo(item.user, packageName),
+                    UI_HELPER_EXECUTOR);
+        } else {
+            siFuture = CompletableFuture.completedFuture(null);
+        }
+        Consumer<SessionInfo> marketLaunchAction = sessionInfo -> {
+            if (sessionInfo != null && Utilities.ATLEAST_Q) {
                 LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
                 try {
                     launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
@@ -191,11 +179,27 @@
                     Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
                 }
             }
-        }
+            // Fallback to using custom market intent.
+            Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+            launcher.startActivitySafely(v, intent, item);
+        };
 
-        // Fallback to using custom market intent.
-        Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
-        launcher.startActivitySafely(v, intent, item);
+        if (downloadStarted) {
+            // If the download has started, simply direct to the market app.
+            siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR);
+            return;
+        }
+        new AlertDialog.Builder(launcher)
+                .setTitle(R.string.abandoned_promises_title)
+                .setMessage(R.string.abandoned_promise_explanation)
+                .setPositiveButton(R.string.abandoned_search,
+                        (d, i) -> siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR))
+                .setNeutralButton(R.string.abandoned_clean_this,
+                        (d, i) -> launcher.getWorkspace()
+                                .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
+                                        Collections.singleton(packageName), item.user),
+                                        "user explicitly removes the promise app icon"))
+                .create().show();
     }
 
     /**
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
new file mode 100644
index 0000000..7b49583
--- /dev/null
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -0,0 +1,57 @@
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.annotation.VisibleForTesting
+
+class LockedUserState(private val mContext: Context) : SafeCloseable {
+    var isUserUnlocked: Boolean
+        private set
+    private val mUserUnlockedActions: RunnableList = RunnableList()
+
+    @VisibleForTesting
+    val mUserUnlockedReceiver = SimpleBroadcastReceiver {
+        if (Intent.ACTION_USER_UNLOCKED == it.action) {
+            isUserUnlocked = true
+            notifyUserUnlocked()
+        }
+    }
+
+    init {
+        isUserUnlocked =
+            mContext
+                .getSystemService(UserManager::class.java)!!
+                .isUserUnlocked(Process.myUserHandle())
+        if (isUserUnlocked) {
+            notifyUserUnlocked()
+        } else {
+            mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+        }
+    }
+
+    private fun notifyUserUnlocked() {
+        mUserUnlockedActions.executeAllAndDestroy()
+        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+    }
+
+    /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
+    override fun close() {
+        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+    }
+
+    /**
+     * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
+     * this runnable will run immediately because RunnableList will already have been destroyed.
+     */
+    fun runOnUserUnlocked(action: Runnable) {
+        mUserUnlockedActions.add(action)
+    }
+
+    companion object {
+        @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+
+        @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
new file mode 100644
index 0000000..84156e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 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.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/** Unit tests for {@link LockedUserUtil} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockedUserStateTest {
+
+    @Mock lateinit var userManager: UserManager
+    @Mock lateinit var context: Context
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+    }
+
+    @Test
+    fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        val action: Runnable = mock()
+
+        LockedUserState.get(context).runOnUserUnlocked(action)
+        verify(action).run()
+    }
+
+    @Test
+    fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        val action: Runnable = mock()
+
+        LockedUserState.get(context).runOnUserUnlocked(action)
+        verifyZeroInteractions(action)
+
+        LockedUserState.get(context)
+            .mUserUnlockedReceiver
+            .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+
+        verify(action).run()
+    }
+
+    @Test
+    fun isUserUnlocked_returns_true_when_user_is_unlocked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+    }
+
+    @Test
+    fun isUserUnlocked_returns_false_when_user_is_locked() {
+        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+        assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+    }
+}