Merge "Adding suport for Private Space QsTile fulfillment." into main
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ad764e3..55438fe 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -260,7 +260,7 @@
         mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
         if (Flags.enablePrivateSpace()) {
             mPrivateSpaceHeaderViewController =
-                    new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+                    new PrivateSpaceHeaderViewController(this, mPrivateProfileManager);
         }
 
         mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
@@ -980,6 +980,11 @@
         return mWorkManager;
     }
 
+    /** Returns whether Private Profile has been setup. */
+    public boolean hasPrivateProfile() {
+        return mHasPrivateApps;
+    }
+
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 35c07c3..ad875e0 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -325,10 +325,6 @@
                     mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
                     position++;
                     addAppsWithSections(mPrivateApps, position);
-                    if (mActivityContext.getAppsView() != null) {
-                        mActivityContext.getAppsView().getActiveRecyclerView()
-                                .scrollToBottomWithMotion();
-                    }
                     break;
             }
         }
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index c99b69f..aee511c 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
 
@@ -34,7 +35,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BuildConfig;
-import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
@@ -59,11 +59,11 @@
     private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER;
     private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key";
     private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
-    private static final int ANIMATION_DURATION = 2000;
     private final ActivityAllAppsContainerView<?> mAllApps;
     private final Predicate<UserHandle> mPrivateProfileMatcher;
     private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
     private boolean mPrivateSpaceSettingsAvailable;
+    private Runnable mUnlockRunnable;
 
     public PrivateProfileManager(UserManager userManager,
             ActivityAllAppsContainerView<?> allApps,
@@ -115,9 +115,17 @@
         mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
     }
 
-    /** Disables quiet mode for Private Space User Profile. */
-    public void unlockPrivateProfile() {
+    /**
+     * Disables quiet mode for Private Space User Profile.
+     * The runnable passed will be executed in the {@link #reset()} method,
+     * when Launcher receives update about profile availability.
+     * The runnable passed is only executed once, and reset after execution.
+     * In case the method is called again, before the previously set runnable was executed,
+     * the runnable will be updated.
+     */
+    public void unlockPrivateProfile(Runnable runnable) {
         enableQuietMode(false);
+        mUnlockRunnable = runnable;
     }
 
     /** Enables quiet mode for Private Space User Profile. */
@@ -133,11 +141,15 @@
 
     /** Resets the current state of Private Profile, w.r.t. to Launcher. */
     public void reset() {
+        int previousState = getCurrentState();
         boolean isEnabled = !mAllApps.getAppsStore()
                 .hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
         int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
         setCurrentState(updatedState);
         resetPrivateSpaceDecorator(updatedState);
+        if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
+            applyUnlockRunnable();
+        }
     }
 
     /** Opens the Private Space Settings Entry Point. */
@@ -182,13 +194,6 @@
             }
             // Add Private Space Decorator to the Recycler view.
             mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
