Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f01687ab authored by Willie Koomson's avatar Willie Koomson Committed by Android (Google) Code Review
Browse files

Merge "Scroll to show WidgetCell when it is tapped." into main

parents 915d7327 dcc2d82d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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>
+19 −0
Original line number Diff line number Diff line
@@ -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;
+48 −0
Original line number Diff line number Diff line
@@ -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");
+13 −0
Original line number Diff line number Diff line
@@ -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;
            }
        }
    }
}
+57 −0
Original line number Diff line number Diff line
@@ -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;
Loading