From f263e9d30754f868dcd92746c4f2aac86eedd798 Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Tue, 9 Jan 2024 12:04:49 +0100 Subject: [PATCH 01/10] Fix TaplTestsQuickstep.testPressback failure (2) Since anim is empty in the predictiveBack case, the animationEnd listener needs to be added to rectFSpringAnim instead Bug: 318675970 Flag: ACONFIG com.android.systemui.predictive_back_system_animations STAGING Test: TaplTestsQuickstep Change-Id: Ibb3fd44c56b9d5370362f994762554eff02edbe6 --- .../android/launcher3/QuickstepTransitionManager.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 67d3827dfb..531caa00d0 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1654,14 +1654,20 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME); - anim.addListener(new AnimatorListenerAdapter() { + AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); AccessibilityManagerCompat.sendTestProtocolEventToTest( mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE); } - }); + }; + + if (fromPredictiveBack) { + rectFSpringAnim.addAnimatorListener(endListener); + } else { + anim.addListener(endListener); + } // Only register the content animation for cancellation when state changes mLauncher.getStateManager().setCurrentAnimation(anim); From b442235208da42eb96676b43700d96249d619adb Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Tue, 23 Jan 2024 15:55:40 -0800 Subject: [PATCH 02/10] Update minSdk to 30. Also update build.gradle to match. Test: Build and install with gradle Flag: NA Change-Id: I74f2109a13cc662be2d66e472ff010a3acd139f5 --- AndroidManifest.xml | 2 +- build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4f580e0bd6..517bd6d70e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,7 +20,7 @@ - + Tap another app to use split screen + Choose another app to use split screen Cancel Exit split screen selection diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java index f39a9011cd..c4b93b7268 100644 --- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java +++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java @@ -120,10 +120,12 @@ public class SplitInstructionsView extends LinearLayout { private void init() { TextView cancelTextView = findViewById(R.id.split_instructions_text_cancel); + TextView instructionTextView = findViewById(R.id.split_instructions_text); if (FeatureFlags.enableSplitContextually()) { cancelTextView.setVisibility(VISIBLE); cancelTextView.setOnClickListener((v) -> exitSplitSelection()); + instructionTextView.setText(R.string.toast_contextual_split_select_app); } } From 0cc11dbcac9ad0f6e985a78a429eeb33956c3091 Mon Sep 17 00:00:00 2001 From: Himanshu Gupta Date: Thu, 21 Dec 2023 17:54:15 +0000 Subject: [PATCH 08/10] Separating system and user-installed apps in PS container. We add a horizontal line separator in PS container which demarcates user-installed vs system installed apps in private space. User-installed are shown above the separator, system ones below. UX Mock: https://www.figma.com/file/K6bIIcG882EiJNjxvSWsFT/V%E2%80%A2-Private-Space?node-id=11546%3A310574&mode=dev Mock Image: https://photos.app.goo.gl/Wj8sJkS7P7bRbovg8 Demo video: https://photos.app.goo.gl/MBw6HpDnf6PJqUfs8 Bug: 308054233 Flag: ACONFIG com.android.launcher3.Flags.private_space_sys_apps_separation DEVELOPMENT Test: AlphabeticalAppsListTest Change-Id: Iad8e289c49a05ac7ef1978bd8e4ebe7aa0add0ca --- res/drawable/private_space_app_divider.xml | 21 ++++++ res/layout/private_space_divider.xml | 25 +++++++ res/values/dimens.xml | 1 + .../allapps/AlphabeticalAppsList.java | 43 +++++++++--- .../launcher3/allapps/BaseAllAppsAdapter.java | 20 +++++- .../allapps/PrivateProfileManager.java | 69 +++++++++++++++---- .../allapps/AlphabeticalAppsListTest.java | 58 +++++++++++++++- .../allapps/PrivateProfileManagerTest.java | 15 ++++ 8 files changed, 226 insertions(+), 26 deletions(-) create mode 100644 res/drawable/private_space_app_divider.xml create mode 100644 res/layout/private_space_divider.xml diff --git a/res/drawable/private_space_app_divider.xml b/res/drawable/private_space_app_divider.xml new file mode 100644 index 0000000000..7d069efd8b --- /dev/null +++ b/res/drawable/private_space_app_divider.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/private_space_divider.xml b/res/layout/private_space_divider.xml new file mode 100644 index 0000000000..fff862970a --- /dev/null +++ b/res/layout/private_space_divider.xml @@ -0,0 +1,25 @@ + + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 603e697395..3016559f96 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -487,4 +487,5 @@ 36dp 36dp 89dp + 16dp diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index ad875e0dc3..fba7537dac 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -35,6 +35,7 @@ import com.android.launcher3.views.ActivityContext; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.TreeMap; import java.util.function.Predicate; @@ -269,10 +270,10 @@ public class AlphabeticalAppsList implement addApps = mWorkProviderManager.shouldShowWorkApps(); } if (addApps) { - addAppsWithSections(mApps, position); + position = addAppsWithSections(mApps, position); } if (Flags.enablePrivateSpace()) { - addPrivateSpaceItems(position); + position = addPrivateSpaceItems(position); } } mAccessibilityResultsCount = (int) mAdapterItems.stream() @@ -287,7 +288,8 @@ public class AlphabeticalAppsList implement for (AdapterItem item : mAdapterItems) { item.rowIndex = 0; if (BaseAllAppsAdapter.isDividerViewType(item.viewType) - || BaseAllAppsAdapter.isPrivateSpaceHeaderView(item.viewType)) { + || BaseAllAppsAdapter.isPrivateSpaceHeaderView(item.viewType) + || BaseAllAppsAdapter.isPrivateSpaceSysAppsDividerView(item.viewType)) { numAppsInSection = 0; } else if (BaseAllAppsAdapter.isIconViewType(item.viewType)) { if (numAppsInSection % mNumAppsPerRowAllApps == 0) { @@ -309,12 +311,12 @@ public class AlphabeticalAppsList implement } } - void addPrivateSpaceItems(int position) { + int addPrivateSpaceItems(int position) { if (mPrivateProviderManager != null && !mPrivateProviderManager.isPrivateSpaceHidden() && !mPrivateApps.isEmpty()) { // Always add PS Header if Space is present and visible. - position += mPrivateProviderManager.addPrivateSpaceHeader(mAdapterItems); + position = mPrivateProviderManager.addPrivateSpaceHeader(mAdapterItems); int privateSpaceState = mPrivateProviderManager.getCurrentState(); switch (privateSpaceState) { case PrivateProfileManager.STATE_DISABLED: @@ -322,15 +324,37 @@ public class AlphabeticalAppsList implement break; case PrivateProfileManager.STATE_ENABLED: // Add PS Apps only in Enabled State. - mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems); - position++; - addAppsWithSections(mPrivateApps, position); + position = addPrivateSpaceApps(position); break; } } + return position; } - private void addAppsWithSections(List appList, int startPosition) { + private int addPrivateSpaceApps(int position) { + // Add Install Apps Button first. + if (Flags.privateSpaceAppInstallerButton()) { + mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems); + position++; + } + + // Split of private space apps into user-installed and system apps. + Map> split = mPrivateApps.stream() + .collect(Collectors.partitioningBy(mPrivateProviderManager + .splitIntoUserInstalledAndSystemApps())); + // Add user installed apps + position = addAppsWithSections(split.get(true), position); + // Add system apps separator. + if (Flags.privateSpaceSysAppsSeparation()) { + position = mPrivateProviderManager.addSystemAppsDivider(mAdapterItems); + } + // Add system apps. + position = addAppsWithSections(split.get(false), position); + + return position; + } + + private int addAppsWithSections(List appList, int startPosition) { String lastSectionName = null; boolean hasPrivateApps = false; if (mPrivateProviderManager != null) { @@ -357,6 +381,7 @@ public class AlphabeticalAppsList implement } startPosition++; } + return startPosition; } /** diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index 5eeb259fe5..28c87b6fa7 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -17,6 +17,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_LEFT; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_RIGHT; import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; @@ -61,7 +62,8 @@ public abstract class BaseAllAppsAdapter ex public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 4; public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5; public static final int VIEW_TYPE_PRIVATE_SPACE_HEADER = 1 << 6; - public static final int NEXT_ID = 7; + public static final int VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER = 1 << 7; + public static final int NEXT_ID = 8; // Common view type masks public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; @@ -69,6 +71,8 @@ public abstract class BaseAllAppsAdapter ex public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER = VIEW_TYPE_PRIVATE_SPACE_HEADER; + public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_SYS_APPS_DIVIDER = + VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER; protected final SearchAdapterProvider mAdapterProvider; @@ -199,6 +203,11 @@ public abstract class BaseAllAppsAdapter ex return isViewType(viewType, VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER); } + /** Checks if the passed viewType represents private space system apps divider. */ + public static boolean isPrivateSpaceSysAppsDividerView(int viewType) { + return isViewType(viewType, VIEW_TYPE_MASK_PRIVATE_SPACE_SYS_APPS_DIVIDER); + } + public void setIconFocusListener(OnFocusChangeListener focusListener) { mIconFocusListener = focusListener; } @@ -227,9 +236,9 @@ public abstract class BaseAllAppsAdapter ex case VIEW_TYPE_EMPTY_SEARCH: return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent, false)); - case VIEW_TYPE_ALL_APPS_DIVIDER: + case VIEW_TYPE_ALL_APPS_DIVIDER, VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER: return new ViewHolder(mLayoutInflater.inflate( - R.layout.all_apps_divider, parent, false)); + R.layout.private_space_divider, parent, false)); case VIEW_TYPE_WORK_EDU_CARD: return new ViewHolder(mLayoutInflater.inflate( R.layout.work_apps_edu, parent, false)); @@ -282,6 +291,11 @@ public abstract class BaseAllAppsAdapter ex new SectionDecorationInfo(mActivityContext, roundRegions, false /* decorateTogether */); break; + case VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER: + adapterItem = mApps.getAdapterItems().get(position); + adapterItem.decorationInfo = new SectionDecorationInfo(mActivityContext, + ROUND_NOTHING, true /* decorateTogether */); + break; case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_WORK_DISABLED_CARD: // nothing to do diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index aee511cad7..e3c56d37ee 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -19,6 +19,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN; 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.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER; 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; @@ -44,10 +45,11 @@ import com.android.launcher3.pm.UserCache; import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SettingsCache; -import com.android.launcher3.util.UserIconInfo; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Predicate; /** @@ -61,6 +63,8 @@ public class PrivateProfileManager extends UserProfileManager { private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal"; private final ActivityAllAppsContainerView mAllApps; private final Predicate mPrivateProfileMatcher; + private Set mPreInstalledSystemPackages = new HashSet<>(); + private Intent mAppInstallerIntent = new Intent(); private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator; private boolean mPrivateSpaceSettingsAvailable; private Runnable mUnlockRunnable; @@ -72,7 +76,7 @@ public class PrivateProfileManager extends UserProfileManager { super(userManager, statsLogManager, userCache); mAllApps = allApps; mPrivateProfileMatcher = (user) -> userCache.getUserInfo(user).isPrivate(); - UI_HELPER_EXECUTOR.post(this::setPrivateSpaceSettingsAvailable); + UI_HELPER_EXECUTOR.post(this::initializeInBackgroundThread); } /** Adds Private Space Header to the layout. */ @@ -82,18 +86,17 @@ public class PrivateProfileManager extends UserProfileManager { return adapterItems.size(); } + /** Adds Private Space System Apps Divider to the layout. */ + public int addSystemAppsDivider(List adapterItems) { + adapterItems.add(new BaseAllAppsAdapter + .AdapterItem(VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER)); + mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1); + return adapterItems.size(); + } + /** Adds Private Space install app button to the layout. */ public void addPrivateSpaceInstallAppButton(List adapterItems) { Context context = mAllApps.getContext(); - // Prepare intent - UserCache userCache = UserCache.getInstance(context); - UserHandle userHandle = userCache.getUserProfiles().stream() - .filter(user -> userCache.getUserInfo(user).type == UserIconInfo.TYPE_PRIVATE) - .findFirst() - .orElse(null); - Intent intent = ApiWrapper.getAppMarketActivityIntent(context, - BuildConfig.APPLICATION_ID, userHandle); - // Prepare bitmapInfo Intent.ShortcutIconResource shortcut = Intent.ShortcutIconResource.fromContext( context, com.android.launcher3.R.drawable.private_space_install_app_icon); @@ -101,7 +104,7 @@ public class PrivateProfileManager extends UserProfileManager { AppInfo itemInfo = new AppInfo(); itemInfo.title = context.getResources().getString(R.string.ps_add_button_label); - itemInfo.intent = intent; + itemInfo.intent = mAppInstallerIntent; itemInfo.bitmap = bitmapInfo; itemInfo.contentDescription = context.getResources().getString( com.android.launcher3.R.string.ps_add_button_content_description); @@ -164,6 +167,22 @@ public class PrivateProfileManager extends UserProfileManager { return mPrivateSpaceSettingsAvailable; } + /** Initializes binder call based properties in non-main thread. + *

