diff --git a/app/src/main/java/androidx/recyclerview/widget/FastScrollerEx.java b/app/src/main/java/androidx/recyclerview/widget/FastScrollerEx.java new file mode 100644 index 0000000000..bcf5de0f34 --- /dev/null +++ b/app/src/main/java/androidx/recyclerview/widget/FastScrollerEx.java @@ -0,0 +1,588 @@ +/* + * Copyright 2018 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 androidx.recyclerview.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; +import android.view.MotionEvent; + +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.core.view.ViewCompat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Class responsible to animate and provide a fast scroller. + */ +//@VisibleForTesting +public class FastScrollerEx extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener { + @IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING}) + @Retention(RetentionPolicy.SOURCE) + private @interface State { } + // Scroll thumb not showing + private static final int STATE_HIDDEN = 0; + // Scroll thumb visible and moving along with the scrollbar + private static final int STATE_VISIBLE = 1; + // Scroll thumb being dragged by user + private static final int STATE_DRAGGING = 2; + + @IntDef({DRAG_X, DRAG_Y, DRAG_NONE}) + @Retention(RetentionPolicy.SOURCE) + private @interface DragState{ } + private static final int DRAG_NONE = 0; + private static final int DRAG_X = 1; + private static final int DRAG_Y = 2; + + @IntDef({ANIMATION_STATE_OUT, ANIMATION_STATE_FADING_IN, ANIMATION_STATE_IN, + ANIMATION_STATE_FADING_OUT}) + @Retention(RetentionPolicy.SOURCE) + private @interface AnimationState { } + private static final int ANIMATION_STATE_OUT = 0; + private static final int ANIMATION_STATE_FADING_IN = 1; + private static final int ANIMATION_STATE_IN = 2; + private static final int ANIMATION_STATE_FADING_OUT = 3; + + private static final int SHOW_DURATION_MS = 500; + private static final int HIDE_DELAY_AFTER_VISIBLE_MS = 1500; + private static final int HIDE_DELAY_AFTER_DRAGGING_MS = 1200; + private static final int HIDE_DURATION_MS = 500; + private static final int SCROLLBAR_FULL_OPAQUE = 255; + + private static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed}; + private static final int[] EMPTY_STATE_SET = new int[]{}; + + private final int mScrollbarMinimumRange; + private final int mMargin; + + // Final values for the vertical scroll bar + @SuppressWarnings("WeakerAccess") /* synthetic access */ + final StateListDrawable mVerticalThumbDrawable; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + final Drawable mVerticalTrackDrawable; + private final int mVerticalThumbWidth; + private final int mVerticalTrackWidth; + + // Final values for the horizontal scroll bar + private final StateListDrawable mHorizontalThumbDrawable; + private final Drawable mHorizontalTrackDrawable; + private final int mHorizontalThumbHeight; + private final int mHorizontalTrackHeight; + + // Dynamic values for the vertical scroll bar + @VisibleForTesting int mVerticalThumbHeight; + @VisibleForTesting int mVerticalThumbCenterY; + @VisibleForTesting float mVerticalDragY; + + // Dynamic values for the horizontal scroll bar + @VisibleForTesting int mHorizontalThumbWidth; + @VisibleForTesting int mHorizontalThumbCenterX; + @VisibleForTesting float mHorizontalDragX; + + private int mRecyclerViewWidth = 0; + private int mRecyclerViewHeight = 0; + + private RecyclerView mRecyclerView; + /** + * Whether the document is long/wide enough to require scrolling. If not, we don't show the + * relevant scroller. + */ + private boolean mNeedVerticalScrollbar = false; + private boolean mNeedHorizontalScrollbar = false; + @State private int mState = STATE_HIDDEN; + @DragState private int mDragState = DRAG_NONE; + + private final int[] mVerticalRange = new int[2]; + private final int[] mHorizontalRange = new int[2]; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1); + @SuppressWarnings("WeakerAccess") /* synthetic access */ + @AnimationState int mAnimationState = ANIMATION_STATE_OUT; + private final Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hide(HIDE_DURATION_MS); + } + }; + private final RecyclerView.OnScrollListener + mOnScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + updateScrollPosition(recyclerView.computeHorizontalScrollOffset(), + recyclerView.computeVerticalScrollOffset()); + } + }; + + public FastScrollerEx(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable, + Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable, + Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange, + int margin) { + mVerticalThumbDrawable = verticalThumbDrawable; + mVerticalTrackDrawable = verticalTrackDrawable; + mHorizontalThumbDrawable = horizontalThumbDrawable; + mHorizontalTrackDrawable = horizontalTrackDrawable; + mVerticalThumbWidth = Math.max(defaultWidth, verticalThumbDrawable.getIntrinsicWidth()); + mVerticalTrackWidth = Math.max(defaultWidth, verticalTrackDrawable.getIntrinsicWidth()); + mHorizontalThumbHeight = Math + .max(defaultWidth, horizontalThumbDrawable.getIntrinsicWidth()); + mHorizontalTrackHeight = Math + .max(defaultWidth, horizontalTrackDrawable.getIntrinsicWidth()); + mScrollbarMinimumRange = scrollbarMinimumRange; + mMargin = margin; + mVerticalThumbDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE); + mVerticalTrackDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE); + + mShowHideAnimator.addListener(new AnimatorListener()); + mShowHideAnimator.addUpdateListener(new AnimatorUpdater()); + + attachToRecyclerView(recyclerView); + } + + public void attachToRecyclerView(@Nullable RecyclerView recyclerView) { + if (mRecyclerView == recyclerView) { + return; // nothing to do + } + if (mRecyclerView != null) { + destroyCallbacks(); + } + mRecyclerView = recyclerView; + if (mRecyclerView != null) { + setupCallbacks(); + } + } + + private void setupCallbacks() { + mRecyclerView.addItemDecoration(this); + mRecyclerView.addOnItemTouchListener(this); + mRecyclerView.addOnScrollListener(mOnScrollListener); + } + + private void destroyCallbacks() { + mRecyclerView.removeItemDecoration(this); + mRecyclerView.removeOnItemTouchListener(this); + mRecyclerView.removeOnScrollListener(mOnScrollListener); + cancelHide(); + } + + @SuppressWarnings("WeakerAccess") /* synthetic access */ + void requestRedraw() { + mRecyclerView.invalidate(); + } + + void setState(@State int state) { + if (state == STATE_DRAGGING && mState != STATE_DRAGGING) { + mVerticalThumbDrawable.setState(PRESSED_STATE_SET); + cancelHide(); + } + + if (state == STATE_HIDDEN) { + requestRedraw(); + } else { + show(); + } + + if (mState == STATE_DRAGGING && state != STATE_DRAGGING) { + mVerticalThumbDrawable.setState(EMPTY_STATE_SET); + resetHideDelay(HIDE_DELAY_AFTER_DRAGGING_MS); + } else if (state == STATE_VISIBLE) { + resetHideDelay(HIDE_DELAY_AFTER_VISIBLE_MS); + } + mState = state; + } + + private boolean isLayoutRTL() { + return ViewCompat.getLayoutDirection(mRecyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + public boolean isDragging() { + return mState == STATE_DRAGGING; + } + + @VisibleForTesting boolean isVisible() { + return mState == STATE_VISIBLE; + } + + public void show() { + switch (mAnimationState) { + case ANIMATION_STATE_FADING_OUT: + mShowHideAnimator.cancel(); + // fall through + case ANIMATION_STATE_OUT: + mAnimationState = ANIMATION_STATE_FADING_IN; + mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 1); + mShowHideAnimator.setDuration(SHOW_DURATION_MS); + mShowHideAnimator.setStartDelay(0); + mShowHideAnimator.start(); + break; + } + } + + @VisibleForTesting + void hide(int duration) { + switch (mAnimationState) { + case ANIMATION_STATE_FADING_IN: + mShowHideAnimator.cancel(); + // fall through + case ANIMATION_STATE_IN: + mAnimationState = ANIMATION_STATE_FADING_OUT; + mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 0); + mShowHideAnimator.setDuration(duration); + mShowHideAnimator.start(); + break; + } + } + + private void cancelHide() { + mRecyclerView.removeCallbacks(mHideRunnable); + } + + private void resetHideDelay(int delay) { + cancelHide(); + mRecyclerView.postDelayed(mHideRunnable, delay); + } + + @Override + public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) { + if (mRecyclerViewWidth != mRecyclerView.getWidth() + || mRecyclerViewHeight != mRecyclerView.getHeight()) { + mRecyclerViewWidth = mRecyclerView.getWidth(); + mRecyclerViewHeight = mRecyclerView.getHeight(); + // This is due to the different events ordering when keyboard is opened or + // retracted vs rotate. Hence to avoid corner cases we just disable the + // scroller when size changed, and wait until the scroll position is recomputed + // before showing it back. + setState(STATE_HIDDEN); + return; + } + + if (mAnimationState != ANIMATION_STATE_OUT) { + if (mNeedVerticalScrollbar) { + drawVerticalScrollbar(canvas); + } + if (mNeedHorizontalScrollbar) { + drawHorizontalScrollbar(canvas); + } + } + } + + private void drawVerticalScrollbar(Canvas canvas) { + int viewWidth = mRecyclerViewWidth; + + int left = viewWidth - mVerticalThumbWidth; + int top = mVerticalThumbCenterY - mVerticalThumbHeight / 2; + mVerticalThumbDrawable.setBounds(0, 0, mVerticalThumbWidth, mVerticalThumbHeight); + mVerticalTrackDrawable + .setBounds(0, 0, mVerticalTrackWidth, mRecyclerViewHeight); + + if (isLayoutRTL()) { + mVerticalTrackDrawable.draw(canvas); + canvas.translate(mVerticalThumbWidth, top); + canvas.scale(-1, 1); + mVerticalThumbDrawable.draw(canvas); + canvas.scale(-1, 1); + canvas.translate(-mVerticalThumbWidth, -top); + } else { + canvas.translate(left, 0); + mVerticalTrackDrawable.draw(canvas); + canvas.translate(0, top); + mVerticalThumbDrawable.draw(canvas); + canvas.translate(-left, -top); + } + } + + private void drawHorizontalScrollbar(Canvas canvas) { + int viewHeight = mRecyclerViewHeight; + + int top = viewHeight - mHorizontalThumbHeight; + int left = mHorizontalThumbCenterX - mHorizontalThumbWidth / 2; + mHorizontalThumbDrawable.setBounds(0, 0, mHorizontalThumbWidth, mHorizontalThumbHeight); + mHorizontalTrackDrawable + .setBounds(0, 0, mRecyclerViewWidth, mHorizontalTrackHeight); + + canvas.translate(0, top); + mHorizontalTrackDrawable.draw(canvas); + canvas.translate(left, 0); + mHorizontalThumbDrawable.draw(canvas); + canvas.translate(-left, -top); + } + + /** + * Notify the scroller of external change of the scroll, e.g. through dragging or flinging on + * the view itself. + * + * @param offsetX The new scroll X offset. + * @param offsetY The new scroll Y offset. + */ + void updateScrollPosition(int offsetX, int offsetY) { + int verticalContentLength = mRecyclerView.computeVerticalScrollRange(); + int verticalVisibleLength = mRecyclerViewHeight; + mNeedVerticalScrollbar = verticalContentLength - verticalVisibleLength > 0 + && mRecyclerViewHeight >= mScrollbarMinimumRange; + + int horizontalContentLength = mRecyclerView.computeHorizontalScrollRange(); + int horizontalVisibleLength = mRecyclerViewWidth; + mNeedHorizontalScrollbar = horizontalContentLength - horizontalVisibleLength > 0 + && mRecyclerViewWidth >= mScrollbarMinimumRange; + + if (!mNeedVerticalScrollbar && !mNeedHorizontalScrollbar) { + if (mState != STATE_HIDDEN) { + setState(STATE_HIDDEN); + } + return; + } + + if (mNeedVerticalScrollbar) { + float middleScreenPos = offsetY + verticalVisibleLength / 2.0f; + mVerticalThumbCenterY = + (int) ((verticalVisibleLength * middleScreenPos) / verticalContentLength); + mVerticalThumbHeight = Math.min(verticalVisibleLength, + (verticalVisibleLength * verticalVisibleLength) / verticalContentLength); + } + + if (mNeedHorizontalScrollbar) { + float middleScreenPos = offsetX + horizontalVisibleLength / 2.0f; + mHorizontalThumbCenterX = + (int) ((horizontalVisibleLength * middleScreenPos) / horizontalContentLength); + mHorizontalThumbWidth = Math.min(horizontalVisibleLength, + (horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength); + } + + if (mState == STATE_HIDDEN || mState == STATE_VISIBLE) { + setState(STATE_VISIBLE); + } + } + + @Override + public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, + @NonNull MotionEvent ev) { + final boolean handled; + if (mState == STATE_VISIBLE) { + boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY()); + boolean insideHorizontalThumb = isPointInsideHorizontalThumb(ev.getX(), ev.getY()); + if (ev.getAction() == MotionEvent.ACTION_DOWN + && (insideVerticalThumb || insideHorizontalThumb)) { + if (insideHorizontalThumb) { + mDragState = DRAG_X; + mHorizontalDragX = (int) ev.getX(); + } else if (insideVerticalThumb) { + mDragState = DRAG_Y; + mVerticalDragY = (int) ev.getY(); + } + + setState(STATE_DRAGGING); + handled = true; + } else { + handled = false; + } + } else if (mState == STATE_DRAGGING) { + handled = true; + } else { + handled = false; + } + return handled; + } + + @Override + public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent me) { + if (mState == STATE_HIDDEN) { + return; + } + + if (me.getAction() == MotionEvent.ACTION_DOWN) { + boolean insideVerticalThumb = isPointInsideVerticalThumb(me.getX(), me.getY()); + boolean insideHorizontalThumb = isPointInsideHorizontalThumb(me.getX(), me.getY()); + if (insideVerticalThumb || insideHorizontalThumb) { + if (insideHorizontalThumb) { + mDragState = DRAG_X; + mHorizontalDragX = (int) me.getX(); + } else if (insideVerticalThumb) { + mDragState = DRAG_Y; + mVerticalDragY = (int) me.getY(); + } + setState(STATE_DRAGGING); + } + } else if (me.getAction() == MotionEvent.ACTION_UP && mState == STATE_DRAGGING) { + mVerticalDragY = 0; + mHorizontalDragX = 0; + setState(STATE_VISIBLE); + mDragState = DRAG_NONE; + } else if (me.getAction() == MotionEvent.ACTION_MOVE && mState == STATE_DRAGGING) { + show(); + if (mDragState == DRAG_X) { + horizontalScrollTo(me.getX()); + } + if (mDragState == DRAG_Y) { + verticalScrollTo(me.getY()); + } + } + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } + + private void verticalScrollTo(float y) { + final int[] scrollbarRange = getVerticalRange(); + y = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], y)); + if (Math.abs(mVerticalThumbCenterY - y) < 2) { + return; + } + int scrollingBy = scrollTo(mVerticalDragY, y, scrollbarRange, + mRecyclerView.computeVerticalScrollRange(), + mRecyclerView.computeVerticalScrollOffset(), mRecyclerViewHeight); + if (scrollingBy != 0) { + mRecyclerView.scrollBy(0, scrollingBy); + } + mVerticalDragY = y; + } + + private void horizontalScrollTo(float x) { + final int[] scrollbarRange = getHorizontalRange(); + x = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], x)); + if (Math.abs(mHorizontalThumbCenterX - x) < 2) { + return; + } + + int scrollingBy = scrollTo(mHorizontalDragX, x, scrollbarRange, + mRecyclerView.computeHorizontalScrollRange(), + mRecyclerView.computeHorizontalScrollOffset(), mRecyclerViewWidth); + if (scrollingBy != 0) { + mRecyclerView.scrollBy(scrollingBy, 0); + } + + mHorizontalDragX = x; + } + + private int scrollTo(float oldDragPos, float newDragPos, int[] scrollbarRange, int scrollRange, + int scrollOffset, int viewLength) { + int scrollbarLength = scrollbarRange[1] - scrollbarRange[0]; + if (scrollbarLength == 0) { + return 0; + } + float percentage = ((newDragPos - oldDragPos) / (float) scrollbarLength); + int totalPossibleOffset = scrollRange - viewLength; + int scrollingBy = (int) (percentage * totalPossibleOffset); + int absoluteOffset = scrollOffset + scrollingBy; + if (absoluteOffset < totalPossibleOffset && absoluteOffset >= 0) { + return scrollingBy; + } else { + return 0; + } + } + + @VisibleForTesting + boolean isPointInsideVerticalThumb(float x, float y) { + return (isLayoutRTL() ? x <= mVerticalThumbWidth + : x >= mRecyclerViewWidth - mVerticalThumbWidth) + && y >= mVerticalThumbCenterY - mVerticalThumbHeight / 2 + && y <= mVerticalThumbCenterY + mVerticalThumbHeight / 2; + } + + @VisibleForTesting + boolean isPointInsideHorizontalThumb(float x, float y) { + return (y >= mRecyclerViewHeight - mHorizontalThumbHeight) + && x >= mHorizontalThumbCenterX - mHorizontalThumbWidth / 2 + && x <= mHorizontalThumbCenterX + mHorizontalThumbWidth / 2; + } + + @VisibleForTesting + Drawable getHorizontalTrackDrawable() { + return mHorizontalTrackDrawable; + } + + @VisibleForTesting + Drawable getHorizontalThumbDrawable() { + return mHorizontalThumbDrawable; + } + + @VisibleForTesting + Drawable getVerticalTrackDrawable() { + return mVerticalTrackDrawable; + } + + @VisibleForTesting + Drawable getVerticalThumbDrawable() { + return mVerticalThumbDrawable; + } + + /** + * Gets the (min, max) vertical positions of the vertical scroll bar. + */ + private int[] getVerticalRange() { + mVerticalRange[0] = mMargin; + mVerticalRange[1] = mRecyclerViewHeight - mMargin; + return mVerticalRange; + } + + /** + * Gets the (min, max) horizontal positions of the horizontal scroll bar. + */ + private int[] getHorizontalRange() { + mHorizontalRange[0] = mMargin; + mHorizontalRange[1] = mRecyclerViewWidth - mMargin; + return mHorizontalRange; + } + + private class AnimatorListener extends AnimatorListenerAdapter { + + private boolean mCanceled = false; + + AnimatorListener() { + } + + @Override + public void onAnimationEnd(Animator animation) { + // Cancel is always followed by a new directive, so don't update state. + if (mCanceled) { + mCanceled = false; + return; + } + if ((float) mShowHideAnimator.getAnimatedValue() == 0) { + mAnimationState = ANIMATION_STATE_OUT; + setState(STATE_HIDDEN); + } else { + mAnimationState = ANIMATION_STATE_IN; + requestRedraw(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + } + + private class AnimatorUpdater implements AnimatorUpdateListener { + AnimatorUpdater() { + } + + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + int alpha = (int) (SCROLLBAR_FULL_OPAQUE * ((float) valueAnimator.getAnimatedValue())); + mVerticalThumbDrawable.setAlpha(alpha); + mVerticalTrackDrawable.setAlpha(alpha); + requestRedraw(); + } + } +} diff --git a/app/src/main/java/eu/faircode/email/FixedRecyclerView.java b/app/src/main/java/eu/faircode/email/FixedRecyclerView.java index cc1eee05c8..f20e9434b8 100644 --- a/app/src/main/java/eu/faircode/email/FixedRecyclerView.java +++ b/app/src/main/java/eu/faircode/email/FixedRecyclerView.java @@ -20,24 +20,51 @@ package eu.faircode.email; */ import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; import android.util.AttributeSet; import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.FastScrollerEx; import androidx.recyclerview.widget.RecyclerView; public class FixedRecyclerView extends RecyclerView { public FixedRecyclerView(@NonNull Context context) { super(context); + initFastScrollerEx(context, null, R.attr.recyclerViewStyle); } public FixedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); + initFastScrollerEx(context, attrs, R.attr.recyclerViewStyle); } public FixedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + initFastScrollerEx(context, attrs, defStyle); + } + + private void initFastScrollerEx(Context context, AttributeSet attrs, int defStyleAttr) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView, + defStyleAttr, 0); + StateListDrawable verticalThumbDrawable = (StateListDrawable) a + .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable); + Drawable verticalTrackDrawable = a + .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable); + StateListDrawable horizontalThumbDrawable = (StateListDrawable) a + .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable); + Drawable horizontalTrackDrawable = a + .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable); + Resources resources = getContext().getResources(); + new FastScrollerEx(this, verticalThumbDrawable, verticalTrackDrawable, + horizontalThumbDrawable, horizontalTrackDrawable, + resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness), + resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range), + resources.getDimensionPixelOffset(R.dimen.fastscroll_margin)); } @Override diff --git a/app/src/main/res/layout/fragment_messages.xml b/app/src/main/res/layout/fragment_messages.xml index 3caa23bfef..1446323644 100644 --- a/app/src/main/res/layout/fragment_messages.xml +++ b/app/src/main/res/layout/fragment_messages.xml @@ -159,7 +159,7 @@ android:paddingBottom="90dp" android:scrollbarStyle="outsideOverlay" android:scrollbars="none" - app:fastScrollEnabled="true" + app:fastScrollEnabled="false" app:fastScrollHorizontalThumbDrawable="@drawable/scroll_thumb" app:fastScrollHorizontalTrackDrawable="@drawable/scroll_track" app:fastScrollVerticalThumbDrawable="@drawable/scroll_thumb"