From a2cf78f82390f1a76b9dde8b6c26f21ce5858bb0 Mon Sep 17 00:00:00 2001 From: Dharanish Date: Wed, 3 Jul 2024 09:14:16 +0200 Subject: [PATCH] Add show habit group activity --- uhabits-android/src/main/AndroidManifest.xml | 8 + .../uhabits/activities/HabitsDirFinder.kt | 11 ++ .../habits/list/ListHabitsScreen.kt | 6 + .../habits/list/views/HabitCardListAdapter.kt | 8 + .../list/views/HabitCardListController.kt | 11 +- .../habits/show/ShowHabitGroupActivity.kt | 152 ++++++++++++++++++ .../habits/show/ShowHabitGroupMenu.kt | 32 ++++ .../habits/show/ShowHabitGroupView.kt | 35 ++++ .../isoron/uhabits/intents/IntentFactory.kt | 6 + .../src/main/res/menu/show_habit_group.xml | 35 ++++ .../isoron/uhabits/core/models/StreakList.kt | 4 +- .../screens/habits/list/ListHabitsBehavior.kt | 6 + .../ui/screens/habits/show/ShowHabitGroup.kt | 78 +++++++++ .../show/ShowHabitGroupMenuPresenter.kt | 46 ++++++ .../ui/screens/habits/show/views/NotesCard.kt | 5 + .../screens/habits/show/views/OverviewCard.kt | 23 +++ .../ui/screens/habits/show/views/ScoreCard.kt | 40 +++++ .../screens/habits/show/views/StreakCart.kt | 9 ++ .../screens/habits/show/views/SubtitleCard.kt | 13 ++ 19 files changed, 524 insertions(+), 4 deletions(-) create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupActivity.kt create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupMenu.kt create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupView.kt create mode 100644 uhabits-android/src/main/res/menu/show_habit_group.xml create mode 100644 uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroup.kt create mode 100644 uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroupMenuPresenter.kt diff --git a/uhabits-android/src/main/AndroidManifest.xml b/uhabits-android/src/main/AndroidManifest.xml index 56529c4bd..36f3928c0 100644 --- a/uhabits-android/src/main/AndroidManifest.xml +++ b/uhabits-android/src/main/AndroidManifest.xml @@ -80,6 +80,14 @@ android:value=".activities.habits.list.ListHabitsActivity" /> + + + + diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsDirFinder.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsDirFinder.kt index 953ee554c..4692799c4 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsDirFinder.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsDirFinder.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.activities import org.isoron.uhabits.AndroidDirFinder import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupMenuPresenter import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter import java.io.File import javax.inject.Inject @@ -33,3 +34,13 @@ constructor( return androidDirFinder.getFilesDir("CSV")!! } } + +class HabitGroupsDirFinder @Inject +constructor( + private val androidDirFinder: AndroidDirFinder +) : ShowHabitGroupMenuPresenter.System, ListHabitsBehavior.DirFinder { + + override fun getCSVOutputDir(): File { + return androidDirFinder.getFilesDir("CSV")!! + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt index c6ad6fae8..4441cdbdc 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt @@ -45,6 +45,7 @@ import org.isoron.uhabits.core.commands.DeleteHabitsCommand import org.isoron.uhabits.core.commands.EditHabitCommand import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.tasks.TaskRunner @@ -188,6 +189,11 @@ class ListHabitsScreen activity.startActivity(intent) } + override fun showHabitGroupScreen(hgr: HabitGroup) { + val intent = intentFactory.startShowHabitGroupActivity(activity, hgr) + activity.startActivity(intent) + } + fun showImportScreen() { val intent = intentFactory.openDocument() activity.startActivityForResult(intent, REQUEST_OPEN_DOCUMENT) 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 index 6f266a207..264978c66 100644 --- 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 @@ -102,6 +102,14 @@ class HabitCardListAdapter @Inject constructor( return cache.getHabitByPosition(position) } + fun getHabit(position: Int): Habit? { + return cache.getHabitByPosition(position) + } + + fun getHabitGroup(position: Int): HabitGroup? { + return cache.getHabitGroupByPosition(position) + } + override fun getItemCount(): Int { return cache.itemCount } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListController.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListController.kt index a70805139..9ae4243c6 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListController.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListController.kt @@ -114,8 +114,15 @@ class HabitCardListController @Inject constructor( */ internal inner class NormalMode : Mode { override fun onItemClick(position: Int) { - val habit = adapter.getItem(position) ?: return - behavior.onClickHabit(habit) + val habit = adapter.getHabit(position) + if (habit != null) { + behavior.onClickHabit(habit) + } else { + val hgr = adapter.getHabitGroup(position) + if (hgr != null) { + behavior.onClickHabitGroup(hgr) + } + } } override fun onItemLongClick(position: Int): Boolean { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupActivity.kt new file mode 100644 index 000000000..97bab67c2 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupActivity.kt @@ -0,0 +1,152 @@ +package org.isoron.uhabits.activities.habits.show + +import android.content.ContentUris +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.isoron.uhabits.AndroidDirFinder +import org.isoron.uhabits.HabitsApplication +import org.isoron.uhabits.R +import org.isoron.uhabits.activities.AndroidThemeSwitcher +import org.isoron.uhabits.activities.HabitGroupsDirFinder +import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog +import org.isoron.uhabits.core.commands.Command +import org.isoron.uhabits.core.commands.CommandRunner +import org.isoron.uhabits.core.models.HabitGroup +import org.isoron.uhabits.core.preferences.Preferences +import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupMenuPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupPresenter +import org.isoron.uhabits.intents.IntentFactory +import org.isoron.uhabits.utils.dismissCurrentAndShow +import org.isoron.uhabits.utils.dismissCurrentDialog +import org.isoron.uhabits.utils.showMessage +import org.isoron.uhabits.utils.showSendFileScreen +import org.isoron.uhabits.widgets.WidgetUpdater + +class ShowHabitGroupActivity : AppCompatActivity(), CommandRunner.Listener { + + private lateinit var commandRunner: CommandRunner + private lateinit var menu: ShowHabitGroupMenu + private lateinit var view: ShowHabitGroupView + private lateinit var habitGroup: HabitGroup + private lateinit var preferences: Preferences + private lateinit var themeSwitcher: AndroidThemeSwitcher + private lateinit var widgetUpdater: WidgetUpdater + + private val scope = CoroutineScope(Dispatchers.Main) + private lateinit var presenter: ShowHabitGroupPresenter + private val screen = Screen() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val appComponent = (applicationContext as HabitsApplication).component + val habitGroupList = appComponent.habitGroupList + habitGroup = habitGroupList.getById(ContentUris.parseId(intent.data!!))!! + preferences = appComponent.preferences + commandRunner = appComponent.commandRunner + widgetUpdater = appComponent.widgetUpdater + + themeSwitcher = AndroidThemeSwitcher(this, preferences) + themeSwitcher.apply() + + presenter = ShowHabitGroupPresenter( + commandRunner = commandRunner, + habitGroup = habitGroup, + preferences = preferences, + screen = screen + ) + + view = ShowHabitGroupView(this) + + val menuPresenter = ShowHabitGroupMenuPresenter( + commandRunner = commandRunner, + habitGroup = habitGroup, + habitGroupList = habitGroupList, + screen = screen, + system = HabitGroupsDirFinder(AndroidDirFinder(this)), + taskRunner = appComponent.taskRunner + ) + + menu = ShowHabitGroupMenu( + activity = this, + presenter = menuPresenter, + preferences = preferences + ) + + view.setListener(presenter) + setContentView(view) + } + + override fun onCreateOptionsMenu(m: Menu): Boolean { + return menu.onCreateOptionsMenu(m) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return menu.onOptionsItemSelected(item) + } + + override fun onResume() { + super.onResume() + commandRunner.addListener(this) + screen.refresh() + } + + override fun onPause() { + dismissCurrentDialog() + commandRunner.removeListener(this) + super.onPause() + } + + override fun onCommandFinished(command: Command) { + screen.refresh() + } + + inner class Screen : ShowHabitGroupMenuPresenter.Screen, ShowHabitGroupPresenter.Screen { + override fun updateWidgets() { + widgetUpdater.updateWidgets() + } + + override fun refresh() { + scope.launch { + view.setState( + ShowHabitGroupPresenter.buildState( + habitGroup = habitGroup, + preferences = preferences, + theme = themeSwitcher.currentTheme + ) + ) + } + } + + override fun showEditHabitGroupScreen(habitGroup: HabitGroup) { + startActivity(IntentFactory().startEditGroupActivity(this@ShowHabitGroupActivity, habitGroup)) + } + + override fun showMessage(m: ShowHabitGroupMenuPresenter.Message?) { + when (m) { + ShowHabitGroupMenuPresenter.Message.COULD_NOT_EXPORT -> { + showMessage(resources.getString(R.string.could_not_export)) + } + else -> {} + } + } + + override fun showSendFileScreen(filename: String) { + this@ShowHabitGroupActivity.showSendFileScreen(filename) + } + + override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback) { + ConfirmDeleteDialog(this@ShowHabitGroupActivity, callback, 1).dismissCurrentAndShow() + } + + override fun close() { + this@ShowHabitGroupActivity.finish() + } + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupMenu.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupMenu.kt new file mode 100644 index 000000000..4ee4eedb5 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupMenu.kt @@ -0,0 +1,32 @@ +package org.isoron.uhabits.activities.habits.show + +import android.view.Menu +import android.view.MenuItem +import org.isoron.uhabits.R +import org.isoron.uhabits.core.preferences.Preferences +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupMenuPresenter + +class ShowHabitGroupMenu( + val activity: ShowHabitGroupActivity, + val presenter: ShowHabitGroupMenuPresenter, + val preferences: Preferences +) { + fun onCreateOptionsMenu(menu: Menu): Boolean { + activity.menuInflater.inflate(R.menu.show_habit_group, menu) + return true + } + + fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_edit_habit_group -> { + presenter.onEditHabit() + return true + } + R.id.action_delete -> { + presenter.onDeleteHabit() + return true + } + } + return false + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupView.kt new file mode 100644 index 000000000..8575a38f5 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitGroupView.kt @@ -0,0 +1,35 @@ +package org.isoron.uhabits.activities.habits.show + +import android.content.Context +import android.view.LayoutInflater +import android.widget.FrameLayout +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitGroupState +import org.isoron.uhabits.databinding.ShowHabitGroupBinding +import org.isoron.uhabits.utils.setupToolbar + +class ShowHabitGroupView(context: Context) : FrameLayout(context) { + private val binding = ShowHabitGroupBinding.inflate(LayoutInflater.from(context)) + + init { + addView(binding.root) + } + + fun setState(data: ShowHabitGroupState) { + setupToolbar( + binding.toolbar, + title = data.title, + color = data.color, + theme = data.theme + ) + binding.subtitleCard.setState(data.subtitle) + binding.overviewCard.setState(data.overview) + binding.notesCard.setState(data.notes) + binding.streakCard.setState(data.streaks) + binding.scoreCard.setState(data.scores) + } + + fun setListener(presenter: ShowHabitGroupPresenter) { + binding.scoreCard.setListener(presenter.scoreCardPresenter) + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt index b2beb3d46..866bfe11c 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt @@ -27,6 +27,7 @@ import org.isoron.uhabits.activities.about.AboutActivity import org.isoron.uhabits.activities.habits.edit.EditHabitActivity import org.isoron.uhabits.activities.habits.edit.EditHabitGroupActivity import org.isoron.uhabits.activities.habits.show.ShowHabitActivity +import org.isoron.uhabits.activities.habits.show.ShowHabitGroupActivity import org.isoron.uhabits.activities.intro.IntroActivity import org.isoron.uhabits.activities.settings.SettingsActivity import org.isoron.uhabits.core.models.Habit @@ -67,6 +68,11 @@ class IntentFactory data = Uri.parse(habit.uriString) } + fun startShowHabitGroupActivity(context: Context, habitGroup: HabitGroup) = + Intent(context, ShowHabitGroupActivity::class.java).apply { + data = Uri.parse(habitGroup.uriString) + } + fun viewFAQ(context: Context) = buildViewIntent(context.getString(R.string.helpURL)) diff --git a/uhabits-android/src/main/res/menu/show_habit_group.xml b/uhabits-android/src/main/res/menu/show_habit_group.xml new file mode 100644 index 000000000..6f1e43a82 --- /dev/null +++ b/uhabits-android/src/main/res/menu/show_habit_group.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/StreakList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/StreakList.kt index 494209fce..d5df21f1a 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/StreakList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/StreakList.kt @@ -74,12 +74,12 @@ class StreakList { from: Timestamp, to: Timestamp ) { + if (habitList.isEmpty) return var current = from var streakRunning = false var streakStart = from while (current <= to) { - if (habitList.all { it.streaks.isInStreaks(current) } && !streakRunning - ) { + if (habitList.all { it.streaks.isInStreaks(current) } && !streakRunning) { streakStart = current streakRunning = true } else if (streakRunning) { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt index b66b08be6..73ac30d23 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt @@ -22,6 +22,7 @@ import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateRepetitionCommand import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitType import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST @@ -51,6 +52,10 @@ open class ListHabitsBehavior @Inject constructor( screen.showHabitScreen(h) } + fun onClickHabitGroup(hgr: HabitGroup) { + screen.showHabitGroupScreen(hgr) + } + fun onEdit(habit: Habit, timestamp: Timestamp?) { val entry = habit.computedEntries.get(timestamp!!) if (habit.type == HabitType.NUMERICAL) { @@ -178,6 +183,7 @@ open class ListHabitsBehavior @Inject constructor( interface Screen { fun showHabitScreen(h: Habit) + fun showHabitGroupScreen(hgr: HabitGroup) fun showIntroScreen() fun showMessage(m: Message) fun showNumberPopup( diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroup.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroup.kt new file mode 100644 index 000000000..56359832e --- /dev/null +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroup.kt @@ -0,0 +1,78 @@ +package org.isoron.uhabits.core.ui.screens.habits.show + +import org.isoron.uhabits.core.commands.CommandRunner +import org.isoron.uhabits.core.models.HabitGroup +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.preferences.Preferences +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardState +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardState +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCartPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState +import org.isoron.uhabits.core.ui.views.Theme + +data class ShowHabitGroupState( + val title: String = "", + val color: PaletteColor = PaletteColor(1), + val subtitle: SubtitleCardState, + val overview: OverviewCardState, + val notes: NotesCardState, + val streaks: StreakCardState, + val scores: ScoreCardState, + val theme: Theme +) + +class ShowHabitGroupPresenter( + val habitGroup: HabitGroup, + val preferences: Preferences, + val screen: Screen, + val commandRunner: CommandRunner +) { + val scoreCardPresenter = ScoreCardPresenter( + preferences = preferences, + screen = screen + ) + + companion object { + fun buildState( + habitGroup: HabitGroup, + preferences: Preferences, + theme: Theme + ): ShowHabitGroupState { + return ShowHabitGroupState( + title = habitGroup.name, + color = habitGroup.color, + theme = theme, + subtitle = SubtitleCardPresenter.buildState( + habitGroup = habitGroup, + theme = theme + ), + overview = OverviewCardPresenter.buildState( + habitGroup = habitGroup, + theme = theme + ), + notes = NotesCardPresenter.buildState( + habitGroup = habitGroup + ), + streaks = StreakCartPresenter.buildState( + habitGroup = habitGroup, + theme = theme + ), + scores = ScoreCardPresenter.buildState( + spinnerPosition = preferences.scoreCardSpinnerPosition, + habitGroup = habitGroup, + firstWeekday = preferences.firstWeekdayInt, + theme = theme + ) + ) + } + } + + interface Screen : + ScoreCardPresenter.Screen +} diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroupMenuPresenter.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroupMenuPresenter.kt new file mode 100644 index 000000000..196d9a8a1 --- /dev/null +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitGroupMenuPresenter.kt @@ -0,0 +1,46 @@ +package org.isoron.uhabits.core.ui.screens.habits.show + +import org.isoron.uhabits.core.commands.CommandRunner +import org.isoron.uhabits.core.commands.DeleteHabitGroupsCommand +import org.isoron.uhabits.core.models.HabitGroup +import org.isoron.uhabits.core.models.HabitGroupList +import org.isoron.uhabits.core.tasks.TaskRunner +import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback +import java.io.File + +class ShowHabitGroupMenuPresenter( + private val commandRunner: CommandRunner, + private val habitGroup: HabitGroup, + private val habitGroupList: HabitGroupList, + private val screen: Screen, + private val system: System, + private val taskRunner: TaskRunner +) { + fun onEditHabit() { + screen.showEditHabitGroupScreen(habitGroup) + } + + fun onDeleteHabit() { + screen.showDeleteConfirmationScreen { + commandRunner.run(DeleteHabitGroupsCommand(habitGroupList, listOf(habitGroup))) + screen.close() + } + } + + enum class Message { + COULD_NOT_EXPORT + } + + interface Screen { + fun showEditHabitGroupScreen(habitGroup: HabitGroup) + fun showMessage(m: Message?) + fun showSendFileScreen(filename: String) + fun showDeleteConfirmationScreen(callback: OnConfirmedCallback) + fun close() + fun refresh() + } + + interface System { + fun getCSVOutputDir(): File + } +} diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt index 47049bb83..8f608b98b 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup data class NotesCardState( val description: String @@ -30,5 +31,9 @@ class NotesCardPresenter { fun buildState(habit: Habit) = NotesCardState( description = habit.description ) + + fun buildState(habitGroup: HabitGroup) = NotesCardState( + description = habitGroup.description + ) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt index e15c08403..d895b7523 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.ui.views.Theme import org.isoron.uhabits.core.utils.DateUtils @@ -57,5 +58,27 @@ class OverviewCardPresenter { theme = theme ) } + + fun buildState(habitGroup: HabitGroup, theme: Theme): OverviewCardState { + val today = DateUtils.getTodayWithOffset() + val lastMonth = today.minus(30) + val lastYear = today.minus(365) + val scores = habitGroup.scores + val scoreToday = scores[today].value.toFloat() + val scoreLastMonth = scores[lastMonth].value.toFloat() + val scoreLastYear = scores[lastYear].value.toFloat() + val totalCount = habitGroup.habitList.sumOf { habit -> + habit.originalEntries.getKnown().count { it.value == Entry.YES_MANUAL } + .toLong() + } + return OverviewCardState( + color = habitGroup.color, + scoreToday = scoreToday, + scoreMonthDiff = scoreToday - scoreLastMonth, + scoreYearDiff = scoreToday - scoreLastYear, + totalCount = totalCount, + theme = theme + ) + } } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt index b85d75204..511bb757a 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.models.Score import org.isoron.uhabits.core.preferences.Preferences @@ -83,6 +84,45 @@ class ScoreCardPresenter( theme = theme ) } + + fun buildState( + habitGroup: HabitGroup, + firstWeekday: Int, + spinnerPosition: Int, + theme: Theme + ): ScoreCardState { + val bucketSize = BUCKET_SIZES[spinnerPosition] + val today = DateUtils.getTodayWithOffset() + val oldest = if (habitGroup.habitList.isEmpty) { + today + } else { + habitGroup.habitList.minOf { + it.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + } + } + + val field = getTruncateField(bucketSize) + val scores = habitGroup.scores.getByInterval(oldest, today).groupBy { + DateUtils.truncate(field, it.timestamp, firstWeekday) + }.map { (timestamp, scores) -> + Score( + timestamp, + scores.map { + it.value + }.average() + ) + }.sortedBy { + it.timestamp + }.reversed() + + return ScoreCardState( + color = habitGroup.color, + scores = scores, + bucketSize = bucketSize, + spinnerPosition = spinnerPosition, + theme = theme + ) + } } fun onSpinnerPosition(position: Int) { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt index a242ab4b7..cb78e19b8 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.models.Streak import org.isoron.uhabits.core.ui.views.Theme @@ -39,5 +40,13 @@ class StreakCartPresenter { theme = theme ) } + + fun buildState(habitGroup: HabitGroup, theme: Theme): StreakCardState { + return StreakCardState( + color = habitGroup.color, + bestStreaks = habitGroup.streaks.getBest(10), + theme = theme + ) + } } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt index fb839933c..e9b569c61 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.NumericalHabitType import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.models.Reminder @@ -54,5 +55,17 @@ class SubtitleCardPresenter { unit = habit.unit, theme = theme ) + + fun buildState( + habitGroup: HabitGroup, + theme: Theme + ): SubtitleCardState = SubtitleCardState( + color = habitGroup.color, + frequency = Frequency.DAILY, + isNumerical = false, + question = habitGroup.question, + reminder = habitGroup.reminder, + theme = theme + ) } }