From 8165b5417fc9d213b66cd8352c5116dd94f043e3 Mon Sep 17 00:00:00 2001 From: Quentin Hibon Date: Thu, 14 Jan 2021 15:35:30 +0100 Subject: [PATCH] Convert HabitCardListAdapter --- .../list/views/HabitCardListAdapter.java | 350 ------------------ .../habits/list/views/HabitCardListAdapter.kt | 263 +++++++++++++ 2 files changed, 263 insertions(+), 350 deletions(-) delete mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java deleted file mode 100644 index 38f1932f7..000000000 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2016-2021 Álinson Santos Xavier - * - * 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 . - */ - -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}. - *

- * 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 implements - HabitCardListCache.Listener, - MidnightTimer.MidnightListener, - ListHabitsMenuBehavior.Adapter, - ListHabitsSelectionMenuBehavior.Adapter -{ - @NonNull - private ModelObservable observable; - - @Nullable - private HabitCardListView listView; - - @NonNull - private final LinkedList 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 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. - *

- * 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 habits) - { - for (Habit h : habits) - cache.remove(h.getId()); - } - - /** - * 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 - */ - 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. - *

- * 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(); - } -} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt new file mode 100644 index 000000000..8dae13f2d --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2016-2021 Álinson Santos Xavier + * + * 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 . + */ +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(), + HabitCardListCache.Listener, + MidnightTimer.MidnightListener, + ListHabitsMenuBehavior.Adapter, + ListHabitsSelectionMenuBehavior.Adapter { + val observable: ModelObservable = ModelObservable() + private var listView: HabitCardListView? = null + private val selected: LinkedList = 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 { + 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) { + 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) + } +}