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

Commit 22c34f76 authored by Martin Brabham's avatar Martin Brabham Committed by Matt Garnes
Browse files

Clean up scrolling

	- Eliminate creation of garbage
	- Square up interface
	- Post animation signals to handler so they can be cancelled and redispatched
		with new data for the updated signal.

Change-Id: I9824b7eb762a8d565e22e118bf3f07a8a4791ce8
parent 5d2f7ac2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@
        android:layout_marginTop="10dp"
        android:layout_marginLeft="6dp"
        android:layout_centerVertical="true"
        android:includeFontPadding="false"
        android:includeFontPadding="true"
        android:gravity="center"
        android:singleLine="true"
        autofit:minTextSize="8sp"
+1 −0
Original line number Diff line number Diff line
@@ -56,4 +56,5 @@
    <color name="app_scrubber_gray_color">@android:color/darker_gray</color>

    <color name="scrubber_background">#CC14191E</color>
    <color name="aftv_shadowColor">#b0000000</color>
</resources>
+8 −0
Original line number Diff line number Diff line
@@ -126,4 +126,12 @@
    <dimen name="vertical_app_drawer_icon_padding">5dp</dimen>

    <dimen name="app_drawer_scrubber_padding">20dp</dimen>

    <!-- App Drawer Item -->
    <dimen name="container_paddingStart">6dp</dimen>
    <dimen name="container_paddingEnd">6dp</dimen>
    <dimen name="aftv_width">32dp</dimen>
    <dimen name="aftv_marginTop">10dp</dimen>
    <dimen name="aftv_minTextSize">8sp</dimen>
    <dimen name="aftv_textSize">24sp</dimen>
