From 3ac7a1a3488fb68ead8e1ef3a6d11d8f38c89d96 Mon Sep 17 00:00:00 2001 From: Floens Date: Fri, 23 Oct 2015 19:27:19 +0200 Subject: [PATCH] Update to Android 6.0. Replace my homemade swipe-to-dismiss and drag implementation SwipeListener with the RecyclerView native ItemTouchHelper. Also add an undo snackbar when removing boards. --- Clover/app/build.gradle | 21 +- .../floens/chan/ui/adapter/PinAdapter.java | 71 +- .../ui/controller/BoardEditController.java | 110 +-- .../chan/ui/controller/DrawerController.java | 9 +- .../chan/ui/helper/SwipeItemAnimator.java | 673 ------------------ .../floens/chan/ui/helper/SwipeListener.java | 408 ----------- Clover/app/src/main/res/values/strings.xml | 1 + 7 files changed, 125 insertions(+), 1168 deletions(-) delete mode 100644 Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java delete mode 100644 Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index e0fbc8f7..d84c1600 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { minSdkVersion 14 - targetSdkVersion 22 + targetSdkVersion 23 versionName "v2.0.0" versionCode 51 @@ -21,6 +21,9 @@ android { abortOnError false } + // Needed for volley + useLibrary 'org.apache.http.legacy' + /* If you want to sign releases, make a file in app/keys.properties with the following content: keystoreFile=yourkey.store @@ -71,12 +74,12 @@ android { } dependencies { - compile 'com.android.support:support-v13:22.2.1' - compile 'com.android.support:appcompat-v7:22.2.1' - compile 'com.android.support:recyclerview-v7:22.2.1' - compile 'com.android.support:cardview-v7:22.2.1' - compile 'com.android.support:support-annotations:22.2.1' - compile 'com.android.support:design:22.2.1' + compile 'com.android.support:support-v13:23.1.0' + compile 'com.android.support:appcompat-v7:23.1.0' + compile 'com.android.support:recyclerview-v7:23.1.0' + compile 'com.android.support:cardview-v7:23.1.0' + compile 'com.android.support:support-annotations:23.1.0' + compile 'com.android.support:design:23.1.0' compile 'org.jsoup:jsoup:1.8.2' compile 'com.j256.ormlite:ormlite-core:4.48' diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java index 51ddfe4f..37f157a4 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java @@ -18,6 +18,7 @@ package org.floens.chan.ui.adapter; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -29,7 +30,6 @@ import org.floens.chan.R; import org.floens.chan.core.model.Pin; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.helper.PostHelper; -import org.floens.chan.ui.helper.SwipeListener; import org.floens.chan.ui.view.ThumbnailView; import org.floens.chan.utils.AndroidUtils; @@ -43,7 +43,7 @@ import static org.floens.chan.utils.AndroidUtils.getAttrColor; import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; import static org.floens.chan.utils.AndroidUtils.sp; -public class PinAdapter extends RecyclerView.Adapter implements SwipeListener.Callback { +public class PinAdapter extends RecyclerView.Adapter { private static final int PIN_OFFSET = 3; private static final int TYPE_HEADER = 0; @@ -64,6 +64,41 @@ public class PinAdapter extends RecyclerView.Adapter im this.highlighted = highlighted; } + public ItemTouchHelper.Callback getItemTouchHelperCallback() { + return new ItemTouchHelper.Callback() { + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + boolean pin = getItemViewType(viewHolder.getAdapterPosition()) == TYPE_PIN; + int dragFlags = pin ? ItemTouchHelper.UP | ItemTouchHelper.DOWN : 0; + int swipeFlags = pin ? ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT : 0; + + return makeMovementFlags(dragFlags, swipeFlags); + } + + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int from = viewHolder.getAdapterPosition(); + int to = target.getAdapterPosition(); + + if (getItemViewType(to) == TYPE_PIN) { + Pin item = pins.remove(from - PIN_OFFSET); + pins.add(to - PIN_OFFSET, item); + notifyItemMoved(from, to); + applyOrder(); + return true; + } else { + return false; + } + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + // Will call #onPinRemoved, and remove the pin from pins + callback.onPinRemoved(pins.get(viewHolder.getAdapterPosition() - PIN_OFFSET)); + } + }; + } + @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { @@ -173,34 +208,6 @@ public class PinAdapter extends RecyclerView.Adapter im } } - @Override - public SwipeListener.Swipeable getSwipeable(int position) { - return getItemViewType(position) == TYPE_PIN ? SwipeListener.Swipeable.RIGHT : SwipeListener.Swipeable.NO; - } - - @Override - public void removeItem(int position) { - applyOrder(); - callback.onPinRemoved(pins.get(position - PIN_OFFSET)); - } - - @Override - public boolean isMoveable(int position) { - return getItemViewType(position) == TYPE_PIN; - } - - @Override - public void moveItem(int from, int to) { - Pin item = pins.remove(from - PIN_OFFSET); - pins.add(to - PIN_OFFSET, item); - notifyItemMoved(from, to); - } - - @Override - public void movingDone() { - applyOrder(); - } - public void updatePinViewHolder(PinViewHolder holder, Pin pin) { CharSequence text = pin.loadable.title; if (pin.archived) { @@ -290,7 +297,7 @@ public class PinAdapter extends RecyclerView.Adapter im } }); - itemView.setOnLongClickListener(new View.OnLongClickListener() { + /*itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = getAdapterPosition() - PIN_OFFSET; @@ -300,7 +307,7 @@ public class PinAdapter extends RecyclerView.Adapter im return true; } - }); + });*/ watchCountText.setOnClickListener(new View.OnClickListener() { @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java index f8b9fc91..e36abd90 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java @@ -23,14 +23,15 @@ import android.content.DialogInterface; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Filter; @@ -46,7 +47,6 @@ import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.Board; import org.floens.chan.ui.drawable.ThumbDrawable; import org.floens.chan.ui.helper.BoardHelper; -import org.floens.chan.ui.helper.SwipeListener; import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; import org.floens.chan.ui.view.FloatingMenuItem; @@ -61,7 +61,7 @@ import java.util.Locale; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.fixSnackbarText; -public class BoardEditController extends Controller implements SwipeListener.Callback, View.OnClickListener, ToolbarMenuItem.ToolbarMenuItemCallback { +public class BoardEditController extends Controller implements View.OnClickListener, ToolbarMenuItem.ToolbarMenuItemCallback { private static final int OPTION_SORT_A_Z = 1; private final BoardManager boardManager = Chan.getBoardManager(); @@ -69,6 +69,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal private RecyclerView recyclerView; private BoardEditAdapter adapter; private FloatingActionButton add; + private ItemTouchHelper itemTouchHelper; private List boards; @@ -90,6 +91,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal view = inflateRes(R.layout.controller_board_edit); recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); add = (FloatingActionButton) view.findViewById(R.id.add); add.setOnClickListener(this); @@ -98,7 +100,53 @@ public class BoardEditController extends Controller implements SwipeListener.Cal adapter = new BoardEditAdapter(); recyclerView.setAdapter(adapter); - new SwipeListener(context, recyclerView, this); + itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() { + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + boolean isBoardItem = viewHolder.getAdapterPosition() > 0; + int dragFlags = isBoardItem ? ItemTouchHelper.UP | ItemTouchHelper.DOWN : 0; + int swipeFlags = isBoardItem ? ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT : 0; + return makeMovementFlags(dragFlags, swipeFlags); + } + + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int from = viewHolder.getAdapterPosition(); + int to = target.getAdapterPosition(); + + if (to > 0) { + Board item = boards.remove(from - 1); + boards.add(to - 1, item); + adapter.notifyItemMoved(from, to); + return true; + } else { + return false; + } + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + final int position = viewHolder.getAdapterPosition(); + final Board board = boards.get(position - 1); + board.saved = false; + boards.remove(position - 1); + adapter.notifyItemRemoved(position); + + Snackbar snackbar = Snackbar.make(view, context.getString(R.string.board_edit_board_removed, board.key), Snackbar.LENGTH_LONG); + fixSnackbarText(context, snackbar); + snackbar.setAction(R.string.undo, new View.OnClickListener() { + @Override + public void onClick(View v) { + board.saved = true; + boards.add(position - 1, board); + adapter.notifyDataSetChanged(); + } + }); + snackbar.show(); + } + }); + + itemTouchHelper.attachToRecyclerView(recyclerView); } @Override @@ -136,35 +184,6 @@ public class BoardEditController extends Controller implements SwipeListener.Cal } } - @Override - public SwipeListener.Swipeable getSwipeable(int position) { - return (position > 0 && boards.size() > 1) ? SwipeListener.Swipeable.BOTH : SwipeListener.Swipeable.NO; - } - - @Override - public void removeItem(int position) { - Board board = boards.get(position - 1); - board.saved = false; - boards.remove(position - 1); - adapter.notifyItemRemoved(position); - } - - @Override - public boolean isMoveable(int position) { - return position > 0; - } - - @Override - public void moveItem(int from, int to) { - Board item = boards.remove(from - 1); - boards.add(to - 1, item); - adapter.notifyItemMoved(from, to); - } - - @Override - public void movingDone() { - } - private void showAddBoardDialog() { LinearLayout wrap = new LinearLayout(context); wrap.setPadding(dp(16), dp(16), dp(16), 0); @@ -303,10 +322,8 @@ public class BoardEditController extends Controller implements SwipeListener.Cal @Override public View getView(int position, View convertView, ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - @SuppressLint("ViewHolder") - TextView view = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, parent, false); + TextView view = (TextView) LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false); Board b = filtered.get(position); view.setText("/" + b.value + "/ - " + b.key); @@ -314,9 +331,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { - InputMethodManager imm = (InputMethodManager) getContext().getSystemService( - Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(autoCompleteView.getWindowToken(), 0); + AndroidUtils.hideKeyboard(autoCompleteView); } return false; @@ -353,7 +368,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal private List getBoards() { // Lets be cheaty here: if the user has nsfw boards in the list, - // show them in the autofiller. + // show them in the autofiller, hide them otherwise. boolean showUnsafe = false; for (Board has : currentlyEditing) { if (!has.workSafe) { @@ -422,16 +437,25 @@ public class BoardEditController extends Controller implements SwipeListener.Cal } private class BoardEditItem extends RecyclerView.ViewHolder { - private ImageView image; + private ImageView thumb; private TextView text; private TextView description; public BoardEditItem(View itemView) { super(itemView); - image = (ImageView) itemView.findViewById(R.id.thumb); + thumb = (ImageView) itemView.findViewById(R.id.thumb); text = (TextView) itemView.findViewById(R.id.text); description = (TextView) itemView.findViewById(R.id.description); - image.setImageDrawable(new ThumbDrawable()); + thumb.setImageDrawable(new ThumbDrawable()); + + thumb.setOnTouchListener(new View.OnTouchListener() { + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemTouchHelper.startDrag(BoardEditItem.this); + } + return false; + } + }); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java index 21caeadb..264ae7d7 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java @@ -23,7 +23,9 @@ import android.content.res.Configuration; import android.support.design.widget.Snackbar; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.text.TextUtils; import android.view.Gravity; import android.view.KeyEvent; @@ -42,7 +44,6 @@ import org.floens.chan.controller.NavigationController; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Pin; import org.floens.chan.ui.adapter.PinAdapter; -import org.floens.chan.ui.helper.SwipeListener; import org.floens.chan.utils.AndroidUtils; import java.util.List; @@ -85,6 +86,7 @@ public class DrawerController extends Controller implements PinAdapter.Callback, drawer = (LinearLayout) view.findViewById(R.id.drawer); recyclerView = (RecyclerView) view.findViewById(R.id.drawer_recycler_view); recyclerView.setHasFixedSize(true); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); settings = (LinearLayout) view.findViewById(R.id.settings); settings.setOnClickListener(this); theme().settingsDrawable.apply((ImageView) settings.findViewById(R.id.image)); @@ -93,10 +95,11 @@ public class DrawerController extends Controller implements PinAdapter.Callback, pinAdapter = new PinAdapter(this); recyclerView.setAdapter(pinAdapter); - new SwipeListener(context, recyclerView, pinAdapter); - pinAdapter.onPinsChanged(watchManager.getPins()); + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(pinAdapter.getItemTouchHelperCallback()); + itemTouchHelper.attachToRecyclerView(recyclerView); + updateBadge(); AndroidUtils.waitForMeasure(drawer, new AndroidUtils.OnMeasuredCallback() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java deleted file mode 100644 index 6f7f6e79..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.helper; - -import android.support.v4.view.ViewCompat; -import android.support.v4.view.ViewPropertyAnimatorCompat; -import android.support.v4.view.ViewPropertyAnimatorListener; -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.LinearInterpolator; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - - -/** - * This is an adaption of the {@link android.support.v7.widget.DefaultItemAnimator} but to animate - * swipes to the left or right. - */ -public class SwipeItemAnimator extends RecyclerView.ItemAnimator { - private static final boolean DEBUG = true; - - private ArrayList mPendingRemovals = new ArrayList(); - private ArrayList mPendingAdditions = new ArrayList(); - private ArrayList mPendingMoves = new ArrayList(); - private ArrayList mPendingChanges = new ArrayList(); - - private ArrayList> mAdditionsList = - new ArrayList>(); - private ArrayList> mMovesList = new ArrayList>(); - private ArrayList> mChangesList = new ArrayList>(); - - private ArrayList mAddAnimations = new ArrayList(); - private ArrayList mMoveAnimations = new ArrayList(); - private ArrayList mRemoveAnimations = new ArrayList(); - private ArrayList mChangeAnimations = new ArrayList(); - - private static class MoveInfo { - public RecyclerView.ViewHolder holder; - public int fromX, fromY, toX, toY; - - private MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) { - this.holder = holder; - this.fromX = fromX; - this.fromY = fromY; - this.toX = toX; - this.toY = toY; - } - } - - private static class ChangeInfo { - public RecyclerView.ViewHolder oldHolder, newHolder; - public int fromX, fromY, toX, toY; - - private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) { - this.oldHolder = oldHolder; - this.newHolder = newHolder; - } - - private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, - int fromX, int fromY, int toX, int toY) { - this(oldHolder, newHolder); - this.fromX = fromX; - this.fromY = fromY; - this.toX = toX; - this.toY = toY; - } - - @Override - public String toString() { - return "ChangeInfo{" + - "oldHolder=" + oldHolder + - ", newHolder=" + newHolder + - ", fromX=" + fromX + - ", fromY=" + fromY + - ", toX=" + toX + - ", toY=" + toY + - '}'; - } - } - - @Override - public void runPendingAnimations() { - boolean removalsPending = !mPendingRemovals.isEmpty(); - boolean movesPending = !mPendingMoves.isEmpty(); - boolean changesPending = !mPendingChanges.isEmpty(); - boolean additionsPending = !mPendingAdditions.isEmpty(); - if (!removalsPending && !movesPending && !additionsPending && !changesPending) { - // nothing to animate - return; - } - // First, remove stuff - for (RecyclerView.ViewHolder holder : mPendingRemovals) { - animateRemoveImpl(holder); - } - mPendingRemovals.clear(); - // Next, move stuff - if (movesPending) { - final ArrayList moves = new ArrayList(); - moves.addAll(mPendingMoves); - mMovesList.add(moves); - mPendingMoves.clear(); - Runnable mover = new Runnable() { - @Override - public void run() { - for (MoveInfo moveInfo : moves) { - animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, - moveInfo.toX, moveInfo.toY); - } - moves.clear(); - mMovesList.remove(moves); - } - }; - if (removalsPending) { - View view = moves.get(0).holder.itemView; - ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); - } else { - mover.run(); - } - } - // Next, change stuff, to run in parallel with move animations - if (changesPending) { - final ArrayList changes = new ArrayList(); - changes.addAll(mPendingChanges); - mChangesList.add(changes); - mPendingChanges.clear(); - Runnable changer = new Runnable() { - @Override - public void run() { - for (ChangeInfo change : changes) { - animateChangeImpl(change); - } - changes.clear(); - mChangesList.remove(changes); - } - }; - if (removalsPending) { - RecyclerView.ViewHolder holder = changes.get(0).oldHolder; - ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); - } else { - changer.run(); - } - } - // Next, add stuff - if (additionsPending) { - final ArrayList additions = new ArrayList(); - additions.addAll(mPendingAdditions); - mAdditionsList.add(additions); - mPendingAdditions.clear(); - Runnable adder = new Runnable() { - public void run() { - for (RecyclerView.ViewHolder holder : additions) { - animateAddImpl(holder); - } - additions.clear(); - mAdditionsList.remove(additions); - } - }; - if (removalsPending || movesPending || changesPending) { - long removeDuration = removalsPending ? getRemoveDuration() : 0; - long moveDuration = movesPending ? getMoveDuration() : 0; - long changeDuration = changesPending ? getChangeDuration() : 0; - long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); - View view = additions.get(0).itemView; - ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); - } else { - adder.run(); - } - } - } - - @Override - public boolean animateRemove(final RecyclerView.ViewHolder holder) { - endAnimation(holder); - mPendingRemovals.add(holder); - return true; - } - - public static class SwipeAnimationData { - public long time; - public boolean right; - } - - private Map removeData = new HashMap<>(); - - public void addRemoveData(View view, SwipeAnimationData data) { - removeData.put(view, data); - } - - private void animateRemoveImpl(final RecyclerView.ViewHolder holder) { - final View view = holder.itemView; - - SwipeAnimationData data = removeData.remove(view); - if (data == null) { - data = new SwipeAnimationData(); - data.time = getRemoveDuration(); - data.right = true; - } - - final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); - animation.setDuration(data.time) - .translationX(data.right ? view.getWidth() : -view.getWidth()) - .alpha(0) - .setInterpolator(new LinearInterpolator()) - .setListener(new VpaListenerAdapter() { - @Override - public void onAnimationStart(View view) { - dispatchRemoveStarting(holder); - } - - @Override - public void onAnimationEnd(View view) { - animation.setListener(null); - - // Reset view properties - ViewCompat.setAlpha(view, 1); - ViewCompat.setTranslationX(view, 0f); - - dispatchRemoveFinished(holder); - mRemoveAnimations.remove(holder); - dispatchFinishedWhenDone(); - } - }).start(); - mRemoveAnimations.add(holder); - } - - @Override - public boolean animateAdd(final RecyclerView.ViewHolder holder) { - endAnimation(holder); - ViewCompat.setAlpha(holder.itemView, 0); - mPendingAdditions.add(holder); - return true; - } - - private void animateAddImpl(final RecyclerView.ViewHolder holder) { - final View view = holder.itemView; - mAddAnimations.add(holder); - final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); - animation.alpha(1).setInterpolator(new AccelerateDecelerateInterpolator()).setDuration(getAddDuration()). - setListener(new VpaListenerAdapter() { - @Override - public void onAnimationStart(View view) { - dispatchAddStarting(holder); - } - - @Override - public void onAnimationCancel(View view) { - ViewCompat.setAlpha(view, 1); - } - - @Override - public void onAnimationEnd(View view) { - animation.setListener(null); - dispatchAddFinished(holder); - mAddAnimations.remove(holder); - dispatchFinishedWhenDone(); - } - }).start(); - } - - @Override - public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY, - int toX, int toY) { - final View view = holder.itemView; - fromX += ViewCompat.getTranslationX(holder.itemView); - fromY += ViewCompat.getTranslationY(holder.itemView); - endAnimation(holder); - int deltaX = toX - fromX; - int deltaY = toY - fromY; - if (deltaX == 0 && deltaY == 0) { - dispatchMoveFinished(holder); - return false; - } - if (deltaX != 0) { - ViewCompat.setTranslationX(view, -deltaX); - } - if (deltaY != 0) { - ViewCompat.setTranslationY(view, -deltaY); - } - mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); - return true; - } - - private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) { - final View view = holder.itemView; - final int deltaX = toX - fromX; - final int deltaY = toY - fromY; - if (deltaX != 0) { - ViewCompat.animate(view).translationX(0); - } - if (deltaY != 0) { - ViewCompat.animate(view).translationY(0); - } - ViewCompat.animate(view).alpha(1f); - // TDO: make EndActions end listeners instead, since end actions aren't called when - // vpas are canceled (and can't end them. why?) - // need listener functionality in VPACompat for this. Ick. - mMoveAnimations.add(holder); - final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); - animation.setDuration(getMoveDuration()).setInterpolator(new AccelerateDecelerateInterpolator()).setListener(new VpaListenerAdapter() { - @Override - public void onAnimationStart(View view) { - dispatchMoveStarting(holder); - } - - @Override - public void onAnimationCancel(View view) { - if (deltaX != 0) { - ViewCompat.setTranslationX(view, 0); - } - if (deltaY != 0) { - ViewCompat.setTranslationY(view, 0); - } - ViewCompat.setAlpha(view, 1f); - } - - @Override - public void onAnimationEnd(View view) { - animation.setListener(null); - dispatchMoveFinished(holder); - mMoveAnimations.remove(holder); - dispatchFinishedWhenDone(); - } - }).start(); - } - - @Override - public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, - int fromX, int fromY, int toX, int toY) { - final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); - final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); - final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); - endAnimation(oldHolder); - int deltaX = (int) (toX - fromX - prevTranslationX); - int deltaY = (int) (toY - fromY - prevTranslationY); - // recover prev translation state after ending animation - ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); - ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); - ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); - if (newHolder != null && newHolder.itemView != null) { - // carry over translation values - endAnimation(newHolder); - ViewCompat.setTranslationX(newHolder.itemView, -deltaX); - ViewCompat.setTranslationY(newHolder.itemView, -deltaY); - ViewCompat.setAlpha(newHolder.itemView, 0); - } - mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); - return true; - } - - private void animateChangeImpl(final ChangeInfo changeInfo) { - final RecyclerView.ViewHolder holder = changeInfo.oldHolder; - final View view = holder.itemView; - final RecyclerView.ViewHolder newHolder = changeInfo.newHolder; - final View newView = newHolder != null ? newHolder.itemView : null; - mChangeAnimations.add(changeInfo.oldHolder); - - final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( - getChangeDuration()).setInterpolator(new AccelerateDecelerateInterpolator()); - oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); - oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); - oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { - @Override - public void onAnimationStart(View view) { - dispatchChangeStarting(changeInfo.oldHolder, true); - } - - @Override - public void onAnimationEnd(View view) { - oldViewAnim.setListener(null); - ViewCompat.setAlpha(view, 1); - ViewCompat.setTranslationX(view, 0); - ViewCompat.setTranslationY(view, 0); - dispatchChangeFinished(changeInfo.oldHolder, true); - mChangeAnimations.remove(changeInfo.oldHolder); - dispatchFinishedWhenDone(); - } - }).start(); - if (newView != null) { - mChangeAnimations.add(changeInfo.newHolder); - final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); - newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). - alpha(1).setInterpolator(new AccelerateDecelerateInterpolator()).setListener(new VpaListenerAdapter() { - @Override - public void onAnimationStart(View view) { - dispatchChangeStarting(changeInfo.newHolder, false); - } - - @Override - public void onAnimationEnd(View view) { - newViewAnimation.setListener(null); - ViewCompat.setAlpha(newView, 1); - ViewCompat.setTranslationX(newView, 0); - ViewCompat.setTranslationY(newView, 0); - dispatchChangeFinished(changeInfo.newHolder, false); - mChangeAnimations.remove(changeInfo.newHolder); - dispatchFinishedWhenDone(); - } - }).start(); - } - } - - private void endChangeAnimation(List infoList, RecyclerView.ViewHolder item) { - for (int i = infoList.size() - 1; i >= 0; i--) { - ChangeInfo changeInfo = infoList.get(i); - if (endChangeAnimationIfNecessary(changeInfo, item)) { - if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { - infoList.remove(changeInfo); - } - } - } - } - - private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { - if (changeInfo.oldHolder != null) { - endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); - } - if (changeInfo.newHolder != null) { - endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); - } - } - - private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) { - boolean oldItem = false; - if (changeInfo.newHolder == item) { - changeInfo.newHolder = null; - } else if (changeInfo.oldHolder == item) { - changeInfo.oldHolder = null; - oldItem = true; - } else { - return false; - } - ViewCompat.setAlpha(item.itemView, 1); - ViewCompat.setTranslationX(item.itemView, 0); - ViewCompat.setTranslationY(item.itemView, 0); - dispatchChangeFinished(item, oldItem); - return true; - } - - @Override - public void endAnimation(RecyclerView.ViewHolder item) { - final View view = item.itemView; - // this will trigger end callback which should set properties to their target values. - ViewCompat.animate(view).cancel(); - // TDO if some other animations are chained to end, how do we cancel them as well? - for (int i = mPendingMoves.size() - 1; i >= 0; i--) { - MoveInfo moveInfo = mPendingMoves.get(i); - if (moveInfo.holder == item) { - ViewCompat.setTranslationY(view, 0); - ViewCompat.setTranslationX(view, 0); - dispatchMoveFinished(item); - mPendingMoves.remove(item); - } - } - endChangeAnimation(mPendingChanges, item); - if (mPendingRemovals.remove(item)) { - ViewCompat.setAlpha(view, 1); - dispatchRemoveFinished(item); - } - if (mPendingAdditions.remove(item)) { - ViewCompat.setAlpha(view, 1); - dispatchAddFinished(item); - } - - for (int i = mChangesList.size() - 1; i >= 0; i--) { - ArrayList changes = mChangesList.get(i); - endChangeAnimation(changes, item); - if (changes.isEmpty()) { - mChangesList.remove(changes); - } - } - for (int i = mMovesList.size() - 1; i >= 0; i--) { - ArrayList moves = mMovesList.get(i); - for (int j = moves.size() - 1; j >= 0; j--) { - MoveInfo moveInfo = moves.get(j); - if (moveInfo.holder == item) { - ViewCompat.setTranslationY(view, 0); - ViewCompat.setTranslationX(view, 0); - dispatchMoveFinished(item); - moves.remove(j); - if (moves.isEmpty()) { - mMovesList.remove(moves); - } - break; - } - } - } - for (int i = mAdditionsList.size() - 1; i >= 0; i--) { - ArrayList additions = mAdditionsList.get(i); - if (additions.remove(item)) { - ViewCompat.setAlpha(view, 1); - dispatchAddFinished(item); - if (additions.isEmpty()) { - mAdditionsList.remove(additions); - } - } - } - - // animations should be ended by the cancel above. - if (mRemoveAnimations.remove(item) && DEBUG) { - throw new IllegalStateException("after animation is cancelled, item should not be in " - + "mRemoveAnimations list"); - } - - if (mAddAnimations.remove(item) && DEBUG) { - throw new IllegalStateException("after animation is cancelled, item should not be in " - + "mAddAnimations list"); - } - - if (mChangeAnimations.remove(item) && DEBUG) { - throw new IllegalStateException("after animation is cancelled, item should not be in " - + "mChangeAnimations list"); - } - - if (mMoveAnimations.remove(item) && DEBUG) { - throw new IllegalStateException("after animation is cancelled, item should not be in " - + "mMoveAnimations list"); - } - dispatchFinishedWhenDone(); - } - - @Override - public boolean isRunning() { - return (!mPendingAdditions.isEmpty() || - !mPendingChanges.isEmpty() || - !mPendingMoves.isEmpty() || - !mPendingRemovals.isEmpty() || - !mMoveAnimations.isEmpty() || - !mRemoveAnimations.isEmpty() || - !mAddAnimations.isEmpty() || - !mChangeAnimations.isEmpty() || - !mMovesList.isEmpty() || - !mAdditionsList.isEmpty() || - !mChangesList.isEmpty()); - } - - /** - * Check the state of currently pending and running animations. If there are none - * pending/running, call {@link #dispatchAnimationsFinished()} to notify any - * listeners. - */ - private void dispatchFinishedWhenDone() { - if (!isRunning()) { - dispatchAnimationsFinished(); - } - } - - @Override - public void endAnimations() { - int count = mPendingMoves.size(); - for (int i = count - 1; i >= 0; i--) { - MoveInfo item = mPendingMoves.get(i); - View view = item.holder.itemView; - ViewCompat.setTranslationY(view, 0); - ViewCompat.setTranslationX(view, 0); - dispatchMoveFinished(item.holder); - mPendingMoves.remove(i); - } - count = mPendingRemovals.size(); - for (int i = count - 1; i >= 0; i--) { - RecyclerView.ViewHolder item = mPendingRemovals.get(i); - dispatchRemoveFinished(item); - mPendingRemovals.remove(i); - } - count = mPendingAdditions.size(); - for (int i = count - 1; i >= 0; i--) { - RecyclerView.ViewHolder item = mPendingAdditions.get(i); - View view = item.itemView; - ViewCompat.setAlpha(view, 1); - dispatchAddFinished(item); - mPendingAdditions.remove(i); - } - count = mPendingChanges.size(); - for (int i = count - 1; i >= 0; i--) { - endChangeAnimationIfNecessary(mPendingChanges.get(i)); - } - mPendingChanges.clear(); - if (!isRunning()) { - return; - } - - int listCount = mMovesList.size(); - for (int i = listCount - 1; i >= 0; i--) { - ArrayList moves = mMovesList.get(i); - count = moves.size(); - for (int j = count - 1; j >= 0; j--) { - MoveInfo moveInfo = moves.get(j); - RecyclerView.ViewHolder item = moveInfo.holder; - View view = item.itemView; - ViewCompat.setTranslationY(view, 0); - ViewCompat.setTranslationX(view, 0); - dispatchMoveFinished(moveInfo.holder); - moves.remove(j); - if (moves.isEmpty()) { - mMovesList.remove(moves); - } - } - } - listCount = mAdditionsList.size(); - for (int i = listCount - 1; i >= 0; i--) { - ArrayList additions = mAdditionsList.get(i); - count = additions.size(); - for (int j = count - 1; j >= 0; j--) { - RecyclerView.ViewHolder item = additions.get(j); - View view = item.itemView; - ViewCompat.setAlpha(view, 1); - dispatchAddFinished(item); - additions.remove(j); - if (additions.isEmpty()) { - mAdditionsList.remove(additions); - } - } - } - listCount = mChangesList.size(); - for (int i = listCount - 1; i >= 0; i--) { - ArrayList changes = mChangesList.get(i); - count = changes.size(); - for (int j = count - 1; j >= 0; j--) { - endChangeAnimationIfNecessary(changes.get(j)); - if (changes.isEmpty()) { - mChangesList.remove(changes); - } - } - } - - cancelAll(mRemoveAnimations); - cancelAll(mMoveAnimations); - cancelAll(mAddAnimations); - cancelAll(mChangeAnimations); - - dispatchAnimationsFinished(); - } - - void cancelAll(List viewHolders) { - for (int i = viewHolders.size() - 1; i >= 0; i--) { - ViewCompat.animate(viewHolders.get(i).itemView).cancel(); - } - } - - private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { - @Override - public void onAnimationStart(View view) { - } - - @Override - public void onAnimationEnd(View view) { - } - - @Override - public void onAnimationCancel(View view) { - } - } - - ; -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java deleted file mode 100644 index d6feefba..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.helper; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.util.Log; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; - -import org.floens.chan.R; - -import static org.floens.chan.utils.AndroidUtils.dp; - - -/** - * An ItemDecorator and Touch listener that enabled the list to be reordered and items to be swiped away. - * It isn't perfect, but works good for what is should do. - */ -public class SwipeListener extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener { - public enum Swipeable { - NO, - LEFT, - RIGHT, - BOTH; - } - - private static final String TAG = "SwipeListener"; - private static final int MAX_SCROLL_SPEED = 14; // dp/s - - private final int slopPixels; - private final int flingPixels; - private final int maxFlingPixels; - - private Callback callback; - private final RecyclerView recyclerView; - private final LinearLayoutManager layoutManager; - private final SwipeItemAnimator swipeItemAnimator; - - private VelocityTracker tracker; - private boolean swiping; - private float touchDownX; - private float touchDownY; - private float totalScrolled; - private float touchDownOffsetX; - private float offsetX; - private View downView; - - private boolean dragging; - private boolean somePositionChanged = false; - private int dragPosition = -1; - private float offsetY; - private float touchDownOffsetY; - - private final Runnable scrollRunnable = new Runnable() { - @Override - public void run() { - if (dragging) { - float scroll; - boolean up = offsetY < recyclerView.getHeight() / 2f; - if (up) { - scroll = Math.max(-dp(MAX_SCROLL_SPEED), (offsetY - recyclerView.getHeight() / 6f) * 0.1f); - } else { - scroll = Math.min(dp(MAX_SCROLL_SPEED), (offsetY - recyclerView.getHeight() * 5f / 6f) * 0.1f); - } - - if (up && scroll < 0f && layoutManager.findFirstCompletelyVisibleItemPosition() != 0) { - recyclerView.scrollBy(0, (int) scroll); - } else if (!up && scroll > 0f && layoutManager.findLastCompletelyVisibleItemPosition() != recyclerView.getAdapter().getItemCount() - 1) { - recyclerView.scrollBy(0, (int) scroll); - } - - if (scroll != 0) { - processDrag(); - recyclerView.post(scrollRunnable); - } - } - } - }; - - public SwipeListener(Context context, RecyclerView rv, Callback callback) { - recyclerView = rv; - this.callback = callback; - - layoutManager = new LinearLayoutManager(context); - rv.setLayoutManager(layoutManager); - swipeItemAnimator = new SwipeItemAnimator(); - swipeItemAnimator.setMoveDuration(250); - rv.setItemAnimator(swipeItemAnimator); - rv.addOnItemTouchListener(this); - rv.addItemDecoration(this); - - ViewConfiguration viewConfiguration = ViewConfiguration.get(context); - slopPixels = viewConfiguration.getScaledTouchSlop(); - flingPixels = viewConfiguration.getScaledMinimumFlingVelocity(); - maxFlingPixels = viewConfiguration.getScaledMaximumFlingVelocity(); - } - - @Override - public void onRequestDisallowInterceptTouchEvent(boolean b) { - } - - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { - switch (e.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - totalScrolled = 0f; - touchDownX = e.getRawX(); - touchDownY = e.getRawY(); - downView = rv.findChildViewUnder(e.getX(), e.getY()); - if (downView == null) { - // There can be gaps when a move animation is running - break; - } - - // Dragging gets initiated immediately if the touch went down on the thumb area - // Do not allow dragging when animations are running - View thumbView = downView.findViewById(R.id.thumb); - if (thumbView != null && e.getX() < rv.getPaddingLeft() + thumbView.getRight() && !swipeItemAnimator.isRunning()) { - int touchAdapterPos = rv.getChildAdapterPosition(downView); - if (touchAdapterPos < 0 || !callback.isMoveable(touchAdapterPos)) { - break; - } - - dragging = true; - dragPosition = touchAdapterPos; - - rv.post(scrollRunnable); - - offsetY = e.getY(); - touchDownOffsetY = offsetY - downView.getTop(); - - downView.setVisibility(View.INVISIBLE); - rv.invalidate(); - - return true; - } - - // Didn't went down on the thumb area, start up the tracker - if (tracker != null) { - Log.w(TAG, "Tracker was not null, recycling extra"); - tracker.recycle(); - } - tracker = VelocityTracker.obtain(); - tracker.addMovement(e); - break; - case MotionEvent.ACTION_MOVE: - if (dragging) { - return true; - } - - float deltaX = e.getRawX() - touchDownX; - float deltaY = e.getRawY() - touchDownY; - totalScrolled += Math.abs(deltaY); - int adapterPosition = rv.getChildAdapterPosition(downView); - if (adapterPosition < 0) { - break; - } - - if (swiping) { - return true; - } else { - // Logic to find out if a swipe should be initiated - Swipeable swipeable = callback.getSwipeable(adapterPosition); - if (swipeable != Swipeable.NO && Math.abs(deltaX) >= slopPixels && totalScrolled < slopPixels) { - boolean wasSwiped = false; - if (swipeable == Swipeable.BOTH) { - wasSwiped = true; - } else if (swipeable == Swipeable.LEFT && deltaX < -slopPixels) { - wasSwiped = true; - } else if (swipeable == Swipeable.RIGHT && deltaX > slopPixels) { - wasSwiped = true; - } - - if (wasSwiped) { - swiping = true; - touchDownOffsetX = deltaX; - return true; - } - } - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - reset(); - break; - } - - return false; - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent e) { - if (swiping) { - float deltaX = e.getRawX() - touchDownX; - switch (e.getActionMasked()) { - case MotionEvent.ACTION_MOVE: { - tracker.addMovement(e); - offsetX = deltaX - touchDownOffsetX; - downView.setTranslationX(offsetX); - downView.setAlpha(Math.min(1f, Math.max(0f, 1f - (Math.abs(offsetX) / (float) downView.getWidth())))); - break; - } - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - boolean reset = false; - - int adapterPosition = rv.getChildAdapterPosition(downView); - if (adapterPosition < 0) { - reset = true; - } else if (e.getActionMasked() == MotionEvent.ACTION_UP) { - tracker.addMovement(e); - tracker.computeCurrentVelocity(1000); - float xVelocity = tracker.getXVelocity(); - - if (Math.abs(xVelocity) > flingPixels && Math.abs(xVelocity) < maxFlingPixels && - (xVelocity < 0) == (deltaX < 0) // Swiping in the same direction - ) { - SwipeItemAnimator.SwipeAnimationData data = new SwipeItemAnimator.SwipeAnimationData(); - data.right = xVelocity > 0; - - // Remove animations are linear, calculate the time here to mimic the fling speed - float timeLeft = (rv.getWidth() - Math.abs(offsetX)) / Math.abs(xVelocity); - timeLeft = Math.min(0.5f, timeLeft); - data.time = (long) (timeLeft * 1000f); - swipeItemAnimator.addRemoveData(downView, data); - callback.removeItem(rv.getChildAdapterPosition(downView)); - } else { - reset = true; - } - } else { - reset = true; - } - - // The item should be reset to its original alpha and position. - // Otherwise our SwipeItemAnimator will handle the swipe remove animation - if (reset) { - swipeItemAnimator.animateMove(rv.getChildViewHolder(downView), 0, 0, 0, 0); - swipeItemAnimator.runPendingAnimations(); - } - - reset(); - break; - } - } - } else if (dragging) { - // Invalidate hover view - recyclerView.invalidate(); - - switch (e.getActionMasked()) { - case MotionEvent.ACTION_MOVE: { - offsetY = e.getY(); - - processDrag(); - - break; - } - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - if (somePositionChanged) { - callback.movingDone(); - } - - RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(downView); - swipeItemAnimator.endAnimation(vh); - float floatingViewPos = offsetY - touchDownOffsetY - downView.getTop(); - swipeItemAnimator.animateMove(vh, 0, (int) floatingViewPos, 0, 0); - swipeItemAnimator.runPendingAnimations(); - - reset(); - break; - } - } - } - } - - private void processDrag() { - float floatingViewPos = offsetY - touchDownOffsetY + downView.getHeight() / 2f; - - View viewAtPosition = null; - - // like findChildUnder, but without looking at the x axis - for (int c = layoutManager.getChildCount(), i = c - 1; i >= 0; i--) { - final View child = layoutManager.getChildAt(i); - if (floatingViewPos >= child.getTop() && floatingViewPos <= child.getBottom()) { - viewAtPosition = child; - break; - } - } - - if (viewAtPosition == null) { - return; - } - - int touchAdapterPos = recyclerView.getChildAdapterPosition(viewAtPosition); - if (touchAdapterPos < 0) { - return; - } - - int firstCompletelyVisible = layoutManager.findFirstCompletelyVisibleItemPosition(); - int lastCompletelyVisible = layoutManager.findLastCompletelyVisibleItemPosition(); - - if (touchAdapterPos < firstCompletelyVisible || touchAdapterPos > lastCompletelyVisible) { - return; - } - - if ((touchAdapterPos > dragPosition && floatingViewPos > viewAtPosition.getTop() + viewAtPosition.getHeight() / 5f) || - (touchAdapterPos < dragPosition && floatingViewPos < viewAtPosition.getTop() + viewAtPosition.getHeight() * 4f / 5f)) { - if (callback.isMoveable(touchAdapterPos)) { - callback.moveItem(dragPosition, touchAdapterPos); - dragPosition = touchAdapterPos; - somePositionChanged = true; - } - } - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { - if (swiping && this.downView == view) { - outRect.set((int) offsetX, 0, (int) -offsetX, 0); - } else { - outRect.set(0, 0, 0, 0); - } - } - - @Override - public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) { - if (dragging) { - for (int i = 0, c = layoutManager.getChildCount(); i < c; i++) { - View child = layoutManager.getChildAt(i); - if (child.getVisibility() != View.VISIBLE) { - child.setVisibility(View.VISIBLE); - } - } - - RecyclerView.ViewHolder vh = parent.findViewHolderForAdapterPosition(dragPosition); - if (vh != null) { - vh.itemView.setVisibility(View.INVISIBLE); - } - } - } - - @Override - public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) { - if (dragging) { - RecyclerView.ViewHolder vh = parent.findViewHolderForAdapterPosition(dragPosition); - if (vh != null) { - int left = parent.getPaddingLeft(); - int top = (int) offsetY - (int) touchDownOffsetY; - canvas.save(); - canvas.translate(left, top); - vh.itemView.draw(canvas); - canvas.restore(); - } - } - } - - private void reset() { - if (tracker != null) { - tracker.recycle(); - tracker = null; - } - downView = null; - for (int i = 0, c = layoutManager.getChildCount(); i < c; i++) { - View child = layoutManager.getChildAt(i); - if (child.getVisibility() != View.VISIBLE) { - child.setVisibility(View.VISIBLE); - } - } - swiping = false; - offsetX = 0f; - dragging = false; - dragPosition = -1; - somePositionChanged = false; - } - - public interface Callback { - Swipeable getSwipeable(int position); - - void removeItem(int position); - - boolean isMoveable(int position); - - void moveItem(int from, int to); - - void movingDone(); - } -} diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index ea8bdaa3..a83c614a 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -142,6 +142,7 @@ along with this program. If not, see . Unknown board code The board with code %1$s is not known. Sort A-Z + Removed board \'%1$s\' all boards Enabled