mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Restructure ShowHabitActivity
This commit is contained in:
@@ -38,7 +38,6 @@ class TestModule {
|
|||||||
ActivityContextModule::class,
|
ActivityContextModule::class,
|
||||||
HabitsActivityModule::class,
|
HabitsActivityModule::class,
|
||||||
ListHabitsModule::class,
|
ListHabitsModule::class,
|
||||||
ShowHabitModule::class,
|
|
||||||
HabitModule::class,
|
HabitModule::class,
|
||||||
TestModule::class
|
TestModule::class
|
||||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||||
|
|||||||
@@ -19,18 +19,20 @@
|
|||||||
package org.isoron.uhabits.activities.habits.show.views
|
package org.isoron.uhabits.activities.habits.show.views
|
||||||
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.view.View.*
|
||||||
import androidx.test.ext.junit.runners.*
|
import androidx.test.ext.junit.runners.*
|
||||||
import androidx.test.filters.*
|
import androidx.test.filters.*
|
||||||
|
import org.hamcrest.Matchers.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
|
import org.junit.Assert.*
|
||||||
import org.junit.runner.*
|
import org.junit.runner.*
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
class NotesCardTest : BaseViewTest() {
|
class NotesCardViewTest : BaseViewTest() {
|
||||||
val PATH = "habits/show/NotesCard"
|
val PATH = "habits/show/NotesCard"
|
||||||
private lateinit var view: NotesCard
|
private lateinit var view: NotesCardView
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
@@ -39,24 +41,18 @@ class NotesCardTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById(R.id.notesCard)
|
.findViewById(R.id.notesCard)
|
||||||
view.onData(ShowHabitViewModel(
|
view.update(NotesCardViewModel(description = "This is a test description"))
|
||||||
description = "This is a test description",
|
|
||||||
))
|
|
||||||
measureView(view, 800f, 200f)
|
measureView(view, 800f, 200f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testRender() {
|
fun testRender() {
|
||||||
assertRenders(view, "$PATH/render.png")
|
assertRenders(view, "$PATH/render.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testRenderEmptyDescription() {
|
fun testRenderEmptyDescription() {
|
||||||
view.onData(ShowHabitViewModel(
|
view.update(NotesCardViewModel(description = ""))
|
||||||
description = "",
|
assertThat(view.visibility, equalTo(GONE))
|
||||||
))
|
|
||||||
assertRenders(view, "$PATH/render-empty-description.png")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,16 +22,15 @@ import android.view.*
|
|||||||
import androidx.test.ext.junit.runners.*
|
import androidx.test.ext.junit.runners.*
|
||||||
import androidx.test.filters.*
|
import androidx.test.filters.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
|
||||||
import org.isoron.uhabits.core.models.*
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.runner.*
|
import org.junit.runner.*
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
class OverviewCardTest : BaseViewTest() {
|
class OverviewCardViewTest : BaseViewTest() {
|
||||||
val PATH = "habits/show/OverviewCard/"
|
val PATH = "habits/show/OverviewCard/"
|
||||||
private lateinit var view: OverviewCard
|
private lateinit var view: OverviewCardView
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
@@ -39,8 +38,8 @@ class OverviewCardTest : BaseViewTest() {
|
|||||||
view = LayoutInflater
|
view = LayoutInflater
|
||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById<View>(R.id.overviewCard) as OverviewCard
|
.findViewById<View>(R.id.overviewCard) as OverviewCardView
|
||||||
view.onData(ShowHabitViewModel(
|
view.update(OverviewCardViewModel(
|
||||||
scoreToday = 0.74f,
|
scoreToday = 0.74f,
|
||||||
scoreMonthDiff = 0.23f,
|
scoreMonthDiff = 0.23f,
|
||||||
scoreYearDiff = 0.74f,
|
scoreYearDiff = 0.74f,
|
||||||
@@ -22,15 +22,15 @@ import android.view.*
|
|||||||
import androidx.test.ext.junit.runners.*
|
import androidx.test.ext.junit.runners.*
|
||||||
import androidx.test.filters.*
|
import androidx.test.filters.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.runner.*
|
import org.junit.runner.*
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
class SubtitleCardTest : BaseViewTest() {
|
class SubtitleCardViewTest : BaseViewTest() {
|
||||||
val PATH = "habits/show/SubtitleCard/"
|
val PATH = "habits/show/SubtitleCard/"
|
||||||
private lateinit var view: SubtitleCard
|
private lateinit var view: SubtitleCardView
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
@@ -39,10 +39,13 @@ class SubtitleCardTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById(R.id.subtitleCard)
|
.findViewById(R.id.subtitleCard)
|
||||||
view.onData(ShowHabitViewModel(
|
view.update(SubtitleCardViewModel(
|
||||||
|
color = PaletteColor(7),
|
||||||
|
frequencyText = "3 times in 7 days",
|
||||||
|
isNumerical = false,
|
||||||
question = "Did you meditate this morning?",
|
question = "Did you meditate this morning?",
|
||||||
reminderText = "8:30 AM",
|
reminderText = "8:30 AM",
|
||||||
frequencyText = "3 times in 7 days",
|
targetText = "",
|
||||||
))
|
))
|
||||||
measureView(view, 800f, 200f)
|
measureView(view, 800f, 200f)
|
||||||
}
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities
|
|
||||||
|
|
||||||
import android.content.*
|
|
||||||
import android.util.*
|
|
||||||
import android.widget.*
|
|
||||||
|
|
||||||
abstract class DataView<T>(
|
|
||||||
context: Context,
|
|
||||||
attrs: AttributeSet,
|
|
||||||
) : LinearLayout(context, attrs), Presenter.Listener<T> {
|
|
||||||
|
|
||||||
lateinit var presenter: Presenter<T>
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
presenter.addListener(this)
|
|
||||||
presenter.requestData(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
presenter.removeListener(this)
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract override fun onData(data: T)
|
|
||||||
}
|
|
||||||
@@ -36,11 +36,9 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*
|
|||||||
BaseActivityModule::class,
|
BaseActivityModule::class,
|
||||||
HabitsActivityModule::class,
|
HabitsActivityModule::class,
|
||||||
ListHabitsModule::class,
|
ListHabitsModule::class,
|
||||||
ShowHabitModule::class,
|
|
||||||
HabitModule::class
|
HabitModule::class
|
||||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||||
interface HabitsActivityComponent {
|
interface HabitsActivityComponent {
|
||||||
val showHabitPresenter: ShowHabitPresenter
|
|
||||||
val colorPickerDialogFactory: ColorPickerDialogFactory
|
val colorPickerDialogFactory: ColorPickerDialogFactory
|
||||||
val habitCardListAdapter: HabitCardListAdapter
|
val habitCardListAdapter: HabitCardListAdapter
|
||||||
val listHabitsBehavior: ListHabitsBehavior
|
val listHabitsBehavior: ListHabitsBehavior
|
||||||
@@ -48,6 +46,5 @@ interface HabitsActivityComponent {
|
|||||||
val listHabitsRootView: ListHabitsRootView
|
val listHabitsRootView: ListHabitsRootView
|
||||||
val listHabitsScreen: ListHabitsScreen
|
val listHabitsScreen: ListHabitsScreen
|
||||||
val listHabitsSelectionMenu: ListHabitsSelectionMenu
|
val listHabitsSelectionMenu: ListHabitsSelectionMenu
|
||||||
val showHabitScreen: ShowHabitScreen
|
|
||||||
val themeSwitcher: ThemeSwitcher
|
val themeSwitcher: ThemeSwitcher
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities
|
|
||||||
|
|
||||||
import org.isoron.uhabits.core.commands.*
|
|
||||||
|
|
||||||
abstract class Presenter<M>(
|
|
||||||
val commandRunner: CommandRunner,
|
|
||||||
) : CommandRunner.Listener {
|
|
||||||
|
|
||||||
private val listeners = mutableListOf<Listener<M>>()
|
|
||||||
private var data: M? = null
|
|
||||||
|
|
||||||
fun onResume() {
|
|
||||||
commandRunner.addListener(this)
|
|
||||||
data = refresh()
|
|
||||||
notifyListeners()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun refresh(): M
|
|
||||||
|
|
||||||
fun onPause() {
|
|
||||||
commandRunner.removeListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addListener(listener: Listener<M>) {
|
|
||||||
listeners.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeListener(listener: Listener<M>) {
|
|
||||||
listeners.remove(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestData(listener: Listener<M>) {
|
|
||||||
if (data == null) data = refresh()
|
|
||||||
listener.onData(data!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCommandExecuted(command: Command?, refreshKey: Long?) {
|
|
||||||
data = refresh()
|
|
||||||
notifyListeners()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun notifyListeners() {
|
|
||||||
for (l in listeners) l.onData(data!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener<T> {
|
|
||||||
fun onData(data: T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,22 +18,127 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.activities.habits.show
|
package org.isoron.uhabits.activities.habits.show
|
||||||
|
|
||||||
|
import android.content.*
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import org.isoron.uhabits.activities.*
|
import android.view.*
|
||||||
|
import android.widget.*
|
||||||
|
import androidx.appcompat.app.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.isoron.uhabits.*
|
||||||
|
import org.isoron.uhabits.activities.habits.show.views.*
|
||||||
|
import org.isoron.uhabits.core.commands.*
|
||||||
|
import org.isoron.uhabits.core.models.*
|
||||||
|
import org.isoron.uhabits.core.preferences.*
|
||||||
|
import org.isoron.uhabits.databinding.*
|
||||||
|
import org.isoron.uhabits.utils.*
|
||||||
|
|
||||||
|
class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
||||||
|
private lateinit var view: ShowHabitView
|
||||||
|
private lateinit var presenter: ShowHabitPresenter
|
||||||
|
private lateinit var commandRunner: CommandRunner
|
||||||
|
private val scope = CoroutineScope(Dispatchers.Main)
|
||||||
|
|
||||||
class ShowHabitActivity : HabitsActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setScreen(component.showHabitScreen)
|
val appComponent = (applicationContext as HabitsApplication).component
|
||||||
|
val habitList = appComponent.habitList
|
||||||
|
val habit = habitList.getById(ContentUris.parseId(intent.data!!))!!
|
||||||
|
commandRunner = appComponent.commandRunner
|
||||||
|
|
||||||
|
view = ShowHabitView(this)
|
||||||
|
presenter = ShowHabitPresenter(
|
||||||
|
habit = habit,
|
||||||
|
context = this,
|
||||||
|
preferences = appComponent.preferences
|
||||||
|
)
|
||||||
|
setContentView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
component.showHabitPresenter.onResume()
|
commandRunner.addListener(this)
|
||||||
|
updateViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
component.showHabitPresenter.onPause()
|
commandRunner.removeListener(this)
|
||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCommandExecuted(command: Command?, refreshKey: Long?) {
|
||||||
|
updateViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateViews() {
|
||||||
|
scope.launch {
|
||||||
|
view.update(presenter.present())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ShowHabitViewModel(
|
||||||
|
val title: String = "",
|
||||||
|
val isNumerical: Boolean = false,
|
||||||
|
val color: PaletteColor = PaletteColor(1),
|
||||||
|
val subtitle: SubtitleCardViewModel,
|
||||||
|
val overview: OverviewCardViewModel,
|
||||||
|
val notes: NotesCardViewModel,
|
||||||
|
val target: TargetCardViewModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ShowHabitView(context: Context) : FrameLayout(context) {
|
||||||
|
private val binding = ShowHabitBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
init {
|
||||||
|
addView(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(data: ShowHabitViewModel) {
|
||||||
|
setupToolbar(binding.toolbar, title = data.title, color = data.color)
|
||||||
|
binding.subtitleCard.update(data.subtitle)
|
||||||
|
binding.overviewCard.update(data.overview)
|
||||||
|
binding.notesCard.update(data.notes)
|
||||||
|
binding.targetCard.update(data.target)
|
||||||
|
|
||||||
|
if (data.isNumerical) {
|
||||||
|
binding.overviewCard.visibility = GONE
|
||||||
|
binding.streakCard.visibility = GONE
|
||||||
|
} else {
|
||||||
|
binding.targetCard.visibility = GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setController(controller: Controller) {
|
||||||
|
binding.historyCard.setController(controller)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Controller : HistoryCard.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShowHabitPresenter(
|
||||||
|
val habit: Habit,
|
||||||
|
val context: Context,
|
||||||
|
val preferences: Preferences,
|
||||||
|
) {
|
||||||
|
private val subtitleCardPresenter = SubtitleCardPresenter(habit, context)
|
||||||
|
private val overviewCardPresenter = OverviewCardPresenter(habit)
|
||||||
|
private val notesCardPresenter = NotesCardPresenter(habit)
|
||||||
|
private val targetCardPresenter = TargetCardPresenter(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = preferences.firstWeekday,
|
||||||
|
resources = context.resources
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun present(): ShowHabitViewModel {
|
||||||
|
return ShowHabitViewModel(
|
||||||
|
title = habit.name,
|
||||||
|
color = habit.color,
|
||||||
|
isNumerical = habit.isNumerical,
|
||||||
|
subtitle = subtitleCardPresenter.present(),
|
||||||
|
overview = overviewCardPresenter.present(),
|
||||||
|
notes = notesCardPresenter.present(),
|
||||||
|
target = targetCardPresenter.present(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.isoron.uhabits.activities.habits.show
|
|
||||||
|
|
||||||
import dagger.*
|
|
||||||
import org.isoron.uhabits.activities.*
|
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.*
|
|
||||||
|
|
||||||
@Module
|
|
||||||
abstract class ShowHabitModule {
|
|
||||||
@Binds
|
|
||||||
abstract fun getScreen(screen: ShowHabitScreen): ShowHabitBehavior.Screen
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
abstract fun getMenuScreen(screen: ShowHabitScreen): ShowHabitMenuBehavior.Screen
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
abstract fun getSystem(system: HabitsDirFinder): ShowHabitMenuBehavior.System
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities.habits.show
|
|
||||||
|
|
||||||
import android.annotation.*
|
|
||||||
import android.content.*
|
|
||||||
import org.isoron.androidbase.activities.*
|
|
||||||
import org.isoron.uhabits.*
|
|
||||||
import org.isoron.uhabits.activities.*
|
|
||||||
import org.isoron.uhabits.activities.habits.list.views.*
|
|
||||||
import org.isoron.uhabits.core.commands.*
|
|
||||||
import org.isoron.uhabits.core.models.*
|
|
||||||
import org.isoron.uhabits.core.preferences.*
|
|
||||||
import org.isoron.uhabits.core.utils.*
|
|
||||||
import org.isoron.uhabits.utils.*
|
|
||||||
import java.util.*
|
|
||||||
import javax.inject.*
|
|
||||||
|
|
||||||
@ActivityScope
|
|
||||||
class ShowHabitPresenter
|
|
||||||
@Inject constructor(
|
|
||||||
val habit: Habit,
|
|
||||||
val preferences: Preferences,
|
|
||||||
commandRunner: CommandRunner,
|
|
||||||
@ActivityContext val context: Context,
|
|
||||||
) : Presenter<ShowHabitViewModel>(commandRunner) {
|
|
||||||
|
|
||||||
private val resources = context.resources
|
|
||||||
|
|
||||||
override fun refresh(): ShowHabitViewModel {
|
|
||||||
val today = DateUtils.getTodayWithOffset()
|
|
||||||
val lastMonth = today.minus(30)
|
|
||||||
val lastYear = today.minus(365)
|
|
||||||
|
|
||||||
val reminderText = if (habit.hasReminder()) {
|
|
||||||
formatTime(context, habit.reminder.hour, habit.reminder.minute)!!
|
|
||||||
} else {
|
|
||||||
resources.getString(R.string.reminder_off)
|
|
||||||
}
|
|
||||||
|
|
||||||
val scores = habit.scores
|
|
||||||
val scoreToday = scores.todayValue.toFloat()
|
|
||||||
val scoreLastMonth = scores.getValue(lastMonth).toFloat()
|
|
||||||
val scoreLastYear = scores.getValue(lastYear).toFloat()
|
|
||||||
|
|
||||||
return ShowHabitViewModel(
|
|
||||||
title = habit.name,
|
|
||||||
description = habit.description,
|
|
||||||
question = habit.question,
|
|
||||||
color = habit.color,
|
|
||||||
isNumerical = habit.isNumerical,
|
|
||||||
scoreToday = scoreToday,
|
|
||||||
scoreMonthDiff = scoreToday - scoreLastMonth,
|
|
||||||
scoreYearDiff = scoreToday - scoreLastYear,
|
|
||||||
totalCount = habit.repetitions.totalCount,
|
|
||||||
targetText = "${habit.targetValue.toShortString()} ${habit.unit}",
|
|
||||||
frequencyText = habit.frequency.format(),
|
|
||||||
reminderText = reminderText,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
|
||||||
private fun Frequency.format(): String {
|
|
||||||
val num = this.numerator
|
|
||||||
val den = this.denominator
|
|
||||||
if (num == den) {
|
|
||||||
return resources.getString(R.string.every_day)
|
|
||||||
}
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.x_times_per_week, num)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.x_times_per_month, num)
|
|
||||||
}
|
|
||||||
if (num == 1) {
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.every_week)
|
|
||||||
}
|
|
||||||
if (den % 7 == 0) {
|
|
||||||
return resources.getString(R.string.every_x_weeks, den / 7)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.every_month)
|
|
||||||
}
|
|
||||||
return resources.getString(R.string.every_x_days, den)
|
|
||||||
}
|
|
||||||
return String.format(
|
|
||||||
Locale.US,
|
|
||||||
"%d %s %d %s",
|
|
||||||
num,
|
|
||||||
resources.getString(R.string.times_every),
|
|
||||||
den,
|
|
||||||
resources.getString(R.string.days),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.isoron.uhabits.activities.habits.show
|
|
||||||
|
|
||||||
import android.content.*
|
|
||||||
import android.view.*
|
|
||||||
import org.isoron.androidbase.activities.*
|
|
||||||
import org.isoron.androidbase.utils.*
|
|
||||||
import org.isoron.uhabits.*
|
|
||||||
import org.isoron.uhabits.activities.*
|
|
||||||
import org.isoron.uhabits.activities.habits.show.views.*
|
|
||||||
import org.isoron.uhabits.core.models.*
|
|
||||||
import org.isoron.uhabits.databinding.*
|
|
||||||
import org.isoron.uhabits.utils.*
|
|
||||||
import javax.inject.*
|
|
||||||
|
|
||||||
@ActivityScope
|
|
||||||
class ShowHabitRootView
|
|
||||||
@Inject constructor(
|
|
||||||
@ActivityContext context: Context,
|
|
||||||
private val habit: Habit,
|
|
||||||
private val presenter: ShowHabitPresenter,
|
|
||||||
targetCardPresenter: TargetCardPresenter,
|
|
||||||
) : BaseRootView(context), Presenter.Listener<ShowHabitViewModel> {
|
|
||||||
|
|
||||||
private var controller: Controller = object : Controller {}
|
|
||||||
private var binding = ShowHabitBinding.inflate(LayoutInflater.from(context))
|
|
||||||
|
|
||||||
init {
|
|
||||||
addView(binding.root)
|
|
||||||
displayHomeAsUp = true
|
|
||||||
|
|
||||||
binding.overviewCard.presenter = presenter
|
|
||||||
binding.notesCard.presenter = presenter
|
|
||||||
binding.subtitleCard.presenter = presenter
|
|
||||||
binding.targetCard.presenter = targetCardPresenter
|
|
||||||
|
|
||||||
binding.scoreCard.habit = habit
|
|
||||||
binding.historyCard.habit = habit
|
|
||||||
binding.streakCard.habit = habit
|
|
||||||
binding.frequencyCard.habit = habit
|
|
||||||
binding.barCard.habit = habit
|
|
||||||
|
|
||||||
initToolbar()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getToolbarColor(): Int {
|
|
||||||
val res = StyledResources(context)
|
|
||||||
return if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) super.getToolbarColor()
|
|
||||||
else habit.color.toThemedAndroidColor(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setController(controller: Controller) {
|
|
||||||
this.controller = controller
|
|
||||||
binding.historyCard.setController(controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
presenter.addListener(this)
|
|
||||||
presenter.requestData(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
presenter.removeListener(this)
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onData(data: ShowHabitViewModel) {
|
|
||||||
binding.toolbar.title = data.title
|
|
||||||
if (data.isNumerical) {
|
|
||||||
binding.overviewCard.visibility = GONE
|
|
||||||
binding.streakCard.visibility = GONE
|
|
||||||
} else {
|
|
||||||
binding.targetCard.visibility = GONE
|
|
||||||
}
|
|
||||||
controller.onToolbarChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Controller : HistoryCard.Controller {
|
|
||||||
fun onToolbarChanged() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -40,8 +40,8 @@ import dagger.*;
|
|||||||
public class ShowHabitScreen extends BaseScreen
|
public class ShowHabitScreen extends BaseScreen
|
||||||
implements ShowHabitMenuBehavior.Screen,
|
implements ShowHabitMenuBehavior.Screen,
|
||||||
ShowHabitBehavior.Screen,
|
ShowHabitBehavior.Screen,
|
||||||
HistoryEditorDialog.Controller,
|
HistoryEditorDialog.Controller
|
||||||
ShowHabitRootView.Controller
|
//ShowHabitRootView.Controller
|
||||||
{
|
{
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Habit habit;
|
private final Habit habit;
|
||||||
@@ -60,7 +60,7 @@ public class ShowHabitScreen extends BaseScreen
|
|||||||
@Inject
|
@Inject
|
||||||
public ShowHabitScreen(@NonNull BaseActivity activity,
|
public ShowHabitScreen(@NonNull BaseActivity activity,
|
||||||
@NonNull Habit habit,
|
@NonNull Habit habit,
|
||||||
@NonNull ShowHabitRootView view,
|
//@NonNull ShowHabitRootView view,
|
||||||
@NonNull ShowHabitsMenu menu,
|
@NonNull ShowHabitsMenu menu,
|
||||||
@NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
|
@NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
|
||||||
@NonNull IntentFactory intentFactory,
|
@NonNull IntentFactory intentFactory,
|
||||||
@@ -70,20 +70,20 @@ public class ShowHabitScreen extends BaseScreen
|
|||||||
super(activity);
|
super(activity);
|
||||||
this.intentFactory = intentFactory;
|
this.intentFactory = intentFactory;
|
||||||
setMenu(menu);
|
setMenu(menu);
|
||||||
setRootView(view);
|
//setRootView(view);
|
||||||
|
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
this.behavior = behavior;
|
this.behavior = behavior;
|
||||||
this.confirmDeleteDialogFactory = confirmDeleteDialogFactory;
|
this.confirmDeleteDialogFactory = confirmDeleteDialogFactory;
|
||||||
this.numberPickerFactory = numberPickerFactory;
|
this.numberPickerFactory = numberPickerFactory;
|
||||||
view.setController(this);
|
//view.setController(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// @Override
|
||||||
public void onEditHistoryButtonClick()
|
// public void onEditHistoryButtonClick()
|
||||||
{
|
// {
|
||||||
behavior.get().onEditHistory();
|
// behavior.get().onEditHistory();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showNumberPicker(double value,
|
public void showNumberPicker(double value,
|
||||||
@@ -99,11 +99,11 @@ public class ShowHabitScreen extends BaseScreen
|
|||||||
behavior.get().onToggleCheckmark(timestamp, value);
|
behavior.get().onToggleCheckmark(timestamp, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// @Override
|
||||||
public void onToolbarChanged()
|
// public void onToolbarChanged()
|
||||||
{
|
// {
|
||||||
invalidateToolbar();
|
// invalidateToolbar();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reattachDialogs()
|
public void reattachDialogs()
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities.habits.show
|
|
||||||
|
|
||||||
import org.isoron.uhabits.core.models.*
|
|
||||||
|
|
||||||
data class ShowHabitViewModel(
|
|
||||||
val title: String = "",
|
|
||||||
val description: String = "",
|
|
||||||
val question: String = "",
|
|
||||||
val isNumerical: Boolean = false,
|
|
||||||
val scoreToday: Float = 0f,
|
|
||||||
val scoreMonthDiff: Float = 0f,
|
|
||||||
val scoreYearDiff: Float = 0f,
|
|
||||||
val totalCount: Long = 0L,
|
|
||||||
val color: PaletteColor = PaletteColor(1),
|
|
||||||
val targetText: String = "",
|
|
||||||
val frequencyText: String = "",
|
|
||||||
val reminderText: String = "",
|
|
||||||
)
|
|
||||||
@@ -35,18 +35,19 @@ import dagger.*;
|
|||||||
@ActivityScope
|
@ActivityScope
|
||||||
public class ShowHabitsMenu extends BaseMenu
|
public class ShowHabitsMenu extends BaseMenu
|
||||||
{
|
{
|
||||||
@NonNull
|
// @NonNull
|
||||||
private Lazy<ShowHabitMenuBehavior> behavior;
|
// private Lazy<ShowHabitMenuBehavior> behavior;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Preferences prefs;
|
private final Preferences prefs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ShowHabitsMenu(@NonNull BaseActivity activity,
|
public ShowHabitsMenu(@NonNull BaseActivity activity,
|
||||||
@NonNull Lazy<ShowHabitMenuBehavior> behavior,
|
//@NonNull Lazy<ShowHabitMenuBehavior> behavior,
|
||||||
@NonNull Preferences prefs)
|
@NonNull Preferences prefs)
|
||||||
{
|
{
|
||||||
super(activity);
|
super(activity);
|
||||||
this.behavior = behavior;
|
//this.behavior = behavior;
|
||||||
this.prefs = prefs;
|
this.prefs = prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,21 +65,21 @@ public class ShowHabitsMenu extends BaseMenu
|
|||||||
{
|
{
|
||||||
switch (item.getItemId())
|
switch (item.getItemId())
|
||||||
{
|
{
|
||||||
case R.id.action_edit_habit:
|
// case R.id.action_edit_habit:
|
||||||
behavior.get().onEditHabit();
|
// behavior.get().onEditHabit();
|
||||||
return true;
|
// return true;
|
||||||
|
//
|
||||||
case R.id.export:
|
// case R.id.export:
|
||||||
behavior.get().onExportCSV();
|
// behavior.get().onExportCSV();
|
||||||
return true;
|
// return true;
|
||||||
|
//
|
||||||
case R.id.action_delete:
|
// case R.id.action_delete:
|
||||||
behavior.get().onDeleteHabit();
|
// behavior.get().onDeleteHabit();
|
||||||
return true;
|
// return true;
|
||||||
|
//
|
||||||
case R.id.action_randomize:
|
// case R.id.action_randomize:
|
||||||
behavior.get().onRandomize();
|
// behavior.get().onRandomize();
|
||||||
return true;
|
// return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -22,20 +22,27 @@ package org.isoron.uhabits.activities.habits.show.views
|
|||||||
import android.content.*
|
import android.content.*
|
||||||
import android.util.*
|
import android.util.*
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import org.isoron.uhabits.activities.*
|
import android.widget.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.isoron.uhabits.databinding.*
|
import org.isoron.uhabits.databinding.*
|
||||||
|
|
||||||
class NotesCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitViewModel>(context, attrs) {
|
data class NotesCardViewModel(val description: String)
|
||||||
|
|
||||||
|
class NotesCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private val binding = ShowHabitNotesBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitNotesBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
fun update(data: NotesCardViewModel) {
|
||||||
override fun onData(data: ShowHabitViewModel) {
|
|
||||||
if (data.description.isEmpty()) {
|
if (data.description.isEmpty()) {
|
||||||
visibility = GONE
|
visibility = GONE
|
||||||
} else {
|
} else {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
binding.habitNotes.text = data.description
|
binding.habitNotes.text = data.description
|
||||||
}
|
}
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotesCardPresenter(val habit: Habit) {
|
||||||
|
fun present() = NotesCardViewModel(
|
||||||
|
description = habit.description,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -21,14 +21,24 @@ package org.isoron.uhabits.activities.habits.show.views
|
|||||||
import android.content.*
|
import android.content.*
|
||||||
import android.util.*
|
import android.util.*
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.widget.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import org.isoron.androidbase.utils.*
|
import org.isoron.androidbase.utils.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.*
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
import org.isoron.uhabits.core.utils.*
|
||||||
import org.isoron.uhabits.databinding.*
|
import org.isoron.uhabits.databinding.*
|
||||||
import org.isoron.uhabits.utils.*
|
import org.isoron.uhabits.utils.*
|
||||||
|
|
||||||
class OverviewCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitViewModel>(context, attrs) {
|
data class OverviewCardViewModel(
|
||||||
|
val color: PaletteColor,
|
||||||
|
val scoreMonthDiff: Float,
|
||||||
|
val scoreYearDiff: Float,
|
||||||
|
val scoreToday: Float,
|
||||||
|
val totalCount: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
private val binding = ShowHabitOverviewBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitOverviewBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
@@ -37,7 +47,7 @@ class OverviewCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitVi
|
|||||||
Math.abs(percentageDiff) * 100)
|
Math.abs(percentageDiff) * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onData(data: ShowHabitViewModel) {
|
fun update(data: OverviewCardViewModel) {
|
||||||
val androidColor = data.color.toThemedAndroidColor(context)
|
val androidColor = data.color.toThemedAndroidColor(context)
|
||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
val inactiveColor = res.getColor(R.attr.mediumContrastTextColor)
|
val inactiveColor = res.getColor(R.attr.mediumContrastTextColor)
|
||||||
@@ -54,4 +64,23 @@ class OverviewCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitVi
|
|||||||
binding.yearDiffLabel.text = formatPercentageDiff(data.scoreYearDiff)
|
binding.yearDiffLabel.text = formatPercentageDiff(data.scoreYearDiff)
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OverviewCardPresenter(val habit: Habit) {
|
||||||
|
suspend fun present(): OverviewCardViewModel = Dispatchers.IO {
|
||||||
|
val today = DateUtils.getTodayWithOffset()
|
||||||
|
val lastMonth = today.minus(30)
|
||||||
|
val lastYear = today.minus(365)
|
||||||
|
val scores = habit.scores
|
||||||
|
val scoreToday = scores.todayValue.toFloat()
|
||||||
|
val scoreLastMonth = scores.getValue(lastMonth).toFloat()
|
||||||
|
val scoreLastYear = scores.getValue(lastYear).toFloat()
|
||||||
|
return@IO OverviewCardViewModel(
|
||||||
|
color = habit.color,
|
||||||
|
scoreToday = scoreToday,
|
||||||
|
scoreMonthDiff = scoreToday - scoreLastMonth,
|
||||||
|
scoreYearDiff = scoreToday - scoreLastYear,
|
||||||
|
totalCount = habit.repetitions.totalCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,16 +18,30 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.activities.habits.show.views
|
package org.isoron.uhabits.activities.habits.show.views
|
||||||
|
|
||||||
|
import android.annotation.*
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
import android.content.res.*
|
||||||
import android.util.*
|
import android.util.*
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.widget.*
|
||||||
import org.isoron.androidbase.utils.*
|
import org.isoron.androidbase.utils.*
|
||||||
import org.isoron.uhabits.activities.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.habits.show.*
|
import org.isoron.uhabits.activities.habits.list.views.*
|
||||||
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.isoron.uhabits.databinding.*
|
import org.isoron.uhabits.databinding.*
|
||||||
import org.isoron.uhabits.utils.*
|
import org.isoron.uhabits.utils.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class SubtitleCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitViewModel>(context, attrs) {
|
data class SubtitleCardViewModel(
|
||||||
|
val color: PaletteColor,
|
||||||
|
val frequencyText: String,
|
||||||
|
val isNumerical: Boolean,
|
||||||
|
val question: String,
|
||||||
|
val reminderText: String,
|
||||||
|
val targetText: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
private val binding = ShowHabitSubtitleBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitSubtitleBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
@@ -38,7 +52,7 @@ class SubtitleCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitVi
|
|||||||
binding.reminderIcon.typeface = fontAwesome
|
binding.reminderIcon.typeface = fontAwesome
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onData(data: ShowHabitViewModel) {
|
fun update(data: SubtitleCardViewModel) {
|
||||||
val color = data.color.toThemedAndroidColor(context)
|
val color = data.color.toThemedAndroidColor(context)
|
||||||
binding.frequencyLabel.text = data.frequencyText
|
binding.frequencyLabel.text = data.frequencyText
|
||||||
binding.questionLabel.setTextColor(color)
|
binding.questionLabel.setTextColor(color)
|
||||||
@@ -59,4 +73,62 @@ class SubtitleCard(context: Context, attrs: AttributeSet) : DataView<ShowHabitVi
|
|||||||
|
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubtitleCardPresenter(
|
||||||
|
val habit: Habit,
|
||||||
|
val context: Context,
|
||||||
|
) {
|
||||||
|
val resources: Resources = context.resources
|
||||||
|
|
||||||
|
fun present(): SubtitleCardViewModel {
|
||||||
|
val reminderText = if (habit.hasReminder()) {
|
||||||
|
formatTime(context, habit.reminder.hour, habit.reminder.minute)!!
|
||||||
|
} else {
|
||||||
|
resources.getString(R.string.reminder_off)
|
||||||
|
}
|
||||||
|
return SubtitleCardViewModel(
|
||||||
|
color = habit.color,
|
||||||
|
frequencyText = habit.frequency.format(),
|
||||||
|
isNumerical = habit.isNumerical,
|
||||||
|
question = habit.question,
|
||||||
|
reminderText = reminderText,
|
||||||
|
targetText = "${habit.targetValue.toShortString()} ${habit.unit}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StringFormatMatches")
|
||||||
|
private fun Frequency.format(): String {
|
||||||
|
val num = this.numerator
|
||||||
|
val den = this.denominator
|
||||||
|
if (num == den) {
|
||||||
|
return resources.getString(R.string.every_day)
|
||||||
|
}
|
||||||
|
if (den == 7) {
|
||||||
|
return resources.getString(R.string.x_times_per_week, num)
|
||||||
|
}
|
||||||
|
if (den == 30 || den == 31) {
|
||||||
|
return resources.getString(R.string.x_times_per_month, num)
|
||||||
|
}
|
||||||
|
if (num == 1) {
|
||||||
|
if (den == 7) {
|
||||||
|
return resources.getString(R.string.every_week)
|
||||||
|
}
|
||||||
|
if (den % 7 == 0) {
|
||||||
|
return resources.getString(R.string.every_x_weeks, den / 7)
|
||||||
|
}
|
||||||
|
if (den == 30 || den == 31) {
|
||||||
|
return resources.getString(R.string.every_month)
|
||||||
|
}
|
||||||
|
return resources.getString(R.string.every_x_days, den)
|
||||||
|
}
|
||||||
|
return String.format(
|
||||||
|
Locale.US,
|
||||||
|
"%d %s %d %s",
|
||||||
|
num,
|
||||||
|
resources.getString(R.string.times_every),
|
||||||
|
den,
|
||||||
|
resources.getString(R.string.days),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,19 +19,17 @@
|
|||||||
package org.isoron.uhabits.activities.habits.show.views
|
package org.isoron.uhabits.activities.habits.show.views
|
||||||
|
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
import android.content.res.*
|
||||||
import android.util.*
|
import android.util.*
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import org.isoron.androidbase.activities.*
|
import android.widget.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
import org.isoron.uhabits.activities.*
|
|
||||||
import org.isoron.uhabits.core.commands.*
|
|
||||||
import org.isoron.uhabits.core.models.*
|
import org.isoron.uhabits.core.models.*
|
||||||
import org.isoron.uhabits.core.preferences.*
|
|
||||||
import org.isoron.uhabits.core.utils.*
|
import org.isoron.uhabits.core.utils.*
|
||||||
import org.isoron.uhabits.databinding.*
|
import org.isoron.uhabits.databinding.*
|
||||||
import org.isoron.uhabits.utils.*
|
import org.isoron.uhabits.utils.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.*
|
|
||||||
|
|
||||||
data class TargetCardViewModel(
|
data class TargetCardViewModel(
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
@@ -40,35 +38,28 @@ data class TargetCardViewModel(
|
|||||||
val labels: List<String> = listOf(),
|
val labels: List<String> = listOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
class TargetCardView(context: Context, attrs: AttributeSet) : DataView<TargetCardViewModel>(context, attrs) {
|
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
fun update(data: TargetCardViewModel) {
|
||||||
override fun onData(data: TargetCardViewModel) {
|
|
||||||
val androidColor = data.color.toThemedAndroidColor(context)
|
val androidColor = data.color.toThemedAndroidColor(context)
|
||||||
binding.targetChart.setValues(data.values)
|
binding.targetChart.setValues(data.values)
|
||||||
binding.targetChart.setTargets(data.targets)
|
binding.targetChart.setTargets(data.targets)
|
||||||
binding.targetChart.setLabels(data.labels)
|
binding.targetChart.setLabels(data.labels)
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.targetChart.setColor(androidColor)
|
binding.targetChart.setColor(androidColor)
|
||||||
|
postInvalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ActivityScope
|
class TargetCardPresenter(
|
||||||
class TargetCardPresenter
|
|
||||||
@Inject constructor(
|
|
||||||
val habit: Habit,
|
val habit: Habit,
|
||||||
val preferences: Preferences,
|
val firstWeekday: Int,
|
||||||
@ActivityContext val context: Context,
|
val resources: Resources,
|
||||||
commandRunner: CommandRunner,
|
) {
|
||||||
) : Presenter<TargetCardViewModel>(commandRunner) {
|
suspend fun present(): TargetCardViewModel = Dispatchers.IO {
|
||||||
|
|
||||||
val resources = context.resources
|
|
||||||
|
|
||||||
override fun refresh(): TargetCardViewModel {
|
|
||||||
val checkmarks = habit.checkmarks
|
val checkmarks = habit.checkmarks
|
||||||
val valueToday = checkmarks.todayValue / 1e3
|
val valueToday = checkmarks.todayValue / 1e3
|
||||||
val valueThisWeek = checkmarks.getThisWeekValue(preferences.firstWeekday) / 1e3
|
val valueThisWeek = checkmarks.getThisWeekValue(firstWeekday) / 1e3
|
||||||
val valueThisMonth = checkmarks.thisMonthValue / 1e3
|
val valueThisMonth = checkmarks.thisMonthValue / 1e3
|
||||||
val valueThisQuarter = checkmarks.thisQuarterValue / 1e3
|
val valueThisQuarter = checkmarks.thisQuarterValue / 1e3
|
||||||
val valueThisYear = checkmarks.thisYearValue / 1e3
|
val valueThisYear = checkmarks.thisYearValue / 1e3
|
||||||
@@ -105,7 +96,7 @@ class TargetCardPresenter
|
|||||||
labels.add(resources.getString(R.string.quarter))
|
labels.add(resources.getString(R.string.quarter))
|
||||||
labels.add(resources.getString(R.string.year))
|
labels.add(resources.getString(R.string.year))
|
||||||
|
|
||||||
return TargetCardViewModel(
|
return@IO TargetCardViewModel(
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
values = values,
|
values = values,
|
||||||
labels = labels,
|
labels = labels,
|
||||||
|
|||||||
@@ -20,15 +20,18 @@
|
|||||||
package org.isoron.uhabits.utils
|
package org.isoron.uhabits.utils
|
||||||
|
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import androidx.annotation.*
|
import android.graphics.drawable.*
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.ViewGroup.LayoutParams.*
|
import android.view.ViewGroup.LayoutParams.*
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import android.widget.RelativeLayout.*
|
import android.widget.RelativeLayout.*
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import androidx.annotation.*
|
||||||
|
import androidx.appcompat.app.*
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import com.google.android.material.snackbar.*
|
||||||
import org.isoron.androidbase.utils.*
|
import org.isoron.androidbase.utils.*
|
||||||
import org.isoron.uhabits.*
|
import org.isoron.uhabits.*
|
||||||
|
import org.isoron.uhabits.core.models.*
|
||||||
|
|
||||||
fun RelativeLayout.addBelow(view: View,
|
fun RelativeLayout.addBelow(view: View,
|
||||||
subject: View,
|
subject: View,
|
||||||
@@ -83,6 +86,23 @@ fun View.showMessage(@StringRes stringId: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun View.setupToolbar(toolbar: Toolbar, title: String, color: PaletteColor) {
|
||||||
|
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
|
||||||
|
val res = StyledResources(context)
|
||||||
|
toolbar.title = title
|
||||||
|
val toolbarColor = if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) {
|
||||||
|
StyledResources(context).getColor(org.isoron.androidbase.R.attr.colorPrimary)
|
||||||
|
} else {
|
||||||
|
color.toThemedAndroidColor(context)
|
||||||
|
}
|
||||||
|
val darkerColor = ColorUtils.mixColors(toolbarColor, Color.BLACK, 0.75f)
|
||||||
|
toolbar.background = ColorDrawable(toolbarColor)
|
||||||
|
val activity = context as AppCompatActivity
|
||||||
|
activity.window.statusBarColor = darkerColor
|
||||||
|
activity.setSupportActionBar(toolbar)
|
||||||
|
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
fun Int.toMeasureSpec(mode: Int) =
|
fun Int.toMeasureSpec(mode: Int) =
|
||||||
View.MeasureSpec.makeMeasureSpec(this, mode)
|
View.MeasureSpec.makeMeasureSpec(this, mode)
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.content.*
|
|||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.ViewGroup.*
|
import android.view.ViewGroup.*
|
||||||
import android.view.ViewGroup.LayoutParams.*
|
import android.view.ViewGroup.LayoutParams.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import org.isoron.uhabits.activities.common.views.*
|
import org.isoron.uhabits.activities.common.views.*
|
||||||
import org.isoron.uhabits.activities.habits.show.views.*
|
import org.isoron.uhabits.activities.habits.show.views.*
|
||||||
import org.isoron.uhabits.core.models.*
|
import org.isoron.uhabits.core.models.*
|
||||||
@@ -38,17 +39,17 @@ class TargetWidget(
|
|||||||
override fun getOnClickPendingIntent(context: Context) =
|
override fun getOnClickPendingIntent(context: Context) =
|
||||||
pendingIntentFactory.showHabit(habit)
|
pendingIntentFactory.showHabit(habit)
|
||||||
|
|
||||||
override fun refreshData(view: View) {
|
override fun refreshData(view: View) = runBlocking {
|
||||||
val widgetView = view as GraphWidgetView
|
val widgetView = view as GraphWidgetView
|
||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
||||||
val chart = (widgetView.dataView as TargetChart)
|
val chart = (widgetView.dataView as TargetChart)
|
||||||
val presenter = TargetCardPresenter(habit, prefs, context, commandRunner)
|
val presenter = TargetCardPresenter(habit, prefs.firstWeekday, context.resources)
|
||||||
val data = presenter.refresh()
|
val data = presenter.present()
|
||||||
chart.setColor(data.color.toThemedAndroidColor(context))
|
chart.setColor(data.color.toThemedAndroidColor(context))
|
||||||
chart.setTargets(data.targets)
|
chart.setTargets(data.targets)
|
||||||
chart.setLabels(data.labels)
|
chart.setLabels(data.labels)
|
||||||
chart.setValues(data.values)
|
chart.setValues(data.values)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildView(): View {
|
override fun buildView(): View {
|
||||||
|
|||||||
@@ -43,16 +43,16 @@
|
|||||||
style="@style/CardList"
|
style="@style/CardList"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.SubtitleCard
|
<org.isoron.uhabits.activities.habits.show.views.SubtitleCardView
|
||||||
android:id="@+id/subtitleCard"
|
android:id="@+id/subtitleCard"
|
||||||
style="@style/ShowHabit.Subtitle"/>
|
style="@style/ShowHabit.Subtitle"/>
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.NotesCard
|
<org.isoron.uhabits.activities.habits.show.views.NotesCardView
|
||||||
android:id="@+id/notesCard"
|
android:id="@+id/notesCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:gravity="center" />
|
android:gravity="center" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.OverviewCard
|
<org.isoron.uhabits.activities.habits.show.views.OverviewCardView
|
||||||
android:id="@+id/overviewCard"
|
android:id="@+id/overviewCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:paddingTop="12dp"/>
|
android:paddingTop="12dp"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user