From 98bf9f75322b9df9bd91260a6f96db822ebc6d05 Mon Sep 17 00:00:00 2001 From: Brandon Dayauon Date: Fri, 17 Nov 2023 15:55:37 -0800 Subject: [PATCH] Apply the expand animation on Private Space - Using the refactoring that took place in ag/25414154 Bug: 299294792 Test: Verified SearchTransitionController didn't regress by turning off BACKGROUND_DRAWABLES flag. - Verified QL highlight still works - video: https://drive.google.com/file/d/15yjBWofebn6m7VgEnLK6kEYqhC_adJQ3/view?usp=sharing Flag: ACONFIG com.android.launcher3.Flags.private_space_animation DEVELOPMENT Change-Id: Ib6229b404b48616966f3e6ab6884099b6e4b4023 --- .../allapps/AlphabeticalAppsList.java | 36 +++++++- .../launcher3/allapps/BaseAllAppsAdapter.java | 22 +++++ .../allapps/PrivateAppsSectionDecorator.java | 90 +++++-------------- .../allapps/PrivateProfileManager.java | 10 ++- .../PrivateSpaceHeaderViewController.java | 4 + 5 files changed, 94 insertions(+), 68 deletions(-) diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 328516e7e0..17827918b8 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -15,6 +15,10 @@ */ 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 android.content.Context; import androidx.annotation.Nullable; @@ -318,6 +322,10 @@ public class AlphabeticalAppsList implement case PrivateProfileManager.STATE_ENABLED: // Add PS Apps only in Enabled State. addAppsWithSections(mPrivateApps, position); + if (mActivityContext.getAppsView() != null) { + mActivityContext.getAppsView().getActiveRecyclerView() + .scrollToBottomWithMotion(); + } break; } } @@ -325,8 +333,34 @@ public class AlphabeticalAppsList implement private void addAppsWithSections(List appList, int startPosition) { String lastSectionName = null; + boolean hasPrivateApps = false; + if (mPrivateProviderManager != null) { + hasPrivateApps = appList.stream(). + allMatch(mPrivateProviderManager.getItemInfoMatcher()); + } + int privateAppCount = 0; + int numberOfColumns = mActivityContext.getDeviceProfile().numShownAllAppsColumns; + int numberOfAppRows = (int) Math.ceil((double) appList.size() / numberOfColumns); for (AppInfo info : appList) { - mAdapterItems.add(AdapterItem.asApp(info)); + // Apply decorator to private apps. + if (hasPrivateApps) { + int roundRegion = ROUND_NOTHING; + if ((privateAppCount / numberOfColumns) == numberOfAppRows - 1) { + if ((privateAppCount % numberOfColumns) == 0) { + // App is the first column + roundRegion = ROUND_BOTTOM_LEFT; + } else if ((privateAppCount % numberOfColumns) == numberOfColumns-1) { + roundRegion = ROUND_BOTTOM_RIGHT; + } + } + mAdapterItems.add(AdapterItem.asAppWithDecorationInfo(info, + new SectionDecorationInfo(mActivityContext.getApplicationContext(), + roundRegion, + true /* decorateTogether */))); + privateAppCount += 1; + } else { + mAdapterItems.add(AdapterItem.asApp(info)); + } String sectionName = info.sectionName; // Create a new section if the section names do not match diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index 035c30f6cd..5eeb259fe5 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -15,6 +15,12 @@ */ 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_TOP_LEFT; +import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_RIGHT; +import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; + import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -108,6 +114,13 @@ public abstract class BaseAllAppsAdapter ex return item; } + public static AdapterItem asAppWithDecorationInfo(AppInfo appInfo, + SectionDecorationInfo decorationInfo) { + AdapterItem item = asApp(appInfo); + item.decorationInfo = decorationInfo; + return item; + } + protected boolean isCountedForAccessibility() { return viewType == VIEW_TYPE_ICON; } @@ -259,6 +272,15 @@ public abstract class BaseAllAppsAdapter ex assert mPrivateSpaceHeaderViewController != null; assert psHeaderLayout != null; mPrivateSpaceHeaderViewController.addPrivateSpaceHeaderViewElements(psHeaderLayout); + AdapterItem adapterItem = mApps.getAdapterItems().get(position); + int roundRegions = ROUND_TOP_LEFT | ROUND_TOP_RIGHT; + if (mPrivateSpaceHeaderViewController.getPrivateProfileManager().getCurrentState() + == STATE_DISABLED) { + roundRegions |= (ROUND_BOTTOM_LEFT | ROUND_BOTTOM_RIGHT); + } + adapterItem.decorationInfo = + new SectionDecorationInfo(mActivityContext, roundRegions, + false /* decorateTogether */); break; case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_WORK_DISABLED_CARD: diff --git a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java index f4ed754d7d..8712b8468f 100644 --- a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java @@ -16,97 +16,55 @@ package com.android.launcher3.allapps; -import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON; -import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER; - -import android.content.Context; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; import android.view.View; -import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; -import com.android.launcher3.R; -import com.android.launcher3.pm.UserCache; -import com.android.launcher3.views.ActivityContext; +import java.util.HashMap; /** * Decorator which changes the background color for Private Space Icon Rows in AllAppsContainer. */ public class PrivateAppsSectionDecorator extends RecyclerView.ItemDecoration { - private final Path mTmpPath = new Path(); - private final RectF mTmpRect = new RectF(); - private final Context mContext; + private static final String PRIVATE_APP_SECTION = "private_apps"; private final AlphabeticalAppsList mAppsList; - private final UserCache mUserCache; - private final Paint mPaint; - private final int mCornerRadius; - public PrivateAppsSectionDecorator(Context context, AlphabeticalAppsList appsList) { - mContext = context; + public PrivateAppsSectionDecorator(AlphabeticalAppsList appsList) { mAppsList = appsList; - mUserCache = UserCache.getInstance(context); - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mPaint.setColor(ContextCompat.getColor(context, - R.color.material_color_surface_container_high)); - mCornerRadius = context.getResources().getDimensionPixelSize( - R.dimen.ps_container_corner_radius); } /** Decorates Private Space Header and Icon Rows to give the shape of a container. */ @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - mTmpPath.reset(); - mTmpRect.setEmpty(); - int numCol = ActivityContext.lookupContext(mContext).getDeviceProfile() - .numShownAllAppsColumns; + HashMap deferredDecorations = + new HashMap<>(); for (int i = 0; i < parent.getChildCount(); i++) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); BaseAllAppsAdapter.AdapterItem adapterItem = mAppsList.getAdapterItems().get(position); - // Rectangle that covers the bottom half of the PS Header View when Space is unlocked. - if (adapterItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) { - // We flatten the bottom corners of the rectangle, so that it merges with - // the private space app row decorator. - mTmpRect.set( - view.getLeft(), - view.getTop() + (float) (view.getBottom() - view.getTop()) / 2, - view.getRight(), - view.getBottom()); - mTmpPath.addRect(mTmpRect, Path.Direction.CW); - c.drawPath(mTmpPath, mPaint); - } else if (adapterItem.viewType == VIEW_TYPE_ICON - && mUserCache.getUserInfo(adapterItem.itemInfo.user).isPrivate() - // No decoration for any private space app icon other than those at first row. - && adapterItem.rowAppIndex == 0) { - c.drawPath(getPrivateAppRowPath(parent, view, position, numCol), mPaint); + SectionDecorationInfo info = adapterItem.decorationInfo; + if (info == null) { + continue; + } + SectionDecorationHandler decorationHandler = info.getDecorationHandler(); + if (info.shouldDecorateItemsTogether()) { + SectionDecorationHandler.UnionDecorationHandler unionHandler = + deferredDecorations.getOrDefault( + PRIVATE_APP_SECTION, + new SectionDecorationHandler.UnionDecorationHandler( + decorationHandler, parent.getPaddingLeft(), + parent.getPaddingRight())); + unionHandler.addChild(decorationHandler, view, true /* applyBackground */); + deferredDecorations.put(PRIVATE_APP_SECTION, unionHandler); + } else { + decorationHandler.onFocusDraw(c, view); } } - } - - /** Returns the path to be decorated for Private Space App Row */ - private Path getPrivateAppRowPath(RecyclerView parent, View iconView, int adapterPosition, - int numCol) { - // We always decorate the entire app row here. - // As the iconView just represents the first icon of the row, we get the right margin of - // our decorator using the parent view. - mTmpRect.set(iconView.getLeft(), - iconView.getTop(), - parent.getRight() - parent.getPaddingRight(), - iconView.getBottom()); - // Decorates last app row with rounded bottom corners. - if (adapterPosition + numCol >= mAppsList.getAdapterItems().size()) { - float[] mCornersBot = new float[]{0, 0, 0, 0, mCornerRadius, mCornerRadius, - mCornerRadius, mCornerRadius}; - mTmpPath.addRoundRect(mTmpRect, mCornersBot, Path.Direction.CW); - } else { - // Decorate other rows as a plain rectangle - mTmpPath.addRect(mTmpRect, Path.Direction.CW); + for (SectionDecorationHandler.UnionDecorationHandler decorationHandler + : deferredDecorations.values()) { + decorationHandler.onGroupDecorate(c); } - return mTmpPath; } } diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index 77eb07e2d3..f5c5d9f7d9 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -30,6 +30,7 @@ import android.os.UserManager; import androidx.annotation.VisibleForTesting; +import com.android.launcher3.Flags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.Preconditions; @@ -47,6 +48,7 @@ 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 mPrivateProfileMatcher; private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator; @@ -128,7 +130,6 @@ public class PrivateProfileManager extends UserProfileManager { // Create a new decorator instance if not already available. if (mPrivateAppsSectionDecorator == null) { mPrivateAppsSectionDecorator = new PrivateAppsSectionDecorator( - mAllApps.mActivityContext, mainAdapterHolder.mAppsList); } for (int i = 0; i < mainAdapterHolder.mRecyclerView.getItemDecorationCount(); i++) { @@ -140,6 +141,13 @@ 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) { diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java index 79e0ccef75..4a5fcce47c 100644 --- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java +++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java @@ -93,4 +93,8 @@ public class PrivateSpaceHeaderViewController { transitionImage.setVisibility(View.GONE); } } + + PrivateProfileManager getPrivateProfileManager() { + return mPrivateProfileManager; + } }