Re-implement reorder and delete (drag&drop and swipe)
This commit is contained in:
parent
d7df8fcc19
commit
0324218735
7 changed files with 309 additions and 11 deletions
|
@ -37,7 +37,7 @@
|
||||||
<ConfirmationsSetting value="0" id="Add" />
|
<ConfirmationsSetting value="0" id="Add" />
|
||||||
<ConfirmationsSetting value="0" id="Remove" />
|
<ConfirmationsSetting value="0" id="Remove" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
@ -39,6 +39,7 @@ goes to Bruno.
|
||||||
|
|
||||||
* [Apache Commons Code](https://commons.apache.org/proper/commons-codec/)
|
* [Apache Commons Code](https://commons.apache.org/proper/commons-codec/)
|
||||||
* [Code Parts from Google's Android Samples](https://android.googlesource.com/platform/development/+/master/samples/Vault/src/com/example/android/vault)
|
* [Code Parts from Google's Android Samples](https://android.googlesource.com/platform/development/+/master/samples/Vault/src/com/example/android/vault)
|
||||||
|
* [SimpleItemTouchHelperCallback](https://github.com/iPaulPro/Android-ItemTouchHelper-Demo/blob/master/app/src/main/java/co/paulburke/android/itemtouchhelperdemo/helper/SimpleItemTouchHelperCallback.java) from [Android-ItemTouchHelper-Demo](https://github.com/iPaulPro/Android-ItemTouchHelper-Demo) by Paul Burke
|
||||||
* [MaterialProgressBar](https://github.com/DreaminginCodeZH/MaterialProgressBar)
|
* [MaterialProgressBar](https://github.com/DreaminginCodeZH/MaterialProgressBar)
|
||||||
* [ZXing](https://github.com/zxing/zxing)
|
* [ZXing](https://github.com/zxing/zxing)
|
||||||
* [ZXing Android Embedded](https://github.com/journeyapps/zxing-android-embedded)
|
* [ZXing Android Embedded](https://github.com/journeyapps/zxing-android-embedded)
|
||||||
|
|
|
@ -28,13 +28,19 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.util.List;
|
import org.shadowice.flocke.andotp.ItemTouchHelper.ItemTouchHelperAdapter;
|
||||||
|
import org.shadowice.flocke.andotp.ItemTouchHelper.ItemTouchHelperViewHolder;
|
||||||
|
|
||||||
public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.EntryViewHolder> {
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
private List<Entry> entries;
|
public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.EntryViewHolder>
|
||||||
|
implements ItemTouchHelperAdapter {
|
||||||
|
|
||||||
public EntriesCardAdapter(List<Entry> entries) {
|
private ArrayList<Entry> entries;
|
||||||
|
public MoveEventCallback moveEventCallback;
|
||||||
|
|
||||||
|
public EntriesCardAdapter(ArrayList<Entry> entries) {
|
||||||
this.entries = entries;
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,28 +56,75 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntriesCardAdapter.
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(EntryViewHolder entryViewHolder, int i) {
|
public void onBindViewHolder(EntryViewHolder entryViewHolder, int i) {
|
||||||
Entry entry = entries.get(i);
|
Entry entry = entries.get(i);
|
||||||
|
|
||||||
entryViewHolder.OTPValue.setText(entry.getCurrentOTP());
|
entryViewHolder.OTPValue.setText(entry.getCurrentOTP());
|
||||||
entryViewHolder.OTPLabel.setText(entry.getLabel());
|
entryViewHolder.OTPLabel.setText(entry.getLabel());
|
||||||
|
entryViewHolder.moveEventCallback = moveEventCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntryViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
public EntryViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
||||||
View itemView = LayoutInflater.
|
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_layout, viewGroup, false);
|
||||||
from(viewGroup.getContext()).
|
|
||||||
inflate(R.layout.card_layout, viewGroup, false);
|
|
||||||
|
|
||||||
return new EntryViewHolder(itemView);
|
return new EntryViewHolder(itemView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EntryViewHolder extends RecyclerView.ViewHolder {
|
@Override
|
||||||
|
public void onItemDismiss(int position) {
|
||||||
|
entries.remove(position);
|
||||||
|
notifyItemRemoved(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemMove(int fromPosition, int toPosition) {
|
||||||
|
if (fromPosition < toPosition) {
|
||||||
|
for (int i = fromPosition; i < toPosition; i++) {
|
||||||
|
Collections.swap(entries, i, i + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = fromPosition; i > toPosition; i--) {
|
||||||
|
Collections.swap(entries, i, i - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMoveEventCallback(MoveEventCallback cb) {
|
||||||
|
this.moveEventCallback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EntryViewHolder extends RecyclerView.ViewHolder
|
||||||
|
implements ItemTouchHelperViewHolder {
|
||||||
|
|
||||||
|
private MoveEventCallback moveEventCallback;
|
||||||
|
|
||||||
protected TextView OTPValue;
|
protected TextView OTPValue;
|
||||||
protected TextView OTPLabel;
|
protected TextView OTPLabel;
|
||||||
|
|
||||||
public EntryViewHolder(View v) {
|
public EntryViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|
||||||
OTPValue = (TextView) v.findViewById(R.id.textViewOTP);
|
OTPValue = (TextView) v.findViewById(R.id.textViewOTP);
|
||||||
OTPLabel = (TextView) v.findViewById(R.id.textViewLabel);
|
OTPLabel = (TextView) v.findViewById(R.id.textViewLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected() {
|
||||||
|
if (moveEventCallback != null)
|
||||||
|
moveEventCallback.onMoveEventStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClear() {
|
||||||
|
if (moveEventCallback != null)
|
||||||
|
moveEventCallback.onMoveEventStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MoveEventCallback {
|
||||||
|
void onMoveEventStart();
|
||||||
|
void onMoveEventStop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Paul Burke
|
||||||
|
*
|
||||||
|
* 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 org.shadowice.flocke.andotp.ItemTouchHelper;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
|
||||||
|
*
|
||||||
|
* @author Paul Burke (ipaulpro)
|
||||||
|
*/
|
||||||
|
public interface ItemTouchHelperAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an item has been dragged far enough to trigger a move. This is called every time
|
||||||
|
* an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
|
||||||
|
* <br/>
|
||||||
|
* Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
|
||||||
|
* adjusting the underlying data to reflect this move.
|
||||||
|
*
|
||||||
|
* @param fromPosition The start position of the moved item.
|
||||||
|
* @param toPosition Then resolved position of the moved item.
|
||||||
|
* @return True if the item was moved to the new adapter position.
|
||||||
|
*
|
||||||
|
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||||
|
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||||
|
*/
|
||||||
|
boolean onItemMove(int fromPosition, int toPosition);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an item has been dismissed by a swipe.<br/>
|
||||||
|
* <br/>
|
||||||
|
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after
|
||||||
|
* adjusting the underlying data to reflect this removal.
|
||||||
|
*
|
||||||
|
* @param position The position of the item dismissed.
|
||||||
|
*
|
||||||
|
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||||
|
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||||
|
*/
|
||||||
|
void onItemDismiss(int position);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Paul Burke
|
||||||
|
*
|
||||||
|
* 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 org.shadowice.flocke.andotp.ItemTouchHelper;
|
||||||
|
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to notify an item ViewHolder of relevant callbacks from {@link
|
||||||
|
* android.support.v7.widget.helper.ItemTouchHelper.Callback}.
|
||||||
|
*
|
||||||
|
* @author Paul Burke (ipaulpro)
|
||||||
|
*/
|
||||||
|
public interface ItemTouchHelperViewHolder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
|
||||||
|
* Implementations should update the item view to indicate it's active state.
|
||||||
|
*/
|
||||||
|
void onItemSelected();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
|
||||||
|
* state should be cleared.
|
||||||
|
*/
|
||||||
|
void onItemClear();
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Paul Burke
|
||||||
|
*
|
||||||
|
* 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 org.shadowice.flocke.andotp.ItemTouchHelper;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
|
||||||
|
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
|
||||||
|
* </br/>
|
||||||
|
* Expects the <code>RecyclerView.Adapter</code> to listen for {@link
|
||||||
|
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
|
||||||
|
* {@link ItemTouchHelperViewHolder}.
|
||||||
|
*
|
||||||
|
* @author Paul Burke (ipaulpro)
|
||||||
|
*/
|
||||||
|
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||||
|
|
||||||
|
public static final float ALPHA_FULL = 1.0f;
|
||||||
|
|
||||||
|
private final ItemTouchHelperAdapter mAdapter;
|
||||||
|
|
||||||
|
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||||
|
mAdapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLongPressDragEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isItemViewSwipeEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||||
|
// Set movement flags based on the layout manager
|
||||||
|
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
|
||||||
|
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
|
||||||
|
final int swipeFlags = 0;
|
||||||
|
return makeMovementFlags(dragFlags, swipeFlags);
|
||||||
|
} else {
|
||||||
|
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||||
|
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
|
||||||
|
return makeMovementFlags(dragFlags, swipeFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
|
||||||
|
if (source.getItemViewType() != target.getItemViewType()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the adapter of the move
|
||||||
|
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
|
||||||
|
// Notify the adapter of the dismissal
|
||||||
|
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||||
|
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||||
|
// Fade out the view as it is swiped out of the parent's bounds
|
||||||
|
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
|
||||||
|
viewHolder.itemView.setAlpha(alpha);
|
||||||
|
viewHolder.itemView.setTranslationX(dX);
|
||||||
|
} else {
|
||||||
|
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||||
|
// We only want the active item to change
|
||||||
|
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
|
||||||
|
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||||
|
// Let the view holder know that this item is being moved or dragged
|
||||||
|
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||||
|
itemViewHolder.onItemSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
|
||||||
|
viewHolder.itemView.setAlpha(ALPHA_FULL);
|
||||||
|
|
||||||
|
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||||
|
// Tell the view holder it's time to restore the idle state
|
||||||
|
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||||
|
itemViewHolder.onItemClear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -51,6 +52,8 @@ import android.widget.TextView;
|
||||||
import com.google.zxing.client.android.Intents;
|
import com.google.zxing.client.android.Intents;
|
||||||
import com.google.zxing.integration.android.IntentIntegrator;
|
import com.google.zxing.integration.android.IntentIntegrator;
|
||||||
|
|
||||||
|
import org.shadowice.flocke.andotp.ItemTouchHelper.SimpleItemTouchHelperCallback;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@ -157,6 +160,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
|
|
||||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||||
|
|
||||||
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
|
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
|
||||||
recList.setHasFixedSize(true);
|
recList.setHasFixedSize(true);
|
||||||
LinearLayoutManager llm = new LinearLayoutManager(this);
|
LinearLayoutManager llm = new LinearLayoutManager(this);
|
||||||
|
@ -168,6 +172,24 @@ public class MainActivity extends AppCompatActivity {
|
||||||
adapter = new EntriesCardAdapter(entries);
|
adapter = new EntriesCardAdapter(entries);
|
||||||
recList.setAdapter(adapter);
|
recList.setAdapter(adapter);
|
||||||
|
|
||||||
|
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
|
||||||
|
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
|
||||||
|
touchHelper.attachToRecyclerView(recList);
|
||||||
|
|
||||||
|
adapter.setMoveEventCallback(new EntriesCardAdapter.MoveEventCallback() {
|
||||||
|
@Override
|
||||||
|
public void onMoveEventStart() {
|
||||||
|
stopUpdater();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMoveEventStop() {
|
||||||
|
startUpdater();
|
||||||
|
|
||||||
|
SettingsHelper.store(getBaseContext(), entries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if(entries.isEmpty()){
|
if(entries.isEmpty()){
|
||||||
showNoAccount();
|
showNoAccount();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue