From c41100eff984a3e05f6c51e004c8b69975425db7 Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Wed, 29 Apr 2020 08:53:27 +0200 Subject: Use official FastScroll fix and refactor PasswordFragment (#753) --- .../widget/FixOnItemTouchDispatchRecyclerView.java | 341 --------------------- .../java/com/zeapo/pwdstore/PasswordFragment.kt | 17 +- 2 files changed, 6 insertions(+), 352 deletions(-) delete mode 100644 app/src/main/java/androidx/recyclerview/widget/FixOnItemTouchDispatchRecyclerView.java (limited to 'app/src/main/java') diff --git a/app/src/main/java/androidx/recyclerview/widget/FixOnItemTouchDispatchRecyclerView.java b/app/src/main/java/androidx/recyclerview/widget/FixOnItemTouchDispatchRecyclerView.java deleted file mode 100644 index 58590a66..00000000 --- a/app/src/main/java/androidx/recyclerview/widget/FixOnItemTouchDispatchRecyclerView.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * 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 - * - * https://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.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import me.zhanghai.android.fastscroll.FastScroller; -import me.zhanghai.android.fastscroll.PopupTextProvider; -import me.zhanghai.android.fastscroll.Predicate; -import me.zhanghai.android.fastscroll.ViewHelperProvider; - -public class FixOnItemTouchDispatchRecyclerView extends RecyclerView implements ViewHelperProvider { - - @NonNull - private final ViewHelper mViewHelper = new ViewHelper(this); - - @Nullable - private OnItemTouchListener mPhantomOnItemTouchListener = null; - private OnItemTouchListener mInterceptingOnItemTouchListener = null; - - public FixOnItemTouchDispatchRecyclerView(@NonNull Context context) { - super(context); - } - - public FixOnItemTouchDispatchRecyclerView(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public FixOnItemTouchDispatchRecyclerView(@NonNull Context context, - @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @NonNull - @Override - public FastScroller.ViewHelper getViewHelper() { - return mViewHelper; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent e) { - mInterceptingOnItemTouchListener = null; - if (findInterceptingOnItemTouchListener(e)) { - cancelScroll(); - return true; - } - return super.onInterceptTouchEvent(e); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent e) { - if (dispatchOnItemTouchListeners(e)) { - cancelScroll(); - return true; - } - return super.onTouchEvent(e); - } - - @Override - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - if (mPhantomOnItemTouchListener != null) { - mPhantomOnItemTouchListener.onRequestDisallowInterceptTouchEvent(disallowIntercept); - } - super.requestDisallowInterceptTouchEvent(disallowIntercept); - } - - private void cancelScroll() { - MotionEvent syntheticCancel = MotionEvent.obtain( - 0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0); - super.onInterceptTouchEvent(syntheticCancel); - syntheticCancel.recycle(); - } - - private boolean dispatchOnItemTouchListeners(@NonNull MotionEvent e) { - if (mInterceptingOnItemTouchListener == null) { - if (e.getAction() == MotionEvent.ACTION_DOWN) { - return false; - } - return findInterceptingOnItemTouchListener(e); - } else { - mInterceptingOnItemTouchListener.onTouchEvent(this, e); - final int action = e.getAction(); - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - mInterceptingOnItemTouchListener = null; - } - return true; - } - } - - private boolean findInterceptingOnItemTouchListener(@NonNull MotionEvent e) { - int action = e.getAction(); - if (mPhantomOnItemTouchListener != null - && mPhantomOnItemTouchListener.onInterceptTouchEvent(this, e) - && action != MotionEvent.ACTION_CANCEL) { - mInterceptingOnItemTouchListener = mPhantomOnItemTouchListener; - return true; - } - return false; - } - - private static class RecyclerViewHelper implements FastScroller.ViewHelper { - - @NonNull - private final RecyclerView mView; - - @NonNull - private final Rect mTempRect = new Rect(); - - public RecyclerViewHelper(@NonNull RecyclerView view) { - mView = view; - } - - @Override - public void addOnPreDrawListener(@NonNull Runnable onPreDraw) { - mView.addItemDecoration(new RecyclerView.ItemDecoration() { - @Override - public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, - @NonNull RecyclerView.State state) { - onPreDraw.run(); - } - }); - } - - @Override - public void addOnScrollChangedListener(@NonNull Runnable onScrollChanged) { - mView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - onScrollChanged.run(); - } - }); - } - - @Override - public void addOnTouchEventListener(@NonNull Predicate onTouchEvent) { - mView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() { - @Override - public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, - @NonNull MotionEvent event) { - return onTouchEvent.test(event); - } - @Override - public void onTouchEvent(@NonNull RecyclerView recyclerView, - @NonNull MotionEvent event) { - onTouchEvent.test(event); - } - }); - } - - @Override - public int getScrollRange() { - int itemCount = getItemCount(); - if (itemCount == 0) { - return 0; - } - int itemHeight = getItemHeight(); - if (itemHeight == 0) { - return 0; - } - return mView.getPaddingTop() + itemCount * itemHeight + mView.getPaddingBottom(); - } - - @Override - public int getScrollOffset() { - int firstItemPosition = getFirstItemPosition(); - if (firstItemPosition == RecyclerView.NO_POSITION) { - return 0; - } - int itemHeight = getItemHeight(); - int firstItemTop = getFirstItemOffset(); - return mView.getPaddingTop() + firstItemPosition * itemHeight - firstItemTop; - } - - @Override - public void scrollTo(int offset) { - // Stop any scroll in progress for RecyclerView. - mView.stopScroll(); - offset -= mView.getPaddingTop(); - int itemHeight = getItemHeight(); - // firstItemPosition should be non-negative even if paddingTop is greater than item height. - int firstItemPosition = Math.max(0, offset / itemHeight); - int firstItemTop = firstItemPosition * itemHeight - offset; - scrollToPositionWithOffset(firstItemPosition, firstItemTop); - } - - @Nullable - @Override - public String getPopupText() { - RecyclerView.Adapter adapter = mView.getAdapter(); - if (!(adapter instanceof PopupTextProvider)) { - return null; - } - PopupTextProvider popupTextProvider = (PopupTextProvider) adapter; - int position = getFirstItemAdapterPosition(); - if (position == RecyclerView.NO_POSITION) { - return null; - } - return popupTextProvider.getPopupText(position); - } - - private int getItemCount() { - LinearLayoutManager linearLayoutManager = getVerticalLinearLayoutManager(); - if (linearLayoutManager == null) { - return 0; - } - int itemCount = linearLayoutManager.getItemCount(); - if (itemCount == 0) { - return 0; - } - if (linearLayoutManager instanceof GridLayoutManager) { - GridLayoutManager gridLayoutManager = (GridLayoutManager) linearLayoutManager; - itemCount = (itemCount - 1) / gridLayoutManager.getSpanCount() + 1; - } - return itemCount; - } - - private int getItemHeight() { - if (mView.getChildCount() == 0) { - return 0; - } - View itemView = mView.getChildAt(0); - mView.getDecoratedBoundsWithMargins(itemView, mTempRect); - return mTempRect.height(); - } - - private int getFirstItemPosition() { - int position = getFirstItemAdapterPosition(); - LinearLayoutManager linearLayoutManager = getVerticalLinearLayoutManager(); - if (linearLayoutManager == null) { - return RecyclerView.NO_POSITION; - } - if (linearLayoutManager instanceof GridLayoutManager) { - GridLayoutManager gridLayoutManager = (GridLayoutManager) linearLayoutManager; - position /= gridLayoutManager.getSpanCount(); - } - return position; - } - - private int getFirstItemAdapterPosition() { - if (mView.getChildCount() == 0) { - return RecyclerView.NO_POSITION; - } - View itemView = mView.getChildAt(0); - LinearLayoutManager linearLayoutManager = getVerticalLinearLayoutManager(); - if (linearLayoutManager == null) { - return RecyclerView.NO_POSITION; - } - return linearLayoutManager.getPosition(itemView); - } - - private int getFirstItemOffset() { - if (mView.getChildCount() == 0) { - return RecyclerView.NO_POSITION; - } - View itemView = mView.getChildAt(0); - mView.getDecoratedBoundsWithMargins(itemView, mTempRect); - return mTempRect.top; - } - - private void scrollToPositionWithOffset(int position, int offset) { - LinearLayoutManager linearLayoutManager = getVerticalLinearLayoutManager(); - if (linearLayoutManager == null) { - return; - } - if (linearLayoutManager instanceof GridLayoutManager) { - GridLayoutManager gridLayoutManager = (GridLayoutManager) linearLayoutManager; - position *= gridLayoutManager.getSpanCount(); - } - // LinearLayoutManager actually takes offset from paddingTop instead of top of RecyclerView. - offset -= mView.getPaddingTop(); - linearLayoutManager.scrollToPositionWithOffset(position, offset); - } - - @Nullable - private LinearLayoutManager getVerticalLinearLayoutManager() { - RecyclerView.LayoutManager layoutManager = mView.getLayoutManager(); - if (!(layoutManager instanceof LinearLayoutManager)) { - return null; - } - LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; - if (linearLayoutManager.getOrientation() != RecyclerView.VERTICAL) { - return null; - } - return linearLayoutManager; - } - } - - private class ViewHelper extends RecyclerViewHelper { - - ViewHelper(@NonNull RecyclerView view) { - super(view); - } - - @Override - public void addOnTouchEventListener(@NonNull Predicate onTouchEvent) { - mPhantomOnItemTouchListener = new RecyclerView.SimpleOnItemTouchListener() { - @Override - public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, - @NonNull MotionEvent event) { - return onTouchEvent.test(event); - } - - @Override - public void onTouchEvent(@NonNull RecyclerView recyclerView, - @NonNull MotionEvent event) { - onTouchEvent.test(event); - } - }; - } - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt index 1b9d3f9c..ecf1a887 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt @@ -19,9 +19,7 @@ import androidx.appcompat.view.ActionMode import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.observe -import androidx.recyclerview.widget.FixOnItemTouchDispatchRecyclerView import androidx.recyclerview.widget.LinearLayoutManager -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar import com.zeapo.pwdstore.databinding.PasswordRecyclerViewBinding import com.zeapo.pwdstore.git.BaseGitActivity @@ -38,9 +36,7 @@ import me.zhanghai.android.fastscroll.FastScrollerBuilder class PasswordFragment : Fragment() { private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter - private lateinit var recyclerView: FixOnItemTouchDispatchRecyclerView private lateinit var listener: OnFragmentInteractionListener - private lateinit var swipeRefresher: SwipeRefreshLayout private var recyclerViewStateToRestore: Parcelable? = null private var actionMode: ActionMode? = null @@ -67,8 +63,7 @@ class PasswordFragment : Fragment() { } private fun initializePasswordList() { - swipeRefresher = binding.swipeRefresher - swipeRefresher.setOnRefreshListener { + binding.swipeRefresher.setOnRefreshListener { if (!PasswordRepository.isGitRepo()) { Snackbar.make(binding.root, getString(R.string.clone_git_repo), Snackbar.LENGTH_INDEFINITE) .setAction(R.string.clone_button) { @@ -77,7 +72,7 @@ class PasswordFragment : Fragment() { startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE) } .show() - swipeRefresher.isRefreshing = false + binding.swipeRefresher.isRefreshing = false } else { val intent = Intent(context, GitOperationActivity::class.java) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_SYNC) @@ -92,7 +87,7 @@ class PasswordFragment : Fragment() { .onSelectionChanged { selection -> // In order to not interfere with drag selection, we disable the SwipeRefreshLayout // once an item is selected. - swipeRefresher.isEnabled = selection.isEmpty + binding.swipeRefresher.isEnabled = selection.isEmpty if (actionMode == null) actionMode = requireStore().startSupportActionMode(actionModeCallback) @@ -105,7 +100,7 @@ class PasswordFragment : Fragment() { actionMode!!.finish() } } - recyclerView = binding.passRecycler + val recyclerView = binding.passRecycler recyclerView.apply { layoutManager = LinearLayoutManager(requireContext()) itemAnimator = OnOffItemAnimator() @@ -232,7 +227,7 @@ class PasswordFragment : Fragment() { requireStore().clearSearch() model.navigateTo( item.file, - recyclerViewState = recyclerView.layoutManager!!.onSaveInstanceState() + recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState() ) requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true) } else { @@ -250,7 +245,7 @@ class PasswordFragment : Fragment() { } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - swipeRefresher.isRefreshing = false + binding.swipeRefresher.isRefreshing = false } /** -- cgit v1.2.3