mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Convert most of code remaining in uhabits-core
This commit is contained in:
@@ -215,7 +215,7 @@ class ListHabitsScreen
|
||||
)
|
||||
}
|
||||
|
||||
override fun showSendBugReportToDeveloperScreen(log: String) {
|
||||
override fun showSendBugReportToDeveloperScreen(log: String?) {
|
||||
val to = R.string.bugReportTo
|
||||
val subject = R.string.bugReportSubject
|
||||
activity.showSendEmailScreen(to, subject, log)
|
||||
@@ -230,10 +230,7 @@ class ListHabitsScreen
|
||||
activity.startActivityForResult(intent, REQUEST_SETTINGS)
|
||||
}
|
||||
|
||||
override fun showColorPicker(
|
||||
defaultColor: PaletteColor,
|
||||
callback: OnColorPickedCallback
|
||||
) {
|
||||
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
||||
val picker = colorPickerFactory.create(defaultColor)
|
||||
picker.setListener(callback)
|
||||
picker.show(activity.supportFragmentManager, "picker")
|
||||
|
||||
@@ -53,7 +53,7 @@ class HabitCardListAdapter @Inject constructor(
|
||||
ListHabitsSelectionMenuBehavior.Adapter {
|
||||
val observable: ModelObservable = ModelObservable()
|
||||
private var listView: HabitCardListView? = null
|
||||
private val selected: LinkedList<Habit> = LinkedList()
|
||||
override val selected: LinkedList<Habit> = LinkedList()
|
||||
override fun atMidnight() {
|
||||
cache.refreshAllHabits()
|
||||
}
|
||||
@@ -90,9 +90,9 @@ class HabitCardListAdapter @Inject constructor(
|
||||
return getItem(position)!!.id!!
|
||||
}
|
||||
|
||||
override fun getSelected(): List<Habit> {
|
||||
return LinkedList(selected)
|
||||
}
|
||||
// override fun getSelected(): List<Habit> {
|
||||
// return LinkedList(selected)
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns whether list of selected items is empty.
|
||||
@@ -182,10 +182,10 @@ class HabitCardListAdapter @Inject constructor(
|
||||
* database operation to finish, the cache can be modified to reflect the
|
||||
* changes immediately.
|
||||
*
|
||||
* @param habits list of habits to be removed
|
||||
* @param selected list of habits to be removed
|
||||
*/
|
||||
override fun performRemove(habits: List<Habit>) {
|
||||
for (habit in habits) cache.remove(habit.id!!)
|
||||
override fun performRemove(selected: List<Habit>) {
|
||||
for (habit in selected) cache.remove(habit.id!!)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,8 +209,8 @@ class HabitCardListAdapter @Inject constructor(
|
||||
cache.refreshAllHabits()
|
||||
}
|
||||
|
||||
override fun setFilter(matcher: HabitMatcher) {
|
||||
cache.setFilter(matcher)
|
||||
override fun setFilter(matcher: HabitMatcher?) {
|
||||
if (matcher != null) cache.setFilter(matcher)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,18 +225,18 @@ class HabitCardListAdapter @Inject constructor(
|
||||
this.listView = listView
|
||||
}
|
||||
|
||||
override fun setPrimaryOrder(order: HabitList.Order) {
|
||||
cache.primaryOrder = order
|
||||
preferences.defaultPrimaryOrder = order
|
||||
override var primaryOrder: HabitList.Order
|
||||
get() = cache.primaryOrder
|
||||
set(value) {
|
||||
cache.primaryOrder = value
|
||||
preferences.defaultPrimaryOrder = value
|
||||
}
|
||||
|
||||
override fun setSecondaryOrder(order: HabitList.Order) {
|
||||
cache.secondaryOrder = order
|
||||
preferences.defaultSecondaryOrder = order
|
||||
}
|
||||
|
||||
override fun getPrimaryOrder(): HabitList.Order {
|
||||
return cache.primaryOrder
|
||||
override var secondaryOrder: HabitList.Order
|
||||
get() = cache.secondaryOrder
|
||||
set(value) {
|
||||
cache.secondaryOrder = value
|
||||
preferences.defaultSecondaryOrder = value
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -117,8 +117,6 @@ class CheckmarkWidgetView : HabitWidgetView {
|
||||
get() = R.layout.widget_checkmark
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
var widthMeasureSpec = widthMeasureSpec
|
||||
var heightMeasureSpec = heightMeasureSpec
|
||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val height = MeasureSpec.getSize(heightMeasureSpec)
|
||||
var w = width.toFloat()
|
||||
@@ -128,15 +126,15 @@ class CheckmarkWidgetView : HabitWidgetView {
|
||||
h *= scale
|
||||
if (h < getDimension(context, R.dimen.checkmarkWidget_heightBreakpoint)) ring.visibility =
|
||||
GONE else ring.visibility = VISIBLE
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(w.toInt(), MeasureSpec.EXACTLY)
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h.toInt(), MeasureSpec.EXACTLY)
|
||||
val newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(w.toInt(), MeasureSpec.EXACTLY)
|
||||
val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(h.toInt(), MeasureSpec.EXACTLY)
|
||||
var textSize = 0.15f * h
|
||||
val maxTextSize = getDimension(context, R.dimen.smallerTextSize)
|
||||
textSize = min(textSize, maxTextSize)
|
||||
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||
ring.setTextSize(textSize)
|
||||
ring.setThickness(0.15f * textSize)
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
|
||||
@@ -77,7 +77,7 @@ class HabitsCSVExporter(
|
||||
}
|
||||
|
||||
private fun sanitizeFilename(name: String): String {
|
||||
val s = name.replace("[^ a-zA-Z0-9\\._-]+".toRegex(), "")
|
||||
val s = name.replace("[^ a-zA-Z0-9._-]+".toRegex(), "")
|
||||
return s.substring(0, min(s.length, 100))
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ public abstract class HabitList implements Iterable<Habit>
|
||||
*/
|
||||
public void writeCSV(@NonNull Writer out) throws IOException
|
||||
{
|
||||
String header[] = {
|
||||
String[] header = {
|
||||
"Position",
|
||||
"Name",
|
||||
"Question",
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -96,7 +97,7 @@ public class MemoryHabitList extends HabitList
|
||||
@Override
|
||||
public synchronized Habit getByUUID(String uuid)
|
||||
{
|
||||
for (Habit h : list) if (h.getUuid().equals(uuid)) return h;
|
||||
for (Habit h : list) if (Objects.requireNonNull(h.getUuid()).equals(uuid)) return h;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,12 +115,14 @@ public class MemoryHabitList extends HabitList
|
||||
return new MemoryHabitList(matcher, comparator, this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized Order getPrimaryOrder()
|
||||
{
|
||||
return primaryOrder;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized Order getSecondaryOrder()
|
||||
{
|
||||
@@ -288,7 +291,7 @@ public class MemoryHabitList extends HabitList
|
||||
|
||||
public synchronized void resort()
|
||||
{
|
||||
if (comparator != null) Collections.sort(list, comparator);
|
||||
if (comparator != null) list.sort(comparator);
|
||||
getObservable().notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.models.memory.*;
|
||||
import org.isoron.uhabits.core.models.sqlite.records.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -161,6 +162,7 @@ public class SQLiteHabitList extends HabitList
|
||||
return list.indexOf(h);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized Iterator<Habit> iterator()
|
||||
{
|
||||
|
||||
@@ -22,6 +22,8 @@ package org.isoron.uhabits.core.models.sqlite.records;
|
||||
import org.isoron.uhabits.core.database.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The SQLite database record corresponding to a {@link Habit}.
|
||||
*/
|
||||
@@ -108,7 +110,7 @@ public class HabitRecord
|
||||
if (model.hasReminder())
|
||||
{
|
||||
Reminder reminder = model.getReminder();
|
||||
this.reminderHour = reminder.getHour();
|
||||
this.reminderHour = Objects.requireNonNull(reminder).getHour();
|
||||
this.reminderMin = reminder.getMinute();
|
||||
this.reminderDays = reminder.getDays().toInteger();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package org.isoron.uhabits.core.preferences;
|
||||
|
||||
import org.isoron.uhabits.core.AppScope;
|
||||
import org.isoron.uhabits.core.models.HabitNotFoundException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -33,7 +32,7 @@ public class WidgetPreferences {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public void addWidget(int widgetId, long habitIds[]) {
|
||||
public void addWidget(int widgetId, long[] habitIds) {
|
||||
storage.putLongArray(getHabitIdKey(widgetId), habitIds);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ReminderScheduler implements CommandRunner.Listener
|
||||
return;
|
||||
}
|
||||
|
||||
long reminderTime = habit.getReminder().getTimeInMillis();
|
||||
long reminderTime = Objects.requireNonNull(habit.getReminder()).getTimeInMillis();
|
||||
long snoozeReminderTime = widgetPreferences.getSnoozeTime(habit.getId());
|
||||
|
||||
if (snoozeReminderTime != 0)
|
||||
|
||||
@@ -145,7 +145,7 @@ public class NotificationTray
|
||||
void log(String msg);
|
||||
}
|
||||
|
||||
class NotificationData
|
||||
static class NotificationData
|
||||
{
|
||||
public final Timestamp timestamp;
|
||||
|
||||
@@ -229,7 +229,7 @@ public class NotificationTray
|
||||
if (!habit.hasReminder()) return false;
|
||||
Reminder reminder = habit.getReminder();
|
||||
|
||||
boolean reminderDays[] = reminder.getDays().toArray();
|
||||
boolean[] reminderDays = Objects.requireNonNull(reminder).getDays().toArray();
|
||||
int weekday = timestamp.getWeekday();
|
||||
|
||||
return reminderDays[weekday];
|
||||
|
||||
@@ -121,8 +121,7 @@ class HabitCardListCache @Inject constructor(
|
||||
@Synchronized
|
||||
override fun onCommandFinished(command: Command) {
|
||||
if (command is CreateRepetitionCommand) {
|
||||
val (_, _, _, id) = command.habit
|
||||
id?.let { refreshHabit(it) }
|
||||
command.habit.id?.let { refreshHabit(it) }
|
||||
} else {
|
||||
refreshAllHabits()
|
||||
}
|
||||
|
||||
@@ -16,54 +16,31 @@
|
||||
* 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.core.ui.screens.habits.list
|
||||
|
||||
package org.isoron.uhabits.core.ui.screens.habits.list;
|
||||
|
||||
import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
||||
|
||||
/**
|
||||
* Provides a list of hints to be shown at the application startup, and takes
|
||||
* care of deciding when a new hint should be shown.
|
||||
*/
|
||||
public class HintList
|
||||
{
|
||||
private final Preferences prefs;
|
||||
|
||||
@NonNull
|
||||
private final String[] hints;
|
||||
|
||||
/**
|
||||
* Constructs a new list containing the provided hints.
|
||||
*
|
||||
* @param hints initial list of hints
|
||||
*/
|
||||
public HintList(@NonNull Preferences prefs,
|
||||
@NonNull String hints[])
|
||||
{
|
||||
this.prefs = prefs;
|
||||
this.hints = hints;
|
||||
}
|
||||
|
||||
class HintList(private val prefs: Preferences, private val hints: Array<String>) {
|
||||
/**
|
||||
* Returns a new hint to be shown to the user.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* The hint returned is marked as read on the list, and will not be returned
|
||||
* again. In case all hints have already been read, and there is nothing
|
||||
* left, returns null.
|
||||
*
|
||||
* @return the next hint to be shown, or null if none
|
||||
*/
|
||||
public String pop()
|
||||
{
|
||||
int next = prefs.getLastHintNumber() + 1;
|
||||
if (next >= hints.length) return null;
|
||||
|
||||
prefs.updateLastHint(next, DateUtils.getToday());
|
||||
return hints[next];
|
||||
fun pop(): String? {
|
||||
val next = prefs.lastHintNumber + 1
|
||||
if (next >= hints.size) return null
|
||||
prefs.updateLastHint(next, getToday())
|
||||
return hints[next]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +48,9 @@ public class HintList
|
||||
*
|
||||
* @return true if hint should be shown, false otherwise
|
||||
*/
|
||||
public boolean shouldShow()
|
||||
{
|
||||
Timestamp today = DateUtils.getToday();
|
||||
Timestamp lastHintTimestamp = prefs.getLastHintTimestamp();
|
||||
return (lastHintTimestamp.isOlderThan(today));
|
||||
fun shouldShow(): Boolean {
|
||||
val today = getToday()
|
||||
val lastHintTimestamp = prefs.lastHintTimestamp
|
||||
return lastHintTimestamp.isOlderThan(today)
|
||||
}
|
||||
}
|
||||
@@ -1,216 +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.core.ui.screens.habits.list;
|
||||
|
||||
import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.commands.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.tasks.*;
|
||||
import org.isoron.uhabits.core.ui.callbacks.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class ListHabitsBehavior
|
||||
{
|
||||
@NonNull
|
||||
private final HabitList habitList;
|
||||
|
||||
@NonNull
|
||||
private final DirFinder dirFinder;
|
||||
|
||||
@NonNull
|
||||
private final TaskRunner taskRunner;
|
||||
|
||||
@NonNull
|
||||
private final Screen screen;
|
||||
|
||||
@NonNull
|
||||
private final CommandRunner commandRunner;
|
||||
|
||||
@NonNull
|
||||
private final Preferences prefs;
|
||||
|
||||
@NonNull
|
||||
private final BugReporter bugReporter;
|
||||
|
||||
@Inject
|
||||
public ListHabitsBehavior(@NonNull HabitList habitList,
|
||||
@NonNull DirFinder dirFinder,
|
||||
@NonNull TaskRunner taskRunner,
|
||||
@NonNull Screen screen,
|
||||
@NonNull CommandRunner commandRunner,
|
||||
@NonNull Preferences prefs,
|
||||
@NonNull BugReporter bugReporter)
|
||||
{
|
||||
this.habitList = habitList;
|
||||
this.dirFinder = dirFinder;
|
||||
this.taskRunner = taskRunner;
|
||||
this.screen = screen;
|
||||
this.commandRunner = commandRunner;
|
||||
this.prefs = prefs;
|
||||
this.bugReporter = bugReporter;
|
||||
}
|
||||
|
||||
public void onClickHabit(@NonNull Habit h)
|
||||
{
|
||||
screen.showHabitScreen(h);
|
||||
}
|
||||
|
||||
public void onEdit(@NonNull Habit habit, Timestamp timestamp)
|
||||
{
|
||||
EntryList entries = habit.getComputedEntries();
|
||||
double oldValue = entries.get(timestamp).getValue();
|
||||
|
||||
screen.showNumberPicker(oldValue / 1000, habit.getUnit(), newValue ->
|
||||
{
|
||||
newValue = Math.round(newValue * 1000);
|
||||
commandRunner.run(
|
||||
new CreateRepetitionCommand(habitList, habit, timestamp, (int) newValue)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public void onExportCSV()
|
||||
{
|
||||
List<Habit> selected = new LinkedList<>();
|
||||
for (Habit h : habitList) selected.add(h);
|
||||
File outputDir = dirFinder.getCSVOutputDir();
|
||||
|
||||
taskRunner.execute(
|
||||
new ExportCSVTask(habitList, selected, outputDir, filename ->
|
||||
{
|
||||
if (filename != null) screen.showSendFileScreen(filename);
|
||||
else screen.showMessage(Message.COULD_NOT_EXPORT);
|
||||
}));
|
||||
}
|
||||
|
||||
public void onFirstRun()
|
||||
{
|
||||
prefs.setFirstRun(false);
|
||||
prefs.updateLastHint(-1, DateUtils.getToday());
|
||||
screen.showIntroScreen();
|
||||
}
|
||||
|
||||
public void onReorderHabit(@NonNull Habit from, @NonNull Habit to)
|
||||
{
|
||||
taskRunner.execute(() -> habitList.reorder(from, to));
|
||||
}
|
||||
|
||||
public void onRepairDB()
|
||||
{
|
||||
taskRunner.execute(() ->
|
||||
{
|
||||
habitList.repair();
|
||||
screen.showMessage(Message.DATABASE_REPAIRED);
|
||||
});
|
||||
}
|
||||
|
||||
public void onSendBugReport()
|
||||
{
|
||||
bugReporter.dumpBugReportToFile();
|
||||
|
||||
try
|
||||
{
|
||||
String log = bugReporter.getBugReport();
|
||||
screen.showSendBugReportToDeveloperScreen(log);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
screen.showMessage(Message.COULD_NOT_GENERATE_BUG_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
public void onStartup()
|
||||
{
|
||||
prefs.incrementLaunchCount();
|
||||
if (prefs.isFirstRun()) onFirstRun();
|
||||
}
|
||||
|
||||
public void onToggle(@NonNull Habit habit, Timestamp timestamp, int value)
|
||||
{
|
||||
commandRunner.run(
|
||||
new CreateRepetitionCommand(habitList, habit, timestamp, value)
|
||||
);
|
||||
}
|
||||
|
||||
public void onSyncKeyOffer(@NotNull String syncKey, @NotNull String encryptionKey)
|
||||
{
|
||||
if(prefs.getSyncKey().equals(syncKey)) {
|
||||
screen.showMessage(Message.SYNC_KEY_ALREADY_INSTALLED);
|
||||
return;
|
||||
}
|
||||
screen.showConfirmInstallSyncKey(() -> {
|
||||
prefs.enableSync(syncKey, encryptionKey);
|
||||
screen.showMessage(Message.SYNC_ENABLED);
|
||||
});
|
||||
}
|
||||
|
||||
public enum Message
|
||||
{
|
||||
COULD_NOT_EXPORT, IMPORT_SUCCESSFUL, IMPORT_FAILED, DATABASE_REPAIRED,
|
||||
COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED, SYNC_ENABLED, SYNC_KEY_ALREADY_INSTALLED
|
||||
}
|
||||
|
||||
public interface BugReporter
|
||||
{
|
||||
void dumpBugReportToFile();
|
||||
|
||||
String getBugReport() throws IOException;
|
||||
}
|
||||
|
||||
public interface DirFinder
|
||||
{
|
||||
File getCSVOutputDir();
|
||||
}
|
||||
|
||||
public interface NumberPickerCallback
|
||||
{
|
||||
void onNumberPicked(double newValue);
|
||||
|
||||
default void onNumberPickerDismissed() {}
|
||||
}
|
||||
|
||||
public interface Screen
|
||||
{
|
||||
void showHabitScreen(@NonNull Habit h);
|
||||
|
||||
void showIntroScreen();
|
||||
|
||||
void showMessage(@NonNull Message m);
|
||||
|
||||
void showNumberPicker(double value,
|
||||
@NonNull String unit,
|
||||
@NonNull NumberPickerCallback callback);
|
||||
|
||||
void showSendBugReportToDeveloperScreen(String log);
|
||||
|
||||
void showSendFileScreen(@NonNull String filename);
|
||||
|
||||
void showConfirmInstallSyncKey(@NonNull OnConfirmedCallback callback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.core.ui.screens.habits.list
|
||||
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.tasks.ExportCSVTask
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.LinkedList
|
||||
import javax.inject.Inject
|
||||
|
||||
class ListHabitsBehavior @Inject constructor(
|
||||
private val habitList: HabitList,
|
||||
private val dirFinder: DirFinder,
|
||||
private val taskRunner: TaskRunner,
|
||||
private val screen: Screen,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val prefs: Preferences,
|
||||
private val bugReporter: BugReporter
|
||||
) {
|
||||
fun onClickHabit(h: Habit) {
|
||||
screen.showHabitScreen(h)
|
||||
}
|
||||
|
||||
fun onEdit(habit: Habit, timestamp: Timestamp?) {
|
||||
val entries = habit.computedEntries
|
||||
val oldValue = entries.get(timestamp!!).value.toDouble()
|
||||
screen.showNumberPicker(
|
||||
oldValue / 1000,
|
||||
habit.unit,
|
||||
{ newValue: Double ->
|
||||
val value = Math.round(newValue * 1000).toDouble()
|
||||
commandRunner.run(
|
||||
CreateRepetitionCommand(habitList, habit, timestamp, value.toInt())
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun onExportCSV() {
|
||||
val selected: MutableList<Habit> = LinkedList()
|
||||
for (h in habitList) selected.add(h)
|
||||
val outputDir = dirFinder.getCSVOutputDir()
|
||||
taskRunner.execute(
|
||||
ExportCSVTask(habitList, selected, outputDir) { filename: String? ->
|
||||
if (filename != null) screen.showSendFileScreen(filename) else screen.showMessage(
|
||||
Message.COULD_NOT_EXPORT
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun onFirstRun() {
|
||||
prefs.isFirstRun = false
|
||||
prefs.updateLastHint(-1, getToday())
|
||||
screen.showIntroScreen()
|
||||
}
|
||||
|
||||
fun onReorderHabit(from: Habit, to: Habit) {
|
||||
taskRunner.execute { habitList.reorder(from, to) }
|
||||
}
|
||||
|
||||
fun onRepairDB() {
|
||||
taskRunner.execute {
|
||||
habitList.repair()
|
||||
screen.showMessage(Message.DATABASE_REPAIRED)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSendBugReport() {
|
||||
bugReporter.dumpBugReportToFile()
|
||||
try {
|
||||
val log = bugReporter.getBugReport()
|
||||
screen.showSendBugReportToDeveloperScreen(log)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
screen.showMessage(Message.COULD_NOT_GENERATE_BUG_REPORT)
|
||||
}
|
||||
}
|
||||
|
||||
fun onStartup() {
|
||||
prefs.incrementLaunchCount()
|
||||
if (prefs.isFirstRun) onFirstRun()
|
||||
}
|
||||
|
||||
fun onToggle(habit: Habit, timestamp: Timestamp?, value: Int) {
|
||||
commandRunner.run(
|
||||
CreateRepetitionCommand(habitList, habit, timestamp!!, value)
|
||||
)
|
||||
}
|
||||
|
||||
fun onSyncKeyOffer(syncKey: String, encryptionKey: String) {
|
||||
if (prefs.syncKey == syncKey) {
|
||||
screen.showMessage(Message.SYNC_KEY_ALREADY_INSTALLED)
|
||||
return
|
||||
}
|
||||
screen.showConfirmInstallSyncKey {
|
||||
prefs.enableSync(syncKey, encryptionKey)
|
||||
screen.showMessage(Message.SYNC_ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Message {
|
||||
COULD_NOT_EXPORT, IMPORT_SUCCESSFUL, IMPORT_FAILED, DATABASE_REPAIRED, COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED, SYNC_ENABLED, SYNC_KEY_ALREADY_INSTALLED
|
||||
}
|
||||
|
||||
interface BugReporter {
|
||||
fun dumpBugReportToFile()
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getBugReport(): String
|
||||
}
|
||||
|
||||
interface DirFinder {
|
||||
fun getCSVOutputDir(): File
|
||||
}
|
||||
|
||||
fun interface NumberPickerCallback {
|
||||
fun onNumberPicked(newValue: Double)
|
||||
fun onNumberPickerDismissed() {}
|
||||
}
|
||||
|
||||
interface Screen {
|
||||
fun showHabitScreen(h: Habit)
|
||||
fun showIntroScreen()
|
||||
fun showMessage(m: Message)
|
||||
fun showNumberPicker(
|
||||
value: Double,
|
||||
unit: String,
|
||||
callback: NumberPickerCallback
|
||||
)
|
||||
|
||||
fun showSendBugReportToDeveloperScreen(log: String?)
|
||||
fun showSendFileScreen(filename: String)
|
||||
fun showConfirmInstallSyncKey(callback: OnConfirmedCallback)
|
||||
}
|
||||
}
|
||||
@@ -1,177 +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.core.ui.screens.habits.list;
|
||||
|
||||
import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class ListHabitsMenuBehavior
|
||||
{
|
||||
@NonNull
|
||||
private final Screen screen;
|
||||
|
||||
@NonNull
|
||||
private final Adapter adapter;
|
||||
|
||||
@NonNull
|
||||
private final Preferences preferences;
|
||||
|
||||
@NonNull
|
||||
private final ThemeSwitcher themeSwitcher;
|
||||
|
||||
private boolean showCompleted;
|
||||
|
||||
private boolean showArchived;
|
||||
|
||||
@Inject
|
||||
public ListHabitsMenuBehavior(@NonNull Screen screen,
|
||||
@NonNull Adapter adapter,
|
||||
@NonNull Preferences preferences,
|
||||
@NonNull ThemeSwitcher themeSwitcher)
|
||||
{
|
||||
this.screen = screen;
|
||||
this.adapter = adapter;
|
||||
this.preferences = preferences;
|
||||
this.themeSwitcher = themeSwitcher;
|
||||
|
||||
showCompleted = preferences.getShowCompleted();
|
||||
showArchived = preferences.getShowArchived();
|
||||
updateAdapterFilter();
|
||||
}
|
||||
|
||||
public void onCreateHabit()
|
||||
{
|
||||
screen.showSelectHabitTypeDialog();
|
||||
}
|
||||
|
||||
public void onViewFAQ()
|
||||
{
|
||||
screen.showFAQScreen();
|
||||
}
|
||||
|
||||
public void onViewAbout()
|
||||
{
|
||||
screen.showAboutScreen();
|
||||
}
|
||||
|
||||
public void onViewSettings()
|
||||
{
|
||||
screen.showSettingsScreen();
|
||||
}
|
||||
|
||||
public void onToggleShowArchived()
|
||||
{
|
||||
showArchived = !showArchived;
|
||||
preferences.setShowArchived(showArchived);
|
||||
updateAdapterFilter();
|
||||
}
|
||||
|
||||
public void onToggleShowCompleted()
|
||||
{
|
||||
showCompleted = !showCompleted;
|
||||
preferences.setShowCompleted(showCompleted);
|
||||
updateAdapterFilter();
|
||||
}
|
||||
|
||||
public void onSortByManually()
|
||||
{
|
||||
adapter.setPrimaryOrder(HabitList.Order.BY_POSITION);
|
||||
}
|
||||
|
||||
public void onSortByColor()
|
||||
{
|
||||
onSortToggleBy(HabitList.Order.BY_COLOR_ASC, HabitList.Order.BY_COLOR_DESC);
|
||||
}
|
||||
|
||||
|
||||
public void onSortByScore()
|
||||
{
|
||||
onSortToggleBy(HabitList.Order.BY_SCORE_DESC, HabitList.Order.BY_SCORE_ASC);
|
||||
}
|
||||
|
||||
public void onSortByName()
|
||||
{
|
||||
onSortToggleBy(HabitList.Order.BY_NAME_ASC, HabitList.Order.BY_NAME_DESC);
|
||||
}
|
||||
|
||||
public void onSortByStatus()
|
||||
{
|
||||
onSortToggleBy(HabitList.Order.BY_STATUS_ASC, HabitList.Order.BY_STATUS_DESC);
|
||||
}
|
||||
|
||||
private void onSortToggleBy(HabitList.Order defaultOrder, HabitList.Order reversedOrder)
|
||||
{
|
||||
if (adapter.getPrimaryOrder() != defaultOrder) {
|
||||
if (adapter.getPrimaryOrder() != reversedOrder) {
|
||||
adapter.setSecondaryOrder(adapter.getPrimaryOrder());
|
||||
}
|
||||
adapter.setPrimaryOrder(defaultOrder);
|
||||
} else {
|
||||
adapter.setPrimaryOrder(reversedOrder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onToggleNightMode()
|
||||
{
|
||||
themeSwitcher.toggleNightMode();
|
||||
screen.applyTheme();
|
||||
}
|
||||
|
||||
private void updateAdapterFilter()
|
||||
{
|
||||
adapter.setFilter(new HabitMatcherBuilder()
|
||||
.setArchivedAllowed(showArchived)
|
||||
.setCompletedAllowed(showCompleted)
|
||||
.build());
|
||||
adapter.refresh();
|
||||
}
|
||||
|
||||
public interface Adapter
|
||||
{
|
||||
void refresh();
|
||||
|
||||
void setFilter(HabitMatcher build);
|
||||
|
||||
void setPrimaryOrder(HabitList.Order order);
|
||||
|
||||
void setSecondaryOrder(HabitList.Order order);
|
||||
|
||||
HabitList.Order getPrimaryOrder();
|
||||
}
|
||||
|
||||
public interface Screen
|
||||
{
|
||||
void applyTheme();
|
||||
|
||||
void showAboutScreen();
|
||||
|
||||
void showFAQScreen();
|
||||
|
||||
void showSettingsScreen();
|
||||
|
||||
void showSelectHabitTypeDialog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.core.ui.screens.habits.list
|
||||
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.HabitMatcher
|
||||
import org.isoron.uhabits.core.models.HabitMatcherBuilder
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import javax.inject.Inject
|
||||
|
||||
class ListHabitsMenuBehavior @Inject constructor(
|
||||
private val screen: Screen,
|
||||
private val adapter: Adapter,
|
||||
private val preferences: Preferences,
|
||||
private val themeSwitcher: ThemeSwitcher
|
||||
) {
|
||||
private var showCompleted: Boolean
|
||||
private var showArchived: Boolean
|
||||
fun onCreateHabit() {
|
||||
screen.showSelectHabitTypeDialog()
|
||||
}
|
||||
|
||||
fun onViewFAQ() {
|
||||
screen.showFAQScreen()
|
||||
}
|
||||
|
||||
fun onViewAbout() {
|
||||
screen.showAboutScreen()
|
||||
}
|
||||
|
||||
fun onViewSettings() {
|
||||
screen.showSettingsScreen()
|
||||
}
|
||||
|
||||
fun onToggleShowArchived() {
|
||||
showArchived = !showArchived
|
||||
preferences.showArchived = showArchived
|
||||
updateAdapterFilter()
|
||||
}
|
||||
|
||||
fun onToggleShowCompleted() {
|
||||
showCompleted = !showCompleted
|
||||
preferences.showCompleted = showCompleted
|
||||
updateAdapterFilter()
|
||||
}
|
||||
|
||||
fun onSortByManually() {
|
||||
adapter.primaryOrder = HabitList.Order.BY_POSITION
|
||||
}
|
||||
|
||||
fun onSortByColor() {
|
||||
onSortToggleBy(HabitList.Order.BY_COLOR_ASC, HabitList.Order.BY_COLOR_DESC)
|
||||
}
|
||||
|
||||
fun onSortByScore() {
|
||||
onSortToggleBy(HabitList.Order.BY_SCORE_DESC, HabitList.Order.BY_SCORE_ASC)
|
||||
}
|
||||
|
||||
fun onSortByName() {
|
||||
onSortToggleBy(HabitList.Order.BY_NAME_ASC, HabitList.Order.BY_NAME_DESC)
|
||||
}
|
||||
|
||||
fun onSortByStatus() {
|
||||
onSortToggleBy(HabitList.Order.BY_STATUS_ASC, HabitList.Order.BY_STATUS_DESC)
|
||||
}
|
||||
|
||||
private fun onSortToggleBy(defaultOrder: HabitList.Order, reversedOrder: HabitList.Order) {
|
||||
if (adapter.primaryOrder != defaultOrder) {
|
||||
if (adapter.primaryOrder != reversedOrder) {
|
||||
adapter.secondaryOrder = adapter.primaryOrder
|
||||
}
|
||||
adapter.primaryOrder = defaultOrder
|
||||
} else {
|
||||
adapter.primaryOrder = reversedOrder
|
||||
}
|
||||
}
|
||||
|
||||
fun onToggleNightMode() {
|
||||
themeSwitcher.toggleNightMode()
|
||||
screen.applyTheme()
|
||||
}
|
||||
|
||||
private fun updateAdapterFilter() {
|
||||
adapter.setFilter(
|
||||
HabitMatcherBuilder()
|
||||
.setArchivedAllowed(showArchived)
|
||||
.setCompletedAllowed(showCompleted)
|
||||
.build()
|
||||
)
|
||||
adapter.refresh()
|
||||
}
|
||||
|
||||
interface Adapter {
|
||||
fun refresh()
|
||||
fun setFilter(matcher: HabitMatcher?)
|
||||
// fun setSecondaryOrder(order: HabitList.Order)
|
||||
// fun setPrimaryOrder(order: HabitList.Order)
|
||||
// fun getPrimaryOrder(): HabitList.Order
|
||||
var primaryOrder: HabitList.Order
|
||||
var secondaryOrder: HabitList.Order
|
||||
}
|
||||
|
||||
interface Screen {
|
||||
fun applyTheme()
|
||||
fun showAboutScreen()
|
||||
fun showFAQScreen()
|
||||
fun showSettingsScreen()
|
||||
fun showSelectHabitTypeDialog()
|
||||
}
|
||||
|
||||
init {
|
||||
showCompleted = preferences.showCompleted
|
||||
showArchived = preferences.showArchived
|
||||
updateAdapterFilter()
|
||||
}
|
||||
}
|
||||
@@ -1,145 +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.core.ui.screens.habits.list;
|
||||
|
||||
import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.commands.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.ui.callbacks.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class ListHabitsSelectionMenuBehavior
|
||||
{
|
||||
@NonNull
|
||||
private final Screen screen;
|
||||
|
||||
@NonNull
|
||||
CommandRunner commandRunner;
|
||||
|
||||
@NonNull
|
||||
private final Adapter adapter;
|
||||
|
||||
@NonNull
|
||||
private final HabitList habitList;
|
||||
|
||||
@Inject
|
||||
public ListHabitsSelectionMenuBehavior(@NonNull HabitList habitList,
|
||||
@NonNull Screen screen,
|
||||
@NonNull Adapter adapter,
|
||||
@NonNull CommandRunner commandRunner)
|
||||
{
|
||||
this.habitList = habitList;
|
||||
this.screen = screen;
|
||||
this.adapter = adapter;
|
||||
this.commandRunner = commandRunner;
|
||||
}
|
||||
|
||||
public boolean canArchive()
|
||||
{
|
||||
for (Habit h : adapter.getSelected())
|
||||
if (h.isArchived()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canEdit()
|
||||
{
|
||||
return (adapter.getSelected().size() == 1);
|
||||
}
|
||||
|
||||
public boolean canUnarchive()
|
||||
{
|
||||
for (Habit h : adapter.getSelected())
|
||||
if (!h.isArchived()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onArchiveHabits()
|
||||
{
|
||||
commandRunner.run(
|
||||
new ArchiveHabitsCommand(habitList, adapter.getSelected()));
|
||||
adapter.clearSelection();
|
||||
}
|
||||
|
||||
public void onChangeColor()
|
||||
{
|
||||
List<Habit> selected = adapter.getSelected();
|
||||
Habit first = selected.get(0);
|
||||
|
||||
screen.showColorPicker(first.getColor(), selectedColor ->
|
||||
{
|
||||
commandRunner.run(
|
||||
new ChangeHabitColorCommand(habitList, selected, selectedColor)
|
||||
);
|
||||
adapter.clearSelection();
|
||||
});
|
||||
}
|
||||
|
||||
public void onDeleteHabits()
|
||||
{
|
||||
List<Habit> selected = adapter.getSelected();
|
||||
screen.showDeleteConfirmationScreen(() ->
|
||||
{
|
||||
adapter.performRemove(selected);
|
||||
commandRunner.run(new DeleteHabitsCommand(habitList, selected)
|
||||
);
|
||||
adapter.clearSelection();
|
||||
}, selected.size());
|
||||
}
|
||||
|
||||
public void onEditHabits()
|
||||
{
|
||||
screen.showEditHabitsScreen(adapter.getSelected());
|
||||
adapter.clearSelection();
|
||||
}
|
||||
|
||||
public void onUnarchiveHabits()
|
||||
{
|
||||
commandRunner.run(
|
||||
new UnarchiveHabitsCommand(habitList, adapter.getSelected()));
|
||||
adapter.clearSelection();
|
||||
}
|
||||
|
||||
public interface Adapter
|
||||
{
|
||||
void clearSelection();
|
||||
|
||||
List<Habit> getSelected();
|
||||
|
||||
void performRemove(List<Habit> selected);
|
||||
}
|
||||
|
||||
public interface Screen
|
||||
{
|
||||
void showColorPicker(PaletteColor defaultColor,
|
||||
@NonNull OnColorPickedCallback callback);
|
||||
|
||||
void showDeleteConfirmationScreen(
|
||||
@NonNull OnConfirmedCallback callback,
|
||||
int quantity);
|
||||
|
||||
void showEditHabitsScreen(@NonNull List<Habit> selected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.core.ui.screens.habits.list
|
||||
|
||||
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
||||
import org.isoron.uhabits.core.commands.ChangeHabitColorCommand
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.DeleteHabitsCommand
|
||||
import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import javax.inject.Inject
|
||||
|
||||
class ListHabitsSelectionMenuBehavior @Inject constructor(
|
||||
private val habitList: HabitList,
|
||||
private val screen: Screen,
|
||||
private val adapter: Adapter,
|
||||
var commandRunner: CommandRunner
|
||||
) {
|
||||
fun canArchive(): Boolean {
|
||||
for ((_, _, _, _, isArchived) in adapter.selected) if (isArchived) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun canEdit(): Boolean {
|
||||
return adapter.selected.size == 1
|
||||
}
|
||||
|
||||
fun canUnarchive(): Boolean {
|
||||
for ((_, _, _, _, isArchived) in adapter.selected) if (!isArchived) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun onArchiveHabits() {
|
||||
commandRunner.run(
|
||||
ArchiveHabitsCommand(habitList, adapter.selected)
|
||||
)
|
||||
adapter.clearSelection()
|
||||
}
|
||||
|
||||
fun onChangeColor() {
|
||||
val selected = adapter.selected
|
||||
val (color) = selected[0]
|
||||
screen.showColorPicker(color) { selectedColor: PaletteColor? ->
|
||||
commandRunner.run(
|
||||
ChangeHabitColorCommand(habitList, selected, selectedColor!!)
|
||||
)
|
||||
adapter.clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteHabits() {
|
||||
val selected = adapter.selected
|
||||
screen.showDeleteConfirmationScreen(
|
||||
{
|
||||
adapter.performRemove(selected)
|
||||
commandRunner.run(
|
||||
DeleteHabitsCommand(habitList, selected)
|
||||
)
|
||||
adapter.clearSelection()
|
||||
},
|
||||
selected.size
|
||||
)
|
||||
}
|
||||
|
||||
fun onEditHabits() {
|
||||
screen.showEditHabitsScreen(adapter.selected)
|
||||
adapter.clearSelection()
|
||||
}
|
||||
|
||||
fun onUnarchiveHabits() {
|
||||
commandRunner.run(
|
||||
UnarchiveHabitsCommand(habitList, adapter.selected)
|
||||
)
|
||||
adapter.clearSelection()
|
||||
}
|
||||
|
||||
interface Adapter {
|
||||
fun clearSelection()
|
||||
val selected: List<Habit>
|
||||
fun performRemove(selected: List<Habit>)
|
||||
}
|
||||
|
||||
interface Screen {
|
||||
fun showColorPicker(
|
||||
defaultColor: PaletteColor,
|
||||
callback: OnColorPickedCallback
|
||||
)
|
||||
|
||||
fun showDeleteConfirmationScreen(
|
||||
callback: OnConfirmedCallback,
|
||||
quantity: Int
|
||||
)
|
||||
|
||||
fun showEditHabitsScreen(selected: List<Habit>)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.isoron.uhabits.core.ui.screens.habits.list
|
||||
|
||||
import com.nhaarman.mockitokotlin2.KArgumentCaptor
|
||||
import com.nhaarman.mockitokotlin2.any
|
||||
import com.nhaarman.mockitokotlin2.argumentCaptor
|
||||
import com.nhaarman.mockitokotlin2.clearInvocations
|
||||
import com.nhaarman.mockitokotlin2.eq
|
||||
@@ -38,7 +39,6 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
|
||||
@@ -89,9 +89,9 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
||||
@Throws(Exception::class)
|
||||
fun testOnExportCSV() {
|
||||
val outputDir = Files.createTempDirectory("CSV").toFile()
|
||||
whenever(dirFinder.csvOutputDir).thenReturn(outputDir)
|
||||
whenever(dirFinder.getCSVOutputDir()).thenReturn(outputDir)
|
||||
behavior.onExportCSV()
|
||||
verify(screen).showSendFileScreen(ArgumentMatchers.any())
|
||||
verify(screen).showSendFileScreen(any())
|
||||
assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1))
|
||||
FileUtils.deleteDirectory(outputDir)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
||||
fun testOnExportCSV_fail() {
|
||||
val outputDir = Files.createTempDirectory("CSV").toFile()
|
||||
outputDir.setWritable(false)
|
||||
whenever(dirFinder.csvOutputDir).thenReturn(outputDir)
|
||||
whenever(dirFinder.getCSVOutputDir()).thenReturn(outputDir)
|
||||
behavior.onExportCSV()
|
||||
verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT)
|
||||
assertTrue(outputDir.delete())
|
||||
@@ -131,11 +131,11 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
||||
@Test
|
||||
@Throws(IOException::class)
|
||||
fun testOnSendBugReport() {
|
||||
whenever(bugReporter.bugReport).thenReturn("hello")
|
||||
whenever(bugReporter.getBugReport()).thenReturn("hello")
|
||||
behavior.onSendBugReport()
|
||||
verify(bugReporter).dumpBugReportToFile()
|
||||
verify(screen).showSendBugReportToDeveloperScreen("hello")
|
||||
whenever(bugReporter.bugReport).thenThrow(IOException())
|
||||
whenever(bugReporter.getBugReport()).thenThrow(IOException())
|
||||
behavior.onSendBugReport()
|
||||
verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_GENERATE_BUG_REPORT)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.isoron.uhabits.core.models.HabitMatcher
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
|
||||
class ListHabitsMenuBehaviorTest : BaseUnitTest() {
|
||||
private lateinit var behavior: ListHabitsMenuBehavior
|
||||
@@ -116,7 +115,7 @@ class ListHabitsMenuBehaviorTest : BaseUnitTest() {
|
||||
whenever(adapter.primaryOrder).thenReturn(HabitList.Order.BY_NAME_ASC)
|
||||
behavior.onSortByStatus()
|
||||
verify(adapter).primaryOrder = orderCaptor.capture()
|
||||
verify(adapter).setSecondaryOrder(secondaryOrderCaptor.capture())
|
||||
verify(adapter).secondaryOrder = secondaryOrderCaptor.capture()
|
||||
assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_STATUS_ASC))
|
||||
assertThat(secondaryOrderCaptor.lastValue, equalTo(HabitList.Order.BY_NAME_ASC))
|
||||
}
|
||||
@@ -126,7 +125,7 @@ class ListHabitsMenuBehaviorTest : BaseUnitTest() {
|
||||
whenever(adapter.primaryOrder).thenReturn(HabitList.Order.BY_STATUS_ASC)
|
||||
behavior.onSortByStatus()
|
||||
verify(adapter).primaryOrder = orderCaptor.capture()
|
||||
verify(adapter, never()).setSecondaryOrder(ArgumentMatchers.any())
|
||||
verify(adapter, never()).secondaryOrder
|
||||
assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_STATUS_DESC))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user