/* * 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.model; import android.support.annotation.*; import android.support.v7.widget.*; import android.view.*; import org.isoron.uhabits.activities.*; import org.isoron.uhabits.activities.habits.list.*; import org.isoron.uhabits.activities.habits.list.views.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.utils.*; import java.util.*; import javax.inject.*; /** * Provides data that backs a {@link HabitCardListView}. * <p> * The data if fetched and cached by a {@link HabitCardListCache}. This adapter * also holds a list of items that have been selected. */ @ActivityScope public class HabitCardListAdapter extends RecyclerView.Adapter<HabitCardViewHolder> implements HabitCardListCache.Listener, MidnightTimer.MidnightListener { @NonNull private ModelObservable observable; @Nullable private HabitCardListView listView; @NonNull private final LinkedList<Habit> selected; @NonNull private final HabitCardListCache cache; @NonNull private Preferences preferences; private final MidnightTimer midnightTimer; @Inject public HabitCardListAdapter(@NonNull HabitCardListCache cache, @NonNull Preferences preferences, @NonNull MidnightTimer midnightTimer) { this.preferences = preferences; this.selected = new LinkedList<>(); this.observable = new ModelObservable(); this.cache = cache; this.midnightTimer = midnightTimer; cache.setListener(this); cache.setCheckmarkCount(ListHabitsRootView.MAX_CHECKMARK_COUNT); cache.setOrder(preferences.getDefaultOrder()); setHasStableIds(true); } @Override public void atMidnight() { cache.refreshAllHabits(); } public void cancelRefresh() { cache.cancelTasks(); } /** * Sets all items as not selected. */ public void clearSelection() { selected.clear(); notifyDataSetChanged(); } /** * Returns the item that occupies a certain position on the list * * @param position position of the item * @return the item at given position * @throws IndexOutOfBoundsException if position is not valid */ @Deprecated @NonNull public Habit getItem(int position) { return cache.getHabitByPosition(position); } @Override public int getItemCount() { return cache.getHabitCount(); } @Override public long getItemId(int position) { return getItem(position).getId(); } @NonNull public ModelObservable getObservable() { return observable; } @NonNull public List<Habit> getSelected() { return new LinkedList<>(selected); } /** * Returns whether list of selected items is empty. * * @return true if selection is empty, false otherwise */ public boolean isSelectionEmpty() { return selected.isEmpty(); } public boolean isSortable() { return cache.getOrder() == HabitList.Order.BY_POSITION; } /** * Notify the adapter that it has been attached to a ListView. */ public void onAttached() { cache.onAttached(); midnightTimer.addListener(this); } @Override public void onBindViewHolder(@Nullable HabitCardViewHolder holder, int position) { if (holder == null) return; if (listView == null) return; Habit habit = cache.getHabitByPosition(position); int score = cache.getScore(habit.getId()); int checkmarks[] = cache.getCheckmarks(habit.getId()); boolean selected = this.selected.contains(habit); listView.bindCardView(holder, habit, score, checkmarks, selected); } @Override public void onViewAttachedToWindow(@Nullable HabitCardViewHolder holder) { if (listView == null) return; listView.attachCardView(holder); } @Override public void onViewDetachedFromWindow(@Nullable HabitCardViewHolder holder) { if (listView == null) return; listView.detachCardView(holder); } @Override public HabitCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (listView == null) return null; View view = listView.createCardView(); return new HabitCardViewHolder(view); } /** * Notify the adapter that it has been detached from a ListView. */ public void onDetached() { cache.onDetached(); midnightTimer.removeListener(this); } @Override public void onItemChanged(int position) { notifyItemChanged(position); observable.notifyListeners(); } @Override public void onItemInserted(int position) { notifyItemInserted(position); observable.notifyListeners(); } @Override public void onItemMoved(int fromPosition, int toPosition) { notifyItemMoved(fromPosition, toPosition); observable.notifyListeners(); } @Override public void onItemRemoved(int position) { notifyItemRemoved(position); observable.notifyListeners(); } @Override public void onRefreshFinished() { observable.notifyListeners(); } /** * Removes a list of habits from the adapter. * <p> * Note that this only has effect on the adapter cache. The database is not * modified, and the change is lost when the cache is refreshed. This method * is useful for making the ListView more responsive: while we wait for the * database operation to finish, the cache can be modified to reflect the * changes immediately. * * @param habits list of habits to be removed */ public void performRemove(List<Habit> habits) { for (Habit h : habits) cache.remove(h.getId()); } /** * Changes the order of habits on the adapter. * <p> * Note that this only has effect on the adapter cache. The database is not * modified, and the change is lost when the cache is refreshed. This method * is useful for making the ListView more responsive: while we wait for the * database operation to finish, the cache can be modified to reflect the * changes immediately. * * @param from the habit that should be moved * @param to the habit that currently occupies the desired position */ public void performReorder(int from, int to) { cache.reorder(from, to); } public void refresh() { cache.refreshAllHabits(); } public void setFilter(HabitMatcher matcher) { cache.setFilter(matcher); } /** * Sets the HabitCardListView that this adapter will provide data for. * <p> * This object will be used to generated new HabitCardViews, upon demand. * * @param listView the HabitCardListView associated with this adapter */ public void setListView(@Nullable HabitCardListView listView) { this.listView = listView; } public void setOrder(HabitList.Order order) { cache.setOrder(order); preferences.setDefaultOrder(order); } /** * Selects or deselects the item at a given position. * * @param position position of the item to be toggled */ public void toggleSelection(int position) { Habit h = getItem(position); int k = selected.indexOf(h); if (k < 0) selected.add(h); else selected.remove(h); notifyDataSetChanged(); } }