Merge "Adding suport for Private Space QsTile fulfillment." into main

This commit is contained in:
Himanshu Gupta
2024-01-17 07:00:10 +00:00
committed by Android (Google) Code Review
7 changed files with 87 additions and 22 deletions

View File

@@ -260,7 +260,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
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 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
return mWorkManager;
}
/** Returns whether Private Profile has been setup. */
public boolean hasPrivateProfile() {
return mHasPrivateApps;
}
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {

View File

@@ -325,10 +325,6 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
position++;
addAppsWithSections(mPrivateApps, position);
if (mActivityContext.getAppsView() != null) {
mActivityContext.getAppsView().getActiveRecyclerView()
.scrollToBottomWithMotion();
}
break;
}
}

View File

@@ -21,6 +21,7 @@ import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
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 android.os.UserManager;
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 @@ public class PrivateProfileManager extends UserProfileManager {
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 @@ public class PrivateProfileManager extends UserProfileManager {
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 @@ public class PrivateProfileManager extends UserProfileManager {
/** 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 @@ public class PrivateProfileManager extends UserProfileManager {
}
// 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 @@ public class PrivateProfileManager extends UserProfileManager {
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;

View File

@@ -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.ImageButton;
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 @@ import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
* {@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 @@ public class PrivateSpaceHeaderViewController {
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 @@ public class PrivateSpaceHeaderViewController {
}
}
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;
}

View File

@@ -22,7 +22,6 @@ import android.os.UserHandle;
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 @@ public abstract class UserProfileManager {
}
/** Returns current state for the profile type. */
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public int getCurrentState() {
return mCurrentState;
}

View File

@@ -22,6 +22,7 @@ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PRO
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 @@ public class PrivateProfileManagerTest {
@Mock
private PackageManager mPackageManager;
private boolean mRunnableCalled = false;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -110,7 +113,7 @@ public class PrivateProfileManagerTest {
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);
@@ -132,6 +135,23 @@ public class PrivateProfileManagerTest {
assertEquals(STATE_DISABLED, privateProfileManager.getCurrentState());
}
@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);
@@ -150,4 +170,8 @@ public class PrivateProfileManagerTest {
private static void awaitTasksCompleted() throws Exception {
UI_HELPER_EXECUTOR.submit(() -> null).get();
}
private void testRunnable() {
mRunnableCalled = true;
}
}

View File

@@ -64,13 +64,16 @@ public class PrivateSpaceHeaderViewControllerTest {
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);
}