From edfec2fc0b299bb5a0882157022a729461e48e4f Mon Sep 17 00:00:00 2001 From: Steven Ng Date: Wed, 5 May 2021 17:41:10 +0100 Subject: [PATCH] Fix scrolling issue in the full widgets picker Before this change, the widgets recommendation table handles all the touch events. The table doesn't distinguish a touch event from a scroll event. This change forwards the motion event to the active recycler view. If the recycler view doesn't handle the event, it will then forward the event back to the view hierarchy. Test: Open the full widgets picker, then start scrolling on the recommended widgets UI. The full widgets picker scrolls perfectly. Drag-n-drop a recommended widget Bug: 186567644 Change-Id: I89d473179796c516d2394332dd75f8e9a1d8b388 --- ..._full_sheet_search_and_recommendations.xml | 4 +- ...rchAndRecommendationsScrollController.java | 45 ++++++++++++++ .../picker/SearchAndRecommendationsView.java | 62 +++++++++++++++++++ .../widget/picker/WidgetsFullSheet.java | 5 +- 4 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml index bfce01d148..ce7a682376 100644 --- a/res/layout/widgets_full_sheet_search_and_recommendations.xml +++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - + diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java index 7f840776b2..d09cb8a52d 100644 --- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java +++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.widget.picker; +import android.graphics.Point; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; import android.widget.RelativeLayout; @@ -33,9 +35,11 @@ final class SearchAndRecommendationsScrollController implements RecyclerViewFastScroller.OnFastScrollChangeListener { private final boolean mHasWorkProfile; private final SearchAndRecommendationViewHolder mViewHolder; + private final View mSearchAndRecommendationViewParent; private final WidgetsRecyclerView mPrimaryRecyclerView; private final WidgetsRecyclerView mSearchRecyclerView; private final int mTabsHeight; + private final Point mTempOffset = new Point(); // The following are only non null if mHasWorkProfile is true. @Nullable private final WidgetsRecyclerView mWorkRecyclerView; @@ -62,6 +66,8 @@ final class SearchAndRecommendationsScrollController implements */ private int mCollapsibleHeightForTabs = 0; + private boolean mShouldForwardToRecyclerView = false; + SearchAndRecommendationsScrollController( boolean hasWorkProfile, int tabsHeight, @@ -73,6 +79,8 @@ final class SearchAndRecommendationsScrollController implements @Nullable PersonalWorkPagedView primaryWorkViewPager) { mHasWorkProfile = hasWorkProfile; mViewHolder = viewHolder; + mViewHolder.mContainer.setSearchAndRecommendationScrollController(this); + mSearchAndRecommendationViewParent = (View) mViewHolder.mContainer.getParent(); mPrimaryRecyclerView = primaryRecyclerView; mCurrentRecyclerView = mPrimaryRecyclerView; mWorkRecyclerView = workRecyclerView; @@ -245,6 +253,43 @@ final class SearchAndRecommendationsScrollController implements } } + /** + * Returns {@code true} if a touch event should be intercepted by this controller. + */ + public boolean onInterceptTouchEvent(MotionEvent event) { + calculateMotionEventOffset(mTempOffset); + event.offsetLocation(mTempOffset.x, mTempOffset.y); + try { + mShouldForwardToRecyclerView = mCurrentRecyclerView.onInterceptTouchEvent(event); + return mShouldForwardToRecyclerView; + } finally { + event.offsetLocation(-mTempOffset.x, -mTempOffset.y); + } + } + + /** + * Returns {@code true} if this controller has intercepted and consumed a touch event. + */ + public boolean onTouchEvent(MotionEvent event) { + if (mShouldForwardToRecyclerView) { + calculateMotionEventOffset(mTempOffset); + event.offsetLocation(mTempOffset.x, mTempOffset.y); + try { + return mCurrentRecyclerView.onTouchEvent(event); + } finally { + event.offsetLocation(-mTempOffset.x, -mTempOffset.y); + } + } + return false; + } + + private void calculateMotionEventOffset(Point p) { + p.x = mViewHolder.mContainer.getLeft() - mCurrentRecyclerView.getLeft() + - mSearchAndRecommendationViewParent.getLeft(); + p.y = mViewHolder.mContainer.getTop() - mCurrentRecyclerView.getTop() + - mSearchAndRecommendationViewParent.getTop(); + } + /** private the height, in pixel, + the vertical margins of a given view. */ private static int measureHeightWithVerticalMargins(View view) { if (view.getVisibility() != View.VISIBLE) { diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java new file mode 100644 index 0000000000..0d7d2b5e78 --- /dev/null +++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.picker; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +/** + * A {@link LinearLayout} container for holding search and widgets recommendation. + * + *

This class intercepts touch events and dispatch them to the right view. + */ +public class SearchAndRecommendationsView extends LinearLayout { + private SearchAndRecommendationsScrollController mController; + + public SearchAndRecommendationsView(Context context) { + this(context, /* attrs= */ null); + } + + public SearchAndRecommendationsView(Context context, AttributeSet attrs) { + this(context, attrs, /* defStyleAttr= */ 0); + } + + public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, /* defStyleRes= */ 0); + } + + public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void setSearchAndRecommendationScrollController( + SearchAndRecommendationsScrollController controller) { + mController = controller; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return mController.onTouchEvent(event) || super.onTouchEvent(event); + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index b4d48562be..b1c5ffcbab 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -709,13 +709,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet } final class SearchAndRecommendationViewHolder { - final ViewGroup mContainer; + final SearchAndRecommendationsView mContainer; final View mCollapseHandle; final WidgetsSearchBar mSearchBar; final TextView mHeaderTitle; final WidgetsRecommendationTableLayout mRecommendedWidgetsTable; - SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) { + SearchAndRecommendationViewHolder( + SearchAndRecommendationsView searchAndRecommendationContainer) { mContainer = searchAndRecommendationContainer; mCollapseHandle = mContainer.findViewById(R.id.collapse_handle); mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);