-            if (Flags.privateSpaceAnimation() && mAllApps.getActiveRecyclerView()
-                    == mainAdapterHolder.mRecyclerView) {
-                RecyclerViewAnimationController recyclerViewAnimationController =
-                        new RecyclerViewAnimationController(mAllApps);
-                recyclerViewAnimationController.animateToState(true /* expand */,
-                        ANIMATION_DURATION, () -> {});
-            }
         } else {
             // Remove Private Space Decorator from the Recycler view.
             if (mPrivateAppsSectionDecorator != null) {
@@ -202,6 +207,18 @@
         setQuietMode(enable);
     }
 
+    void applyUnlockRunnable() {
+        if (mUnlockRunnable != null) {
+            // reset the runnable to prevent re-execution.
+            MAIN_EXECUTOR.post(mUnlockRunnable);
+            mUnlockRunnable = null;
+        }
+    }
+
+    private boolean transitioningFromLockedToUnlocked(int previousState, int updatedState) {
+        return previousState == STATE_DISABLED && updatedState == STATE_ENABLED;
+    }
+
     @Override
     public Predicate<UserHandle> getUserMatcher() {
         return mPrivateProfileMatcher;
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index 568ce32..bc3269d 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION;
@@ -28,6 +29,7 @@
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
 
@@ -36,9 +38,13 @@
  * {@link UserProfileState}
  */
 public class PrivateSpaceHeaderViewController {
+    private static final int ANIMATION_DURATION = 2000;
+    private final ActivityAllAppsContainerView mAllApps;
     private final PrivateProfileManager mPrivateProfileManager;
 
-    public PrivateSpaceHeaderViewController(PrivateProfileManager privateProfileManager) {
+    public PrivateSpaceHeaderViewController(ActivityAllAppsContainerView allApps,
+            PrivateProfileManager privateProfileManager) {
+        this.mAllApps = allApps;
         this.mPrivateProfileManager = privateProfileManager;
     }
 
@@ -77,7 +83,8 @@
                 quietModeButton.setOnClickListener(
                         view -> {
                             mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
-                            mPrivateProfileManager.unlockPrivateProfile();
+                            mPrivateProfileManager.unlockPrivateProfile((this::
+                                    onPrivateProfileUnlocked));
                         });
             }
             default -> quietModeButton.setVisibility(View.GONE);
@@ -106,6 +113,21 @@
         }
     }
 
+    private void onPrivateProfileUnlocked() {
+        // If we are on main adapter view, we apply the PS Container expansion animation and
+        // then scroll down to load the entire container, making animation visible.
+        ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder =
+                (ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
+        if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
+                && mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
+            RecyclerViewAnimationController recyclerViewAnimationController =
+                    new RecyclerViewAnimationController(mAllApps);
+            recyclerViewAnimationController.animateToState(true /* expand */,
+                    ANIMATION_DURATION, () -> {});
+            mAllApps.getActiveRecyclerView().scrollToBottomWithMotion();
+        }
+    }
+
     PrivateProfileManager getPrivateProfileManager() {
         return mPrivateProfileManager;
     }
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 6bef725..8894f45 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -22,7 +22,6 @@
 import android.os.UserManager;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager;
@@ -89,7 +88,6 @@
     }
 
     /** Returns current state for the profile type. */
-    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     public int getCurrentState() {
         return mCurrentState;
     }
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 79d00c9..24f9acd 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
@@ -81,6 +82,8 @@
     @Mock
     private PackageManager mPackageManager;
 
+    private boolean mRunnableCalled = false;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -110,7 +113,7 @@
     public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
         when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
 
-        mPrivateProfileManager.unlockPrivateProfile();
+        mPrivateProfileManager.unlockPrivateProfile(() -> {});
 
         awaitTasksCompleted();
         Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -133,6 +136,23 @@
     }
 
     @Test
+    public void transitioningToUnlocked_resetCallsPendingRunnable() throws Exception {
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+        when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
+                .thenReturn(false);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
+        mRunnableCalled = false;
+
+        privateProfileManager.unlockPrivateProfile(this::testRunnable);
+        privateProfileManager.reset();
+
+        awaitTasksCompleted();
+        Mockito.verify(privateProfileManager).applyUnlockRunnable();
+        assertTrue(mRunnableCalled);
+    }
+
+    @Test
     public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() {
         Intent expectedIntent = new Intent(SAFETY_CENTER_INTENT);
         expectedIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
@@ -150,4 +170,8 @@
     private static void awaitTasksCompleted() throws Exception {
         UI_HELPER_EXECUTOR.submit(() -> null).get();
     }
+
+    private void testRunnable() {
+        mRunnableCalled = true;
+    }
 }
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
index bc09cdd..92fff49 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
@@ -64,13 +64,16 @@
     private RelativeLayout mPsHeaderLayout;
     @Mock
     private PrivateProfileManager mPrivateProfileManager;
+    @Mock
+    private ActivityAllAppsContainerView mAllApps;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = new ActivityContextWrapper(getApplicationContext());
         mLayoutInflater = LayoutInflater.from(getApplicationContext());
-        mPsHeaderViewController = new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+        mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
+                mPrivateProfileManager);
         mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header,
                 null);
     }