mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-17 17:48:20 +00:00
Scroll to show WidgetCell when it is tapped.
Scroll to show WidgetCell when it is tapped in a widget sheet. Otherwise, the add button may show/hide without the user seeing it if the bottom is clipped. Bug: 329861721 Test: manual- tap WidgetCell when top or bottom is scrolled out of view Flag: ACONFIG com.android.launcher3.enable_widget_tap_to_add TEAMFOOD Change-Id: Ie21730c193e845cb1c1fa447b7c0a7e719984a8f
This commit is contained in:
@@ -183,6 +183,7 @@
|
||||
<dimen name="widget_cell_add_button_height">48dp</dimen>
|
||||
<dimen name="widget_cell_add_button_start_padding">8dp</dimen>
|
||||
<dimen name="widget_cell_add_button_end_padding">16dp</dimen>
|
||||
<dimen name="widget_cell_add_button_scroll_padding">24dp</dimen>
|
||||
|
||||
<dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
|
||||
<dimen name="widget_tabs_horizontal_padding">16dp</dimen>
|
||||
|
||||
@@ -36,6 +36,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link LinearLayout} container which allows scrolling parts of its content based on the
|
||||
* scroll of a different view. Views which are marked as sticky are not scrolled, giving the
|
||||
@@ -242,6 +245,22 @@ public class StickyHeaderLayout extends LinearLayout implements
|
||||
return p instanceof MyLayoutParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all the children that have the sticky layout param set.
|
||||
*/
|
||||
public List<View> getStickyChildren() {
|
||||
List<View> stickyChildren = new ArrayList<>();
|
||||
int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
View v = getChildAt(i);
|
||||
MyLayoutParams lp = (MyLayoutParams) v.getLayoutParams();
|
||||
if (lp.sticky) {
|
||||
stickyChildren.add(v);
|
||||
}
|
||||
}
|
||||
return stickyChildren;
|
||||
}
|
||||
|
||||
private static class MyLayoutParams extends LayoutParams {
|
||||
|
||||
public final boolean sticky;
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
@@ -141,6 +142,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
|
||||
}
|
||||
|
||||
if (enableWidgetTapToAdd()) {
|
||||
scrollToWidgetCell(wc);
|
||||
if (mWidgetCellWithAddButton != null) {
|
||||
// If there is a add button currently showing, hide it.
|
||||
mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
|
||||
@@ -187,6 +189,52 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
|
||||
handleClose(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to show the widget cell. If both the bottom and top of the cell are clipped, this will
|
||||
* prioritize showing the bottom of the cell (where the add button is).
|
||||
*/
|
||||
private void scrollToWidgetCell(@NonNull WidgetCell wc) {
|
||||
final int headerTopClip = getHeaderTopClip(wc);
|
||||
final Rect visibleRect = new Rect();
|
||||
final boolean isPartiallyVisible = wc.getLocalVisibleRect(visibleRect);
|
||||
int scrollByY = 0;
|
||||
if (isPartiallyVisible) {
|
||||
final int scrollPadding = getResources()
|
||||
.getDimensionPixelSize(R.dimen.widget_cell_add_button_scroll_padding);
|
||||
final int topClip = visibleRect.top + headerTopClip;
|
||||
final int bottomClip = wc.getHeight() - visibleRect.bottom;
|
||||
if (bottomClip != 0) {
|
||||
scrollByY = bottomClip + scrollPadding;
|
||||
} else if (topClip != 0) {
|
||||
scrollByY = -topClip - scrollPadding;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPartiallyVisible && scrollByY == 0) {
|
||||
// Widget is fully visible.
|
||||
return;
|
||||
} else if (!isPartiallyVisible) {
|
||||
Log.e("BaseWidgetSheet", "click on invisible WidgetCell should not be possible");
|
||||
return;
|
||||
}
|
||||
|
||||
scrollCellContainerByY(wc, scrollByY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the nearest scrollable container of the given WidgetCell, and scroll by the given
|
||||
* amount.
|
||||
*/
|
||||
protected abstract void scrollCellContainerByY(WidgetCell wc, int scrollByY);
|
||||
|
||||
|
||||
/**
|
||||
* Return the top clip of any sticky headers over the given cell.
|
||||
*/
|
||||
protected int getHeaderTopClip(@NonNull WidgetCell cell) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TableLayout;
|
||||
@@ -282,4 +283,16 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
|
||||
target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollCellContainerByY(WidgetCell wc, int scrollByY) {
|
||||
for (ViewParent parent = wc.getParent(); parent != null; parent = parent.getParent()) {
|
||||
if (parent instanceof ScrollView scrollView) {
|
||||
scrollView.smoothScrollBy(0, scrollByY);
|
||||
return;
|
||||
} else if (parent == this) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.animation.AnimationUtils;
|
||||
@@ -46,6 +47,7 @@ import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
@@ -69,6 +71,7 @@ import com.android.launcher3.views.SpringRelativeLayout;
|
||||
import com.android.launcher3.views.StickyHeaderLayout;
|
||||
import com.android.launcher3.views.WidgetsEduView;
|
||||
import com.android.launcher3.widget.BaseWidgetSheet;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.picker.search.SearchModeListener;
|
||||
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
|
||||
@@ -991,6 +994,60 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHeaderTopClip(@NonNull WidgetCell cell) {
|
||||
StickyHeaderLayout header = findViewById(R.id.search_and_recommendations_container);
|
||||
if (header == null) {
|
||||
return 0;
|
||||
}
|
||||
Rect cellRect = new Rect();
|
||||
boolean cellIsPartiallyVisible = cell.getGlobalVisibleRect(cellRect);
|
||||
if (cellIsPartiallyVisible) {
|
||||
Rect occludingRect = new Rect();
|
||||
for (View headerChild : header.getStickyChildren()) {
|
||||
Rect childRect = new Rect();
|
||||
boolean childVisible = headerChild.getGlobalVisibleRect(childRect);
|
||||
if (childVisible && childRect.intersect(cellRect)) {
|
||||
occludingRect.union(childRect);
|
||||
}
|
||||
}
|
||||
if (!occludingRect.isEmpty() && cellRect.top < occludingRect.bottom) {
|
||||
return occludingRect.bottom - cellRect.top;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollCellContainerByY(WidgetCell wc, int scrollByY) {
|
||||
for (ViewParent parent = wc.getParent(); parent != null; parent = parent.getParent()) {
|
||||
if (parent instanceof WidgetsRecyclerView recyclerView) {
|
||||
// Scrollable container for main widget list.
|
||||
recyclerView.smoothScrollBy(0, scrollByY);
|
||||
return;
|
||||
} else if (parent instanceof StickyHeaderLayout header) {
|
||||
// Scrollable container for recommendations. We still scroll on the recycler (even
|
||||
// though the recommendations are not in the recycler view) because the
|
||||
// StickyHeaderLayout scroll is connected to the currently visible recycler view.
|
||||
WidgetsRecyclerView recyclerView = findVisibleRecyclerView();
|
||||
if (recyclerView != null) {
|
||||
recyclerView.smoothScrollBy(0, scrollByY);
|
||||
}
|
||||
return;
|
||||
} else if (parent == this) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private WidgetsRecyclerView findVisibleRecyclerView() {
|
||||
if (mViewPager != null) {
|
||||
return (WidgetsRecyclerView) mViewPager.getPageAt(mViewPager.getCurrentPage());
|
||||
}
|
||||
return findViewById(R.id.primary_widgets_list_view);
|
||||
}
|
||||
|
||||
/** A holder class for holding adapters & their corresponding recycler view. */
|
||||
final class AdapterHolder {
|
||||
static final int PRIMARY = 0;
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.recyclerview.ViewHolderBinder;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||
@@ -423,6 +424,23 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHeaderTopClip(@NonNull WidgetCell cell) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollCellContainerByY(WidgetCell wc, int scrollByY) {
|
||||
for (ViewParent parent = wc.getParent(); parent != null; parent = parent.getParent()) {
|
||||
if (parent instanceof ScrollView scrollView) {
|
||||
scrollView.smoothScrollBy(0, scrollByY);
|
||||
return;
|
||||
} else if (parent == this) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a listener for when the selected header gets changed in the left pane.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user