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

Commit c0880491 authored by Winson's avatar Winson
Browse files

Highlighting sectioned apps on fast-scroll.

- This CL fixes an old assumption we had about the height
  of rows in AllApps, and ensures that we account for the
  difference in height between the predictive icons and the
  normal icons.
- In addition, we refactor FastBitmapDrawable to have multiple
  states, which it manages in drawing itself, including the
  press state and fast scroll focus states.  And we also refactor
  some of the fast scroll logic in the all apps recycler view
  out to its own class.

Change-Id: I1988159b2767df733bbbfc7dc601859cde6c9943
parent 51894540
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
@@ -19,11 +19,6 @@
  public float getAlpha();
}

-keep class com.android.launcher3.BubbleTextView {
  public void setFastScrollFocus(float);
  public float getFastScrollFocus();
}

-keep class com.android.launcher3.ButtonDropTarget {
  public int getTextColor();
}
@@ -56,8 +51,10 @@
}

-keep class com.android.launcher3.FastBitmapDrawable {
  public int getBrightness();
  public void setBrightness(int);
  public void setDesaturation(float);
  public float getDesaturation();
  public void setBrightness(float);
  public float getBrightness();
}

-keep class com.android.launcher3.MemoryDumpActivity {
+47 −20
Original line number Diff line number Diff line
@@ -52,8 +52,8 @@ public abstract class BaseRecyclerView extends RecyclerView
        public int rowIndex;
        // The offset of the first visible row
        public int rowTopOffset;
        // The height of a given row (they are currently all the same height)
        public int rowHeight;
        // The adapter position of the first visible item
        public int itemPos;
    }

    protected BaseRecyclerViewFastScrollBar mScrollbar;
@@ -186,16 +186,22 @@ public abstract class BaseRecyclerView extends RecyclerView
        return mScrollbar.getThumbMaxWidth();
    }

    /**
     * Returns the visible height of the recycler view:
     *   VisibleHeight = View height - top padding - bottom padding
     */
    protected int getVisibleHeight() {
        int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
        return visibleHeight;
    }

    /**
     * Returns the available scroll height:
     *   AvailableScrollHeight = Total height of the all items - last page height
     *
     * This assumes that all rows are the same height.
     */
    protected int getAvailableScrollHeight(int rowCount, int rowHeight) {
        int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
        int scrollHeight = getPaddingTop() + rowCount * rowHeight + getPaddingBottom();
        int availableScrollHeight = scrollHeight - visibleHeight;
    protected int getAvailableScrollHeight(int rowCount) {
        int totalHeight = getPaddingTop() + getTop(rowCount) + getPaddingBottom();
        int availableScrollHeight = totalHeight - getVisibleHeight();
        return availableScrollHeight;
    }

@@ -204,8 +210,7 @@ public abstract class BaseRecyclerView extends RecyclerView
     *   AvailableScrollBarHeight = Total height of the visible view - thumb height
     */
    protected int getAvailableScrollBarHeight() {
        int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
        int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
        int availableScrollBarHeight = getVisibleHeight() - mScrollbar.getThumbHeight();
        return availableScrollBarHeight;
    }

@@ -223,6 +228,13 @@ public abstract class BaseRecyclerView extends RecyclerView
        return defaultInactiveThumbColor;
    }

    /**
     * Returns the scrollbar for this recycler view.
     */
    public BaseRecyclerViewFastScrollBar getScrollBar() {
        return mScrollbar;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
@@ -243,7 +255,7 @@ public abstract class BaseRecyclerView extends RecyclerView
            int rowCount) {
        // Only show the scrollbar if there is height to be scrolled
        int availableScrollBarHeight = getAvailableScrollBarHeight();
        int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight);
        int availableScrollHeight = getAvailableScrollHeight(rowCount);
        if (availableScrollHeight <= 0) {
            mScrollbar.setThumbOffset(-1, -1);
            return;
@@ -252,8 +264,7 @@ public abstract class BaseRecyclerView extends RecyclerView
        // Calculate the current scroll position, the scrollY of the recycler view accounts for the
        // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
        // padding)
        int scrollY = getPaddingTop() +
                (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
        int scrollY = getScrollTop(scrollPosState);
        int scrollBarY = mBackgroundPadding.top +
                (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);

@@ -268,7 +279,7 @@ public abstract class BaseRecyclerView extends RecyclerView
    }

    /**
     * Returns whether fast scrolling is supported in the current state.
     * @return whether fast scrolling is supported in the current state.
     */
    protected boolean supportsFastScrolling() {
        return true;
@@ -277,22 +288,38 @@ public abstract class BaseRecyclerView extends RecyclerView
    /**
     * Maps the touch (from 0..1) to the adapter position that should be visible.
     * <p>Override in each subclass of this base class.
     *
     * @return the scroll top of this recycler view.
     */
    public abstract String scrollToPositionAtProgress(float touchFraction);
    protected int getScrollTop(ScrollPositionState scrollPosState) {
        return getPaddingTop() + getTop(scrollPosState.rowIndex) -
                scrollPosState.rowTopOffset;
    }

    /**
     * Updates the bounds for the scrollbar.
     * Returns information about the item that the recycler view is currently scrolled to.
     */
    protected abstract void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask);

    /**
     * Returns the top (or y position) of the row at the specified index.
     */
    protected abstract int getTop(int rowIndex);

    /**
     * Maps the touch (from 0..1) to the adapter position that should be visible.
     * <p>Override in each subclass of this base class.
     */
    public abstract void onUpdateScrollbar(int dy);
    protected abstract String scrollToPositionAtProgress(float touchFraction);

    /**
     * Updates the bounds for the scrollbar.
     * <p>Override in each subclass of this base class.
     */
    public void onFastScrollCompleted() {}
    protected abstract void onUpdateScrollbar(int dy);

    /**
     * Returns information about the item that the recycler view is currently scrolled to.
     * <p>Override in each subclass of this base class.
     */
    protected abstract void getCurScrollState(ScrollPositionState stateOut);
    protected void onFastScrollCompleted() {}
}
 No newline at end of file
