From acebd1f69adbb577cd2ad2adc8123875bdce6ed8 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Thu, 6 May 2021 10:02:03 -0500 Subject: [PATCH] [Search][Motion] Introduce header protection in AllApps ScrimView exposes ScrimDrawingController that allow FloatingHeaderView to observe AllApps scroll progress and draw header protection on Scrim. In addition, search box independently adopts header protection background to ensure proper clipping behavior. Test: local preview: https://drive.google.com/file/d/1_E577AAJ0LBg0zrKJQSEJK9GQnrtx7uv/view?usp=sharing&resourcekey=0-MTxjlB3xWyJ3qPvr4qMdig Bug: 184946772 Change-Id: I64c0f4f50d26c475d31542148a15c7c145588d3f --- res/values/dimens.xml | 3 +- src/com/android/launcher3/Launcher.java | 2 +- .../allapps/AllAppsContainerView.java | 71 ++++++++++++++++++- .../allapps/AllAppsRecyclerView.java | 11 ++- .../allapps/AllAppsTransitionController.java | 15 +++- .../android/launcher3/views/ScrimView.java | 34 ++++++++- 6 files changed, 127 insertions(+), 9 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index fe0b11bb3f..0adafaf7fb 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -92,7 +92,8 @@ 700dp 475dp 50dp - 50dp + 48dp + 48dp 2dp 36dp 16dp diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index deb114765c..5896f5a4de 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1192,7 +1192,7 @@ public class Launcher extends StatefulActivity implements Launche // Setup the drag controller (drop targets have to be added in reverse order in priority) mDropTargetBar.setup(mDragController); - mAllAppsController.setupViews(mAppsView); + mAllAppsController.setupViews(mScrimView, mAppsView); } /** diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index b11b63ed69..47236b6994 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; @@ -44,6 +45,7 @@ import android.view.WindowInsets; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import androidx.core.graphics.ColorUtils; import androidx.core.os.BuildCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; @@ -66,6 +68,7 @@ import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Themes; import com.android.launcher3.views.RecyclerViewFastScroller; +import com.android.launcher3.views.ScrimView; import com.android.launcher3.views.SpringRelativeLayout; import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener; @@ -73,13 +76,16 @@ import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePag * The all apps view container. */ public class AllAppsContainerView extends SpringRelativeLayout implements DragSource, - Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener { + Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener, + ScrimView.ScrimDrawingController { private static final float FLING_VELOCITY_MULTIPLIER = 1000f; // Starts the springs after at least 25% of the animation has passed. private static final float FLING_ANIMATION_THRESHOLD = 0.25f; + private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + protected final BaseDraggingActivity mLauncher; protected final AdapterHolder[] mAH; private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle()); @@ -93,7 +99,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo private View mSearchContainer; private AllAppsPagedView mViewPager; - private FloatingHeaderView mHeader; + protected FloatingHeaderView mHeader; private WorkModeSwitch mWorkModeSwitch; @@ -107,7 +113,14 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo private Rect mInsets = new Rect(); - SearchAdapterProvider mSearchAdapterProvider; + private SearchAdapterProvider mSearchAdapterProvider; + private final int mHeaderTopPadding; + private final int mScrimColor; + private final int mHeaderProtectionColor; + private final float mHeaderThreshold; + private ScrimView mScrimView; + private int mHeaderColor; + public AllAppsContainerView(Context context) { this(context, null); @@ -121,8 +134,19 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo super(context, attrs, defStyleAttr); mLauncher = BaseDraggingActivity.fromContext(context); + + mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor); + mHeaderThreshold = getResources().getDimensionPixelSize( + R.dimen.dynamic_grid_cell_border_spacing); + mHeaderTopPadding = context.getResources() + .getDimensionPixelSize(R.dimen.all_apps_header_top_padding); + int accentColor = Themes.getColorAccent(getContext()); + mHeaderProtectionColor = ColorUtils.blendARGB(mScrimColor, accentColor, .3f); + mLauncher.addOnDeviceProfileChangeListener(this); + + mSearchAdapterProvider = mLauncher.createSearchAdapterProvider(this); mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); @@ -300,6 +324,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo } // Reset the search bar and base recycler view after transitioning home mSearchUiManager.resetSearch(); + updateHeaderScroll(0); } @Override @@ -625,6 +650,26 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo outRect.offset(0, (int) getTranslationY()); } + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + invalidateHeader(); + } + + public void setScrimView(ScrimView scrimView) { + mScrimView = scrimView; + } + + @Override + public void drawOnScrim(Canvas canvas) { + mHeaderPaint.setColor(mHeaderColor); + mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor))); + if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) { + canvas.drawRect(0, 0, getWidth(), mHeaderTopPadding + getTranslationY(), + mHeaderPaint); + } + } + public class AdapterHolder { public static final int MAIN = 0; public static final int WORK = 1; @@ -725,4 +770,24 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mOverlay; } } + + + protected void updateHeaderScroll(int scrolledOffset) { + float prog = Math.max(0, Math.min(1, (float) scrolledOffset / mHeaderThreshold)); + int headerColor = ColorUtils.setAlphaComponent(mHeaderProtectionColor, (int) (prog * 255)); + if (headerColor != mHeaderColor) { + mHeaderColor = headerColor; + getSearchView().setBackgroundColor(mHeaderColor); + invalidateHeader(); + } + } + + /** + * redraws header protection + */ + public void invalidateHeader() { + if (mScrimView != null) { + mScrimView.invalidate(); + } + } } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index b1c764c1d1..197d74c401 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -38,6 +38,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.logging.StatsLogManager; @@ -52,6 +53,7 @@ import java.util.List; public class AllAppsRecyclerView extends BaseRecyclerView { private static final String TAG = "AllAppsContainerView"; private static final boolean DEBUG = false; + private final Launcher mLauncher; private AlphabeticalAppsList mApps; private final int mNumAppsPerRow; @@ -87,6 +89,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { R.dimen.all_apps_empty_search_bg_top_offset); mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns; mFastScrollHelper = new AllAppsFastScrollHelper(this); + mLauncher = Launcher.getLauncher(context); } /** @@ -199,6 +202,12 @@ public class AllAppsRecyclerView extends BaseRecyclerView { } } + @Override + public void onScrolled(int dx, int dy) { + super.onScrolled(dx, dy); + mLauncher.getAppsView().updateHeaderScroll(getCurrentScrollY()); + } + @Override public boolean onInterceptTouchEvent(MotionEvent e) { boolean result = super.onInterceptTouchEvent(e); @@ -410,7 +419,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { /** * Returns the available scroll height: - * AvailableScrollHeight = Total height of the all items - last page height + * AvailableScrollHeight = Total height of the all items - last page height */ @Override protected int getAvailableScrollHeight() { diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index c4c7891d58..90f2d7cde5 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -45,6 +45,7 @@ import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.views.ScrimView; /** * Handles AllApps view transition. @@ -88,6 +89,7 @@ public class AllAppsTransitionController private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent private float mScrollRangeDelta = 0; + private ScrimView mScrimView; public AllAppsTransitionController(Launcher l) { mLauncher = l; @@ -158,7 +160,7 @@ public class AllAppsTransitionController Interpolator interpolator = toState.equals(ALL_APPS) ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) : - (config.userControlled ? DEACCEL_2 : DEACCEL); + (config.userControlled ? DEACCEL_2 : DEACCEL); Animator anim = createSpringAnimation(mProgress, targetProgress); anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); @@ -181,19 +183,28 @@ public class AllAppsTransitionController Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR); setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade); + + boolean shouldProtectHeader = + ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS; + mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null); } public AnimatorListenerAdapter getProgressAnimatorListener() { return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd); } - public void setupViews(AllAppsContainerView appsView) { + /** + * see Launcher#setupViews + */ + public void setupViews(ScrimView scrimView, AllAppsContainerView appsView) { + mScrimView = scrimView; mAppsView = appsView; if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) { mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); } + mAppsView.setScrimView(scrimView); } /** diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 5387a3ed17..0ba94ab240 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -18,6 +18,7 @@ package com.android.launcher3.views; import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; @@ -37,13 +38,16 @@ public class ScrimView extends View implements Insettable { private SystemUiController mSystemUiController; + private ScrimDrawingController mDrawingController; + public ScrimView(Context context, AttributeSet attrs) { super(context, attrs); setFocusable(false); } @Override - public void setInsets(Rect insets) { } + public void setInsets(Rect insets) { + } @Override public boolean hasOverlappingRendering() { @@ -62,6 +66,14 @@ public class ScrimView extends View implements Insettable { super.setBackgroundColor(color); } + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mDrawingController != null) { + mDrawingController.drawOnScrim(canvas); + } + } + @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); @@ -96,4 +108,24 @@ public class ScrimView extends View implements Insettable { return ColorUtils.calculateLuminance( ((ColorDrawable) getBackground()).getColor()) < 0.5f; } + + /** + * Sets drawing controller. Invalidates ScrimView if drawerController has changed. + */ + public void setDrawingController(ScrimDrawingController drawingController) { + if (mDrawingController != drawingController) { + mDrawingController = drawingController; + invalidate(); + } + } + + /** + * A Utility interface allowing for other surfaces to draw on ScrimView + */ + public interface ScrimDrawingController { + /** + * Called inside ScrimView#OnDraw + */ + void drawOnScrim(Canvas canvas); + } }