diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml index a5f72ef30a..b76eef7608 100644 --- a/res/layout/widgets_bottom_sheet_content.xml +++ b/res/layout/widgets_bottom_sheet_content.xml @@ -18,7 +18,6 @@ android:id="@+id/widgets_bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/bg_rounded_corner_bottom_sheet" android:paddingTop="@dimen/bottom_sheet_handle_margin" android:orientation="vertical"> diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml index 3dbe6f5bf8..1c0037d698 100644 --- a/res/layout/widgets_full_sheet_large_screen.xml +++ b/res/layout/widgets_full_sheet_large_screen.xml @@ -24,7 +24,6 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/bg_widgets_full_sheet" android:focusable="true" android:importantForAccessibility="no"> diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml index 2819b990a9..b02e3e3c3f 100644 --- a/res/layout/widgets_full_sheet_paged_view.xml +++ b/res/layout/widgets_full_sheet_paged_view.xml @@ -47,6 +47,7 @@ android:layout_height="wrap_content" android:layout_below="@id/collapse_handle" android:paddingBottom="0dp" + android:clipToOutline="true" android:orientation="vertical"> public void getOutline(View view, Outline outline) { @Px final int bottomOffsetPx = (int) (ActivityAllAppsContainerView.this.getMeasuredHeight() - * SWIPE_ALL_APPS_TO_HOME_MIN_SCALE); + * PREDICTIVE_BACK_MIN_SCALE); outline.setRect( 0, 0, diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 866932ab16..df383bf9f6 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -21,7 +21,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import androidx.annotation.Px; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; @@ -145,19 +144,6 @@ public class AllAppsGridAdapter extends cic.isSelected())); } - /** - * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra - * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap - * created during predictive back's scale animation from all apps to home. - */ - @Override - protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { - super.calculateExtraLayoutSpace(state, extraLayoutSpace); - @Px int extraSpacePx = (int) (getHeight() - * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2); - extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx); - } - /** * Returns the number of rows before {@param adapterPosition}, including this position * which should not be counted towards the collection info. diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index b6187244f2..92c017ce88 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -61,6 +61,7 @@ import com.android.launcher3.touch.AllAppsSwipeController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.ScrollableLayoutManager; import com.android.launcher3.util.Themes; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.ScrimView; @@ -79,8 +80,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { // This constant should match the second derivative of the animator interpolator. public static final float INTERP_COEFF = 1.7f; - public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; - private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; + public static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f; private static final float SWIPE_DRAG_COMMIT_THRESHOLD = @@ -280,8 +280,9 @@ public class AllAppsTransitionController float deceleratedProgress = Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress); - float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE - + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress); + float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE + + (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE) + * (1 - deceleratedProgress); mAllAppScale.updateValue(scaleProgress); } diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java index 9bc4ddce38..cb6ecaa664 100644 --- a/src/com/android/launcher3/util/ScrollableLayoutManager.java +++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java @@ -20,6 +20,7 @@ import android.util.SparseIntArray; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Px; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; @@ -31,6 +32,10 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; */ public class ScrollableLayoutManager extends GridLayoutManager { + public static final float PREDICTIVE_BACK_MIN_SCALE = 0.9f; + private static final float EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT = + (1 - PREDICTIVE_BACK_MIN_SCALE) / 2; + // keyed on item type protected final SparseIntArray mCachedSizes = new SparseIntArray(); @@ -111,6 +116,13 @@ public class ScrollableLayoutManager extends GridLayoutManager { return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount()); } + @Override + protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { + super.calculateExtraLayoutSpace(state, extraLayoutSpace); + @Px int extraSpacePx = (int) (getHeight() * EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT); + extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx); + } + /** * Returns the sum of the height, in pixels, of this list adapter's items from index * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount, diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index f73347a7b2..e2f1c04ced 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -17,15 +17,20 @@ package com.android.launcher3.views; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS; +import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Property; import android.view.MotionEvent; @@ -33,10 +38,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import androidx.annotation.FloatRange; import androidx.annotation.Nullable; +import androidx.annotation.Px; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector; @@ -85,6 +93,10 @@ public abstract class AbstractSlideInView protected @Nullable OnCloseListener mOnCloseBeginListener; protected List mOnCloseListeners = new ArrayList<>(); + private final AnimatedFloat mSlidInViewScale = new AnimatedFloat(this::onScaleProgressChanged); + private boolean mIsBackProgressing; + @Nullable private Drawable mContentBackground; + public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mActivityContext = ActivityContext.lookupContext(context); @@ -105,6 +117,10 @@ public abstract class AbstractSlideInView mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null; } + protected void setContentBackground(Drawable drawable) { + mContentBackground = drawable; + } + protected void attachToContainer() { if (mColorScrim != null) { getPopupContainer().addView(mColorScrim); @@ -132,6 +148,7 @@ public abstract class AbstractSlideInView if (mColorScrim != null) { mColorScrim.setAlpha(1 - mTranslationShift); } + invalidate(); } @Override @@ -161,6 +178,68 @@ public abstract class AbstractSlideInView return true; } + @Override + public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) { + super.onBackProgressed(progress); + float deceleratedProgress = + Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress); + mIsBackProgressing = progress > 0f; + mSlidInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE + + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress)); + } + + private void onScaleProgressChanged() { + float scaleProgress = mSlidInViewScale.value; + SCALE_PROPERTY.set(this, scaleProgress); + setClipChildren(!mIsBackProgressing); + mContent.setClipChildren(!mIsBackProgressing); + invalidate(); + } + + @Override + public void onBackInvoked() { + super.onBackInvoked(); + animateSlideInViewToNoScale(); + } + + @Override + public void onBackCancelled() { + super.onBackCancelled(); + animateSlideInViewToNoScale(); + } + + protected void animateSlideInViewToNoScale() { + mSlidInViewScale.animateToValue(1f) + .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS) + .start(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + drawScaledBackground(canvas); + super.dispatchDraw(canvas); + } + + /** Draw scaled background during predictive back animation. */ + protected void drawScaledBackground(Canvas canvas) { + if (mContentBackground == null) { + return; + } + mContentBackground.setBounds( + mContent.getLeft(), + mContent.getTop() + (int) mContent.getTranslationY(), + mContent.getRight(), + mContent.getBottom() + (mIsBackProgressing ? getBottomOffsetPx() : 0)); + mContentBackground.draw(canvas); + } + + /** Return extra space revealed during predictive back animation. */ + @Px + protected int getBottomOffsetPx() { + return (int) (getMeasuredHeight() + * (1 - PREDICTIVE_BACK_MIN_SCALE) / 2); + } + /** * Returns {@code true} if the touch event is over the visible area of the bottom sheet. * diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index bf521cc7eb..4099302c07 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -113,6 +113,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { } mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize( R.dimen.widget_cell_horizontal_padding); + setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet)); } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 2ce400ed68..66e3a435e9 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -17,9 +17,7 @@ package com.android.launcher3.widget.picker; import static android.view.View.MeasureSpec.makeMeasureSpec; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE; import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED; import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; @@ -31,6 +29,7 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Outline; import android.graphics.Rect; import android.os.Process; import android.os.UserHandle; @@ -42,6 +41,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; import android.view.WindowInsets; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -62,7 +62,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.model.UserManagerState; @@ -170,6 +169,18 @@ public class WidgetsFullSheet extends BaseWidgetSheet } }; + private final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRect( + 0, + 0, + view.getMeasuredWidth(), + view.getMeasuredHeight() + getBottomOffsetPx() + ); + } + }; + private final int mTabsHeight; private final int mWidgetSheetContentHorizontalPadding; @@ -195,6 +206,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet private int mOrientation; private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView; + private RecyclerViewFastScroller mFastScroller; + public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile(); @@ -213,6 +226,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet mUserManagerState.init(UserCache.INSTANCE.get(context), context.getSystemService(UserManager.class)); + setContentBackground(getContext().getDrawable(R.drawable.bg_widgets_full_sheet)); } public WidgetsFullSheet(Context context, AttributeSet attrs) { @@ -224,6 +238,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet super.onFinishInflate(); mContent = findViewById(R.id.container); + mContent.setOutlineProvider(mViewOutlineProvider); + mContent.setClipToOutline(true); + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view : R.layout.widgets_full_sheet_recyclerview; @@ -233,14 +250,17 @@ public class WidgetsFullSheet extends BaseWidgetSheet } layoutInflater.inflate(contentLayoutRes, mContent, true); - RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller); + mFastScroller = findViewById(R.id.fast_scroller); if (mIsTwoPane) { - fastScroller.setVisibility(GONE); + mFastScroller.setVisibility(GONE); } mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view)); mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view)); if (mHasWorkProfile) { mViewPager = findViewById(R.id.widgets_view_pager); + mViewPager.setOutlineProvider(mViewOutlineProvider); + mViewPager.setClipToOutline(true); + mViewPager.setClipChildren(false); mViewPager.initParentViews(this); mViewPager.getPageIndicator().setOnActivePageChangedListener(this); mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY); @@ -349,11 +369,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) { - float deceleratedProgress = - Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress); - float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE - + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress); - SCALE_PROPERTY.set(this, scaleProgress); + super.onBackProgressed(progress); + mFastScroller.setVisibility(progress > 0 ? View.INVISIBLE : View.VISIBLE); } private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) { @@ -859,6 +876,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet public void onBackInvoked() { if (mIsInSearchMode) { mSearchBar.reset(); + animateSlideInViewToNoScale(); } else { super.onBackInvoked(); } @@ -1003,6 +1021,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet void setup(WidgetsRecyclerView recyclerView) { mWidgetsRecyclerView = recyclerView; + mWidgetsRecyclerView.setOutlineProvider(mViewOutlineProvider); + mWidgetsRecyclerView.setClipToOutline(true); + mWidgetsRecyclerView.setClipChildren(false); mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter); mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator); mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);