+6 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;

import com.android.launcher3.util.Thunk;
@@ -37,7 +38,7 @@ import com.android.launcher3.util.Thunk;
public class BaseRecyclerViewFastScrollBar {

    public interface FastScrollFocusableView {
        void setFastScrollFocused(boolean focused, boolean animated);
        void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
    }

    private final static int MAX_TRACK_ALPHA = 30;
@@ -199,7 +200,7 @@ public class BaseRecyclerViewFastScrollBar {
                    }
                    mTouchOffset += (lastY - downY);
                    mPopup.animateVisibility(true);
                    animateScrollbar(true);
                    showActiveScrollbar(true);
                }
                if (mIsDragging) {
                    // Update the fastscroller section name at this touch position
@@ -210,7 +211,7 @@ public class BaseRecyclerViewFastScrollBar {
                            (bottom - top));
                    mPopup.setSectionName(sectionName);
                    mPopup.animateVisibility(!sectionName.isEmpty());
                    mRv.invalidate(mPopup.updateFastScrollerBounds(mRv, lastY));
                    mRv.invalidate(mPopup.updateFastScrollerBounds(lastY));
                    mLastTouchY = boundedY;
                }
                break;
@@ -222,7 +223,7 @@ public class BaseRecyclerViewFastScrollBar {
                if (mIsDragging) {
                    mIsDragging = false;
                    mPopup.animateVisibility(false);
                    animateScrollbar(false);
                    showActiveScrollbar(false);
                }
                break;
        }
