Merge "[Search][Motion] Introduce header protection in AllApps" into sc-dev am: 054f45486e
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/14454752
Change-Id: I745171e45537ff5da58dd2a81e016475f26760bf
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fe0b11b..0adafaf 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -92,7 +92,8 @@
<dimen name="all_apps_background_canvas_width">700dp</dimen>
<dimen name="all_apps_background_canvas_height">475dp</dimen>
<dimen name="all_apps_header_pill_height">50dp</dimen>
- <dimen name="all_apps_header_pill_corner_radius">50dp</dimen>
+ <dimen name="all_apps_header_pill_corner_radius">48dp</dimen>
+ <dimen name="all_apps_header_tab_height">48dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
<dimen name="all_apps_header_top_padding">36dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index deb1147..5896f5a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1192,7 +1192,7 @@
// 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 b11b63e..47236b6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -26,6 +26,7 @@
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 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.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 @@
* 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 @@
private View mSearchContainer;
private AllAppsPagedView mViewPager;
- private FloatingHeaderView mHeader;
+ protected FloatingHeaderView mHeader;
private WorkModeSwitch mWorkModeSwitch;
@@ -107,7 +113,14 @@
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 @@
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 @@
}
// Reset the search bar and base recycler view after transitioning home
mSearchUiManager.resetSearch();
+ updateHeaderScroll(0);
}
@Override
@@ -625,6 +650,26 @@
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 @@
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 b1c764c..197d74c 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -38,6 +38,7 @@
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 @@
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 @@
R.dimen.all_apps_empty_search_bg_top_offset);
mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
mFastScrollHelper = new AllAppsFastScrollHelper(this);
+ mLauncher = Launcher.getLauncher(context);
}
/**
@@ -200,6 +203,12 @@
}
@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);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
@@ -410,7 +419,7 @@
/**
* 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 c4c7891..90f2d7c 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.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 @@
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 @@
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 @@
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 5387a3e..0ba94ab 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,6 +18,7 @@
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 @@
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() {
@@ -63,6 +67,14 @@
}
@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);
updateSysUiColors();
@@ -96,4 +108,24 @@
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);
+ }
}