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

Commit 5b370735 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Make common base class to update container bounds and to handle scroll...

Merge "Make common base class to update container bounds and to handle scroll logic." into ub-launcher3-burnaby
parents ea449fa3 9480415d
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -50,8 +50,6 @@
    <dimen name="apps_container_width">0dp</dimen>
    <dimen name="apps_container_height">0dp</dimen>
    <dimen name="apps_container_inset">8dp</dimen>
    <!-- Note: This needs to match the fixed insets for the search box -->
    <dimen name="apps_container_fixed_bounds_inset">8dp</dimen>
    <dimen name="apps_grid_view_start_margin">52dp</dimen>
    <dimen name="apps_grid_section_y_offset">8dp</dimen>
    <dimen name="apps_view_row_height">64dp</dimen>
@@ -62,6 +60,9 @@
    <dimen name="apps_view_fast_scroll_popup_size">64dp</dimen>
    <dimen name="apps_view_fast_scroll_text_size">40dp</dimen>

    <!-- Note: This needs to match the fixed insets for the search box. -->
    <dimen name="container_fixed_bounds_inset">8dp</dimen>

<!-- AllApps/Customize/AppsCustomize -->
    <dimen name="app_icon_size">48dp</dimen>
    <dimen name="apps_customize_horizontal_padding">0dp</dimen>
+2 −38
Original line number Diff line number Diff line
@@ -30,23 +30,15 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.android.launcher3.util.Thunk;

import java.util.List;

/**
 * A RecyclerView with custom fastscroll support.  This is the main container for the all apps
 * icons.
 */
public class AppsContainerRecyclerView extends RecyclerView
        implements RecyclerView.OnItemTouchListener {
public class AppsContainerRecyclerView extends BaseContainerRecyclerView {

    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
    private static final int SCROLL_DELTA_THRESHOLD = 4;

    /** Keeps the last known scrolling delta/velocity along y-axis. */
    @Thunk int mDy = 0;
    private float mDeltaThreshold;

    private AlphabeticalAppsList mApps;
    private int mNumAppsPerRow;
@@ -66,7 +58,6 @@ public class AppsContainerRecyclerView extends RecyclerView
    private int mScrollbarWidth;
    private int mScrollbarMinHeight;
    private int mScrollbarInset;
    private RecyclerView.OnScrollListener mScrollListenerProxy;

    public AppsContainerRecyclerView(Context context) {
        this(context, null);
@@ -100,21 +91,6 @@ public class AppsContainerRecyclerView extends RecyclerView
        mScrollbarInset =
                res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset);
        setFastScrollerAlpha(getFastScrollerAlpha());
        mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;

        ScrollListener listener = new ScrollListener();
        setOnScrollListener(listener);
    }

    private class ScrollListener extends RecyclerView.OnScrollListener {
        public ScrollListener() {
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            mDy = dy;
            mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
        }
    }

    /**
@@ -131,13 +107,6 @@ public class AppsContainerRecyclerView extends RecyclerView
        mNumAppsPerRow = rowSize;
    }

    /**
     * Sets an additional scroll listener, not necessary in master support lib.
     */
    public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
        mScrollListenerProxy = listener;
    }

    /**
     * Sets the fast scroller alpha.
     */
@@ -187,10 +156,6 @@ public class AppsContainerRecyclerView extends RecyclerView
        handleTouchEvent(ev);
    }

    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
    }

    /**
     * Handles the touch event and determines whether to show the fast scroller (or updates it if
     * it is already showing).
@@ -206,8 +171,7 @@ public class AppsContainerRecyclerView extends RecyclerView
                // Keep track of the down positions
                mDownX = mLastX = x;
                mDownY = mLastY = y;
                if ((Math.abs(mDy) < mDeltaThreshold &&
                        getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
                if (shouldStopScroll(ev)) {
                    stopScroll();
                }
                break;
+71 −102
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.view.ViewConfiguration;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.util.Thunk;
@@ -40,10 +39,10 @@ import java.util.List;


/**
 * The all apps list view container.
 * The all apps view container.
 */
public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher,
        TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
        TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
        View.OnClickListener, View.OnLongClickListener {

    public static final boolean GRID_MERGE_SECTIONS = true;
@@ -73,13 +72,9 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
    private int mNumAppsPerRow;
    private Point mLastTouchDownPos = new Point(-1, -1);
    private Point mLastTouchPos = new Point();
    private Rect mInsets = new Rect();
    private Rect mFixedBounds = new Rect();
    private int mContentMarginStart;
    // Normal container insets
    private int mContainerInset;
    // Fixed bounds container insets
    private int mFixedBoundsContainerInset;
    // RecyclerView scroll position
    @Thunk int mRecyclerViewScrollY;

@@ -99,8 +94,6 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett

        mContainerInset = context.getResources().getDimensionPixelSize(
                R.dimen.apps_container_inset);
        mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
                R.dimen.apps_container_fixed_bounds_inset);
        mLauncher = (Launcher) context;
        mNumAppsPerRow = grid.appsViewNumCols;
        mApps = new AlphabeticalAppsList(context, mNumAppsPerRow);
