/*
* 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.controllers;
import android.support.annotation.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
/**
* Controller responsible for receiving and processing the events generated by a
* HabitListView. These include selecting and reordering items, toggling
* checkmarks and clicking habits.
*/
public class HabitCardListController implements HabitCardListView.Controller
{
private final Mode NORMAL_MODE = new NormalMode();
private final Mode SELECTION_MODE = new SelectionMode();
@NonNull
private final HabitCardListAdapter adapter;
@Nullable
private HabitListener habitListener;
@Nullable
private SelectionListener selectionListener;
@NonNull
private Mode activeMode;
public HabitCardListController(@NonNull HabitCardListAdapter adapter)
{
this.adapter = adapter;
this.activeMode = new NormalMode();
}
/**
* Called when the user drags a habit and drops it somewhere. Note that the
* dragging operation is already complete.
*
* @param from the original position of the habit
* @param to the position where the habit was released
*/
@Override
public void drop(int from, int to)
{
if (from == to) return;
cancelSelection();
Habit habitFrom = adapter.getItem(from);
Habit habitTo = adapter.getItem(to);
adapter.performReorder(from, to);
if (habitListener != null)
habitListener.onHabitReorder(habitFrom, habitTo);
}
/**
* Called when the user attempts to perform a toggle, but attempt is
* rejected.
*/
@Override
public void onInvalidToggle()
{
if (habitListener != null) habitListener.onInvalidToggle();
}
/**
* Called when the user clicks at some item.
*
* @param position the position of the clicked item
*/
@Override
public void onItemClick(int position)
{
activeMode.onItemClick(position);
}
/**
* Called when the user long clicks at some item.
*
* @param position the position of the clicked item
*/
@Override
public void onItemLongClick(int position)
{
activeMode.onItemLongClick(position);
}
/**
* Called when the selection operation is cancelled externally, by something
* other than this controller. This happens, for example, when the user
* presses the back button.
*/
public void onSelectionFinished()
{
cancelSelection();
}
/**
* Called when the user wants to toggle a checkmark.
*
* @param habit the habit of the checkmark
* @param timestamp the timestamps of the checkmark
*/
@Override
public void onToggle(@NonNull Habit habit, long timestamp)
{
if (habitListener != null) habitListener.onToggle(habit, timestamp);
}
public void setHabitListener(@Nullable HabitListener habitListener)
{
this.habitListener = habitListener;
}
public void setSelectionListener(@Nullable SelectionListener listener)
{
this.selectionListener = listener;
}
/**
* Called when the user starts dragging an item.
*
* @param position the position of the habit dragged
*/
@Override
public void startDrag(int position)
{
activeMode.startDrag(position);
}
/**
* Selects or deselects the item at a given position
*
* @param position the position of the item to be selected/deselected
*/
protected void toggleSelection(int position)
{
adapter.toggleSelection(position);
activeMode = adapter.isSelectionEmpty() ? NORMAL_MODE : SELECTION_MODE;
}
/**
* Marks all items as not selected and finishes the selection operation.
*/
private void cancelSelection()
{
adapter.clearSelection();
activeMode = new NormalMode();
if (selectionListener != null) selectionListener.onSelectionFinish();
}
public interface HabitListener extends CheckmarkButtonController.Listener
{
/**
* Called when the user clicks a habit.
*
* @param habit the habit clicked
*/
void onHabitClick(@NonNull Habit habit);
/**
* Called when the user wants to change the position of a habit on the
* list.
*
* @param from habit to be moved
* @param to habit that currently occupies the desired position
*/
void onHabitReorder(@NonNull Habit from, @NonNull Habit to);
}
/**
* A Mode describes the behaviour of the list upon clicking, long clicking
* and dragging an item. This depends on whether some items are already
* selected or not.
*/
private interface Mode
{
void onItemClick(int position);
boolean onItemLongClick(int position);
void startDrag(int position);
}
public interface SelectionListener
{
/**
* Called when the user changes the list of selected item. This is only
* called if there were previously selected items. If the selection was
* previously empty, then onHabitSelectionStart is called instead.
*/
void onSelectionChange();
/**
* Called when the user deselects all items or cancels the selection.
*/
void onSelectionFinish();
/**
* Called after the user selects the first item.
*/
void onSelectionStart();
}
/**
* Mode activated when there are no items selected. Clicks trigger habit
* click. Long clicks start selection.
*/
class NormalMode implements Mode
{
@Override
public void onItemClick(int position)
{
Habit habit = adapter.getItem(position);
if (habitListener != null) habitListener.onHabitClick(habit);
}
@Override
public boolean onItemLongClick(int position)
{
startSelection(position);
return true;
}
@Override
public void startDrag(int position)
{
startSelection(position);
}
protected void startSelection(int position)
{
toggleSelection(position);
activeMode = SELECTION_MODE;
if (selectionListener != null) selectionListener.onSelectionStart();
}
}
/**
* Mode activated when some items are already selected.
* <p>
* Clicks toggle item selection. Long clicks select more items.
*/
class SelectionMode implements Mode
{
@Override
public void onItemClick(int position)
{
toggleSelection(position);
notifyListener();
}
@Override
public boolean onItemLongClick(int position)
{
toggleSelection(position);
notifyListener();
return true;
}
@Override
public void startDrag(int position)
{
toggleSelection(position);
notifyListener();
}
protected void notifyListener()
{
if (selectionListener == null) return;
if (activeMode == SELECTION_MODE)
selectionListener.onSelectionChange();
else selectionListener.onSelectionFinish();
}
}
}