package com.mikepenz.fastadapter_extensions;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.TextView;
import com.mikepenz.fastadapter.FastAdapter;
import com.mikepenz.fastadapter.IItem;
import com.mikepenz.fastadapter.IItemAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Created by mikepenz on 04.01.16.
*/
public class UndoHelper<Item extends IItem> {
private static final int ACTION_REMOVE = 2;
private FastAdapter<Item> mAdapter;
private UndoListener<Item> mUndoListener;
private History mHistory = null;
private Snackbar mSnackBar = null;
private String mSnackbarActionText = "";
/**
* Constructor to create the UndoHelper
*
* @param adapter the root FastAdapter
* @param undoListener the listener which gets called when an item was really removed
*/
public UndoHelper(FastAdapter<Item> adapter, UndoListener<Item> undoListener) {
this.mAdapter = adapter;
this.mUndoListener = undoListener;
}
/**
* an optional method to add a {@link Snackbar} of your own with custom styling.
* note that using this method will override your custom action
*
* @param snackBar your own Snackbar
* @param actionText the text to show for the Undo Action
*/
public void withSnackBar(@NonNull Snackbar snackBar, String actionText) {
mSnackBar = snackBar;
mSnackbarActionText = actionText;
mSnackBar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
switch (event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
//we can ignore it
break;
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
notifyCommit();
break;
case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
notifyCommit();
break;
case Snackbar.Callback.DISMISS_EVENT_SWIPE:
notifyCommit();
break;
}
}
@Override
public void onShown(Snackbar snackbar) {
super.onShown(snackbar);
doChange();
}
}).setAction(actionText, new View.OnClickListener() {
@Override
public void onClick(View v) {
undoChange();
}
});
}
public @Nullable Snackbar getSnackBar() {
return mSnackBar;
}
/**
* convenience method to be used if you have previously set a {@link Snackbar} with {@link #withSnackBar(Snackbar, String)}
*
* @param positions the positions where the items were removed
* @return the snackbar or null if {@link #withSnackBar(Snackbar, String)} was not previously called
*/
public @Nullable Snackbar remove(Set<Integer> positions) {
if (mSnackBar == null) {
return null;
}
View snackbarView = mSnackBar.getView();
TextView snackbarText = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
return remove(snackbarView, snackbarText.getText().toString(), mSnackbarActionText, mSnackBar.getDuration(), positions);
}
/**
* removes items from the ItemAdapter.
* note that the values of "view", "text", "actionText", and "duration"
* will be ignored if {@link #withSnackBar(Snackbar, String)} was used.
* if it was not used, a default snackbar will be generated
*
* @param view the view which will host the SnackBar
* @param text the text to show on the SnackBar
* @param actionText the text to show for the Undo Action
* @param positions the positions where the items were removed
* @return the generated Snackbar
*/
public Snackbar remove(final View view, final String text, final String actionText, @Snackbar.Duration int duration, final Set<Integer> positions) {
if (mHistory != null) {
notifyCommit();
}
History history = new History();
history.action = ACTION_REMOVE;
for (int position : positions) {
history.items.add(mAdapter.getRelativeInfo(position));
}
Collections.sort(history.items, new Comparator<FastAdapter.RelativeInfo<Item>>() {
@Override
public int compare(FastAdapter.RelativeInfo<Item> lhs, FastAdapter.RelativeInfo<Item> rhs) {
return Integer.valueOf(lhs.position).compareTo(rhs.position);
}
});
mHistory = history;
mSnackBar = Snackbar.make(view, text, duration)
.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
switch (event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
//we can ignore it
break;
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
notifyCommit();
break;
case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
notifyCommit();
break;
case Snackbar.Callback.DISMISS_EVENT_SWIPE:
notifyCommit();
break;
}
}
@Override
public void onShown(Snackbar snackbar) {
super.onShown(snackbar);
doChange();
}
});
mSnackBar.setAction(actionText, new View.OnClickListener() {
@Override
public void onClick(View v) {
undoChange();
}
});
mSnackBar.show();
return mSnackBar;
}
private void notifyCommit() {
if (mHistory != null) {
if (mHistory.action == ACTION_REMOVE) {
SortedSet<Integer> positions = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer lhs, Integer rhs) {
return lhs.compareTo(rhs);
}
});
for (FastAdapter.RelativeInfo<Item> relativeInfo : mHistory.items) {
positions.add(relativeInfo.position);
}
mUndoListener.commitRemove(positions, mHistory.items);
mHistory = null;
}
}
}
private void doChange() {
if (mHistory != null) {
if (mHistory.action == ACTION_REMOVE) {
for (int i = mHistory.items.size() - 1; i >= 0; i--) {
FastAdapter.RelativeInfo<Item> relativeInfo = mHistory.items.get(i);
if (relativeInfo.adapter instanceof IItemAdapter) {
((IItemAdapter) relativeInfo.adapter).remove(relativeInfo.position);
}
}
}
}
}
private void undoChange() {
if (mHistory != null) {
if (mHistory.action == ACTION_REMOVE) {
for (int i = 0, size = mHistory.items.size(); i < size; i++) {
FastAdapter.RelativeInfo<Item> relativeInfo = mHistory.items.get(i);
if (relativeInfo.adapter instanceof IItemAdapter) {
IItemAdapter<Item> adapter = (IItemAdapter<Item>) relativeInfo.adapter;
adapter.add(relativeInfo.position, relativeInfo.item);
if (relativeInfo.item.isSelected()) {
mAdapter.select(relativeInfo.position);
}
}
}
}
}
mHistory = null;
}
public interface UndoListener<Item extends IItem> {
void commitRemove(Set<Integer> positions, ArrayList<FastAdapter.RelativeInfo<Item>> removed);
}
private class History {
public int action;
public ArrayList<FastAdapter.RelativeInfo<Item>> items = new ArrayList<>();
}
}