@@ -146,8 +139,8 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
     */
    public void hideHeaderBar() {
        mHeaderView.setVisibility(View.GONE);
        updateBackgrounds();
        updatePaddings();
        onUpdateBackgrounds();
        onUpdatePaddings();
    }

    /**
@@ -225,46 +218,81 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
        if (mItemDecoration != null) {
            mAppsRecyclerView.addItemDecoration(mItemDecoration);
        }
        updateBackgrounds();
        updatePaddings();
        onUpdateBackgrounds();
        onUpdatePaddings();
    }

    @Override
    public void setInsets(Rect insets) {
        mInsets.set(insets);
        updatePaddings();
    }

    /**
     * Sets the fixed bounds for this Apps view.
     */
    public void setFixedBounds(Context context, Rect fixedBounds) {
        if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) {
    protected void onFixedBoundsUpdated() {
        // Update the number of items in the grid
        LauncherAppState app = LauncherAppState.getInstance();
        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
            if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) {
        if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
            mNumAppsPerRow = grid.appsViewNumCols;
            mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
            mAdapter.setNumAppsPerRow(mNumAppsPerRow);
            mApps.setNumAppsPerRow(mNumAppsPerRow);
        }
    }

    /**
     * Update the padding of the Apps view and children.  To ensure that the RecyclerView has the
     * full width to handle touches right to the edge of the screen, we only apply the top and
     * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView
     * itself.  In particular, the left/right padding is applied to the background of the view,
     * and then additionally inset by the start margin.
     */
    @Override
    protected void onUpdatePaddings() {
        boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
                LAYOUT_DIRECTION_RTL);
        boolean hasSearchBar = (mSearchBarEditView != null) &&
                (mSearchBarEditView.getVisibility() == View.VISIBLE);

            mFixedBounds.set(fixedBounds);
            if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
                mFixedBounds.top = mInsets.top;
                mFixedBounds.bottom = getMeasuredHeight();
        if (mFixedBounds.isEmpty()) {
            // If there are no fixed bounds, then use the default padding and insets
            setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
                    mContainerInset + mInsets.bottom);
        } else {
            // If there are fixed bounds, then we update the padding to reflect the fixed bounds.
            setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
                    mInsets.bottom);
        }

        // Update the apps recycler view, inset it by the container inset as well
        int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
        if (isRtl) {
            mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
        } else {
            mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
        }
        // Post the updates since they can trigger a relayout, and this call can be triggered from
        // a layout pass itself.
        post(new Runnable() {
            @Override
            public void run() {
                updateBackgrounds();
                updatePaddings();

        // Update the header bar
        if (hasSearchBar) {
            LinearLayout.LayoutParams lp =
                    (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
            lp.leftMargin = lp.rightMargin = inset;
        }
        });
    }

    /**
     * Update the background of the Apps view and children.
     */
    @Override
    protected void onUpdateBackgrounds() {
        int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
        boolean hasSearchBar = (mSearchBarEditView != null) &&
                (mSearchBarEditView.getVisibility() == View.VISIBLE);

        // Update the background of the reveal view and list to be inset with the fixed bound
        // insets instead of the default insets
        mAppsRecyclerView.setBackground(new InsetDrawable(
                getContext().getResources().getDrawable(
                        hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg),
                inset, 0, inset, 0));
        getRevealView().setBackground(new InsetDrawable(
                getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
                inset, 0, inset, 0));
    }

    @Override
@@ -530,65 +558,6 @@ public class AppsContainerView extends FrameLayout implements DragSource, Insett
        return false;
    }

    /**
     * Update the padding of the Apps view and children.  To ensure that the RecyclerView has the
     * full width to handle touches right to the edge of the screen, we only apply the top and
     * bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView
     * itself.  In particular, the left/right padding is applied to the background of the view,
     * and then additionally inset by the start margin.
     */
    private void updatePaddings() {
        boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
                LAYOUT_DIRECTION_RTL);
        boolean hasSearchBar = (mSearchBarEditView != null) &&
                (mSearchBarEditView.getVisibility() == View.VISIBLE);

        if (mFixedBounds.isEmpty()) {
            // If there are no fixed bounds, then use the default padding and insets
            setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
                    mContainerInset + mInsets.bottom);
        } else {
            // If there are fixed bounds, then we update the padding to reflect the fixed bounds.
            setPadding(mFixedBounds.left, mFixedBounds.top + mFixedBoundsContainerInset,
                    getMeasuredWidth() - mFixedBounds.right,
                    mInsets.bottom + mFixedBoundsContainerInset);
        }

        // Update the apps recycler view
        int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
        if (isRtl) {
            mAppsRecyclerView.setPadding(inset, inset, inset + mContentMarginStart, inset);
        } else {
            mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
        }

        // Update the header
        if (hasSearchBar) {
            LinearLayout.LayoutParams lp =
                    (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
            lp.leftMargin = lp.rightMargin = inset;
        }
    }

    /**
     * Update the background of the Apps view and children.
     */
    private void updateBackgrounds() {
        int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
        boolean hasSearchBar = (mSearchBarEditView != null) &&
                (mSearchBarEditView.getVisibility() == View.VISIBLE);

        // Update the background of the reveal view and list to be inset with the fixed bound
        // insets instead of the default insets
        mAppsRecyclerView.setBackground(new InsetDrawable(
                getContext().getResources().getDrawable(
                        hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.apps_list_bg),
                inset, 0, inset, 0));
        getRevealView().setBackground(new InsetDrawable(
                getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
                inset, 0, inset, 0));
    }

    /**
     * Shows the search field.
     */
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.android.launcher3.util.Thunk;

/**
 * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling
 * velocity is below a predefined threshold.
 */
public class BaseContainerRecyclerView extends RecyclerView
        implements RecyclerView.OnItemTouchListener {

    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;

    /** Keeps the last known scrolling delta/velocity along y-axis. */
    @Thunk int mDy = 0;
    private float mDeltaThreshold;
    private RecyclerView.OnScrollListener mScrollListenerProxy;

    public BaseContainerRecyclerView(Context context) {
        this(context, null);
    }

    public BaseContainerRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseContainerRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;

        ScrollListener listener = new ScrollListener();
        setOnScrollListener(listener);
    }

    private class ScrollListener extends OnScrollListener {
        public ScrollListener() {
            // Do nothing
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            mDy = dy;
            if (mScrollListenerProxy != null) {
                mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
            }
        }
    }

    /**
     * Sets an additional scroll listener, only needed for LMR1 version of the support lib.
     */
    public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
        mScrollListenerProxy = listener;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        addOnItemTouchListener(this);
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
        if (shouldStopScroll(ev)) {
            stopScroll();
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
        // Do nothing.
    }

    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
    }

    /**
     * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
     */
    protected boolean shouldStopScroll(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            if ((Math.abs(mDy) < mDeltaThreshold &&
                    getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
                // now the touch events are being passed to the {@link WidgetCell} until the
                // touch sequence goes over the touch slop.
                return true;
            }
        }
        return false;
    }
}
 No newline at end of file
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.FrameLayout;

/**
 * A base container view, which supports resizing.
 */
public class BaseContainerView extends FrameLayout implements Insettable {

    protected Rect mInsets = new Rect();
    protected Rect mFixedBounds = new Rect();
    protected int mFixedBoundsContainerInset;

    public BaseContainerView(Context context) {
        this(context, null);
    }

    public BaseContainerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
                R.dimen.container_fixed_bounds_inset);
    }

    @Override
    final public void setInsets(Rect insets) {
        mInsets.set(insets);
        onUpdateBackgrounds();
        onUpdatePaddings();
    }

    /**
     * Sets the fixed bounds for this container view.
     */
    final public void setFixedBounds(Rect fixedBounds) {
        if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) {
            mFixedBounds.set(fixedBounds);
            if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
                mFixedBounds.top = mInsets.top;
                mFixedBounds.bottom = getMeasuredHeight();
            }
            // To ensure that the child RecyclerView has the full width to handle touches right to
            // the edge of the screen, we only apply the top and bottom padding to the bounds
            mFixedBounds.inset(0, mFixedBoundsContainerInset);
            onFixedBoundsUpdated();
        }
        // Post the updates since they can trigger a relayout, and this call can be triggered from
        // a layout pass itself.
        post(new Runnable() {
            @Override
            public void run() {
                onUpdateBackgrounds();
                onUpdatePaddings();
            }
        });
    }

    /**
     * Update the UI in response to a change in the fixed bounds.
     */
    protected void onFixedBoundsUpdated() {
        // Do nothing
    }

    /**
     * Update the paddings in response to a change in the bounds or insets.
     */
    protected void onUpdatePaddings() {
        // Do nothing
    }

    /**
     * Update the backgrounds in response to a change in the bounds or insets.
     */
    protected void onUpdateBackgrounds() {
        // Do nothing
    }
}
 No newline at end of file
Loading