Convert HabitCardListAdapter

pull/709/head
Quentin Hibon 5 years ago
parent 714771fbc3
commit 8165b5417f

@ -1,350 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.*;
import androidx.recyclerview.widget.RecyclerView;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.inject.*;
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,
ListHabitsMenuBehavior.Adapter,
ListHabitsSelectionMenuBehavior.Adapter
{
@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(
ListHabitsRootViewKt.MAX_CHECKMARK_COUNT);
cache.setSecondaryOrder(preferences.getDefaultSecondaryOrder());
cache.setPrimaryOrder(preferences.getDefaultPrimaryOrder());
setHasStableIds(true);
}
@Override
public void atMidnight()
{
cache.refreshAllHabits();
}
public void cancelRefresh()
{
cache.cancelTasks();
}
/**
* Sets all items as not selected.
*/
@Override
public void clearSelection()
{
selected.clear();
notifyDataSetChanged();
observable.notifyListeners();
}
/**
* Returns the item that occupies a certain position on the list
*
* @param position position of the item
* @return the item at given position or null if position is invalid
*/
@Deprecated
@Nullable
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;
}
@Override
@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.getPrimaryOrder() == 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);
double 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.createHabitCardView();
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
*/
@Override
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);
}
@Override
public void refresh()
{
cache.refreshAllHabits();
}
@Override
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;
}
@Override
public void setPrimaryOrder(HabitList.Order order)
{
cache.setPrimaryOrder(order);
preferences.setDefaultPrimaryOrder(order);
}
@Override
public void setSecondaryOrder(HabitList.Order order) {
cache.setSecondaryOrder(order);
preferences.setDefaultSecondaryOrder(order);
}
@Override
public HabitList.Order getPrimaryOrder()
{
return cache.getPrimaryOrder();
}
/**
* 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);
if (h == null) return;
int k = selected.indexOf(h);
if (k < 0) selected.add(h);
else selected.remove(h);
notifyDataSetChanged();
}
}

@ -0,0 +1,263 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.isoron.uhabits.activities.habits.list.MAX_CHECKMARK_COUNT
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.models.ModelObservable
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.ui.screens.habits.list.HabitCardListCache
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsMenuBehavior
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBehavior
import org.isoron.uhabits.core.utils.MidnightTimer
import org.isoron.uhabits.inject.ActivityScope
import java.util.LinkedList
import javax.inject.Inject
/**
* Provides data that backs a [HabitCardListView].
*
*
* The data if fetched and cached by a [HabitCardListCache]. This adapter
* also holds a list of items that have been selected.
*/
@ActivityScope
class HabitCardListAdapter @Inject constructor(
private val cache: HabitCardListCache,
private val preferences: Preferences,
private val midnightTimer: MidnightTimer
) : RecyclerView.Adapter<HabitCardViewHolder?>(),
HabitCardListCache.Listener,
MidnightTimer.MidnightListener,
ListHabitsMenuBehavior.Adapter,
ListHabitsSelectionMenuBehavior.Adapter {
val observable: ModelObservable = ModelObservable()
private var listView: HabitCardListView? = null
private val selected: LinkedList<Habit> = LinkedList()
override fun atMidnight() {
cache.refreshAllHabits()
}
fun cancelRefresh() {
cache.cancelTasks()
}
/**
* Sets all items as not selected.
*/
override fun clearSelection() {
selected.clear()
notifyDataSetChanged()
observable.notifyListeners()
}
/**
* Returns the item that occupies a certain position on the list
*
* @param position position of the item
* @return the item at given position or null if position is invalid
*/
@Deprecated("")
fun getItem(position: Int): Habit? {
return cache.getHabitByPosition(position)
}
override fun getItemCount(): Int {
return cache.habitCount
}
override fun getItemId(position: Int): Long {
return getItem(position)!!.id!!
}
override fun getSelected(): List<Habit> {
return LinkedList(selected)
}
/**
* Returns whether list of selected items is empty.
*
* @return true if selection is empty, false otherwise
*/
val isSelectionEmpty: Boolean
get() = selected.isEmpty()
val isSortable: Boolean
get() = cache.primaryOrder == HabitList.Order.BY_POSITION
/**
* Notify the adapter that it has been attached to a ListView.
*/
fun onAttached() {
cache.onAttached()
midnightTimer.addListener(this)
}
override fun onBindViewHolder(
holder: HabitCardViewHolder,
position: Int
) {
if (listView == null) return
val habit = cache.getHabitByPosition(position)
val score = cache.getScore(habit!!.id!!)
val checkmarks = cache.getCheckmarks(habit.id!!)
val selected = selected.contains(habit)
listView!!.bindCardView(holder, habit, score, checkmarks, selected)
}
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
listView!!.attachCardView(holder)
}
override fun onViewDetachedFromWindow(holder: HabitCardViewHolder) {
listView!!.detachCardView(holder)
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): HabitCardViewHolder {
val view = listView!!.createHabitCardView()
return HabitCardViewHolder(view)
}
/**
* Notify the adapter that it has been detached from a ListView.
*/
fun onDetached() {
cache.onDetached()
midnightTimer.removeListener(this)
}
override fun onItemChanged(position: Int) {
notifyItemChanged(position)
observable.notifyListeners()
}
override fun onItemInserted(position: Int) {
notifyItemInserted(position)
observable.notifyListeners()
}
override fun onItemMoved(fromPosition: Int, toPosition: Int) {
notifyItemMoved(fromPosition, toPosition)
observable.notifyListeners()
}
override fun onItemRemoved(position: Int) {
notifyItemRemoved(position)
observable.notifyListeners()
}
override fun onRefreshFinished() {
observable.notifyListeners()
}
/**
* Removes a list of habits from the adapter.
*
*
* 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
*/
override fun performRemove(habits: List<Habit>) {
for (habit in habits) cache.remove(habit.id!!)
}
/**
* Changes the order of habits on the adapter.
*
*
* 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
*/
fun performReorder(from: Int, to: Int) {
cache.reorder(from, to)
}
override fun refresh() {
cache.refreshAllHabits()
}
override fun setFilter(matcher: HabitMatcher) {
cache.setFilter(matcher)
}
/**
* Sets the HabitCardListView that this adapter will provide data for.
*
*
* This object will be used to generated new HabitCardViews, upon demand.
*
* @param listView the HabitCardListView associated with this adapter
*/
fun setListView(listView: HabitCardListView?) {
this.listView = listView
}
override fun setPrimaryOrder(order: HabitList.Order) {
cache.primaryOrder = order
preferences.defaultPrimaryOrder = order
}
override fun setSecondaryOrder(order: HabitList.Order) {
cache.secondaryOrder = order
preferences.defaultSecondaryOrder = order
}
override fun getPrimaryOrder(): HabitList.Order {
return cache.primaryOrder
}
/**
* Selects or deselects the item at a given position.
*
* @param position position of the item to be toggled
*/
fun toggleSelection(position: Int) {
val h = getItem(position) ?: return
val k = selected.indexOf(h)
if (k < 0) selected.add(h) else selected.remove(h)
notifyDataSetChanged()
}
init {
cache.setListener(this)
cache.setCheckmarkCount(
MAX_CHECKMARK_COUNT
)
cache.secondaryOrder = preferences.defaultSecondaryOrder
cache.primaryOrder = preferences.defaultPrimaryOrder
setHasStableIds(true)
}
}
Loading…
Cancel
Save