/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker 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.
*
* Loop Habit Tracker 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.views;
import android.content.*;
import android.os.*;
import android.support.annotation.*;
import android.support.v7.widget.*;
import android.support.v7.widget.helper.*;
import android.util.*;
import android.view.*;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.models.*;
import java.util.*;
public class HabitCardListView extends RecyclerView
{
@Nullable
private HabitCardListAdapter adapter;
@Nullable
private Controller controller;
private final ItemTouchHelper touchHelper;
private int checkmarkCount;
private int dataOffset;
private LinkedList<HabitCardViewHolder> attachedHolders;
public HabitCardListView(Context context, AttributeSet attrs)
{
super(context, attrs);
setLongClickable(true);
setHasFixedSize(true);
setLayoutManager(new LinearLayoutManager(getContext()));
TouchHelperCallback callback = new TouchHelperCallback();
touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(this);
attachedHolders = new LinkedList<>();
}
public void attachCardView(HabitCardViewHolder holder)
{
attachedHolders.add(holder);
}
/**
* Builds a new HabitCardView to be eventually added to this list,
* containing the given data.
*
* @param holder the ViewHolder containing the HabitCardView that should
* be built
* @param habit the habit for this card
* @param score the current score for the habit
* @param checkmarks the list of checkmark values to be included in the
* card
* @param selected true if the card is selected, false otherwise
* @return the HabitCardView generated
*/
public View bindCardView(@NonNull HabitCardViewHolder holder,
@NonNull Habit habit,
int score,
int[] checkmarks,
boolean selected)
{
HabitCardView cardView = (HabitCardView) holder.itemView;
cardView.setHabit(habit);
cardView.setSelected(selected);
cardView.setCheckmarkValues(checkmarks);
cardView.setCheckmarkCount(checkmarkCount);
cardView.setDataOffset(dataOffset);
cardView.setScore(score);
if (controller != null) setupCardViewController(holder);
return cardView;
}
public View createCardView()
{
return new HabitCardView(getContext());
}
public void detachCardView(HabitCardViewHolder holder)
{
attachedHolders.remove(holder);
}
@Override
public void setAdapter(RecyclerView.Adapter adapter)
{
this.adapter = (HabitCardListAdapter) adapter;
super.setAdapter(adapter);
}
public void setCheckmarkCount(int checkmarkCount)
{
this.checkmarkCount = checkmarkCount;
}
public void setController(@Nullable Controller controller)
{
this.controller = controller;
}
public void setDataOffset(int dataOffset)
{
this.dataOffset = dataOffset;
for (HabitCardViewHolder holder : attachedHolders)
{
HabitCardView cardView = (HabitCardView) holder.itemView;
cardView.setDataOffset(dataOffset);
}
}
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
if (adapter != null) adapter.onAttached();
}
@Override
protected void onDetachedFromWindow()
{
if (adapter != null) adapter.onDetached();
super.onDetachedFromWindow();
}
@Override
protected void onRestoreInstanceState(Parcelable state)
{
BundleSavedState bss = (BundleSavedState) state;
dataOffset = bss.bundle.getInt("dataOffset");
super.onRestoreInstanceState(bss.getSuperState());
}
@Override
protected Parcelable onSaveInstanceState()
{
Parcelable superState = super.onSaveInstanceState();
Bundle bundle = new Bundle();
bundle.putInt("dataOffset", dataOffset);
return new BundleSavedState(superState, bundle);
}
protected void setupCardViewController(@NonNull HabitCardViewHolder holder)
{
HabitCardView cardView = (HabitCardView) holder.itemView;
HabitCardController cardController = new HabitCardController();
cardController.setListener(controller);
cardView.setController(cardController);
cardController.setView(cardView);
GestureDetector detector = new GestureDetector(getContext(),
new CardViewGestureDetector(holder));
cardView.setOnTouchListener((v, ev) -> {
detector.onTouchEvent(ev);
return true;
});
}
public interface Controller
extends CheckmarkButtonController.Listener, HabitCardController.Listener
{
void drop(int from, int to);
void onItemClick(int pos);
void onItemLongClick(int pos);
void startDrag(int position);
}
private class CardViewGestureDetector
extends GestureDetector.SimpleOnGestureListener
{
@NonNull
private final HabitCardViewHolder holder;
public CardViewGestureDetector(@NonNull HabitCardViewHolder holder)
{
this.holder = holder;
}
@Override
public void onLongPress(MotionEvent e)
{
int position = holder.getAdapterPosition();
if (controller != null) controller.onItemLongClick(position);
if (adapter.isSortable()) touchHelper.startDrag(holder);
}
@Override
public boolean onSingleTapUp(MotionEvent e)
{
int position = holder.getAdapterPosition();
if (controller != null) controller.onItemClick(position);
return true;
}
}
class TouchHelperCallback extends ItemTouchHelper.Callback
{
@Override
public int getMovementFlags(RecyclerView recyclerView,
ViewHolder viewHolder)
{
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean isItemViewSwipeEnabled()
{
return false;
}
@Override
public boolean isLongPressDragEnabled()
{
return false;
}
@Override
public boolean onMove(RecyclerView recyclerView,
ViewHolder from,
ViewHolder to)
{
if (controller == null) return false;
controller.drop(from.getAdapterPosition(), to.getAdapterPosition());
return true;
}
@Override
public void onSwiped(ViewHolder viewHolder, int direction)
{
// NOP
}
}
}