</resources>
+62 −15
Original line number Diff line number Diff line
@@ -24,14 +24,16 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v7.widget.RecyclerView;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SectionIndexer;
import com.android.launcher3.locale.LocaleSetManager;
@@ -51,6 +53,7 @@ import java.util.List;
public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdapter.ViewHolder>
        implements View.OnLongClickListener, DragSource, SectionIndexer {

    private static final String TAG = AppDrawerListAdapter.class.getSimpleName();
    private static final String NUMERIC_OR_SPECIAL_HEADER = "#";

    /**
@@ -110,14 +113,17 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public AutoFitTextView mTextView;
        public ViewGroup mLayout;
        public View mFadingBackgroundFront;
        public View mFadingBackgroundBack;
        public View mContainerView;
        public View mFadingBackgroundBackView;
        public View mFadingBackgroundFrontView;
        public ViewHolder(View itemView) {
            super(itemView);
            mContainerView = itemView;
            mFadingBackgroundBackView = itemView.findViewById(R.id.fading_background_back);
            mFadingBackgroundFrontView = itemView.findViewById(R.id.fading_background_front);
            mTextView = (AutoFitTextView) itemView.findViewById(R.id.drawer_item_title);
            mTextView.bringToFront();
            mLayout = (ViewGroup) itemView.findViewById(R.id.drawer_item_flow);
            mFadingBackgroundFront = itemView.findViewById(R.id.fading_background_front);
            mFadingBackgroundBack = itemView.findViewById(R.id.fading_background_back);
        }
    }

@@ -158,6 +164,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
            mViewHolderSet = new HashSet<>();
            mInterpolator = new DecelerateInterpolator();
            YDPI = ctx.getResources().getDisplayMetrics().ydpi;

            mLayoutChangeListener = new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom,
@@ -288,19 +295,22 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
                percentage = 1 - percentage;
            }

            // Scale header text letters
            final float targetScale = (MAX_SCALE - MIN_SCALE) * percentage + MIN_SCALE;
            holder.mTextView.setScaleX(targetScale);
            holder.mTextView.setScaleY(targetScale);

            // Perform animation
            if (getSectionForPosition(holder.getPosition()) == mSectionTarget) {
                holder.mFadingBackgroundFront.setVisibility(View.INVISIBLE);
                holder.mFadingBackgroundBack.setAlpha(percentage);
                holder.mFadingBackgroundBack.setVisibility(View.VISIBLE);
                holder.mFadingBackgroundFrontView.setVisibility(View.INVISIBLE);
                holder.mFadingBackgroundBackView.setAlpha(percentage);
                holder.mFadingBackgroundBackView.setVisibility(View.VISIBLE);
            } else {
                holder.mFadingBackgroundFront.setAlpha(percentage);
                holder.mFadingBackgroundFront.setVisibility(View.VISIBLE);
                holder.mFadingBackgroundBack.setVisibility(View.INVISIBLE);
                holder.mFadingBackgroundBackView.setVisibility(View.INVISIBLE);
                holder.mFadingBackgroundFrontView.setAlpha(percentage);
                holder.mFadingBackgroundFrontView.setVisibility(View.VISIBLE);
            }

        }

        /**
@@ -314,7 +324,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
        }
    }

    private static class ItemAnimator implements ValueAnimator.AnimatorUpdateListener {
    private class ItemAnimator implements ValueAnimator.AnimatorUpdateListener {
        private ViewHolder mViewHolder;
        private ItemAnimatorSet mAnimatorSet;

@@ -325,7 +335,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mAnimatorSet.animate(mViewHolder, animation);
            mItemAnimatorSet.animate(mViewHolder, animation);
        }
    }

@@ -365,9 +375,25 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
    private void initParams() {
        mDeviceProfile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();

        int width = mDeviceProfile.cellWidthPx + 2 * mDeviceProfile.edgeMarginPx;
        int width = mDeviceProfile.allAppsIconSizePx + 2 * mDeviceProfile.edgeMarginPx;
        int drawnWidth = (mDeviceProfile.allAppsCellWidthPx * mDeviceProfile.numColumnsBase) +
                ((mDeviceProfile.edgeMarginPx * 2) * mDeviceProfile.numColumnsBase);

        mIconParams = new
                LinearLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT);

        boolean isLarge = SettingsProvider.getBoolean(mLauncher,
                SettingsProvider.SETTINGS_UI_GENERAL_ICONS_LARGE,
                R.bool.preferences_interface_general_icons_large_default);

        if (!isLarge) {
            mIconParams.setMarginStart(mDeviceProfile.edgeMarginPx);
            mIconParams.setMarginEnd(mDeviceProfile.edgeMarginPx);
        }

        mIconParams.topMargin = mDeviceProfile.edgeMarginPx;
        mIconParams.bottomMargin = mDeviceProfile.edgeMarginPx;
        mIconParams.gravity = Gravity.CENTER;
        mIconRect = new Rect(0, 0, mDeviceProfile.allAppsIconSizePx,
                mDeviceProfile.allAppsIconSizePx);

@@ -470,6 +496,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
        ArrayList<AppInfo> infos = getAllApps();

        mLauncher.mAppDrawer.getLayoutManager().removeAllViews();

        setApps(infos);
    }

@@ -662,6 +689,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
        holder.mTextView.setPivotY(holder.mTextView.getHeight() / 2);

        final int size = indexedInfo.mInfo.size();

        for (int i = 0; i < holder.mLayout.getChildCount(); i++) {
            AppDrawerIconView icon = (AppDrawerIconView) holder.mLayout.getChildAt(i);
            icon.setLayoutParams(mIconParams);
@@ -816,7 +844,26 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap

    @Override
    public int getSectionForPosition(int position) {
        return mSectionHeaders.get(mHeaderList.get(position).mStartString).mSectionIndex;
        if (mSectionHeaders == null) {
            return 0;
        }

        position = (position < 0) ? 0 : position;
        position = (position > mHeaderList.size()) ? mHeaderList.size() : position;

        int index = 0;
        AppItemIndexedInfo info = mHeaderList.get(position);
        if (info != null) {
            SectionIndices indices = mSectionHeaders.get(info.mStartString);
            if (indices != null) {
                index = indices.mSectionIndex;
            } else {
                Log.w(TAG, "SectionIndices are null");
            }
        } else {
            Log.w(TAG, "AppItemIndexedInfo is null");
        }
        return index;
    }

    private void filterProtectedApps(ArrayList<AppInfo> list) {
+210 −41
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
@@ -34,8 +36,18 @@ import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import java.lang.IllegalArgumentException;
import java.util.ArrayList;

/**
 * AppDrawerScrubber
 * <pre>
 *     This is the scrubber at the bottom of the app drawer layout for navigating the application
 *     list
 * </pre>
 *
 * @see {@link android.widget.LinearLayout}
 */
public class AppDrawerScrubber extends LinearLayout {
    private AppDrawerListAdapter mAdapter;
    private RecyclerView mListView;
@@ -45,17 +57,197 @@ public class AppDrawerScrubber extends LinearLayout {
    private SectionContainer mSectionContainer;
    private LinearLayoutManager mLayoutManager;
    private ScrubberAnimationState mScrubberAnimationState;
    private Drawable mTransparentDrawable;
    private AppDrawerSmoothScroller mLinearSmoothScroller;

    private static final int MSG_SET_TARGET = 1000;
    private static final int MSG_SMOOTH_SCROLL = MSG_SET_TARGET + 1;
    private static final int MSG_ANIMATE_PICK = MSG_SMOOTH_SCROLL + 1;

    /**
     * UiHandler
     * <pre>
     *     Using a handler for sending signals to perform certain actions.  The reason for
     *     using this is to be able to remove and replace a signal if signals are being
     *     sent too fast (e.g. user scrubbing like crazy). This allows the touch loop to
     *     complete then later run the animations in their own loops.
     * </pre>
     */
    private class UiHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SET_TARGET:
                    int adapterIndex = msg.arg1;
                    performSetTarget(adapterIndex);
                    break;
                case MSG_ANIMATE_PICK:
                    int index = msg.arg1;
                    int width = msg.arg2;
                    int lastIndex = (Integer)msg.obj;
                    performAnimatePickMessage(index, width, lastIndex);
                    break;
                case MSG_SMOOTH_SCROLL:
                    int itemDiff = msg.arg1;
                    int itemIndex = msg.arg2;
                    performSmoothScroll(itemDiff, itemIndex);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

        /**
         * Overidden to remove identical calls if they are called subsequently fast enough.
         *
         * This is the final point that is public in the call chain.  Other calls to sendMessageXXX
         * will eventually call this function which calls "enqueueMessage" which is private.
         *
         * @param msg {@link android.os.Message}
         * @param uptimeMillis {@link java.lang.Long}
         *
         * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException}
         */
        @Override
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) throws
                IllegalArgumentException {
            if (msg == null) {
                throw new IllegalArgumentException("'msg' cannot be null!");
            }
            if (hasMessages(msg.what)) {
                removeMessages(msg.what);
            }
            return super.sendMessageAtTime(msg, uptimeMillis);
        }

    }
    private Handler mUiHandler = new UiHandler();
    private void sendSetTargetMessage(int adapterIndex) {
        Message msg = mUiHandler.obtainMessage(MSG_SET_TARGET);
        msg.what = MSG_SET_TARGET;
        msg.arg1 = adapterIndex;
        mUiHandler.sendMessage(msg);
    }
    private void performSetTarget(int adapterIndex) {
        if (mAdapter != null) {
            mAdapter.setSectionTarget(adapterIndex);
        }
    }
    private void sendAnimatePickMessage(int index, int width, int lastIndex) {
        Message msg = mUiHandler.obtainMessage(MSG_ANIMATE_PICK);
        msg.what = MSG_ANIMATE_PICK;
        msg.arg1 = index;
        msg.arg2 = width;
        msg.obj = lastIndex;
        mUiHandler.sendMessage(msg);
    }
    private void performAnimatePickMessage(int index, int width, int lastIndex) {
        if (mScrubberIndicator != null) {
            // get the index based on the direction the user is scrolling
            int directionalIndex = mSectionContainer.getDirectionalIndex(lastIndex, index);
            String sectionText = mSectionContainer.getHeader(directionalIndex);
            float translateX = (index * width) / (float) mSectionContainer.size();
            // if we are showing letters, grab the position based on the text view
            if (mSectionContainer.showLetters()) {
                translateX = mScrubberText.getPositionOfSection(index);
            }
            // center the x position
            translateX -= mScrubberIndicator.getMeasuredWidth() / 2;
            mScrubberIndicator.setTranslationX(translateX);
            mScrubberIndicator.setText(sectionText);
        }
    }
    private void sendSmoothScrollMessage(int itemDiff, int itemIndex) {
        Message msg = mUiHandler.obtainMessage(MSG_SMOOTH_SCROLL);
        msg.what = MSG_SMOOTH_SCROLL;
        msg.arg1 = itemDiff;
        msg.arg2 = itemIndex;
        mUiHandler.sendMessage(msg);
    }
    private void performSmoothScroll(int itemDiff, int itemIndex) {
        if (mLinearSmoothScroller == null) {
            mLinearSmoothScroller = new AppDrawerSmoothScroller(mContext);
        }
        mLinearSmoothScroller.setItemDiff(itemDiff);
        mLinearSmoothScroller.setTargetPosition(itemIndex);
        mLayoutManager.startSmoothScroll(mLinearSmoothScroller);
    }

    /**
     * Constructor
     *
     * @param context {@link android.content.Context}
     * @param attrs {@link android.util.AttributeSet}
     */
    public AppDrawerScrubber(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * Constructor
     *
     * @param context {@link android.content.Context}
     */
    public AppDrawerScrubber(Context context) {
        super(context);
        init(context);
    }

    /**
     * AppDrawerSmoothScroller
     * <pre>
     *     This is a smooth scroller with the ability to set an item diff
     * </pre>
     *
     * @see {@link android.support.v7.widget.LinearSmoothScroller}
     */
    private class AppDrawerSmoothScroller extends LinearSmoothScroller {

        // Members
        private int mItemDiff = 0;

        public AppDrawerSmoothScroller(Context context) {
            super(context);
        }

        @Override
        protected int getVerticalSnapPreference() {
            // position the item against the end of the list view
            return SNAP_TO_END;
        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return mLayoutManager.computeScrollVectorForPosition(targetPosition);
        }

        @Override
        public int calculateDyToMakeVisible(View view, int snapPreference) {
            int dy = super.calculateDyToMakeVisible(view, snapPreference);
            return dy - mItemDiff;
        }

        /**
         * Set the item difference
         *
         * @param itemDiff
         */
        public void setItemDiff(int itemDiff) {
            mItemDiff = itemDiff;
        }

        /**
         * Get the item difference
         *
         * @return {@link java.lang.Integer}
         */
        public int getItemDiff() {
            return mItemDiff;
        }

    }

    /**
     * Simple container class that tries to abstract out the knowledge of complex sections vs
     * simple string sections
@@ -131,7 +323,7 @@ public class AppDrawerScrubber extends LinearLayout {
        mSeekBar.setMax(mSectionContainer.size() - 1);

        // show a white line if there are no letters, otherwise show transparent
        Drawable d = mSectionContainer.showLetters() ? new ColorDrawable(Color.TRANSPARENT)
        Drawable d = mSectionContainer.showLetters() ? mTransparentDrawable
            : getContext().getResources().getDrawable(R.drawable.seek_back);
        ((ViewGroup)mSeekBar.getParent()).setBackground(d);

@@ -155,6 +347,7 @@ public class AppDrawerScrubber extends LinearLayout {

    private void init(Context context) {
        LayoutInflater.from(context).inflate(R.layout.scrub_layout, this);
        mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
        mScrubberAnimationState = new ScrubberAnimationState();
        mSeekBar = (SeekBar) findViewById(R.id.scrubber);
        mScrubberText = (AutoExpandTextView) findViewById(R.id.scrubberText);
@@ -193,6 +386,9 @@ public class AppDrawerScrubber extends LinearLayout {
        }

        private void animateIn() {
            if (mScrubberIndicator == null) {
                return;
            }
            // start from a scratch position when animating in
            mScrubberIndicator.animate().cancel();
            mScrubberIndicator.setPivotX(mScrubberIndicator.getMeasuredWidth() / 2);
@@ -218,11 +414,13 @@ public class AppDrawerScrubber extends LinearLayout {
                            animateOut();
                        }
                    }
                })
                .start();
                }).start();
        }

        private void animateOut() {
            if (mScrubberIndicator == null) {
                return;
            }
            mScrubberIndicator.animate()
                .alpha(SCRUBBER_ALPHA_START)
                .scaleX(SCRUBBER_SCALE_START)
@@ -242,24 +440,12 @@ public class AppDrawerScrubber extends LinearLayout {
            if (!isReady()) {
                return;
            }

            if (mScrubberIndicator != null) {
                // get the index based on the direction the user is scrolling
                int directionalIndex = mSectionContainer.getDirectionalIndex(mLastIndex, index);
                String sectionText = mSectionContainer.getHeader(directionalIndex);

                float translateX = (index * seekBar.getWidth()) / (float)mSectionContainer.size();
                // if we are showing letters, grab the position based on the text view
                if (mSectionContainer.showLetters()) {
                    translateX = mScrubberText.getPositionOfSection(index);
            progressChanged(seekBar, index, fromUser);
        }

                // center the x position
                translateX -= mScrubberIndicator.getMeasuredWidth() / 2;
        private void progressChanged(SeekBar seekBar, int index, boolean fromUser) {

                mScrubberIndicator.setTranslationX(translateX);
                mScrubberIndicator.setText(sectionText);
            }
            sendAnimatePickMessage(index, seekBar.getWidth(), mLastIndex);

            // get the index of the underlying list
            int adapterIndex = mSectionContainer.getAdapterIndex(mLastIndex, index);
@@ -272,32 +458,15 @@ public class AppDrawerScrubber extends LinearLayout {
                itemHeight = child.getMeasuredHeight();
            }

            // Start smooth scroll from this Looper loop
            if (itemHeight != 0) {
                // scroll to the item such that there are 2 rows beneath it from the bottom
                final int itemDiff = 2 * itemHeight;
                LinearSmoothScroller scroller = new LinearSmoothScroller(mListView.getContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        // position the item against the end of the list view
                        return SNAP_TO_END;
                    }

                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return mLayoutManager.computeScrollVectorForPosition(targetPosition);
                sendSmoothScrollMessage(itemDiff, itemIndex);
            }

                    @Override
                    public int calculateDyToMakeVisible(View view, int snapPreference) {
                        int dy = super.calculateDyToMakeVisible(view, snapPreference);
                        return dy - itemDiff;
                    }
                };
                scroller.setTargetPosition(itemIndex);
                mLayoutManager.startSmoothScroll(scroller);
            }

            mAdapter.setSectionTarget(adapterIndex);
            // Post set target index on queue to get processed by Looper later
            sendSetTargetMessage(adapterIndex);

            mLastIndex = index;
        }
+1 −1

File changed.

Contains only whitespace changes.

Loading