@@ -246,7 +247,7 @@ public class BaseRecyclerViewFastScrollBar {
    /**
     * Animates the width and color of the scrollbar.
     */
    private void animateScrollbar(boolean isScrolling) {
    private void showActiveScrollbar(boolean isScrolling) {
        if (mScrollbarAnimator != null) {
            mScrollbarAnimator.cancel();
        }
+6 −6
Original line number Diff line number Diff line
@@ -77,26 +77,26 @@ public class BaseRecyclerViewFastScrollPopup {
     * Updates the bounds for the fast scroller.
     * @return the invalidation rect for this update.
     */
    public Rect updateFastScrollerBounds(BaseRecyclerView rv, int lastTouchY) {
    public Rect updateFastScrollerBounds(int lastTouchY) {
        mInvalidateRect.set(mBgBounds);

        if (isVisible()) {
            // Calculate the dimensions and position of the fast scroller popup
            int edgePadding = rv.getMaxScrollbarWidth();
            int edgePadding = mRv.getMaxScrollbarWidth();
            int bgPadding = (mBgOriginalSize - mTextBounds.height()) / 2;
            int bgHeight = mBgOriginalSize;
            int bgWidth = Math.max(mBgOriginalSize, mTextBounds.width() + (2 * bgPadding));
            if (Utilities.isRtl(mRes)) {
                mBgBounds.left = rv.getBackgroundPadding().left + (2 * rv.getMaxScrollbarWidth());
                mBgBounds.left = mRv.getBackgroundPadding().left + (2 * mRv.getMaxScrollbarWidth());
                mBgBounds.right = mBgBounds.left + bgWidth;
            } else {
                mBgBounds.right = rv.getWidth() - rv.getBackgroundPadding().right -
                        (2 * rv.getMaxScrollbarWidth());
                mBgBounds.right = mRv.getWidth() - mRv.getBackgroundPadding().right -
                        (2 * mRv.getMaxScrollbarWidth());
                mBgBounds.left = mBgBounds.right - bgWidth;
            }
            mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight);
            mBgBounds.top = Math.max(edgePadding,
                    Math.min(mBgBounds.top, rv.getHeight() - edgePadding - bgHeight));
                    Math.min(mBgBounds.top, mRv.getHeight() - edgePadding - bgHeight));
            mBgBounds.bottom = mBgBounds.top + bgHeight;
        } else {
            mBgBounds.setEmpty();
+56 −75
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.launcher3;

import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -25,7 +24,6 @@ import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -37,8 +35,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.TextView;

import com.android.launcher3.IconCache.IconLoadRequest;
@@ -63,13 +59,6 @@ public class BubbleTextView extends TextView
    private static final int DISPLAY_WORKSPACE = 0;
    private static final int DISPLAY_ALL_APPS = 1;

    private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
    private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0;
    private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1;
    private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2;
    private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
    private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;

    private final Launcher mLauncher;
    private Drawable mIcon;
    private final Drawable mBackground;
@@ -93,12 +82,6 @@ public class BubbleTextView extends TextView
    private boolean mIgnorePressedStateChange;
    private boolean mDisableRelayout = false;

    private ObjectAnimator mFastScrollFocusAnimator;
    private Paint mFastScrollFocusBgPaint;
    private float mFastScrollFocusFraction;
    private boolean mFastScrollFocused;
    private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON;

    private IconLoadRequest mIconLoadRequest;

    public BubbleTextView(Context context) {
@@ -151,13 +134,6 @@ public class BubbleTextView extends TextView
            setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
        }

        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG) {
            mFastScrollFocusBgPaint = new Paint();
            mFastScrollFocusBgPaint.setAntiAlias(true);
            mFastScrollFocusBgPaint.setColor(
                    getResources().getColor(R.color.container_fastscroll_thumb_active_color));
        }

        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
    }

@@ -170,8 +146,9 @@ public class BubbleTextView extends TextView
        Bitmap b = info.getIcon(iconCache);

        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
        iconDrawable.setGhostModeEnabled(info.isDisabled != 0);

        if (info.isDisabled != 0) {
            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
        }
        setIcon(iconDrawable, mIconSize);
        if (info.contentDescription != null) {
            setContentDescription(info.contentDescription);
@@ -259,7 +236,12 @@ public class BubbleTextView extends TextView

    private void updateIconState() {
        if (mIcon instanceof FastBitmapDrawable) {
            ((FastBitmapDrawable) mIcon).setPressed(isPressed() || mStayPressed);
            FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
            if (isPressed() || mStayPressed) {
                d.animateState(FastBitmapDrawable.State.PRESSED);
            } else {
                d.animateState(FastBitmapDrawable.State.NORMAL);
            }
        }
    }

@@ -362,18 +344,7 @@ public class BubbleTextView extends TextView
    @Override
    public void draw(Canvas canvas) {
        if (!mCustomShadowsEnabled) {
            // Draw the fast scroll focus bg if we have one
            if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG &&
                    mFastScrollFocusFraction > 0f) {
                DeviceProfile grid = mLauncher.getDeviceProfile();
                int iconCenterX = getScrollX() + (getWidth() / 2);
                int iconCenterY = getScrollY() + getPaddingTop() + (grid.iconSizePx / 2);
                canvas.drawCircle(iconCenterX, iconCenterY,
                        mFastScrollFocusFraction * (getWidth() / 2), mFastScrollFocusBgPaint);
            }

            super.draw(canvas);

            return;
        }

@@ -533,8 +504,13 @@ public class BubbleTextView extends TextView
     */
    public void reapplyItemInfo(final ItemInfo info) {
        if (getTag() == info) {
            FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
            if (mIcon instanceof FastBitmapDrawable) {
                prevState = ((FastBitmapDrawable) mIcon).getCurrentState();
            }
            mIconLoadRequest = null;
            mDisableRelayout = true;

            if (info instanceof AppInfo) {
                applyFromApplicationInfo((AppInfo) info);
            } else if (info instanceof ShortcutInfo) {
@@ -550,6 +526,13 @@ public class BubbleTextView extends TextView
            } else if (info instanceof PackageItemInfo) {
                applyFromPackageItemInfo((PackageItemInfo) info);
            }

            // If we are reapplying over an old icon, then we should update the new icon to the same
            // state as the old icon
            if (mIcon instanceof FastBitmapDrawable) {
                ((FastBitmapDrawable) mIcon).setState(prevState);
            }

            mDisableRelayout = false;
        }
    }
@@ -583,55 +566,53 @@ public class BubbleTextView extends TextView
        }
    }

    // Setters & getters for the animation
    public void setFastScrollFocus(float fraction) {
        mFastScrollFocusFraction = fraction;
        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) {
            setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
            setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
        } else {
            invalidate();
        }
    }

    public float getFastScrollFocus() {
        return mFastScrollFocusFraction;
    }

    @Override
    public void setFastScrollFocused(final boolean focused, boolean animated) {
        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
    public void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated) {
        // We can only set the fast scroll focus state on a FastBitmapDrawable
        if (!(mIcon instanceof FastBitmapDrawable)) {
            return;
        }

        if (mFastScrollFocused != focused) {
            mFastScrollFocused = focused;

        FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
        if (animated) {
                // Clean up the previous focus animator
                if (mFastScrollFocusAnimator != null) {
                    mFastScrollFocusAnimator.cancel();
                }
                mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus",
                        focused ? 1f : 0f);
                if (focused) {
                    mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
                } else {
                    mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
            FastBitmapDrawable.State prevState = d.getCurrentState();
            if (d.animateState(focusState)) {
                // If the state was updated, then update the view accordingly
                animate().scaleX(focusState.viewScale)
                        .scaleY(focusState.viewScale)
                        .setStartDelay(getStartDelayForStateChange(prevState, focusState))
                        .setDuration(d.getDurationForStateChange(prevState, focusState))
                        .start();
            }
                mFastScrollFocusAnimator.setDuration(focused ?
                        FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
                mFastScrollFocusAnimator.start();
        } else {
                mFastScrollFocusFraction = focused ? 1f : 0f;
            if (d.setState(focusState)) {
                // If the state was updated, then update the view accordingly
                animate().cancel();
                setScaleX(focusState.viewScale);
                setScaleY(focusState.viewScale);
            }
        }
    }

    /**
     * Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
     */
    private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
            final FastBitmapDrawable.State toState) {
        switch (toState) {
            case NORMAL:
                switch (fromState) {
                    case FAST_SCROLL_HIGHLIGHTED:
                        return FastBitmapDrawable.FAST_SCROLL_INACTIVE_DURATION / 4;
                }
        }
        return 0;
    }

    /**
     * Interface to be implemented by the grand parent to allow click shadow effect.
     */
    public static interface BubbleTextShadowHandler {
    public interface BubbleTextShadowHandler {
        void setPressedIcon(BubbleTextView icon, Bitmap background);
    }
}
Loading