+ * This can cause the Private Space container items to not load/respond correctly sometimes, + * when the All Apps Container loads for the first time (device restarts, new profiles + * added/removed, etc.), as the properties are being set in non-ui thread whereas the container + * loads in the ui thread. + * This case should still be ok, as locking the Private Space container and unlocking it, + * reloads the values, fixing the incorrect UI. + */ + private void initializeInBackgroundThread() { + Preconditions.assertNonUiThread(); + setPreInstalledSystemPackages(); + setAppInstallerIntent(); + setPrivateSpaceSettingsAvailable(); + } + private void setPrivateSpaceSettingsAvailable() { if (mPrivateSpaceSettingsAvailable) { return; @@ -176,6 +195,22 @@ public class PrivateProfileManager extends UserProfileManager { mPrivateSpaceSettingsAvailable = resolveInfo != null; } + private void setPreInstalledSystemPackages() { + Preconditions.assertNonUiThread(); + if (getProfileUser() != null) { + mPreInstalledSystemPackages = new HashSet<>(ApiWrapper + .getPreInstalledSystemPackages(mAllApps.getContext(), getProfileUser())); + } + } + + private void setAppInstallerIntent() { + Preconditions.assertNonUiThread(); + if (getProfileUser() != null) { + mAppInstallerIntent = ApiWrapper.getAppMarketActivityIntent(mAllApps.getContext(), + BuildConfig.APPLICATION_ID, getProfileUser()); + } + } + @VisibleForTesting void resetPrivateSpaceDecorator(int updatedState) { ActivityAllAppsContainerView.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN); @@ -223,4 +258,14 @@ public class PrivateProfileManager extends UserProfileManager { public Predicate getUserMatcher() { return mPrivateProfileMatcher; } + + /** + * Splits private apps into user installed and system apps. + * When the list of system apps is empty, all apps are treated as system. + */ + public Predicate splitIntoUserInstalledAndSystemApps() { + return appInfo -> !mPreInstalledSystemPackages.isEmpty() + && (appInfo.componentName == null + || !(mPreInstalledSystemPackages.contains(appInfo.componentName.getPackageName()))); + } } diff --git a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java index 306878531c..423ca2415f 100644 --- a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java +++ b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java @@ -19,6 +19,7 @@ package com.android.launcher3.allapps; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER; +import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT; import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING; @@ -63,9 +64,10 @@ public class AlphabeticalAppsListTest { private static final int PRIVATE_SPACE_HEADER_ITEM_COUNT = 1; private static final int MAIN_USER_APP_COUNT = 2; - private static final int PRIVATE_USER_APP_COUNT = 1; + private static final int PRIVATE_USER_APP_COUNT = 2; private static final int NUM_APP_COLS = 4; private static final int NUM_APP_ROWS = 3; + private static final int PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT = 1; private AlphabeticalAppsList mAlphabeticalAppsList; @Mock @@ -96,6 +98,10 @@ public class AlphabeticalAppsListTest { when(mPrivateProfileManager.addPrivateSpaceHeader(any())) .thenAnswer(answer(this::addPrivateSpaceHeader)); when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED); + when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps()) + .thenReturn(iteminfo -> iteminfo.componentName == null + || !iteminfo.componentName.getPackageName() + .equals("com.android.launcher3.tests.camera")); mAlphabeticalAppsList.updateItemFilter(info -> info != null && info.user.equals(MAIN_HANDLE)); @@ -111,6 +117,44 @@ public class AlphabeticalAppsListTest { && item.itemInfo.user.equals(PRIVATE_HANDLE)).toList().size()); } + @Test + public void privateProfileEnabled_privateProfileAppsShownWithSeparator() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + mSetFlagsRule.enableFlags(Flags.FLAG_PRIVATE_SPACE_SYS_APPS_SEPARATION); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser()); + when(mPrivateProfileManager.addPrivateSpaceHeader(any())) + .thenAnswer(answer(this::addPrivateSpaceHeader)); + when(mPrivateProfileManager.addSystemAppsDivider(any())) + .thenAnswer(answer(this::addSystemAppsDivider)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED); + when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps()) + .thenReturn(iteminfo -> iteminfo.componentName == null + || !iteminfo.componentName.getPackageName() + .equals("com.android.launcher3.tests.camera")); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT + + PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT + + PRIVATE_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, + mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size()); + assertEquals(PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT, + mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER).toList().size()); + List psApps = mAlphabeticalAppsList.getAdapterItems() + .stream() + .filter(item -> item.itemInfo != null && item.itemInfo.user.equals(PRIVATE_HANDLE)) + .toList(); + assertEquals(PRIVATE_USER_APP_COUNT, psApps.size()); + assert psApps.get(0).itemInfo.title != null; + assertEquals("Private Messenger", psApps.get(0).itemInfo.title.toString()); + assert psApps.get(1).itemInfo.title != null; + assertEquals("Private Camera", psApps.get(1).itemInfo.title.toString()); + } + @Test public void privateProfileDisabled_onlyPrivateProfileHeaderViewIsPresent() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); @@ -281,6 +325,12 @@ public class AlphabeticalAppsListTest { return adapterItemList.size(); } + private int addSystemAppsDivider(List adapterItemList) { + adapterItemList.add(new BaseAllAppsAdapter + .AdapterItem(VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER)); + return adapterItemList.size(); + } + private AppInfo[] createAppInfoListForMainUser() { ComponentName gmailComponentName = new ComponentName(mContext, "com.android.launcher3.tests.Activity" + "Gmail"); @@ -298,7 +348,11 @@ public class AlphabeticalAppsListTest { "com.android.launcher3.tests.Activity" + "PrivateMessenger"); AppInfo privateMessengerAppInfo = new AppInfo(privateMessengercomponentName, "Private Messenger", PRIVATE_HANDLE, new Intent()); - return new AppInfo[]{privateMessengerAppInfo}; + ComponentName privateCameraComponentName = new ComponentName( + "com.android.launcher3.tests.camera", "CameraActivity"); + AppInfo privateCameraAppInfo = new AppInfo(privateCameraComponentName, + "Private Camera", PRIVATE_HANDLE, new Intent()); + return new AppInfo[]{privateMessengerAppInfo, privateCameraAppInfo}; } private AppInfo[] createAppInfoListForMainAndPrivateUser() { diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java index 69edd0f2a5..fd8739c48a 100644 --- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java +++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java @@ -16,6 +16,8 @@ package com.android.launcher3.allapps; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; @@ -31,8 +33,10 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Process; @@ -43,6 +47,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.ActivityContextWrapper; import com.android.launcher3.util.UserIconInfo; import com.android.launcher3.util.rule.TestStabilityRule; @@ -56,6 +61,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; @RunWith(AndroidJUnit4.class) @@ -89,6 +95,8 @@ public class PrivateProfileManagerTest { private AllAppsStore mAllAppsStore; @Mock private PackageManager mPackageManager; + @Mock + private LauncherApps mLauncherApps; private boolean mRunnableCalled = false; @@ -103,6 +111,13 @@ public class PrivateProfileManagerTest { when(mActivityAllAppsContainerView.getAppsStore()).thenReturn(mAllAppsStore); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.resolveActivity(any(), any())).thenReturn(new ResolveInfo()); + when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); + when(mLauncherApps.getAppMarketActivityIntent(any(), any())).thenReturn(PendingIntent + .getActivity(new ActivityContextWrapper(getApplicationContext()), 0, + new Intent(), PendingIntent.FLAG_IMMUTABLE).getIntentSender()); + when(mContext.getPackageName()) + .thenReturn("com.android.launcher3.tests.privateProfileManager"); + when(mLauncherApps.getPreInstalledSystemPackages(any())).thenReturn(new ArrayList<>()); mPrivateProfileManager = new PrivateProfileManager(mUserManager, mActivityAllAppsContainerView, mStatsLogManager, mUserCache); } From 4b67d7f47fb64bf5ec0e1990dadbb7380a8daccc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 24 Jan 2024 15:26:37 -0800 Subject: [PATCH 09/10] Adding an abort listener in app launches to clear state Bug: 318394698 Test: Verified on device Flag: None Change-Id: Ib6108782429abb5dfd9c4e42246e4a93ddf10f55 --- .../launcher3/QuickstepTransitionManager.java | 5 +++++ .../taskbar/TaskbarActivityContext.java | 13 +++++++------ .../uioverrides/QuickstepLauncher.java | 13 +++++++------ .../com/android/quickstep/util/AnimUtils.java | 19 +++++++++++++++++++ .../com/android/quickstep/views/TaskView.java | 19 ++----------------- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 2e3cad4cf8..e8a3d3eef5 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -64,6 +64,7 @@ import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATI import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; +import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; @@ -88,6 +89,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.SystemProperties; import android.os.UserHandle; @@ -351,6 +353,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay), new RemoteTransition(runner.toRemoteTransition(), mLauncher.getIApplicationThread(), "QuickstepLaunch")); + IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback); + options.setOnAnimationAbortListener(endCallback); + options.setOnAnimationFinishedListener(endCallback); return new ActivityOptionsWrapper(options, onEndCallback); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 2b88f0202c..c905c85120 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -37,6 +37,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN; import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName; +import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; @@ -51,10 +52,10 @@ import android.content.Intent; import android.content.pm.ActivityInfo.Config; import android.content.pm.LauncherApps; import android.content.res.Resources; -import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.display.DisplayManager; +import android.os.IRemoteCallback; import android.os.Process; import android.os.Trace; import android.provider.Settings; @@ -66,7 +67,6 @@ import android.view.Surface; import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; -import android.widget.FrameLayout; import android.widget.Toast; import androidx.annotation.NonNull; @@ -744,13 +744,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext { @Override public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { RunnableList callbacks = new RunnableList(); - ActivityOptions options = ActivityOptions.makeCustomAnimation( - this, 0, 0, Color.TRANSPARENT, - Executors.MAIN_EXECUTOR.getHandler(), null, - elapsedRealTime -> callbacks.executeAllAndDestroy()); + ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0); options.setSplashScreenStyle(splashScreenStyle); options.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + IRemoteCallback endCallback = completeRunnableListCallback(callbacks); + options.setOnAnimationAbortListener(endCallback); + options.setOnAnimationFinishedListener(endCallback); + return new ActivityOptionsWrapper(options, callbacks); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 729609b699..0991fce04b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -54,6 +54,7 @@ import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STA import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -67,7 +68,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.res.Configuration; -import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.display.DisplayManager; @@ -75,6 +75,7 @@ import android.media.permission.SafeCloseable; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.SystemProperties; import android.os.Trace; import android.util.AttributeSet; @@ -145,7 +146,6 @@ import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchT import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.Executors; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.ObjectWrapper; @@ -1140,13 +1140,14 @@ public class QuickstepLauncher extends Launcher { @Override public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { RunnableList callbacks = new RunnableList(); - ActivityOptions options = ActivityOptions.makeCustomAnimation( - this, 0, 0, Color.TRANSPARENT, - Executors.MAIN_EXECUTOR.getHandler(), null, - elapsedRealTime -> callbacks.executeAllAndDestroy()); + ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0); options.setSplashScreenStyle(splashScreenStyle); options.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + + IRemoteCallback endCallback = completeRunnableListCallback(callbacks); + options.setOnAnimationAbortListener(endCallback); + options.setOnAnimationFinishedListener(endCallback); return new ActivityOptionsWrapper(options, callbacks); } diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java index 7fbbb6e5b8..1f2a02c816 100644 --- a/quickstep/src/com/android/quickstep/util/AnimUtils.java +++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java @@ -16,6 +16,13 @@ package com.android.quickstep.util; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + +import android.os.Bundle; +import android.os.IRemoteCallback; + +import com.android.launcher3.util.RunnableList; + /** * Utility class containing methods to help manage animations, interpolators, and timings. */ @@ -48,4 +55,16 @@ public class AnimUtils { ? SplitAnimationTimings.TABLET_APP_PAIR_LAUNCH : SplitAnimationTimings.PHONE_APP_PAIR_LAUNCH; } + + /** + * Returns a IRemoteCallback which completes the provided list as a result + */ + public static IRemoteCallback completeRunnableListCallback(RunnableList list) { + return new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle bundle) { + MAIN_EXECUTOR.execute(list::executeAllAndDestroy); + } + }; + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index dda51b3452..68710a7946 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -57,7 +57,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; @@ -119,8 +118,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; -import kotlin.Unit; - import java.lang.annotation.Retention; import java.util.Arrays; import java.util.Collections; @@ -129,6 +126,8 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; +import kotlin.Unit; + /** * A task in the Recents view. */ @@ -969,20 +968,6 @@ public class TaskView extends FrameLayout implements Reusable { } } - /** - * Returns ActivityOptions for overriding task transition animation. - */ - private ActivityOptions makeCustomAnimation(Context context, int enterResId, - int exitResId, final Runnable callback, final Handler callbackHandler) { - return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId, - callbackHandler, - elapsedRealTime -> { - if (callback != null) { - callbackHandler.post(callback); - } - }, null /* finishedListener */); - } - /** * Launch of the current task (both live and inactive tasks) with an animation. */ From 8a13e275ecf4a0cd148e8e3a64dbfb506af0b03b Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 26 Jan 2024 19:01:48 +0000 Subject: [PATCH 10/10] Revert "Ignore taskbar launch request if app(s) are already running" This reverts commit 9a7b6f972b3dd83c7027bd2d66c56ed810b0112e. Reason for revert: b/319750648 Change-Id: I778453cf65bb48e31062365a069cfe5b1cb77b96 --- .../taskbar/FallbackTaskbarUIController.java | 7 --- .../taskbar/TaskbarActivityContext.java | 50 +++---------------- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java index 535c8ecec6..f9816102d4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -104,13 +104,6 @@ public class FallbackTaskbarUIController extends TaskbarUIController { } } - @Override - protected boolean isInOverview() { - TopTaskTracker.CachedTaskInfo topTask = TopTaskTracker.INSTANCE - .get(mControllers.taskbarActivityContext).getCachedTopTask(true); - return topTask.isRecentsTask(); - } - @Override public RecentsView getRecentsView() { return mRecentsActivity.getOverviewPanel(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index eff6e27898..fb25ec12d7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -42,9 +42,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_V import android.animation.AnimatorSet; import android.animation.ValueAnimator; -import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; -import android.app.ActivityTaskManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -132,13 +130,10 @@ import com.android.systemui.unfold.updates.RotationChangeProvider; import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; import java.io.PrintWriter; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; /** * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements @@ -1152,25 +1147,15 @@ public class TaskbarActivityContext extends BaseTaskbarContext { @Nullable Task foundTask = foundTasks[0]; if (foundTask != null) { TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id); - if (foundTaskView != null) { - // The foundTaskView contains the 1-2 taskIds we are looking for. - // If we are already in-app and running the correct tasks, no need - // to do anything. - if (FeatureFlags.enableAppPairs() - && isAlreadyInApp(foundTaskView.getTaskIds())) { - return; - } - // If we are in Overview and the TaskView tile is visible, expand that - // tile. - if (foundTaskView.isVisibleToUser()) { - TestLogging.recordEvent( - TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon"); - foundTaskView.launchTasks(); - return; - } + if (foundTaskView != null + && foundTaskView.isVisibleToUser()) { + TestLogging.recordEvent( + TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon"); + foundTaskView.launchTasks(); + return; } } - // If none of the above cases apply, launch a new app or app pair. + if (findExactPairMatch) { // We did not find the app pair we were looking for, so launch one. recents.getSplitSelectController().getAppPairsController().launchAppPair( @@ -1182,27 +1167,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { ); } - /** - * Checks if a given list of taskIds are all already running in-app. - */ - private boolean isAlreadyInApp(int[] ids) { - if (mControllers.uiController.isInOverview()) { - return false; - } - - RunningTaskInfo[] currentlyRunningTasks = ActivityManagerWrapper.getInstance() - .getRunningTasks(false /* filterOnlyVisibleRecents */); - Set currentlyRunningIds = Arrays.stream(currentlyRunningTasks) - .map(task -> task.taskId).collect(Collectors.toSet()); - - for (int id : ids) { - if (id != ActivityTaskManager.INVALID_TASK_ID && !currentlyRunningIds.contains(id)) { - return false; - } - } - return true; - } - private void startItemInfoActivity(ItemInfo info) { Intent intent = new Intent(info.getIntent()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);