mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 17:18:52 -06:00
Reformat all Kotlin files with ktlint; add check to build script
This commit is contained in:
@@ -2,6 +2,9 @@ buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -9,6 +12,7 @@ buildscript {
|
||||
classpath "com.neenbedankt.gradle.plugins:android-apt:1.8"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
|
||||
classpath "org.ajoberstar:grgit:1.5.0"
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:9.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +22,5 @@ allprojects {
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
jcenter()
|
||||
}
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ run_adb_as_root() {
|
||||
$ADB root
|
||||
}
|
||||
|
||||
lint() {
|
||||
log_info "Running ktlint..."
|
||||
$GRADLE ktlintCheck || fail
|
||||
}
|
||||
|
||||
build_apk() {
|
||||
log_info "Removing old APKs..."
|
||||
rm -vf build/*.apk
|
||||
@@ -258,6 +263,7 @@ case "$1" in
|
||||
build)
|
||||
shift; parse_opts $*
|
||||
|
||||
lint
|
||||
build_apk
|
||||
build_instrumentation_apk
|
||||
run_jvm_tests
|
||||
|
||||
@@ -19,12 +19,22 @@
|
||||
|
||||
package org.isoron.uhabits
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.mockito.Mockito.*
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsModule
|
||||
import org.isoron.uhabits.activities.habits.list.views.CheckmarkButtonViewFactory
|
||||
import org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelViewFactory
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardViewFactory
|
||||
import org.isoron.uhabits.activities.habits.list.views.NumberButtonViewFactory
|
||||
import org.isoron.uhabits.activities.habits.list.views.NumberPanelViewFactory
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.inject.ActivityContextModule
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import org.isoron.uhabits.inject.HabitModule
|
||||
import org.isoron.uhabits.inject.HabitsActivityModule
|
||||
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
@Module
|
||||
class TestModule {
|
||||
@@ -32,17 +42,20 @@ class TestModule {
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Component(modules = arrayOf(
|
||||
@Component(
|
||||
modules = arrayOf(
|
||||
ActivityContextModule::class,
|
||||
HabitsActivityModule::class,
|
||||
ListHabitsModule::class,
|
||||
HabitModule::class,
|
||||
TestModule::class
|
||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||
),
|
||||
dependencies = arrayOf(HabitsApplicationComponent::class)
|
||||
)
|
||||
interface HabitsActivityTestComponent {
|
||||
fun getCheckmarkPanelViewFactory(): CheckmarkPanelViewFactory
|
||||
fun getHabitCardViewFactory(): HabitCardViewFactory
|
||||
fun getEntryButtonViewFactory(): CheckmarkButtonViewFactory
|
||||
fun getNumberButtonViewFactory(): NumberButtonViewFactory
|
||||
fun getNumberPanelViewFactory(): NumberPanelViewFactory
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,20 @@
|
||||
|
||||
package org.isoron.uhabits.acceptance
|
||||
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.acceptance.steps.*
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.*
|
||||
import org.junit.*
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.isoron.uhabits.BaseUserInterfaceTest
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.longClickText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDisplaysText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDoesNotDisplayText
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps
|
||||
import org.isoron.uhabits.acceptance.steps.clearBackupFolder
|
||||
import org.isoron.uhabits.acceptance.steps.clearDownloadFolder
|
||||
import org.isoron.uhabits.acceptance.steps.copyBackupToDownloadFolder
|
||||
import org.isoron.uhabits.acceptance.steps.exportFullBackup
|
||||
import org.isoron.uhabits.acceptance.steps.importBackupFromDownloadFolder
|
||||
import org.junit.Test
|
||||
|
||||
@LargeTest
|
||||
class BackupTest : BaseUserInterfaceTest() {
|
||||
@@ -44,4 +53,4 @@ class BackupTest : BaseUserInterfaceTest() {
|
||||
|
||||
verifyDisplaysText("Wake up early")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
|
||||
package org.isoron.uhabits.acceptance.steps
|
||||
|
||||
import androidx.test.uiautomator.*
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.*
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*
|
||||
import androidx.test.uiautomator.UiSelector
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.device
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.SETTINGS
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
|
||||
|
||||
const val BACKUP_FOLDER = "/sdcard/Android/data/org.isoron.uhabits/files/Backups/"
|
||||
const val DOWNLOAD_FOLDER = "/sdcard/Download/"
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.utils.PaletteUtils
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -96,4 +97,4 @@ class EntryButtonViewTest : BaseViewTest() {
|
||||
private fun assertRendersUnchecked() {
|
||||
assertRenders(view, "$PATH/render_unchecked.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,20 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.utils.PaletteUtils
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -44,13 +46,15 @@ class EntryPanelViewTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
prefs.isCheckmarkSequenceReversed = false
|
||||
|
||||
val checkmarks = intArrayOf(YES_MANUAL,
|
||||
YES_MANUAL,
|
||||
YES_AUTO,
|
||||
NO,
|
||||
NO,
|
||||
NO,
|
||||
YES_MANUAL)
|
||||
val checkmarks = intArrayOf(
|
||||
YES_MANUAL,
|
||||
YES_MANUAL,
|
||||
YES_AUTO,
|
||||
NO,
|
||||
NO,
|
||||
NO,
|
||||
YES_MANUAL
|
||||
)
|
||||
|
||||
view = component.getCheckmarkPanelViewFactory().create().apply {
|
||||
values = checkmarks
|
||||
@@ -112,4 +116,4 @@ class EntryPanelViewTest : BaseViewTest() {
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(listOf(day(3), day(5), day(6))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,22 +19,24 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class HabitCardViewTest : BaseViewTest() {
|
||||
|
||||
val PATH = "habits/list/HabitCardView"
|
||||
lateinit private var view: HabitCardView
|
||||
lateinit private var habit1: Habit
|
||||
lateinit private var habit2: Habit
|
||||
private lateinit var view: HabitCardView
|
||||
private lateinit var habit1: Habit
|
||||
private lateinit var habit2: Habit
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.*
|
||||
import androidx.test.runner.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.utils.PaletteUtils
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -104,4 +104,4 @@ class NumberButtonViewTest : BaseViewTest() {
|
||||
view.performLongClick()
|
||||
assertTrue(edited)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,16 @@
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.*
|
||||
import androidx.test.runner.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.utils.PaletteUtils
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -42,8 +43,14 @@ class NumberPanelViewTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
prefs.isCheckmarkSequenceReversed = false
|
||||
|
||||
val checkmarks = doubleArrayOf(1400.0, 5300.0, 0.0,
|
||||
14600.0, 2500.0, 45000.0)
|
||||
val checkmarks = doubleArrayOf(
|
||||
1400.0,
|
||||
5300.0,
|
||||
0.0,
|
||||
14600.0,
|
||||
2500.0,
|
||||
45000.0
|
||||
)
|
||||
|
||||
view = component.getNumberPanelViewFactory().create().apply {
|
||||
values = checkmarks
|
||||
@@ -107,4 +114,4 @@ class NumberPanelViewTest : BaseViewTest() {
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(listOf(day(3), day(5), day(6))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -36,9 +39,9 @@ class FrequencyCardTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
val habit = fixtures.createLongHabit()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.frequencyCard) as FrequencyCard
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.frequencyCard) as FrequencyCard
|
||||
view.update(FrequencyCardPresenter(habit, 0).present())
|
||||
measureView(view, 800f, 600f)
|
||||
}
|
||||
@@ -47,4 +50,4 @@ class FrequencyCardTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -36,12 +39,16 @@ class HistoryCardTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
val habit = fixtures.createLongHabit()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.historyCard) as HistoryCard
|
||||
view.update(HistoryCardPresenter(habit = habit,
|
||||
firstWeekday = 1,
|
||||
isSkipEnabled = false).present())
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.historyCard) as HistoryCard
|
||||
view.update(
|
||||
HistoryCardPresenter(
|
||||
habit = habit,
|
||||
firstWeekday = 1,
|
||||
isSkipEnabled = false
|
||||
).present()
|
||||
)
|
||||
measureView(view, 800f, 600f)
|
||||
}
|
||||
|
||||
@@ -49,4 +56,4 @@ class HistoryCardTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,17 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import android.view.View.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.junit.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View.GONE
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.junit.Assert.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -38,9 +40,9 @@ class NotesCardViewTest : BaseViewTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById(R.id.notesCard)
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById(R.id.notesCard)
|
||||
view.update(NotesCardViewModel(description = "This is a test description"))
|
||||
measureView(view, 800f, 200f)
|
||||
}
|
||||
@@ -55,4 +57,4 @@ class NotesCardViewTest : BaseViewTest() {
|
||||
view.update(NotesCardViewModel(description = ""))
|
||||
assertThat(view.visibility, equalTo(GONE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -36,16 +39,18 @@ class OverviewCardViewTest : BaseViewTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.overviewCard) as OverviewCardView
|
||||
view.update(OverviewCardViewModel(
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.overviewCard) as OverviewCardView
|
||||
view.update(
|
||||
OverviewCardViewModel(
|
||||
scoreToday = 0.74f,
|
||||
scoreMonthDiff = 0.23f,
|
||||
scoreYearDiff = 0.74f,
|
||||
totalCount = 44,
|
||||
color = PaletteColor(7),
|
||||
))
|
||||
)
|
||||
)
|
||||
measureView(view, 800f, 300f)
|
||||
}
|
||||
|
||||
@@ -53,4 +58,4 @@ class OverviewCardViewTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -36,9 +39,9 @@ class ScoreCardTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
val habit = fixtures.createLongHabit()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.scoreCard) as ScoreCard
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.scoreCard) as ScoreCard
|
||||
view.update(ScoreCardPresenter(habit, 0).present(0))
|
||||
measureView(view, 800f, 600f)
|
||||
}
|
||||
@@ -47,4 +50,4 @@ class ScoreCardTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import org.junit.runner.RunWith
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.activities.habits.show.views.StreakCardView
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.habits.show.views.StreakCardViewTest
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.junit.*
|
||||
import java.lang.Exception
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -41,13 +39,15 @@ class StreakCardViewTest : BaseViewTest() {
|
||||
super.setUp()
|
||||
val habit = fixtures.createLongHabit()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.streakCard) as StreakCardView
|
||||
view.update(StreakCardViewModel(
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById<View>(R.id.streakCard) as StreakCardView
|
||||
view.update(
|
||||
StreakCardViewModel(
|
||||
bestStreaks = habit.streaks.getBest(10),
|
||||
color = habit.color,
|
||||
))
|
||||
)
|
||||
)
|
||||
measureView(view, 800f, 600f)
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ class StreakCardViewTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.view.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
import android.view.LayoutInflater
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.isoron.uhabits.BaseViewTest
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
@@ -36,17 +38,19 @@ class SubtitleCardViewTest : BaseViewTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
view = LayoutInflater
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById(R.id.subtitleCard)
|
||||
view.update(SubtitleCardViewModel(
|
||||
.from(targetContext)
|
||||
.inflate(R.layout.show_habit, null)
|
||||
.findViewById(R.id.subtitleCard)
|
||||
view.update(
|
||||
SubtitleCardViewModel(
|
||||
color = PaletteColor(7),
|
||||
frequencyText = "3 times in 7 days",
|
||||
isNumerical = false,
|
||||
question = "Did you meditate this morning?",
|
||||
reminderText = "8:30 AM",
|
||||
targetText = "",
|
||||
))
|
||||
)
|
||||
)
|
||||
measureView(view, 800f, 200f)
|
||||
}
|
||||
|
||||
@@ -54,4 +58,4 @@ class SubtitleCardViewTest : BaseViewTest() {
|
||||
fun testRender() {
|
||||
assertRenders(view, PATH + "render.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.database
|
||||
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.junit.*
|
||||
import java.io.*
|
||||
import org.isoron.uhabits.AndroidDirFinder
|
||||
import org.isoron.uhabits.BaseAndroidTest
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class AutoBackupTest : BaseAndroidTest() {
|
||||
@Test
|
||||
@@ -32,7 +34,7 @@ class AutoBackupTest : BaseAndroidTest() {
|
||||
createTestFiles(basedir, 30)
|
||||
|
||||
val autoBackup = AutoBackup(targetContext)
|
||||
autoBackup.run(keep=5)
|
||||
autoBackup.run(keep = 5)
|
||||
|
||||
for (k in 1..25) assertDoesNotExist("${basedir.path}/test-$k.txt")
|
||||
for (k in 26..30) assertExists("${basedir.path}/test-$k.txt")
|
||||
@@ -77,4 +79,4 @@ class AutoBackupTest : BaseAndroidTest() {
|
||||
assertTrue(file.delete())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,27 @@
|
||||
*/
|
||||
package org.isoron.uhabits.intents
|
||||
|
||||
import android.content.ContentUris.*
|
||||
import androidx.test.ext.junit.runners.*
|
||||
import androidx.test.filters.*
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler.SchedulerResult.*
|
||||
import org.isoron.uhabits.receivers.*
|
||||
import org.junit.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.runner.*
|
||||
import java.util.*
|
||||
import java.util.Calendar.*
|
||||
import android.content.ContentUris.parseId
|
||||
import androidx.test.filters.MediumTest
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.isoron.uhabits.BaseAndroidTest
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler.SchedulerResult.OK
|
||||
import org.isoron.uhabits.receivers.ReminderReceiver
|
||||
import org.isoron.uhabits.receivers.WidgetReceiver
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.Calendar.DAY_OF_MONTH
|
||||
import java.util.Calendar.DECEMBER
|
||||
import java.util.Calendar.HOUR_OF_DAY
|
||||
import java.util.Calendar.JUNE
|
||||
import java.util.Calendar.MAY
|
||||
import java.util.Calendar.MINUTE
|
||||
import java.util.Calendar.MONTH
|
||||
import java.util.Calendar.YEAR
|
||||
import java.util.GregorianCalendar
|
||||
import java.util.TimeZone
|
||||
|
||||
class IntentSchedulerTest : BaseAndroidTest() {
|
||||
|
||||
@@ -117,4 +126,4 @@ class IntentSchedulerTest : BaseAndroidTest() {
|
||||
val intent = WidgetReceiver.getLastReceivedIntent()
|
||||
assertNotNull(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,23 @@
|
||||
|
||||
package org.isoron.uhabits.regression
|
||||
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.*
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.*
|
||||
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.*
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*
|
||||
import org.junit.*
|
||||
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.isoron.uhabits.BaseUserInterfaceTest
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.EDIT_HABIT
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.LIST_HABITS
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.SELECT_HABIT_TYPE
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.longClickText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDisplaysText
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyShowsScreen
|
||||
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.clickSave
|
||||
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.typeName
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.ADD
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.DELETE
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
|
||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.longPressCheckmarks
|
||||
import org.junit.Test
|
||||
|
||||
@LargeTest
|
||||
class ListHabitsRegressionTest : BaseUserInterfaceTest() {
|
||||
@@ -54,4 +62,4 @@ class ListHabitsRegressionTest : BaseUserInterfaceTest() {
|
||||
verifyDisplaysText("Hello world")
|
||||
longPressCheckmarks("Hello world", 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.regression
|
||||
|
||||
import androidx.test.filters.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.*
|
||||
import org.isoron.uhabits.activities.about.*
|
||||
import org.junit.*
|
||||
import java.lang.Thread.*
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.isoron.uhabits.BaseUserInterfaceTest
|
||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
|
||||
import org.isoron.uhabits.activities.about.AboutActivity
|
||||
import org.junit.Test
|
||||
import java.lang.Thread.sleep
|
||||
|
||||
@LargeTest
|
||||
class SavedStateTest : BaseUserInterfaceTest() {
|
||||
|
||||
@@ -19,18 +19,29 @@
|
||||
|
||||
package org.isoron.uhabits.sync
|
||||
|
||||
import androidx.test.filters.*
|
||||
import com.fasterxml.jackson.databind.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.mock.*
|
||||
import io.ktor.client.features.json.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
import junit.framework.Assert.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import org.junit.*
|
||||
import androidx.test.filters.MediumTest
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.mock.MockEngine
|
||||
import io.ktor.client.engine.mock.MockRequestHandleScope
|
||||
import io.ktor.client.engine.mock.respond
|
||||
import io.ktor.client.engine.mock.respondError
|
||||
import io.ktor.client.engine.mock.respondOk
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.request.HttpRequestData
|
||||
import io.ktor.client.request.HttpResponseData
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.fullPath
|
||||
import io.ktor.http.headersOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.isoron.uhabits.BaseAndroidTest
|
||||
import org.isoron.uhabits.core.sync.AbstractSyncServer
|
||||
import org.isoron.uhabits.core.sync.GetDataVersionResponse
|
||||
import org.isoron.uhabits.core.sync.KeyNotFoundException
|
||||
import org.isoron.uhabits.core.sync.RegisterReponse
|
||||
import org.isoron.uhabits.core.sync.ServiceUnavailable
|
||||
import org.isoron.uhabits.core.sync.SyncData
|
||||
import org.junit.Test
|
||||
|
||||
@MediumTest
|
||||
class RemoteSyncServerTest : BaseAndroidTest() {
|
||||
@@ -115,23 +126,29 @@ class RemoteSyncServerTest : BaseAndroidTest() {
|
||||
return@runBlocking
|
||||
}
|
||||
|
||||
private fun server(expectedPath: String,
|
||||
action: MockRequestHandleScope.(HttpRequestData) -> HttpResponseData
|
||||
private fun server(
|
||||
expectedPath: String,
|
||||
action: MockRequestHandleScope.(HttpRequestData) -> HttpResponseData
|
||||
): AbstractSyncServer {
|
||||
return RemoteSyncServer(httpClient = HttpClient(MockEngine) {
|
||||
install(JsonFeature)
|
||||
engine {
|
||||
addHandler { request ->
|
||||
when (request.url.fullPath) {
|
||||
expectedPath -> action(request)
|
||||
else -> error("unexpected call: ${request.url.fullPath}")
|
||||
return RemoteSyncServer(
|
||||
httpClient = HttpClient(MockEngine) {
|
||||
install(JsonFeature)
|
||||
engine {
|
||||
addHandler { request ->
|
||||
when (request.url.fullPath) {
|
||||
expectedPath -> action(request)
|
||||
else -> error("unexpected call: ${request.url.fullPath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, preferences = prefs)
|
||||
},
|
||||
preferences = prefs
|
||||
)
|
||||
}
|
||||
|
||||
private fun MockRequestHandleScope.respondWithJson(content: Any) =
|
||||
respond(mapper.writeValueAsBytes(content),
|
||||
headers = headersOf("Content-Type" to listOf("application/json")))
|
||||
}
|
||||
respond(
|
||||
mapper.writeValueAsBytes(content),
|
||||
headers = headersOf("Content-Type" to listOf("application/json"))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,14 +18,21 @@
|
||||
*/
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import java.io.*
|
||||
import java.text.*
|
||||
import java.util.*
|
||||
import javax.inject.*
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.view.WindowManager
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
open class AndroidBugReporter @Inject constructor(@AppContext private val context: Context) {
|
||||
|
||||
@@ -56,7 +63,7 @@ open class AndroidBugReporter @Inject constructor(@AppContext private val contex
|
||||
var line: String?
|
||||
while (true) {
|
||||
line = bufferedReader.readLine()
|
||||
if (line == null) break;
|
||||
if (line == null) break
|
||||
log.addLast(line)
|
||||
if (log.size > maxLineCount) log.removeFirst()
|
||||
}
|
||||
@@ -79,7 +86,7 @@ open class AndroidBugReporter @Inject constructor(@AppContext private val contex
|
||||
try {
|
||||
val date = SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(Date())
|
||||
val dir = AndroidDirFinder(context).getFilesDir("Logs")
|
||||
?: throw IOException("log dir should not be null")
|
||||
?: throw IOException("log dir should not be null")
|
||||
val logFile = File(String.format("%s/Log %s.txt", dir.path, date))
|
||||
val output = FileWriter(logFile)
|
||||
output.write(getBugReport())
|
||||
@@ -106,5 +113,4 @@ open class AndroidBugReporter @Inject constructor(@AppContext private val contex
|
||||
appendln()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.isoron.uhabits
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import org.isoron.uhabits.utils.FileUtils
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
@@ -28,8 +28,8 @@ import javax.inject.Inject
|
||||
class AndroidDirFinder @Inject constructor(@param:AppContext private val context: Context) {
|
||||
fun getFilesDir(relativePath: String): File? {
|
||||
return FileUtils.getDir(
|
||||
ContextCompat.getExternalFilesDirs(context, null),
|
||||
relativePath
|
||||
ContextCompat.getExternalFilesDirs(context, null),
|
||||
relativePath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
*/
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.app.*
|
||||
import android.app.Activity
|
||||
|
||||
class BaseExceptionHandler(private val activity: Activity) : Thread.UncaughtExceptionHandler {
|
||||
|
||||
private val originalHandler: Thread.UncaughtExceptionHandler? =
|
||||
Thread.getDefaultUncaughtExceptionHandler()
|
||||
Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
override fun uncaughtException(thread: Thread?, ex: Throwable?) {
|
||||
if (ex == null) return
|
||||
@@ -36,4 +36,4 @@ class BaseExceptionHandler(private val activity: Activity) : Thread.UncaughtExce
|
||||
}
|
||||
originalHandler?.uncaughtException(thread, ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,19 @@
|
||||
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.reminders.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.widgets.*
|
||||
import java.io.*
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import org.isoron.uhabits.core.database.UnsupportedDatabaseVersionException
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler
|
||||
import org.isoron.uhabits.core.ui.NotificationTray
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.AppContextModule
|
||||
import org.isoron.uhabits.inject.DaggerHabitsApplicationComponent
|
||||
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||
import org.isoron.uhabits.inject.HabitsModule
|
||||
import org.isoron.uhabits.utils.DatabaseUtils
|
||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* The Android application for Loop Habit Tracker.
|
||||
@@ -59,10 +62,10 @@ class HabitsApplication : Application() {
|
||||
|
||||
val db = DatabaseUtils.getDatabaseFile(this)
|
||||
HabitsApplication.component = DaggerHabitsApplicationComponent
|
||||
.builder()
|
||||
.appContextModule(AppContextModule(context))
|
||||
.habitsModule(HabitsModule(db))
|
||||
.build()
|
||||
.builder()
|
||||
.appContextModule(AppContextModule(context))
|
||||
.habitsModule(HabitsModule(db))
|
||||
.build()
|
||||
|
||||
DateUtils.setStartDayOffset(3, 0)
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.app.backup.*
|
||||
import android.app.backup.BackupAgentHelper
|
||||
import android.app.backup.FileBackupHelper
|
||||
import android.app.backup.SharedPreferencesBackupHelper
|
||||
|
||||
/**
|
||||
* An Android BackupAgentHelper customized for this application.
|
||||
|
||||
@@ -21,17 +21,18 @@
|
||||
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.content.*
|
||||
import android.database.sqlite.*
|
||||
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.database.*
|
||||
import java.io.*
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import org.isoron.uhabits.core.database.MigrationHelper
|
||||
import org.isoron.uhabits.core.database.UnsupportedDatabaseVersionException
|
||||
import org.isoron.uhabits.database.AndroidDatabase
|
||||
import java.io.File
|
||||
|
||||
class HabitsDatabaseOpener(
|
||||
context: Context,
|
||||
private val databaseFilename: String,
|
||||
private val version: Int
|
||||
context: Context,
|
||||
private val databaseFilename: String,
|
||||
private val version: Int
|
||||
) : SQLiteOpenHelper(context, databaseFilename, null, version) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
@@ -45,18 +46,22 @@ class HabitsDatabaseOpener(
|
||||
db.disableWriteAheadLogging()
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase,
|
||||
oldVersion: Int,
|
||||
newVersion: Int) {
|
||||
override fun onUpgrade(
|
||||
db: SQLiteDatabase,
|
||||
oldVersion: Int,
|
||||
newVersion: Int
|
||||
) {
|
||||
db.disableWriteAheadLogging()
|
||||
if (db.version < 8) throw UnsupportedDatabaseVersionException()
|
||||
val helper = MigrationHelper(AndroidDatabase(db, File(databaseFilename)))
|
||||
helper.migrateTo(newVersion)
|
||||
}
|
||||
|
||||
override fun onDowngrade(db: SQLiteDatabase,
|
||||
oldVersion: Int,
|
||||
newVersion: Int) {
|
||||
override fun onDowngrade(
|
||||
db: SQLiteDatabase,
|
||||
oldVersion: Int,
|
||||
newVersion: Int
|
||||
) {
|
||||
throw UnsupportedDatabaseVersionException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,37 +19,39 @@
|
||||
|
||||
package org.isoron.uhabits.activities
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.content.res.Configuration.*
|
||||
import android.os.Build.VERSION.*
|
||||
import androidx.core.content.*
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
|
||||
@ActivityScope
|
||||
class AndroidThemeSwitcher
|
||||
constructor(
|
||||
@ActivityContext val context: Context,
|
||||
preferences: Preferences
|
||||
@ActivityContext val context: Context,
|
||||
preferences: Preferences
|
||||
) : ThemeSwitcher(preferences) {
|
||||
|
||||
override fun getSystemTheme(): Int {
|
||||
if (SDK_INT < 29) return THEME_LIGHT;
|
||||
if (SDK_INT < 29) return THEME_LIGHT
|
||||
val uiMode = context.resources.configuration.uiMode
|
||||
return if ((uiMode and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES) {
|
||||
THEME_DARK;
|
||||
THEME_DARK
|
||||
} else {
|
||||
THEME_LIGHT;
|
||||
THEME_LIGHT
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyDarkTheme() {
|
||||
context.setTheme(R.style.AppBaseThemeDark)
|
||||
(context as Activity).window.navigationBarColor =
|
||||
ContextCompat.getColor(context, R.color.grey_900)
|
||||
ContextCompat.getColor(context, R.color.grey_900)
|
||||
}
|
||||
|
||||
override fun applyLightTheme() {
|
||||
@@ -59,6 +61,6 @@ constructor(
|
||||
override fun applyPureBlackTheme() {
|
||||
context.setTheme(R.style.AppBaseThemeDark_PureBlack)
|
||||
(context as Activity).window.navigationBarColor =
|
||||
ContextCompat.getColor(context, R.color.black)
|
||||
ContextCompat.getColor(context, R.color.black)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities
|
||||
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.AndroidDirFinder
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuBehavior
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
class HabitsDirFinder @Inject
|
||||
constructor(
|
||||
private val androidDirFinder: AndroidDirFinder
|
||||
private val androidDirFinder: AndroidDirFinder
|
||||
) : ShowHabitMenuBehavior.System, ListHabitsBehavior.DirFinder {
|
||||
|
||||
override fun getCSVOutputDir(): File {
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.about
|
||||
|
||||
import android.os.*
|
||||
import androidx.appcompat.app.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
|
||||
/**
|
||||
* Activity that allows the user to see information about the app itself.
|
||||
@@ -31,10 +31,12 @@ class AboutActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val app = application as HabitsApplication
|
||||
val screen = AboutScreen(this,
|
||||
app.component.intentFactory,
|
||||
app.component.preferences)
|
||||
val screen = AboutScreen(
|
||||
this,
|
||||
app.component.intentFactory,
|
||||
app.component.preferences
|
||||
)
|
||||
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
||||
setContentView(AboutView(this, screen))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,36 +18,37 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.about
|
||||
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.startActivitySafely
|
||||
|
||||
class AboutScreen(
|
||||
private val activity: AboutActivity,
|
||||
private val intents: IntentFactory,
|
||||
private val prefs: Preferences,
|
||||
private val activity: AboutActivity,
|
||||
private val intents: IntentFactory,
|
||||
private val prefs: Preferences,
|
||||
) {
|
||||
|
||||
private var developerCountdown = 5
|
||||
|
||||
fun showRateAppWebsite() =
|
||||
activity.startActivitySafely(intents.rateApp(activity))
|
||||
activity.startActivitySafely(intents.rateApp(activity))
|
||||
|
||||
fun showSendFeedbackScreen() =
|
||||
activity.startActivitySafely(intents.sendFeedback(activity))
|
||||
activity.startActivitySafely(intents.sendFeedback(activity))
|
||||
|
||||
fun showSourceCodeWebsite() =
|
||||
activity.startActivitySafely(intents.viewSourceCode(activity))
|
||||
activity.startActivitySafely(intents.viewSourceCode(activity))
|
||||
|
||||
fun showTranslationWebsite() =
|
||||
activity.startActivitySafely(intents.helpTranslate(activity))
|
||||
activity.startActivitySafely(intents.helpTranslate(activity))
|
||||
|
||||
fun showPrivacyPolicyWebsite() =
|
||||
activity.startActivitySafely(intents.privacyPolicy(activity))
|
||||
activity.startActivitySafely(intents.privacyPolicy(activity))
|
||||
|
||||
fun showCodeContributorsWebsite() =
|
||||
activity.startActivitySafely(intents.codeContributors(activity))
|
||||
activity.startActivitySafely(intents.codeContributors(activity))
|
||||
|
||||
fun onPressDeveloperCountdown() {
|
||||
developerCountdown--
|
||||
@@ -56,4 +57,4 @@ class AboutScreen(
|
||||
activity.showMessage(activity.resources.getString(R.string.you_are_now_a_developer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,20 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.about
|
||||
|
||||
import android.annotation.*
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import org.isoron.uhabits.BuildConfig
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.databinding.AboutBinding
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class AboutView(
|
||||
context: Context,
|
||||
private val screen: AboutScreen,
|
||||
context: Context,
|
||||
private val screen: AboutScreen,
|
||||
) : FrameLayout(context) {
|
||||
|
||||
private var binding = AboutBinding.inflate(LayoutInflater.from(context))
|
||||
@@ -38,9 +39,9 @@ class AboutView(
|
||||
init {
|
||||
addView(binding.root)
|
||||
setupToolbar(
|
||||
toolbar = binding.toolbar,
|
||||
color = PaletteColor(11),
|
||||
title = resources.getString(R.string.about)
|
||||
toolbar = binding.toolbar,
|
||||
color = PaletteColor(11),
|
||||
title = resources.getString(R.string.about)
|
||||
)
|
||||
val version = resources.getString(R.string.version_n)
|
||||
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }
|
||||
@@ -52,4 +53,4 @@ class AboutView(
|
||||
binding.tvVersion.setOnClickListener { screen.onPressDeveloperCountdown() }
|
||||
binding.tvVersion.text = String.format(version, BuildConfig.VERSION_NAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,25 @@
|
||||
|
||||
package org.isoron.uhabits.activities.common.dialogs
|
||||
|
||||
import android.app.*
|
||||
import android.os.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.*
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.RadioButton
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import kotlinx.android.synthetic.main.frequency_picker_dialog.view.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.R
|
||||
|
||||
|
||||
class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
var freqDenominator: Int
|
||||
) : AppCompatDialogFragment() {
|
||||
class FrequencyPickerDialog(
|
||||
var freqNumerator: Int,
|
||||
var freqDenominator: Int
|
||||
) : AppCompatDialogFragment() {
|
||||
|
||||
lateinit var contentView: View
|
||||
var onFrequencyPicked: (num: Int, den: Int) -> Unit = {_,_ -> }
|
||||
var onFrequencyPicked: (num: Int, den: Int) -> Unit = { _, _ -> }
|
||||
|
||||
constructor() : this(1, 1)
|
||||
|
||||
@@ -55,7 +57,7 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
}
|
||||
|
||||
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||
if(hasFocus) check(contentView.everyXDaysRadioButton)
|
||||
if (hasFocus) check(contentView.everyXDaysRadioButton)
|
||||
}
|
||||
|
||||
contentView.xTimesPerWeekRadioButton.setOnClickListener {
|
||||
@@ -64,7 +66,7 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
}
|
||||
|
||||
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||
if(hasFocus) check(contentView.xTimesPerWeekRadioButton)
|
||||
if (hasFocus) check(contentView.xTimesPerWeekRadioButton)
|
||||
}
|
||||
|
||||
contentView.xTimesPerMonthRadioButton.setOnClickListener {
|
||||
@@ -73,13 +75,13 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
}
|
||||
|
||||
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||
if(hasFocus) check(contentView.xTimesPerMonthRadioButton)
|
||||
if (hasFocus) check(contentView.xTimesPerMonthRadioButton)
|
||||
}
|
||||
|
||||
return AlertDialog.Builder(activity!!)
|
||||
.setView(contentView)
|
||||
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
|
||||
.create()
|
||||
.setView(contentView)
|
||||
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun onSaveClicked() {
|
||||
@@ -129,7 +131,7 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
private fun populateViews() {
|
||||
uncheckAll()
|
||||
if (freqNumerator == 1) {
|
||||
if(freqDenominator == 1) {
|
||||
if (freqDenominator == 1) {
|
||||
contentView.everyDayRadioButton.isChecked = true
|
||||
} else {
|
||||
contentView.everyXDaysRadioButton.isChecked = true
|
||||
@@ -137,7 +139,7 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
focus(contentView.everyXDaysTextView)
|
||||
}
|
||||
} else {
|
||||
if(freqDenominator == 7) {
|
||||
if (freqDenominator == 7) {
|
||||
contentView.xTimesPerWeekRadioButton.isChecked = true
|
||||
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
||||
focus(contentView.xTimesPerWeekTextView)
|
||||
@@ -169,4 +171,4 @@ class FrequencyPickerDialog(var freqNumerator: Int,
|
||||
contentView.xTimesPerWeekTextView.clearFocus()
|
||||
contentView.xTimesPerMonthTextView.clearFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,26 +19,31 @@
|
||||
|
||||
package org.isoron.uhabits.activities.common.dialogs
|
||||
|
||||
import android.content.*
|
||||
import androidx.appcompat.app.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.WindowManager.LayoutParams.*
|
||||
import android.view.inputmethod.*
|
||||
import android.widget.*
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.text.InputFilter
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.NumberPicker
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.utils.InterfaceUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
class NumberPickerFactory
|
||||
@Inject constructor(
|
||||
@ActivityContext private val context: Context
|
||||
@ActivityContext private val context: Context
|
||||
) {
|
||||
fun create(value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback): AlertDialog {
|
||||
fun create(
|
||||
value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback
|
||||
): AlertDialog {
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.number_picker_dialog, null)
|
||||
@@ -63,28 +68,31 @@ class NumberPickerFactory
|
||||
tvUnit.text = unit
|
||||
|
||||
val dialog = AlertDialog.Builder(context)
|
||||
.setView(view)
|
||||
.setTitle(R.string.change_value)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
picker.clearFocus()
|
||||
val v = picker.value + 0.05 * picker2.value
|
||||
callback.onNumberPicked(v)
|
||||
}
|
||||
.setOnDismissListener{
|
||||
callback.onNumberPickerDismissed()
|
||||
}
|
||||
.create()
|
||||
.setView(view)
|
||||
.setTitle(R.string.change_value)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
picker.clearFocus()
|
||||
val v = picker.value + 0.05 * picker2.value
|
||||
callback.onNumberPicked(v)
|
||||
}
|
||||
.setOnDismissListener {
|
||||
callback.onNumberPickerDismissed()
|
||||
}
|
||||
.create()
|
||||
|
||||
dialog.setOnShowListener {
|
||||
picker.getChildAt(0)?.requestFocus()
|
||||
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
}
|
||||
|
||||
InterfaceUtils.setupEditorAction(picker, TextView.OnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE)
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||
false
|
||||
})
|
||||
InterfaceUtils.setupEditorAction(
|
||||
picker,
|
||||
TextView.OnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE)
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||
false
|
||||
}
|
||||
)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
@@ -19,19 +19,21 @@
|
||||
|
||||
package org.isoron.uhabits.activities.common.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import org.isoron.uhabits.core.tasks.Task
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
|
||||
class TaskProgressBar(
|
||||
context: Context,
|
||||
private val runner: TaskRunner
|
||||
context: Context,
|
||||
private val runner: TaskRunner
|
||||
) : ProgressBar(
|
||||
context,
|
||||
null,
|
||||
android.R.attr.progressBarStyleHorizontal
|
||||
), TaskRunner.Listener {
|
||||
context,
|
||||
null,
|
||||
android.R.attr.progressBarStyleHorizontal
|
||||
),
|
||||
TaskRunner.Listener {
|
||||
|
||||
init {
|
||||
visibility = View.GONE
|
||||
|
||||
@@ -19,25 +19,38 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.edit
|
||||
|
||||
import android.annotation.*
|
||||
import android.content.res.*
|
||||
import android.graphics.*
|
||||
import android.os.*
|
||||
import android.text.format.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.*
|
||||
import androidx.fragment.app.*
|
||||
import com.android.datetimepicker.time.*
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateFormat
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.android.datetimepicker.time.RadialPickerLayout
|
||||
import com.android.datetimepicker.time.TimePickerDialog
|
||||
import kotlinx.android.synthetic.main.activity_edit_habit.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.FrequencyPickerDialog
|
||||
import org.isoron.uhabits.activities.common.dialogs.WeekdayPickerDialog
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateHabitCommand
|
||||
import org.isoron.uhabits.core.commands.EditHabitCommand
|
||||
import org.isoron.uhabits.core.models.Frequency
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.models.Reminder
|
||||
import org.isoron.uhabits.core.models.WeekdayList
|
||||
import org.isoron.uhabits.databinding.ActivityEditHabitBinding
|
||||
import org.isoron.uhabits.utils.ColorUtils
|
||||
import org.isoron.uhabits.utils.formatTime
|
||||
import org.isoron.uhabits.utils.toFormattedString
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
class EditHabitActivity : AppCompatActivity() {
|
||||
|
||||
@@ -143,7 +156,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
arrayAdapter.add(getString(R.string.every_week))
|
||||
arrayAdapter.add(getString(R.string.every_month))
|
||||
builder.setAdapter(arrayAdapter) { dialog, which ->
|
||||
freqDen = when(which) {
|
||||
freqDen = when (which) {
|
||||
1 -> 7
|
||||
2 -> 30
|
||||
else -> 1
|
||||
@@ -159,20 +172,26 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
val currentHour = if (reminderHour >= 0) reminderHour else 8
|
||||
val currentMin = if (reminderMin >= 0) reminderMin else 0
|
||||
val is24HourMode = DateFormat.is24HourFormat(this)
|
||||
val dialog = TimePickerDialog.newInstance(object : TimePickerDialog.OnTimeSetListener {
|
||||
override fun onTimeSet(view: RadialPickerLayout?, hourOfDay: Int, minute: Int) {
|
||||
reminderHour = hourOfDay
|
||||
reminderMin = minute
|
||||
populateReminder()
|
||||
}
|
||||
val dialog = TimePickerDialog.newInstance(
|
||||
object : TimePickerDialog.OnTimeSetListener {
|
||||
override fun onTimeSet(view: RadialPickerLayout?, hourOfDay: Int, minute: Int) {
|
||||
reminderHour = hourOfDay
|
||||
reminderMin = minute
|
||||
populateReminder()
|
||||
}
|
||||
|
||||
override fun onTimeCleared(view: RadialPickerLayout?) {
|
||||
reminderHour = -1
|
||||
reminderMin = -1
|
||||
reminderDays = WeekdayList.EVERY_DAY
|
||||
populateReminder()
|
||||
}
|
||||
}, currentHour, currentMin, is24HourMode, androidColor)
|
||||
override fun onTimeCleared(view: RadialPickerLayout?) {
|
||||
reminderHour = -1
|
||||
reminderMin = -1
|
||||
reminderDays = WeekdayList.EVERY_DAY
|
||||
populateReminder()
|
||||
}
|
||||
},
|
||||
currentHour,
|
||||
currentMin,
|
||||
is24HourMode,
|
||||
androidColor
|
||||
)
|
||||
dialog.show(supportFragmentManager, "timePicker")
|
||||
}
|
||||
|
||||
@@ -188,7 +207,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
binding.buttonSave.setOnClickListener {
|
||||
if(validate()) save()
|
||||
if (validate()) save()
|
||||
}
|
||||
|
||||
for (fragment in supportFragmentManager.fragments) {
|
||||
@@ -226,14 +245,16 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
|
||||
val command = if (habitId >= 0) {
|
||||
EditHabitCommand(
|
||||
component.habitList,
|
||||
habitId,
|
||||
habit)
|
||||
component.habitList,
|
||||
habitId,
|
||||
habit
|
||||
)
|
||||
} else {
|
||||
CreateHabitCommand(
|
||||
component.modelFactory,
|
||||
component.habitList,
|
||||
habit)
|
||||
component.modelFactory,
|
||||
component.habitList,
|
||||
habit
|
||||
)
|
||||
}
|
||||
component.commandRunner.run(command)
|
||||
finish()
|
||||
@@ -246,11 +267,11 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
isValid = false
|
||||
}
|
||||
if (habitType == Habit.NUMBER_HABIT) {
|
||||
if(unitInput.text.isEmpty()) {
|
||||
if (unitInput.text.isEmpty()) {
|
||||
unitInput.error = getString(R.string.validation_cannot_be_blank)
|
||||
isValid = false
|
||||
}
|
||||
if(targetInput.text.isEmpty()) {
|
||||
if (targetInput.text.isEmpty()) {
|
||||
targetInput.error = getString(R.string.validation_cannot_be_blank)
|
||||
isValid = false
|
||||
}
|
||||
@@ -282,7 +303,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
|
||||
else -> "Unknown"
|
||||
}
|
||||
binding.numericalFrequencyPicker.text = when(freqDen) {
|
||||
binding.numericalFrequencyPicker.text = when (freqDen) {
|
||||
1 -> getString(R.string.every_day)
|
||||
7 -> getString(R.string.every_week)
|
||||
30 -> getString(R.string.every_month)
|
||||
|
||||
@@ -19,20 +19,24 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.edit
|
||||
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.databinding.SelectHabitTypeBinding
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
|
||||
class HabitTypeDialog : AppCompatDialogFragment() {
|
||||
override fun getTheme() = R.style.Translucent
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
|
||||
|
||||
binding.buttonYesNo.setOnClickListener {
|
||||
@@ -53,4 +57,4 @@ class HabitTypeDialog : AppCompatDialogFragment() {
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,22 +19,26 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.database.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Intent
|
||||
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.BaseExceptionHandler
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.sync.SyncManager
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.THEME_DARK
|
||||
import org.isoron.uhabits.core.utils.MidnightTimer
|
||||
import org.isoron.uhabits.database.AutoBackup
|
||||
import org.isoron.uhabits.inject.ActivityContextModule
|
||||
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
|
||||
import org.isoron.uhabits.utils.restartWithFade
|
||||
|
||||
class ListHabitsActivity : AppCompatActivity() {
|
||||
|
||||
@@ -55,10 +59,10 @@ class ListHabitsActivity : AppCompatActivity() {
|
||||
|
||||
val appComponent = (applicationContext as HabitsApplication).component
|
||||
val component = DaggerHabitsActivityComponent
|
||||
.builder()
|
||||
.activityContextModule(ActivityContextModule(this))
|
||||
.habitsApplicationComponent(appComponent)
|
||||
.build()
|
||||
.builder()
|
||||
.activityContextModule(ActivityContextModule(this))
|
||||
.habitsApplicationComponent(appComponent)
|
||||
.build()
|
||||
component.themeSwitcher.apply()
|
||||
|
||||
prefs = appComponent.preferences
|
||||
|
||||
@@ -19,22 +19,25 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import android.content.Context
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsMenuBehavior
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsMenu @Inject constructor(
|
||||
@ActivityContext context: Context,
|
||||
private val preferences: Preferences,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val behavior: ListHabitsMenuBehavior
|
||||
@ActivityContext context: Context,
|
||||
private val preferences: Preferences,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val behavior: ListHabitsMenuBehavior
|
||||
) {
|
||||
val activity = (context as AppCompatActivity)
|
||||
|
||||
|
||||
@@ -19,18 +19,21 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import android.content.Context
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import org.isoron.uhabits.AndroidBugReporter
|
||||
import org.isoron.uhabits.activities.HabitsDirFinder
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsMenuBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBehavior
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class BugReporterProxy
|
||||
@Inject constructor(
|
||||
@AppContext context: Context
|
||||
@AppContext context: Context
|
||||
) : AndroidBugReporter(context), ListHabitsBehavior.BugReporter
|
||||
|
||||
@Module
|
||||
|
||||
@@ -19,33 +19,50 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.RelativeLayout
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.views.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.lang.Math.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.activities.common.views.ScrollableChart
|
||||
import org.isoron.uhabits.activities.common.views.TaskProgressBar
|
||||
import org.isoron.uhabits.activities.habits.list.views.EmptyListView
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListView
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListViewFactory
|
||||
import org.isoron.uhabits.activities.habits.list.views.HeaderView
|
||||
import org.isoron.uhabits.activities.habits.list.views.HintView
|
||||
import org.isoron.uhabits.core.models.ModelObservable
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.HintListFactory
|
||||
import org.isoron.uhabits.core.utils.MidnightTimer
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import org.isoron.uhabits.utils.addAtBottom
|
||||
import org.isoron.uhabits.utils.addAtTop
|
||||
import org.isoron.uhabits.utils.addBelow
|
||||
import org.isoron.uhabits.utils.buildToolbar
|
||||
import org.isoron.uhabits.utils.dim
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
import org.isoron.uhabits.utils.sres
|
||||
import java.lang.Math.max
|
||||
import java.lang.Math.min
|
||||
import javax.inject.Inject
|
||||
|
||||
const val MAX_CHECKMARK_COUNT = 60
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsRootView @Inject constructor(
|
||||
@ActivityContext context: Context,
|
||||
hintListFactory: HintListFactory,
|
||||
preferences: Preferences,
|
||||
midnightTimer: MidnightTimer,
|
||||
runner: TaskRunner,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
habitCardListViewFactory: HabitCardListViewFactory
|
||||
@ActivityContext context: Context,
|
||||
hintListFactory: HintListFactory,
|
||||
preferences: Preferences,
|
||||
midnightTimer: MidnightTimer,
|
||||
runner: TaskRunner,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
habitCardListViewFactory: HabitCardListViewFactory
|
||||
) : FrameLayout(context), ModelObservable.Listener {
|
||||
|
||||
val listView: HabitCardListView = habitCardListViewFactory.create()
|
||||
@@ -72,10 +89,10 @@ class ListHabitsRootView @Inject constructor(
|
||||
addAtBottom(hintView)
|
||||
}
|
||||
rootView.setupToolbar(
|
||||
toolbar = tbar,
|
||||
title = resources.getString(R.string.main_activity_title),
|
||||
color = PaletteColor(17),
|
||||
displayHomeAsUpEnabled = false,
|
||||
toolbar = tbar,
|
||||
title = resources.getString(R.string.main_activity_title),
|
||||
color = PaletteColor(17),
|
||||
displayHomeAsUpEnabled = false,
|
||||
)
|
||||
addView(rootView, MATCH_PARENT, MATCH_PARENT)
|
||||
listAdapter.setListView(listView)
|
||||
@@ -86,11 +103,13 @@ class ListHabitsRootView @Inject constructor(
|
||||
}
|
||||
|
||||
private fun setupControllers() {
|
||||
header.setScrollController(object : ScrollableChart.ScrollController {
|
||||
override fun onDataOffsetChanged(newDataOffset: Int) {
|
||||
listView.dataOffset = newDataOffset
|
||||
header.setScrollController(
|
||||
object : ScrollableChart.ScrollController {
|
||||
override fun onDataOffsetChanged(newDataOffset: Int) {
|
||||
listView.dataOffset = newDataOffset
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
|
||||
@@ -19,29 +19,58 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import androidx.annotation.*
|
||||
import androidx.appcompat.app.*
|
||||
import dagger.*
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dagger.Lazy
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.activities.habits.edit.*
|
||||
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.tasks.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.callbacks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import org.isoron.uhabits.tasks.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmSyncKeyDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
||||
import org.isoron.uhabits.core.commands.ChangeHabitColorCommand
|
||||
import org.isoron.uhabits.core.commands.Command
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateHabitCommand
|
||||
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.PaletteColor
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.COULD_NOT_EXPORT
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.COULD_NOT_GENERATE_BUG_REPORT
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.DATABASE_REPAIRED
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.FILE_NOT_RECOGNIZED
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.IMPORT_FAILED
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.IMPORT_SUCCESSFUL
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.SYNC_ENABLED
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.SYNC_KEY_ALREADY_INSTALLED
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsMenuBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBehavior
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
import org.isoron.uhabits.tasks.ExportDBTaskFactory
|
||||
import org.isoron.uhabits.tasks.ImportDataTask
|
||||
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
||||
import org.isoron.uhabits.utils.copyTo
|
||||
import org.isoron.uhabits.utils.restartWithFade
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.showSendEmailScreen
|
||||
import org.isoron.uhabits.utils.showSendFileScreen
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
const val RESULT_IMPORT_DATA = 101
|
||||
const val RESULT_EXPORT_CSV = 102
|
||||
@@ -54,19 +83,19 @@ const val REQUEST_SETTINGS = 107
|
||||
@ActivityScope
|
||||
class ListHabitsScreen
|
||||
@Inject constructor(
|
||||
@ActivityContext val context: Context,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val intentFactory: IntentFactory,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val taskRunner: TaskRunner,
|
||||
private val exportDBFactory: ExportDBTaskFactory,
|
||||
private val importTaskFactory: ImportDataTaskFactory,
|
||||
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
|
||||
private val confirmSyncKeyDialogFactory: ConfirmSyncKeyDialogFactory,
|
||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||
private val numberPickerFactory: NumberPickerFactory,
|
||||
private val behavior: Lazy<ListHabitsBehavior>
|
||||
@ActivityContext val context: Context,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val intentFactory: IntentFactory,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val taskRunner: TaskRunner,
|
||||
private val exportDBFactory: ExportDBTaskFactory,
|
||||
private val importTaskFactory: ImportDataTaskFactory,
|
||||
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
|
||||
private val confirmSyncKeyDialogFactory: ConfirmSyncKeyDialogFactory,
|
||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||
private val numberPickerFactory: NumberPickerFactory,
|
||||
private val behavior: Lazy<ListHabitsBehavior>
|
||||
) : CommandRunner.Listener,
|
||||
ListHabitsBehavior.Screen,
|
||||
ListHabitsMenuBehavior.Screen,
|
||||
@@ -76,7 +105,7 @@ class ListHabitsScreen
|
||||
|
||||
fun onAttached() {
|
||||
commandRunner.addListener(this)
|
||||
if(activity.intent.action == "android.intent.action.VIEW") {
|
||||
if (activity.intent.action == "android.intent.action.VIEW") {
|
||||
val uri = activity.intent.data!!.toString()
|
||||
val parts = uri.replace(Regex("^.*sync/"), "").split("#")
|
||||
val syncKey = parts[0]
|
||||
@@ -172,16 +201,20 @@ class ListHabitsScreen
|
||||
}
|
||||
|
||||
override fun showMessage(m: ListHabitsBehavior.Message) {
|
||||
activity.showMessage(activity.resources.getString(when (m) {
|
||||
COULD_NOT_EXPORT -> R.string.could_not_export
|
||||
IMPORT_SUCCESSFUL -> R.string.habits_imported
|
||||
IMPORT_FAILED -> R.string.could_not_import
|
||||
DATABASE_REPAIRED -> R.string.database_repaired
|
||||
COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
|
||||
FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
|
||||
SYNC_ENABLED -> R.string.sync_enabled
|
||||
SYNC_KEY_ALREADY_INSTALLED -> R.string.sync_key_already_installed
|
||||
}))
|
||||
activity.showMessage(
|
||||
activity.resources.getString(
|
||||
when (m) {
|
||||
COULD_NOT_EXPORT -> R.string.could_not_export
|
||||
IMPORT_SUCCESSFUL -> R.string.habits_imported
|
||||
IMPORT_FAILED -> R.string.could_not_import
|
||||
DATABASE_REPAIRED -> R.string.database_repaired
|
||||
COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
|
||||
FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
|
||||
SYNC_ENABLED -> R.string.sync_enabled
|
||||
SYNC_KEY_ALREADY_INSTALLED -> R.string.sync_key_already_installed
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun showSendBugReportToDeveloperScreen(log: String) {
|
||||
@@ -199,16 +232,20 @@ 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")
|
||||
}
|
||||
|
||||
override fun showNumberPicker(value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback) {
|
||||
override fun showNumberPicker(
|
||||
value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback
|
||||
) {
|
||||
numberPickerFactory.create(value, unit, callback).show()
|
||||
}
|
||||
|
||||
@@ -219,49 +256,61 @@ class ListHabitsScreen
|
||||
private fun getExecuteString(command: Command): String? {
|
||||
when (command) {
|
||||
is ArchiveHabitsCommand -> {
|
||||
return activity.resources.getQuantityString(R.plurals.toast_habits_archived,
|
||||
command.selected.size)
|
||||
return activity.resources.getQuantityString(
|
||||
R.plurals.toast_habits_archived,
|
||||
command.selected.size
|
||||
)
|
||||
}
|
||||
is ChangeHabitColorCommand -> {
|
||||
return activity.resources.getQuantityString(R.plurals.toast_habits_changed,
|
||||
command.selected.size)
|
||||
return activity.resources.getQuantityString(
|
||||
R.plurals.toast_habits_changed,
|
||||
command.selected.size
|
||||
)
|
||||
}
|
||||
is CreateHabitCommand -> {
|
||||
return activity.resources.getString(R.string.toast_habit_created)
|
||||
}
|
||||
is DeleteHabitsCommand -> {
|
||||
return activity.resources.getQuantityString(R.plurals.toast_habits_deleted,
|
||||
command.selected.size)
|
||||
return activity.resources.getQuantityString(
|
||||
R.plurals.toast_habits_deleted,
|
||||
command.selected.size
|
||||
)
|
||||
}
|
||||
is EditHabitCommand -> {
|
||||
return activity.resources.getQuantityString(R.plurals.toast_habits_changed, 1)
|
||||
}
|
||||
is UnarchiveHabitsCommand -> {
|
||||
return activity.resources.getQuantityString(R.plurals.toast_habits_unarchived,
|
||||
command.selected.size)
|
||||
return activity.resources.getQuantityString(
|
||||
R.plurals.toast_habits_unarchived,
|
||||
command.selected.size
|
||||
)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun onImportData(file: File, onFinished: () -> Unit) {
|
||||
taskRunner.execute(importTaskFactory.create(file) { result ->
|
||||
if (result == ImportDataTask.SUCCESS) {
|
||||
adapter.refresh()
|
||||
activity.showMessage(activity.resources.getString(R.string.habits_imported))
|
||||
} else if (result == ImportDataTask.NOT_RECOGNIZED) {
|
||||
activity.showMessage(activity.resources.getString(R.string.file_not_recognized))
|
||||
} else {
|
||||
activity.showMessage(activity.resources.getString(R.string.could_not_import))
|
||||
taskRunner.execute(
|
||||
importTaskFactory.create(file) { result ->
|
||||
if (result == ImportDataTask.SUCCESS) {
|
||||
adapter.refresh()
|
||||
activity.showMessage(activity.resources.getString(R.string.habits_imported))
|
||||
} else if (result == ImportDataTask.NOT_RECOGNIZED) {
|
||||
activity.showMessage(activity.resources.getString(R.string.file_not_recognized))
|
||||
} else {
|
||||
activity.showMessage(activity.resources.getString(R.string.could_not_import))
|
||||
}
|
||||
onFinished()
|
||||
}
|
||||
onFinished()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private fun onExportDB() {
|
||||
taskRunner.execute(exportDBFactory.create { filename ->
|
||||
if (filename != null) activity.showSendFileScreen(filename)
|
||||
else activity.showMessage(activity.resources.getString(R.string.could_not_export))
|
||||
})
|
||||
taskRunner.execute(
|
||||
exportDBFactory.create { filename ->
|
||||
if (filename != null) activity.showSendFileScreen(filename)
|
||||
else activity.showMessage(activity.resources.getString(R.string.could_not_export))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,30 +19,33 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import android.content.Context
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import dagger.*
|
||||
import dagger.Lazy
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListController
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.NotificationTray
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBehavior
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsSelectionMenu @Inject constructor(
|
||||
@ActivityContext context: Context,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
var commandRunner: CommandRunner,
|
||||
private val prefs: Preferences,
|
||||
private val behavior: ListHabitsSelectionMenuBehavior,
|
||||
private val listController: Lazy<HabitCardListController>,
|
||||
private val notificationTray: NotificationTray
|
||||
@ActivityContext context: Context,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
var commandRunner: CommandRunner,
|
||||
private val prefs: Preferences,
|
||||
private val behavior: ListHabitsSelectionMenuBehavior,
|
||||
private val listController: Lazy<HabitCardListController>,
|
||||
private val notificationTray: NotificationTray
|
||||
) : ActionMode.Callback {
|
||||
|
||||
val activity = (context as AppCompatActivity)
|
||||
|
||||
@@ -19,17 +19,18 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec.EXACTLY
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.utils.dim
|
||||
import org.isoron.uhabits.utils.toMeasureSpec
|
||||
|
||||
abstract class ButtonPanelView<T : View>(
|
||||
context: Context,
|
||||
val preferences: Preferences
|
||||
context: Context,
|
||||
val preferences: Preferences
|
||||
) : LinearLayout(context),
|
||||
Preferences.Listener {
|
||||
|
||||
@@ -79,10 +80,12 @@ abstract class ButtonPanelView<T : View>(
|
||||
val buttonWidth = dim(R.dimen.checkmarkWidth)
|
||||
val buttonHeight = dim(R.dimen.checkmarkHeight)
|
||||
val width = (buttonWidth * buttonCount)
|
||||
super.onMeasure(width.toMeasureSpec(EXACTLY),
|
||||
buttonHeight.toMeasureSpec(EXACTLY))
|
||||
super.onMeasure(
|
||||
width.toMeasureSpec(EXACTLY),
|
||||
buttonHeight.toMeasureSpec(EXACTLY)
|
||||
)
|
||||
}
|
||||
|
||||
protected abstract fun setupButtons()
|
||||
protected abstract fun createButton(): T
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,26 +19,35 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.text.TextPaint
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec.EXACTLY
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.utils.dim
|
||||
import org.isoron.uhabits.utils.getFontAwesome
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.sres
|
||||
import org.isoron.uhabits.utils.toMeasureSpec
|
||||
|
||||
@AutoFactory
|
||||
class CheckmarkButtonView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided val preferences: Preferences
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided val preferences: Preferences
|
||||
) : View(context),
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
@@ -65,7 +74,7 @@ class CheckmarkButtonView(
|
||||
}
|
||||
|
||||
fun performToggle() {
|
||||
value = if(preferences.isSkipEnabled) {
|
||||
value = if (preferences.isSkipEnabled) {
|
||||
Entry.nextToggleValueWithSkip(value)
|
||||
} else {
|
||||
Entry.nextToggleValueWithoutSkip(value)
|
||||
@@ -93,8 +102,10 @@ class CheckmarkButtonView(
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = resources.getDimensionPixelSize(R.dimen.checkmarkHeight)
|
||||
val width = resources.getDimensionPixelSize(R.dimen.checkmarkWidth)
|
||||
super.onMeasure(width.toMeasureSpec(EXACTLY),
|
||||
height.toMeasureSpec(EXACTLY))
|
||||
super.onMeasure(
|
||||
width.toMeasureSpec(EXACTLY),
|
||||
height.toMeasureSpec(EXACTLY)
|
||||
)
|
||||
}
|
||||
|
||||
private inner class Drawer {
|
||||
@@ -118,7 +129,7 @@ class CheckmarkButtonView(
|
||||
SKIP -> R.string.fa_skipped
|
||||
NO -> R.string.fa_times
|
||||
UNKNOWN -> {
|
||||
if(preferences.areQuestionMarksEnabled()) R.string.fa_question
|
||||
if (preferences.areQuestionMarksEnabled()) R.string.fa_question
|
||||
else R.string.fa_times
|
||||
}
|
||||
else -> R.string.fa_check
|
||||
|
||||
@@ -19,19 +19,20 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import android.content.Context
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
|
||||
@AutoFactory
|
||||
class CheckmarkPanelView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided preferences: Preferences,
|
||||
@Provided private val buttonFactory: CheckmarkButtonViewFactory
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided preferences: Preferences,
|
||||
@Provided private val buttonFactory: CheckmarkButtonViewFactory
|
||||
) : ButtonPanelView<CheckmarkButtonView>(context, preferences) {
|
||||
|
||||
var values = IntArray(0)
|
||||
@@ -46,7 +47,7 @@ class CheckmarkPanelView(
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
var onToggle: (Timestamp, Int) -> Unit = {_, _ ->}
|
||||
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
||||
set(value) {
|
||||
field = value
|
||||
setupButtons()
|
||||
|
||||
@@ -19,13 +19,19 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.view.Gravity.*
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.view.Gravity.CENTER
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.getFontAwesome
|
||||
import org.isoron.uhabits.utils.sp
|
||||
import org.isoron.uhabits.utils.sres
|
||||
import org.isoron.uhabits.utils.str
|
||||
|
||||
class EmptyListView(context: Context) : LinearLayout(context) {
|
||||
init {
|
||||
@@ -33,19 +39,27 @@ class EmptyListView(context: Context) : LinearLayout(context) {
|
||||
gravity = CENTER
|
||||
visibility = View.GONE
|
||||
|
||||
addView(TextView(context).apply {
|
||||
text = str(R.string.fa_star_half_o)
|
||||
typeface = getFontAwesome()
|
||||
textSize = sp(40.0f)
|
||||
gravity = CENTER
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
}, MATCH_PARENT, WRAP_CONTENT)
|
||||
addView(
|
||||
TextView(context).apply {
|
||||
text = str(R.string.fa_star_half_o)
|
||||
typeface = getFontAwesome()
|
||||
textSize = sp(40.0f)
|
||||
gravity = CENTER
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
},
|
||||
MATCH_PARENT,
|
||||
WRAP_CONTENT
|
||||
)
|
||||
|
||||
addView(TextView(context).apply {
|
||||
text = str(R.string.no_habits_found)
|
||||
gravity = CENTER
|
||||
setPadding(0, dp(20.0f).toInt(), 0, 0)
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
}, MATCH_PARENT, WRAP_CONTENT)
|
||||
addView(
|
||||
TextView(context).apply {
|
||||
text = str(R.string.no_habits_found)
|
||||
gravity = CENTER
|
||||
setPadding(0, dp(20.0f).toInt(), 0, 0)
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
},
|
||||
MATCH_PARENT,
|
||||
WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import dagger.Lazy
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsSelectionMenu
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.ModelObservable
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.inject.ActivityScope
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Controller responsible for receiving and processing the events generated by a
|
||||
@@ -33,9 +34,9 @@ import javax.inject.*
|
||||
*/
|
||||
@ActivityScope
|
||||
class HabitCardListController @Inject constructor(
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val behavior: ListHabitsBehavior,
|
||||
private val selectionMenu: Lazy<ListHabitsSelectionMenu>
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val behavior: ListHabitsBehavior,
|
||||
private val selectionMenu: Lazy<ListHabitsSelectionMenu>
|
||||
) : HabitCardListView.Controller, ModelObservable.Listener {
|
||||
|
||||
private val NORMAL_MODE = NormalMode()
|
||||
@@ -53,7 +54,7 @@ class HabitCardListController @Inject constructor(
|
||||
|
||||
val habitFrom = adapter.getItem(from)
|
||||
val habitTo = adapter.getItem(to)
|
||||
if(habitFrom == null || habitTo == null) return
|
||||
if (habitFrom == null || habitTo == null) return
|
||||
|
||||
adapter.performReorder(from, to)
|
||||
behavior.onReorderHabit(habitFrom, habitTo)
|
||||
|
||||
@@ -19,24 +19,33 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import androidx.recyclerview.widget.*
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.*
|
||||
import android.view.*
|
||||
import com.google.auto.factory.*
|
||||
import dagger.*
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.DOWN
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.END
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.START
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.UP
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import dagger.Lazy
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.activities.common.views.BundleSavedState
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
|
||||
@AutoFactory
|
||||
class HabitCardListView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided private val adapter: HabitCardListAdapter,
|
||||
@Provided private val cardViewFactory: HabitCardViewFactory,
|
||||
@Provided private val controller: Lazy<HabitCardListController>
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided private val adapter: HabitCardListAdapter,
|
||||
@Provided private val cardViewFactory: HabitCardViewFactory,
|
||||
@Provided private val controller: Lazy<HabitCardListController>
|
||||
) : RecyclerView(context, null, R.attr.scrollableRecyclerViewStyle) {
|
||||
|
||||
var checkmarkCount: Int = 0
|
||||
@@ -45,8 +54,8 @@ class HabitCardListView(
|
||||
set(value) {
|
||||
field = value
|
||||
attachedHolders
|
||||
.map { it.itemView as HabitCardView }
|
||||
.forEach { it.dataOffset = value }
|
||||
.map { it.itemView as HabitCardView }
|
||||
.forEach { it.dataOffset = value }
|
||||
}
|
||||
|
||||
private val attachedHolders = mutableListOf<HabitCardViewHolder>()
|
||||
@@ -65,11 +74,13 @@ class HabitCardListView(
|
||||
return cardViewFactory.create()
|
||||
}
|
||||
|
||||
fun bindCardView(holder: HabitCardViewHolder,
|
||||
habit: Habit,
|
||||
score: Double,
|
||||
checkmarks: IntArray,
|
||||
selected: Boolean): View {
|
||||
fun bindCardView(
|
||||
holder: HabitCardViewHolder,
|
||||
habit: Habit,
|
||||
score: Double,
|
||||
checkmarks: IntArray,
|
||||
selected: Boolean
|
||||
): View {
|
||||
val cardView = holder.itemView as HabitCardView
|
||||
cardView.habit = habit
|
||||
cardView.isSelected = selected
|
||||
@@ -132,7 +143,7 @@ class HabitCardListView(
|
||||
}
|
||||
|
||||
private inner class CardViewGestureDetector(
|
||||
private val holder: HabitCardViewHolder
|
||||
private val holder: HabitCardViewHolder
|
||||
) : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
override fun onLongPress(e: MotionEvent) {
|
||||
@@ -149,20 +160,26 @@ class HabitCardListView(
|
||||
}
|
||||
|
||||
inner class TouchHelperCallback : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder): Int {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
return makeMovementFlags(UP or DOWN, START or END)
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView,
|
||||
from: RecyclerView.ViewHolder,
|
||||
to: RecyclerView.ViewHolder): Boolean {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
from: RecyclerView.ViewHolder,
|
||||
to: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
controller.get().drop(from.adapterPosition, to.adapterPosition)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder,
|
||||
direction: Int) {
|
||||
override fun onSwiped(
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
direction: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun isItemViewSwipeEnabled() = false
|
||||
|
||||
@@ -19,29 +19,41 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.os.Build.VERSION.*
|
||||
import android.os.Build.VERSION_CODES.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import com.google.auto.factory.*
|
||||
import android.content.Context
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||
import android.os.Build.VERSION_CODES.M
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.Layout
|
||||
import android.text.TextUtils
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.activities.common.views.RingView
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.ModelObservable
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.sres
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
@AutoFactory
|
||||
class HabitCardView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided private val checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
||||
@Provided private val numberPanelFactory: NumberPanelViewFactory,
|
||||
@Provided private val behavior: ListHabitsBehavior
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided private val checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
||||
@Provided private val numberPanelFactory: NumberPanelViewFactory,
|
||||
@Provided private val behavior: ListHabitsBehavior
|
||||
) : FrameLayout(context),
|
||||
ModelObservable.Listener {
|
||||
|
||||
@@ -228,8 +240,10 @@ class HabitCardView(
|
||||
private fun triggerRipple(x: Float, y: Float) {
|
||||
val background = innerFrame.background
|
||||
if (SDK_INT >= LOLLIPOP) background.setHotspot(x, y)
|
||||
background.state = intArrayOf(android.R.attr.state_pressed,
|
||||
android.R.attr.state_enabled)
|
||||
background.state = intArrayOf(
|
||||
android.R.attr.state_pressed,
|
||||
android.R.attr.state_enabled
|
||||
)
|
||||
Handler().postDelayed({ background.state = intArrayOf() }, 25)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import androidx.appcompat.widget.*
|
||||
import android.view.*
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class HabitCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
||||
@@ -19,24 +19,33 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.os.Build.VERSION.*
|
||||
import android.os.Build.VERSION_CODES.*
|
||||
import android.text.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.common.views.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.core.utils.DateUtils.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.util.*
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||
import android.text.TextPaint
|
||||
import android.view.View.MeasureSpec.EXACTLY
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.views.ScrollableChart
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.utils.DateUtils.formatHeaderDate
|
||||
import org.isoron.uhabits.core.utils.DateUtils.getStartOfTodayCalendarWithOffset
|
||||
import org.isoron.uhabits.core.utils.MidnightTimer
|
||||
import org.isoron.uhabits.utils.dim
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.isRTL
|
||||
import org.isoron.uhabits.utils.sres
|
||||
import org.isoron.uhabits.utils.toMeasureSpec
|
||||
import java.util.GregorianCalendar
|
||||
|
||||
class HeaderView(
|
||||
context: Context,
|
||||
val prefs: Preferences,
|
||||
val midnightTimer: MidnightTimer
|
||||
context: Context,
|
||||
val prefs: Preferences,
|
||||
val midnightTimer: MidnightTimer
|
||||
) : ScrollableChart(context),
|
||||
Preferences.Listener,
|
||||
MidnightTimer.MidnightListener {
|
||||
@@ -121,8 +130,12 @@ class HeaderView(
|
||||
if (isReversed) rect.offset(-(index + 1) * width, 0f)
|
||||
else rect.offset((index - buttonCount) * width, 0f)
|
||||
|
||||
if (isRTL()) rect.set(canvas.width - rect.right, rect.top,
|
||||
canvas.width - rect.left, rect.bottom)
|
||||
if (isRTL()) rect.set(
|
||||
canvas.width - rect.right,
|
||||
rect.top,
|
||||
canvas.width - rect.left,
|
||||
rect.bottom
|
||||
)
|
||||
|
||||
val y1 = rect.centerY() - 0.25 * em
|
||||
val y2 = rect.centerY() + 1.25 * em
|
||||
|
||||
@@ -19,20 +19,21 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.animation.*
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.graphics.Color.*
|
||||
import android.view.*
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.Context
|
||||
import android.graphics.Color.WHITE
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.HintList
|
||||
import org.isoron.uhabits.utils.dp
|
||||
|
||||
class HintView(
|
||||
context: Context,
|
||||
private val hintList: HintList
|
||||
context: Context,
|
||||
private val hintList: HintList
|
||||
) : LinearLayout(context) {
|
||||
|
||||
val hintContent: TextView
|
||||
|
||||
@@ -19,18 +19,25 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.View.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.text.TextPaint
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.View.OnLongClickListener
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
import org.isoron.uhabits.utils.InterfaceUtils.getDimension
|
||||
import java.text.*
|
||||
import org.isoron.uhabits.utils.StyledResources
|
||||
import org.isoron.uhabits.utils.getFontAwesome
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import java.text.DecimalFormat
|
||||
|
||||
private val BOLD_TYPEFACE = Typeface.create("sans-serif-condensed", Typeface.BOLD)
|
||||
private val NORMAL_TYPEFACE = Typeface.create("sans-serif-condensed", Typeface.NORMAL)
|
||||
@@ -50,8 +57,8 @@ fun Double.toShortString(): String = when {
|
||||
|
||||
@AutoFactory
|
||||
class NumberButtonView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided val preferences: Preferences
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided val preferences: Preferences
|
||||
) : View(context),
|
||||
OnClickListener,
|
||||
OnLongClickListener {
|
||||
@@ -148,10 +155,10 @@ class NumberButtonView(
|
||||
val label: String
|
||||
val typeface: Typeface
|
||||
|
||||
if(value >= 0) {
|
||||
if (value >= 0) {
|
||||
label = value.toShortString()
|
||||
typeface = BOLD_TYPEFACE
|
||||
} else if(preferences.areQuestionMarksEnabled()) {
|
||||
} else if (preferences.areQuestionMarksEnabled()) {
|
||||
label = resources.getString(R.string.fa_question)
|
||||
typeface = getFontAwesome()
|
||||
} else {
|
||||
|
||||
@@ -19,18 +19,19 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import android.content.Context
|
||||
import com.google.auto.factory.AutoFactory
|
||||
import com.google.auto.factory.Provided
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
|
||||
@AutoFactory
|
||||
class NumberPanelView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided preferences: Preferences,
|
||||
@Provided private val buttonFactory: NumberButtonViewFactory
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided preferences: Preferences,
|
||||
@Provided private val buttonFactory: NumberButtonViewFactory
|
||||
) : ButtonPanelView<NumberButtonView>(context, preferences) {
|
||||
|
||||
var values = DoubleArray(0)
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec.EXACTLY
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.toMeasureSpec
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class ShadowView(context: Context) : View(context) {
|
||||
@@ -33,7 +34,9 @@ class ShadowView(context: Context) : View(context) {
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec,
|
||||
dp(2.0f).toInt().toMeasureSpec(EXACTLY))
|
||||
super.onMeasure(
|
||||
widthMeasureSpec,
|
||||
dp(2.0f).toInt().toMeasureSpec(EXACTLY)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,25 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
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.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.activities.HabitsDirFinder
|
||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||
import org.isoron.uhabits.core.commands.Command
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuBehavior
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
|
||||
class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
||||
|
||||
@@ -51,41 +59,41 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
||||
|
||||
view = ShowHabitView(this)
|
||||
presenter = ShowHabitPresenter(
|
||||
context = this,
|
||||
habit = habit,
|
||||
preferences = appComponent.preferences,
|
||||
context = this,
|
||||
habit = habit,
|
||||
preferences = appComponent.preferences,
|
||||
)
|
||||
|
||||
val screen = ShowHabitScreen(
|
||||
activity = this,
|
||||
confirmDeleteDialogFactory = ConfirmDeleteDialogFactory { this },
|
||||
habit = habit,
|
||||
intentFactory = IntentFactory(),
|
||||
numberPickerFactory = NumberPickerFactory(this),
|
||||
widgetUpdater = appComponent.widgetUpdater,
|
||||
activity = this,
|
||||
confirmDeleteDialogFactory = ConfirmDeleteDialogFactory { this },
|
||||
habit = habit,
|
||||
intentFactory = IntentFactory(),
|
||||
numberPickerFactory = NumberPickerFactory(this),
|
||||
widgetUpdater = appComponent.widgetUpdater,
|
||||
)
|
||||
|
||||
val behavior = ShowHabitBehavior(
|
||||
commandRunner = commandRunner,
|
||||
habit = habit,
|
||||
habitList = habitList,
|
||||
preferences = preferences,
|
||||
screen = screen,
|
||||
commandRunner = commandRunner,
|
||||
habit = habit,
|
||||
habitList = habitList,
|
||||
preferences = preferences,
|
||||
screen = screen,
|
||||
)
|
||||
|
||||
val menuBehavior = ShowHabitMenuBehavior(
|
||||
commandRunner = commandRunner,
|
||||
habit = habit,
|
||||
habitList = habitList,
|
||||
screen = screen,
|
||||
system = HabitsDirFinder(AndroidDirFinder(this)),
|
||||
taskRunner = appComponent.taskRunner,
|
||||
commandRunner = commandRunner,
|
||||
habit = habit,
|
||||
habitList = habitList,
|
||||
screen = screen,
|
||||
system = HabitsDirFinder(AndroidDirFinder(this)),
|
||||
taskRunner = appComponent.taskRunner,
|
||||
)
|
||||
|
||||
menu = ShowHabitMenu(
|
||||
activity = this,
|
||||
behavior = menuBehavior,
|
||||
preferences = preferences,
|
||||
activity = this,
|
||||
behavior = menuBehavior,
|
||||
preferences = preferences,
|
||||
)
|
||||
|
||||
view.onScoreCardSpinnerPosition = behavior::onScoreCardSpinnerPosition
|
||||
@@ -125,4 +133,3 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,16 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.show
|
||||
|
||||
import android.view.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.screens.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.ShowHabitMenuBehavior
|
||||
|
||||
class ShowHabitMenu(
|
||||
val activity: ShowHabitActivity,
|
||||
val behavior: ShowHabitMenuBehavior,
|
||||
val preferences: Preferences,
|
||||
val activity: ShowHabitActivity,
|
||||
val behavior: ShowHabitMenuBehavior,
|
||||
val preferences: Preferences,
|
||||
) {
|
||||
fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
activity.menuInflater.inflate(R.menu.show_habit, menu)
|
||||
@@ -38,7 +39,7 @@ class ShowHabitMenu(
|
||||
}
|
||||
|
||||
fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when(item.itemId) {
|
||||
when (item.itemId) {
|
||||
R.id.action_edit_habit -> {
|
||||
behavior.onEditHabit()
|
||||
return true
|
||||
@@ -58,4 +59,4 @@ class ShowHabitMenu(
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,32 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.show
|
||||
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.callbacks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.widgets.*
|
||||
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnToggleCheckmarkListener
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitBehavior
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuBehavior
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.showSendFileScreen
|
||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||
|
||||
class ShowHabitScreen(
|
||||
val activity: ShowHabitActivity,
|
||||
val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
|
||||
val habit: Habit,
|
||||
val intentFactory: IntentFactory,
|
||||
val numberPickerFactory: NumberPickerFactory,
|
||||
val widgetUpdater: WidgetUpdater,
|
||||
val activity: ShowHabitActivity,
|
||||
val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
|
||||
val habit: Habit,
|
||||
val intentFactory: IntentFactory,
|
||||
val numberPickerFactory: NumberPickerFactory,
|
||||
val widgetUpdater: WidgetUpdater,
|
||||
) : ShowHabitBehavior.Screen, ShowHabitMenuBehavior.Screen {
|
||||
|
||||
override fun showNumberPicker(value: Double, unit: String, callback: ListHabitsBehavior.NumberPickerCallback) {
|
||||
numberPickerFactory.create(value, unit, callback).show();
|
||||
numberPickerFactory.create(value, unit, callback).show()
|
||||
}
|
||||
|
||||
override fun updateWidgets() {
|
||||
@@ -81,4 +85,4 @@ class ShowHabitScreen(
|
||||
override fun close() {
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,46 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.show
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.activities.habits.show.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import org.isoron.uhabits.activities.habits.show.views.BarCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.BarCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.FrequencyCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.FrequencyCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.HistoryCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.HistoryCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.NotesCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.NotesCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.OverviewCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.OverviewCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.ScoreCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.StreakCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.StreakCartPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.SubtitleCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.SubtitleCardViewModel
|
||||
import org.isoron.uhabits.activities.habits.show.views.TargetCardPresenter
|
||||
import org.isoron.uhabits.activities.habits.show.views.TargetCardViewModel
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.databinding.ShowHabitBinding
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
|
||||
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,
|
||||
val streaks: StreakCardViewModel,
|
||||
val scores: ScoreCardViewModel,
|
||||
val frequency: FrequencyCardViewModel,
|
||||
val history: HistoryCardViewModel,
|
||||
val bar: BarCardViewModel,
|
||||
val title: String = "",
|
||||
val isNumerical: Boolean = false,
|
||||
val color: PaletteColor = PaletteColor(1),
|
||||
val subtitle: SubtitleCardViewModel,
|
||||
val overview: OverviewCardViewModel,
|
||||
val notes: NotesCardViewModel,
|
||||
val target: TargetCardViewModel,
|
||||
val streaks: StreakCardViewModel,
|
||||
val scores: ScoreCardViewModel,
|
||||
val frequency: FrequencyCardViewModel,
|
||||
val history: HistoryCardViewModel,
|
||||
val bar: BarCardViewModel,
|
||||
)
|
||||
|
||||
class ShowHabitView(context: Context) : FrameLayout(context) {
|
||||
@@ -80,56 +98,56 @@ class ShowHabitView(context: Context) : FrameLayout(context) {
|
||||
}
|
||||
|
||||
class ShowHabitPresenter(
|
||||
val habit: Habit,
|
||||
val context: Context,
|
||||
val preferences: Preferences,
|
||||
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,
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
resources = context.resources,
|
||||
)
|
||||
private val streakCartPresenter = StreakCartPresenter(habit)
|
||||
private val scoreCardPresenter = ScoreCardPresenter(
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
)
|
||||
private val frequencyCardPresenter = FrequencyCardPresenter(
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
)
|
||||
private val historyCardViewModel = HistoryCardPresenter(
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
isSkipEnabled = preferences.isSkipEnabled,
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
isSkipEnabled = preferences.isSkipEnabled,
|
||||
)
|
||||
private val barCardPresenter = BarCardPresenter(
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
habit = habit,
|
||||
firstWeekday = preferences.firstWeekday,
|
||||
)
|
||||
|
||||
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(),
|
||||
streaks = streakCartPresenter.present(),
|
||||
scores = scoreCardPresenter.present(
|
||||
spinnerPosition = preferences.scoreCardSpinnerPosition
|
||||
),
|
||||
frequency = frequencyCardPresenter.present(),
|
||||
history = historyCardViewModel.present(),
|
||||
bar = barCardPresenter.present(
|
||||
boolSpinnerPosition = preferences.barCardBoolSpinnerPosition,
|
||||
numericalSpinnerPosition = preferences.barCardNumericalSpinnerPosition,
|
||||
),
|
||||
title = habit.name,
|
||||
color = habit.color,
|
||||
isNumerical = habit.isNumerical,
|
||||
subtitle = subtitleCardPresenter.present(),
|
||||
overview = overviewCardPresenter.present(),
|
||||
notes = notesCardPresenter.present(),
|
||||
target = targetCardPresenter.present(),
|
||||
streaks = streakCartPresenter.present(),
|
||||
scores = scoreCardPresenter.present(
|
||||
spinnerPosition = preferences.scoreCardSpinnerPosition
|
||||
),
|
||||
frequency = frequencyCardPresenter.present(),
|
||||
history = historyCardViewModel.present(),
|
||||
bar = barCardPresenter.present(
|
||||
boolSpinnerPosition = preferences.barCardBoolSpinnerPosition,
|
||||
numericalSpinnerPosition = preferences.barCardNumericalSpinnerPosition,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,23 +18,27 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter.Companion.getTruncateField
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.databinding.ShowHabitBarBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
data class BarCardViewModel(
|
||||
val entries: List<Entry>,
|
||||
val bucketSize: Int,
|
||||
val color: PaletteColor,
|
||||
val isNumerical: Boolean,
|
||||
val numericalSpinnerPosition: Int,
|
||||
val boolSpinnerPosition: Int,
|
||||
val entries: List<Entry>,
|
||||
val bucketSize: Int,
|
||||
val color: PaletteColor,
|
||||
val isNumerical: Boolean,
|
||||
val numericalSpinnerPosition: Int,
|
||||
val boolSpinnerPosition: Int,
|
||||
)
|
||||
|
||||
class BarCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -78,17 +82,17 @@ class BarCard(context: Context, attrs: AttributeSet) : LinearLayout(context, att
|
||||
}
|
||||
|
||||
class BarCardPresenter(
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
) {
|
||||
val numericalBucketSizes = intArrayOf(1, 7, 31, 92, 365)
|
||||
val boolBucketSizes = intArrayOf(7, 31, 92, 365)
|
||||
|
||||
fun present(
|
||||
numericalSpinnerPosition: Int,
|
||||
boolSpinnerPosition: Int,
|
||||
numericalSpinnerPosition: Int,
|
||||
boolSpinnerPosition: Int,
|
||||
): BarCardViewModel {
|
||||
val bucketSize = if(habit.isNumerical) {
|
||||
val bucketSize = if (habit.isNumerical) {
|
||||
numericalBucketSizes[numericalSpinnerPosition]
|
||||
} else {
|
||||
boolBucketSizes[boolSpinnerPosition]
|
||||
@@ -101,19 +105,19 @@ class BarCardPresenter(
|
||||
}
|
||||
} else {
|
||||
habit.computedEntries.groupBy(
|
||||
original = habit.computedEntries.getByInterval(oldest, today),
|
||||
field = getTruncateField(bucketSize),
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = habit.isNumerical,
|
||||
original = habit.computedEntries.getByInterval(oldest, today),
|
||||
field = getTruncateField(bucketSize),
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = habit.isNumerical,
|
||||
)
|
||||
}
|
||||
return BarCardViewModel(
|
||||
entries = entries,
|
||||
bucketSize = bucketSize,
|
||||
color = habit.color,
|
||||
isNumerical = habit.isNumerical,
|
||||
numericalSpinnerPosition = numericalSpinnerPosition,
|
||||
boolSpinnerPosition = boolSpinnerPosition,
|
||||
entries = entries,
|
||||
bucketSize = bucketSize,
|
||||
color = habit.color,
|
||||
isNumerical = habit.isNumerical,
|
||||
numericalSpinnerPosition = numericalSpinnerPosition,
|
||||
boolSpinnerPosition = boolSpinnerPosition,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,21 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.util.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
import java.util.HashMap
|
||||
|
||||
data class FrequencyCardViewModel(
|
||||
val frequency: HashMap<Timestamp, Array<Int>>,
|
||||
val firstWeekday: Int,
|
||||
val color: PaletteColor,
|
||||
val frequency: HashMap<Timestamp, Array<Int>>,
|
||||
val firstWeekday: Int,
|
||||
val color: PaletteColor,
|
||||
)
|
||||
|
||||
class FrequencyCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -47,14 +49,14 @@ class FrequencyCard(context: Context, attrs: AttributeSet) : LinearLayout(contex
|
||||
}
|
||||
|
||||
class FrequencyCardPresenter(
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
) {
|
||||
fun present() = FrequencyCardViewModel(
|
||||
color = habit.color,
|
||||
frequency = habit.originalEntries.computeWeekdayFrequency(
|
||||
isNumerical = habit.isNumerical
|
||||
),
|
||||
firstWeekday = firstWeekday,
|
||||
color = habit.color,
|
||||
frequency = habit.originalEntries.computeWeekdayFrequency(
|
||||
isNumerical = habit.isNumerical
|
||||
),
|
||||
firstWeekday = firstWeekday,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,21 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.databinding.ShowHabitHistoryBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
data class HistoryCardViewModel(
|
||||
val entries: IntArray,
|
||||
val color: PaletteColor,
|
||||
val firstWeekday: Int,
|
||||
val isNumerical: Boolean,
|
||||
val isSkipEnabled: Boolean,
|
||||
val entries: IntArray,
|
||||
val color: PaletteColor,
|
||||
val firstWeekday: Int,
|
||||
val isNumerical: Boolean,
|
||||
val isSkipEnabled: Boolean,
|
||||
)
|
||||
|
||||
class HistoryCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -54,20 +55,19 @@ class HistoryCard(context: Context, attrs: AttributeSet) : LinearLayout(context,
|
||||
if (data.isNumerical) {
|
||||
binding.historyChart.setNumerical(true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryCardPresenter(
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val isSkipEnabled: Boolean,
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val isSkipEnabled: Boolean,
|
||||
) {
|
||||
fun present() = HistoryCardViewModel(
|
||||
entries = habit.computedEntries.getAllValues(),
|
||||
color = habit.color,
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = habit.isNumerical,
|
||||
isSkipEnabled = isSkipEnabled,
|
||||
entries = habit.computedEntries.getAllValues(),
|
||||
color = habit.color,
|
||||
firstWeekday = firstWeekday,
|
||||
isNumerical = habit.isNumerical,
|
||||
isSkipEnabled = isSkipEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.databinding.ShowHabitNotesBinding
|
||||
|
||||
data class NotesCardViewModel(val description: String)
|
||||
|
||||
@@ -43,6 +43,6 @@ class NotesCardView(context: Context, attrs: AttributeSet) : LinearLayout(contex
|
||||
|
||||
class NotesCardPresenter(val habit: Habit) {
|
||||
fun present() = NotesCardViewModel(
|
||||
description = habit.description,
|
||||
description = habit.description,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,27 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.databinding.ShowHabitOverviewBinding
|
||||
import org.isoron.uhabits.utils.StyledResources
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
data class OverviewCardViewModel(
|
||||
val color: PaletteColor,
|
||||
val scoreMonthDiff: Float,
|
||||
val scoreYearDiff: Float,
|
||||
val scoreToday: Float,
|
||||
val totalCount: Long,
|
||||
val color: PaletteColor,
|
||||
val scoreMonthDiff: Float,
|
||||
val scoreYearDiff: Float,
|
||||
val scoreToday: Float,
|
||||
val totalCount: Long,
|
||||
)
|
||||
|
||||
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -43,8 +46,11 @@ class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
||||
private val binding = ShowHabitOverviewBinding.inflate(LayoutInflater.from(context), this)
|
||||
|
||||
private fun formatPercentageDiff(percentageDiff: Float): String {
|
||||
return String.format("%s%.0f%%", if (percentageDiff >= 0) "+" else "\u2212",
|
||||
Math.abs(percentageDiff) * 100)
|
||||
return String.format(
|
||||
"%s%.0f%%",
|
||||
if (percentageDiff >= 0) "+" else "\u2212",
|
||||
Math.abs(percentageDiff) * 100
|
||||
)
|
||||
}
|
||||
|
||||
fun update(data: OverviewCardViewModel) {
|
||||
@@ -76,15 +82,15 @@ class OverviewCardPresenter(val habit: Habit) {
|
||||
val scoreLastMonth = scores.get(lastMonth).value.toFloat()
|
||||
val scoreLastYear = scores.get(lastYear).value.toFloat()
|
||||
val totalCount = habit.originalEntries.getKnown()
|
||||
.filter { it.value == YES_MANUAL }
|
||||
.count()
|
||||
.toLong()
|
||||
.filter { it.value == YES_MANUAL }
|
||||
.count()
|
||||
.toLong()
|
||||
return@IO OverviewCardViewModel(
|
||||
color = habit.color,
|
||||
scoreToday = scoreToday,
|
||||
scoreMonthDiff = scoreToday - scoreLastMonth,
|
||||
scoreYearDiff = scoreToday - scoreLastYear,
|
||||
totalCount = totalCount,
|
||||
color = habit.color,
|
||||
scoreToday = scoreToday,
|
||||
scoreMonthDiff = scoreToday - scoreLastMonth,
|
||||
scoreYearDiff = scoreToday - scoreLastYear,
|
||||
totalCount = totalCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,29 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.models.Score
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.DAY
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.MONTH
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.QUARTER
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.WEEK_NUMBER
|
||||
import org.isoron.uhabits.core.utils.DateUtils.TruncateField.YEAR
|
||||
import org.isoron.uhabits.databinding.ShowHabitScoreBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
data class ScoreCardViewModel(
|
||||
val scores: List<Score>,
|
||||
val bucketSize: Int,
|
||||
val spinnerPosition: Int,
|
||||
val color: PaletteColor,
|
||||
val scores: List<Score>,
|
||||
val bucketSize: Int,
|
||||
val spinnerPosition: Int,
|
||||
val color: PaletteColor,
|
||||
)
|
||||
|
||||
class ScoreCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -60,8 +68,8 @@ class ScoreCard(context: Context, attrs: AttributeSet) : LinearLayout(context, a
|
||||
}
|
||||
|
||||
class ScoreCardPresenter(
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
) {
|
||||
companion object {
|
||||
val BUCKET_SIZES = intArrayOf(1, 7, 31, 92, 365)
|
||||
@@ -86,18 +94,21 @@ class ScoreCardPresenter(
|
||||
val scores = habit.scores.getByInterval(oldest, today).groupBy {
|
||||
DateUtils.truncate(field, it.timestamp, firstWeekday)
|
||||
}.map { (timestamp, scores) ->
|
||||
Score(timestamp, scores.map {
|
||||
it.value
|
||||
}.average())
|
||||
Score(
|
||||
timestamp,
|
||||
scores.map {
|
||||
it.value
|
||||
}.average()
|
||||
)
|
||||
}.sortedBy {
|
||||
it.timestamp
|
||||
}.reversed()
|
||||
|
||||
return ScoreCardViewModel(
|
||||
color = habit.color,
|
||||
scores = scores,
|
||||
bucketSize = bucketSize,
|
||||
spinnerPosition = spinnerPosition,
|
||||
color = habit.color,
|
||||
scores = scores,
|
||||
bucketSize = bucketSize,
|
||||
spinnerPosition = spinnerPosition,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,21 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.models.Streak
|
||||
import org.isoron.uhabits.databinding.ShowHabitStreakBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
|
||||
data class StreakCardViewModel(
|
||||
val color: PaletteColor,
|
||||
val bestStreaks: List<Streak>
|
||||
val color: PaletteColor,
|
||||
val bestStreaks: List<Streak>
|
||||
)
|
||||
|
||||
class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -46,8 +49,8 @@ class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(conte
|
||||
class StreakCartPresenter(val habit: Habit) {
|
||||
suspend fun present(): StreakCardViewModel = Dispatchers.IO {
|
||||
return@IO StreakCardViewModel(
|
||||
color = habit.color,
|
||||
bestStreaks = habit.streaks.getBest(10),
|
||||
color = habit.color,
|
||||
bestStreaks = habit.streaks.getBest(10),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,26 +18,31 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.annotation.*
|
||||
import android.content.*
|
||||
import android.content.res.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.util.*
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
||||
import org.isoron.uhabits.core.models.Frequency
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
||||
import org.isoron.uhabits.utils.InterfaceUtils
|
||||
import org.isoron.uhabits.utils.formatTime
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
import java.util.Locale
|
||||
|
||||
data class SubtitleCardViewModel(
|
||||
val color: PaletteColor,
|
||||
val frequencyText: String,
|
||||
val isNumerical: Boolean,
|
||||
val question: String,
|
||||
val reminderText: String,
|
||||
val targetText: String,
|
||||
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) {
|
||||
@@ -75,8 +80,8 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
||||
}
|
||||
|
||||
class SubtitleCardPresenter(
|
||||
val habit: Habit,
|
||||
val context: Context,
|
||||
val habit: Habit,
|
||||
val context: Context,
|
||||
) {
|
||||
val resources: Resources = context.resources
|
||||
|
||||
@@ -87,12 +92,12 @@ class SubtitleCardPresenter(
|
||||
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}",
|
||||
color = habit.color,
|
||||
frequencyText = habit.frequency.format(),
|
||||
isNumerical = habit.isNumerical,
|
||||
question = habit.question,
|
||||
reminderText = reminderText,
|
||||
targetText = "${habit.targetValue.toShortString()} ${habit.unit}",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -122,12 +127,12 @@ class SubtitleCardPresenter(
|
||||
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),
|
||||
Locale.US,
|
||||
"%d %s %d %s",
|
||||
num,
|
||||
resources.getString(R.string.times_every),
|
||||
den,
|
||||
resources.getString(R.string.days),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,27 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.show.views
|
||||
|
||||
import android.content.*
|
||||
import android.content.res.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.util.*
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.databinding.ShowHabitTargetBinding
|
||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
||||
import java.util.ArrayList
|
||||
import java.util.Calendar
|
||||
|
||||
data class TargetCardViewModel(
|
||||
val color: PaletteColor,
|
||||
val values: List<Double> = listOf(),
|
||||
val targets: List<Double> = listOf(),
|
||||
val labels: List<String> = listOf(),
|
||||
val color: PaletteColor,
|
||||
val values: List<Double> = listOf(),
|
||||
val targets: List<Double> = listOf(),
|
||||
val labels: List<String> = listOf(),
|
||||
)
|
||||
|
||||
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||
@@ -52,9 +55,9 @@ class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(conte
|
||||
}
|
||||
|
||||
class TargetCardPresenter(
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val resources: Resources,
|
||||
val habit: Habit,
|
||||
val firstWeekday: Int,
|
||||
val resources: Resources,
|
||||
) {
|
||||
suspend fun present(): TargetCardViewModel = Dispatchers.IO {
|
||||
val today = DateUtils.getTodayWithOffset()
|
||||
@@ -98,10 +101,10 @@ class TargetCardPresenter(
|
||||
labels.add(resources.getString(R.string.year))
|
||||
|
||||
return@IO TargetCardViewModel(
|
||||
color = habit.color,
|
||||
values = values,
|
||||
labels = labels,
|
||||
targets = targets,
|
||||
color = habit.color,
|
||||
values = values,
|
||||
labels = labels,
|
||||
targets = targets,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,10 @@
|
||||
|
||||
package org.isoron.uhabits.activities.intro
|
||||
|
||||
import android.graphics.*
|
||||
import android.os.*
|
||||
|
||||
import com.github.paolorotolo.appintro.*
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import com.github.paolorotolo.appintro.AppIntro2
|
||||
import com.github.paolorotolo.appintro.AppIntroFragment
|
||||
import org.isoron.uhabits.R
|
||||
|
||||
/**
|
||||
@@ -34,17 +33,32 @@ class IntroActivity : AppIntro2() {
|
||||
override fun init(savedInstanceState: Bundle?) {
|
||||
showStatusBar(false)
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_1),
|
||||
getString(R.string.intro_description_1), R.drawable.intro_icon_1,
|
||||
Color.parseColor("#194673")))
|
||||
addSlide(
|
||||
AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_title_1),
|
||||
getString(R.string.intro_description_1),
|
||||
R.drawable.intro_icon_1,
|
||||
Color.parseColor("#194673")
|
||||
)
|
||||
)
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_2),
|
||||
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
||||
Color.parseColor("#ffa726")))
|
||||
addSlide(
|
||||
AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_title_2),
|
||||
getString(R.string.intro_description_2),
|
||||
R.drawable.intro_icon_2,
|
||||
Color.parseColor("#ffa726")
|
||||
)
|
||||
)
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
|
||||
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
||||
Color.parseColor("#9575cd")))
|
||||
addSlide(
|
||||
AppIntroFragment.newInstance(
|
||||
getString(R.string.intro_title_4),
|
||||
getString(R.string.intro_description_4),
|
||||
R.drawable.intro_icon_4,
|
||||
Color.parseColor("#9575cd")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onNextPressed() {}
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.settings
|
||||
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.databinding.SettingsActivityBinding
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -35,10 +36,10 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
val binding = SettingsActivityBinding.inflate(LayoutInflater.from(this))
|
||||
binding.root.setupToolbar(
|
||||
toolbar = binding.toolbar,
|
||||
title = resources.getString(R.string.settings),
|
||||
color = PaletteColor(11),
|
||||
toolbar = binding.toolbar,
|
||||
title = resources.getString(R.string.settings),
|
||||
color = PaletteColor(11),
|
||||
)
|
||||
setContentView(binding.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,25 +19,31 @@
|
||||
|
||||
package org.isoron.uhabits.activities.sync
|
||||
|
||||
import android.content.*
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.graphics.*
|
||||
import android.os.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.*
|
||||
import com.google.zxing.*
|
||||
import com.google.zxing.qrcode.*
|
||||
import kotlinx.coroutines.*
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import kotlinx.coroutines.launch
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.ui.screens.sync.SyncBehavior
|
||||
import org.isoron.uhabits.databinding.ActivitySyncBinding
|
||||
import org.isoron.uhabits.sync.RemoteSyncServer
|
||||
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.screens.sync.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.sync.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
|
||||
class SyncActivity : AppCompatActivity(), SyncBehavior.Screen {
|
||||
|
||||
@@ -57,9 +63,9 @@ class SyncActivity : AppCompatActivity(), SyncBehavior.Screen {
|
||||
binding = ActivitySyncBinding.inflate(layoutInflater)
|
||||
binding.errorIcon.typeface = getFontAwesome(this)
|
||||
binding.root.setupToolbar(
|
||||
toolbar = binding.toolbar,
|
||||
color = PaletteColor(11),
|
||||
title = resources.getString(R.string.device_sync),
|
||||
toolbar = binding.toolbar,
|
||||
color = PaletteColor(11),
|
||||
title = resources.getString(R.string.device_sync),
|
||||
)
|
||||
binding.syncLink.setOnClickListener { copyToClipboard() }
|
||||
binding.instructions.setText(Html.fromHtml(resources.getString(R.string.sync_instructions)))
|
||||
@@ -100,7 +106,6 @@ class SyncActivity : AppCompatActivity(), SyncBehavior.Screen {
|
||||
binding.progress.visibility = View.GONE
|
||||
binding.qrCode.visibility = View.VISIBLE
|
||||
binding.qrCode.setImageBitmap(generateQR(msg))
|
||||
|
||||
}
|
||||
|
||||
override suspend fun showLoadingScreen() {
|
||||
@@ -122,4 +127,4 @@ class SyncActivity : AppCompatActivity(), SyncBehavior.Screen {
|
||||
binding.syncLink.text = link
|
||||
showQR(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,30 +19,31 @@
|
||||
|
||||
package org.isoron.uhabits.automation
|
||||
|
||||
import android.os.*
|
||||
import androidx.appcompat.app.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.core.models.HabitMatcherBuilder
|
||||
|
||||
class EditSettingActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val app = applicationContext as HabitsApplication
|
||||
val habits = app.component.habitList.getFiltered(
|
||||
HabitMatcherBuilder()
|
||||
.setArchivedAllowed(false)
|
||||
.setCompletedAllowed(true)
|
||||
.build())
|
||||
HabitMatcherBuilder()
|
||||
.setArchivedAllowed(false)
|
||||
.setCompletedAllowed(true)
|
||||
.build()
|
||||
)
|
||||
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
||||
|
||||
val args = SettingUtils.parseIntent(this.intent, habits)
|
||||
val controller = EditSettingController(this)
|
||||
val view = EditSettingRootView(
|
||||
context = this,
|
||||
habitList = app.component.habitList,
|
||||
onSave = controller::onSave,
|
||||
args = args,
|
||||
context = this,
|
||||
habitList = app.component.habitList,
|
||||
onSave = controller::onSave,
|
||||
args = args,
|
||||
)
|
||||
setContentView(view)
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
package org.isoron.uhabits.automation
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
|
||||
class EditSettingController(private val activity: Activity) {
|
||||
|
||||
@@ -36,10 +36,13 @@ class EditSettingController(private val activity: Activity) {
|
||||
bundle.putInt("action", action)
|
||||
bundle.putLong("habit", habit.id!!)
|
||||
|
||||
activity.setResult(Activity.RESULT_OK, Intent().apply {
|
||||
putExtra(EXTRA_STRING_BLURB, blurb)
|
||||
putExtra(EXTRA_BUNDLE, bundle)
|
||||
})
|
||||
activity.setResult(
|
||||
Activity.RESULT_OK,
|
||||
Intent().apply {
|
||||
putExtra(EXTRA_STRING_BLURB, blurb)
|
||||
putExtra(EXTRA_BUNDLE, bundle)
|
||||
}
|
||||
)
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,29 @@
|
||||
|
||||
package org.isoron.uhabits.automation
|
||||
|
||||
import android.R.layout.*
|
||||
import android.annotation.*
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.util.*
|
||||
import android.R.layout.simple_spinner_dropdown_item
|
||||
import android.R.layout.simple_spinner_item
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.FrameLayout
|
||||
import org.isoron.uhabits.R
|
||||
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.databinding.AutomationBinding
|
||||
import org.isoron.uhabits.utils.setupToolbar
|
||||
import java.util.LinkedList
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class EditSettingRootView(
|
||||
context: Context,
|
||||
private val habitList: HabitList,
|
||||
private val onSave: (habit: Habit, action: Int) -> Unit,
|
||||
args: SettingUtils.Arguments?
|
||||
context: Context,
|
||||
private val habitList: HabitList,
|
||||
private val onSave: (habit: Habit, action: Int) -> Unit,
|
||||
args: SettingUtils.Arguments?
|
||||
) : FrameLayout(context) {
|
||||
|
||||
private var binding = AutomationBinding.inflate(LayoutInflater.from(context))
|
||||
@@ -43,10 +49,10 @@ class EditSettingRootView(
|
||||
init {
|
||||
addView(binding.root)
|
||||
setupToolbar(
|
||||
toolbar = binding.toolbar,
|
||||
title = resources.getString(R.string.app_name),
|
||||
color = PaletteColor(11),
|
||||
displayHomeAsUpEnabled = false,
|
||||
toolbar = binding.toolbar,
|
||||
title = resources.getString(R.string.app_name),
|
||||
color = PaletteColor(11),
|
||||
displayHomeAsUpEnabled = false,
|
||||
)
|
||||
populateHabitSpinner()
|
||||
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
@@ -60,8 +66,8 @@ class EditSettingRootView(
|
||||
binding.buttonSave.setOnClickListener {
|
||||
val habit = habitList.getByPosition(binding.habitSpinner.selectedItemPosition)
|
||||
val action = mapSpinnerPositionToAction(
|
||||
isNumerical = habit.isNumerical,
|
||||
itemPosition = binding.actionSpinner.selectedItemPosition,
|
||||
isNumerical = habit.isNumerical,
|
||||
itemPosition = binding.actionSpinner.selectedItemPosition,
|
||||
)
|
||||
onSave(habit, action)
|
||||
}
|
||||
|
||||
@@ -19,14 +19,16 @@
|
||||
|
||||
package org.isoron.uhabits.automation
|
||||
|
||||
import android.content.*
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.widgets.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.receivers.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import dagger.Component
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.ui.widgets.WidgetBehavior
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||
import org.isoron.uhabits.receivers.ReceiverScope
|
||||
|
||||
const val ACTION_CHECK = 0
|
||||
const val ACTION_UNCHECK = 1
|
||||
@@ -44,9 +46,9 @@ class FireSettingReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val app = context.applicationContext as HabitsApplication
|
||||
val component = DaggerFireSettingReceiver_ReceiverComponent
|
||||
.builder()
|
||||
.habitsApplicationComponent(app.component)
|
||||
.build()
|
||||
.builder()
|
||||
.habitsApplicationComponent(app.component)
|
||||
.build()
|
||||
allHabits = app.component.habitList
|
||||
val args = SettingUtils.parseIntent(intent, allHabits) ?: return
|
||||
val timestamp = DateUtils.getTodayWithOffset()
|
||||
|
||||
@@ -15,4 +15,4 @@ object SettingUtils {
|
||||
}
|
||||
|
||||
class Arguments(var action: Int, var habit: Habit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
package org.isoron.uhabits.database
|
||||
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.database.Cursor
|
||||
|
||||
class AndroidCursor(private val cursor: android.database.Cursor) : Cursor {
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
package org.isoron.uhabits.database
|
||||
|
||||
import android.content.*
|
||||
import android.database.sqlite.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import java.io.*
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import org.isoron.uhabits.core.database.Database
|
||||
import java.io.File
|
||||
|
||||
class AndroidDatabase(
|
||||
private val db: SQLiteDatabase,
|
||||
override val file: File?,
|
||||
private val db: SQLiteDatabase,
|
||||
override val file: File?,
|
||||
) : Database {
|
||||
|
||||
override fun beginTransaction() = db.beginTransaction()
|
||||
@@ -42,10 +42,10 @@ class AndroidDatabase(
|
||||
override fun execute(query: String, vararg params: Any) = db.execSQL(query, params)
|
||||
|
||||
override fun update(
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
tableName: String,
|
||||
values: Map<String, Any?>,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
): Int {
|
||||
val contValues = mapToContentValues(values)
|
||||
return db.update(tableName, contValues, where, params)
|
||||
@@ -57,9 +57,9 @@ class AndroidDatabase(
|
||||
}
|
||||
|
||||
override fun delete(
|
||||
tableName: String,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
tableName: String,
|
||||
where: String,
|
||||
vararg params: String,
|
||||
) {
|
||||
db.delete(tableName, where, params)
|
||||
}
|
||||
|
||||
@@ -19,19 +19,20 @@
|
||||
|
||||
package org.isoron.uhabits.database
|
||||
|
||||
import android.database.sqlite.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import org.isoron.uhabits.core.database.DatabaseOpener
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
class AndroidDatabaseOpener @Inject constructor() : DatabaseOpener {
|
||||
override fun open(file: File): AndroidDatabase {
|
||||
return AndroidDatabase(
|
||||
db = SQLiteDatabase.openDatabase(
|
||||
file.absolutePath,
|
||||
null,
|
||||
SQLiteDatabase.OPEN_READWRITE,
|
||||
),
|
||||
file = file)
|
||||
db = SQLiteDatabase.openDatabase(
|
||||
file.absolutePath,
|
||||
null,
|
||||
SQLiteDatabase.OPEN_READWRITE,
|
||||
),
|
||||
file = file
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.database
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.io.*
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import org.isoron.uhabits.AndroidDirFinder
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import org.isoron.uhabits.utils.DatabaseUtils
|
||||
import java.io.File
|
||||
|
||||
class AutoBackup(private val context: Context) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class AutoBackup(private val context: Context) {
|
||||
fun run(keep: Int = 5) {
|
||||
Log.i("AutoBackup", "Starting automatic backups...")
|
||||
val files = listBackupFiles()
|
||||
var newestTimestamp = 0L;
|
||||
var newestTimestamp = 0L
|
||||
if (files.isNotEmpty()) {
|
||||
newestTimestamp = files.last().lastModified()
|
||||
}
|
||||
@@ -61,4 +61,4 @@ class AutoBackup(private val context: Context) {
|
||||
}
|
||||
return files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import javax.inject.*
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ActivityContext
|
||||
annotation class ActivityContext
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import javax.inject.*
|
||||
import javax.inject.Scope
|
||||
|
||||
/**
|
||||
* Scope used by objects that live as long as the activity is alive.
|
||||
*/
|
||||
@Scope
|
||||
annotation class ActivityScope
|
||||
annotation class ActivityScope
|
||||
|
||||
@@ -23,4 +23,4 @@ import javax.inject.Qualifier
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class AppContext
|
||||
annotation class AppContext
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
|
||||
@Module
|
||||
class HabitModule(private val habit: Habit) {
|
||||
|
||||
@@ -19,20 +19,27 @@
|
||||
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import dagger.Component
|
||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsMenu
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsModule
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsRootView
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsScreen
|
||||
import org.isoron.uhabits.activities.habits.list.ListHabitsSelectionMenu
|
||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||
|
||||
@ActivityScope
|
||||
@Component(modules = arrayOf(
|
||||
@Component(
|
||||
modules = arrayOf(
|
||||
ActivityContextModule::class,
|
||||
HabitsActivityModule::class,
|
||||
ListHabitsModule::class,
|
||||
HabitModule::class
|
||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||
),
|
||||
dependencies = arrayOf(HabitsApplicationComponent::class)
|
||||
)
|
||||
interface HabitsActivityComponent {
|
||||
val colorPickerDialogFactory: ColorPickerDialogFactory
|
||||
val habitCardListAdapter: HabitCardListAdapter
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import android.content.*
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||
|
||||
@Module
|
||||
class HabitsActivityModule {
|
||||
@@ -31,8 +32,8 @@ class HabitsActivityModule {
|
||||
@Provides
|
||||
@ActivityScope
|
||||
fun getThemeSwitcher(
|
||||
@ActivityContext context: Context,
|
||||
prefs: Preferences
|
||||
@ActivityContext context: Context,
|
||||
prefs: Preferences
|
||||
): ThemeSwitcher {
|
||||
return AndroidThemeSwitcher(context, prefs)
|
||||
}
|
||||
|
||||
@@ -19,28 +19,35 @@
|
||||
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import android.content.*
|
||||
import dagger.*
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.database.*
|
||||
import org.isoron.uhabits.core.io.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.models.sqlite.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.reminders.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.database.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import org.isoron.uhabits.io.*
|
||||
import org.isoron.uhabits.notifications.*
|
||||
import org.isoron.uhabits.preferences.*
|
||||
import org.isoron.uhabits.sync.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.io.*
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.database.Database
|
||||
import org.isoron.uhabits.core.database.DatabaseOpener
|
||||
import org.isoron.uhabits.core.io.Logging
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
|
||||
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler
|
||||
import org.isoron.uhabits.core.sync.AbstractSyncServer
|
||||
import org.isoron.uhabits.core.sync.NetworkManager
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.NotificationTray
|
||||
import org.isoron.uhabits.database.AndroidDatabase
|
||||
import org.isoron.uhabits.database.AndroidDatabaseOpener
|
||||
import org.isoron.uhabits.intents.IntentScheduler
|
||||
import org.isoron.uhabits.io.AndroidLogging
|
||||
import org.isoron.uhabits.notifications.AndroidNotificationTray
|
||||
import org.isoron.uhabits.preferences.SharedPreferencesStorage
|
||||
import org.isoron.uhabits.sync.AndroidNetworkManager
|
||||
import org.isoron.uhabits.sync.RemoteSyncServer
|
||||
import org.isoron.uhabits.utils.DatabaseUtils
|
||||
import java.io.File
|
||||
|
||||
@Module
|
||||
class HabitsModule(dbFile: File) {
|
||||
@@ -56,10 +63,10 @@ class HabitsModule(dbFile: File) {
|
||||
@Provides
|
||||
@AppScope
|
||||
fun getReminderScheduler(
|
||||
sys: IntentScheduler,
|
||||
commandRunner: CommandRunner,
|
||||
habitList: HabitList,
|
||||
widgetPreferences: WidgetPreferences
|
||||
sys: IntentScheduler,
|
||||
commandRunner: CommandRunner,
|
||||
habitList: HabitList,
|
||||
widgetPreferences: WidgetPreferences
|
||||
): ReminderScheduler {
|
||||
return ReminderScheduler(commandRunner, habitList, sys, widgetPreferences)
|
||||
}
|
||||
@@ -67,10 +74,10 @@ class HabitsModule(dbFile: File) {
|
||||
@Provides
|
||||
@AppScope
|
||||
fun getTray(
|
||||
taskRunner: TaskRunner,
|
||||
commandRunner: CommandRunner,
|
||||
preferences: Preferences,
|
||||
screen: AndroidNotificationTray
|
||||
taskRunner: TaskRunner,
|
||||
commandRunner: CommandRunner,
|
||||
preferences: Preferences,
|
||||
screen: AndroidNotificationTray
|
||||
): NotificationTray {
|
||||
return NotificationTray(taskRunner, commandRunner, preferences, screen)
|
||||
}
|
||||
@@ -78,7 +85,7 @@ class HabitsModule(dbFile: File) {
|
||||
@Provides
|
||||
@AppScope
|
||||
fun getWidgetPreferences(
|
||||
storage: SharedPreferencesStorage
|
||||
storage: SharedPreferencesStorage
|
||||
): WidgetPreferences {
|
||||
return WidgetPreferences(storage)
|
||||
}
|
||||
@@ -115,7 +122,7 @@ class HabitsModule(dbFile: File) {
|
||||
|
||||
@Provides
|
||||
@AppScope
|
||||
fun getSyncServer(preferences: Preferences) : AbstractSyncServer {
|
||||
fun getSyncServer(preferences: Preferences): AbstractSyncServer {
|
||||
return RemoteSyncServer(preferences)
|
||||
}
|
||||
|
||||
@@ -125,4 +132,3 @@ class HabitsModule(dbFile: File) {
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,24 @@
|
||||
|
||||
package org.isoron.uhabits.intents
|
||||
|
||||
import android.content.*
|
||||
import android.net.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.about.*
|
||||
import org.isoron.uhabits.activities.habits.edit.*
|
||||
import org.isoron.uhabits.activities.habits.show.*
|
||||
import org.isoron.uhabits.activities.intro.*
|
||||
import org.isoron.uhabits.activities.settings.*
|
||||
import org.isoron.uhabits.activities.sync.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import javax.inject.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.about.AboutActivity
|
||||
import org.isoron.uhabits.activities.habits.edit.EditHabitActivity
|
||||
import org.isoron.uhabits.activities.habits.show.ShowHabitActivity
|
||||
import org.isoron.uhabits.activities.intro.IntroActivity
|
||||
import org.isoron.uhabits.activities.settings.SettingsActivity
|
||||
import org.isoron.uhabits.activities.sync.SyncActivity
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import javax.inject.Inject
|
||||
|
||||
class IntentFactory
|
||||
@Inject constructor() {
|
||||
|
||||
fun helpTranslate(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.translateURL))
|
||||
buildViewIntent(context.getString(R.string.translateURL))
|
||||
|
||||
fun openDocument() = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
@@ -43,33 +44,33 @@ class IntentFactory
|
||||
}
|
||||
|
||||
fun rateApp(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.playStoreURL))
|
||||
buildViewIntent(context.getString(R.string.playStoreURL))
|
||||
|
||||
fun sendFeedback(context: Context) =
|
||||
buildSendToIntent(context.getString(R.string.feedbackURL))
|
||||
buildSendToIntent(context.getString(R.string.feedbackURL))
|
||||
|
||||
fun privacyPolicy(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.privacyPolicyURL))
|
||||
buildViewIntent(context.getString(R.string.privacyPolicyURL))
|
||||
|
||||
fun startAboutActivity(context: Context) =
|
||||
Intent(context, AboutActivity::class.java)
|
||||
Intent(context, AboutActivity::class.java)
|
||||
|
||||
fun startIntroActivity(context: Context) =
|
||||
Intent(context, IntroActivity::class.java)
|
||||
Intent(context, IntroActivity::class.java)
|
||||
|
||||
fun startSettingsActivity(context: Context) =
|
||||
Intent(context, SettingsActivity::class.java)
|
||||
Intent(context, SettingsActivity::class.java)
|
||||
|
||||
fun startShowHabitActivity(context: Context, habit: Habit) =
|
||||
Intent(context, ShowHabitActivity::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
}
|
||||
Intent(context, ShowHabitActivity::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
}
|
||||
|
||||
fun viewFAQ(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.helpURL))
|
||||
buildViewIntent(context.getString(R.string.helpURL))
|
||||
|
||||
fun viewSourceCode(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.sourceCodeURL))
|
||||
buildViewIntent(context.getString(R.string.sourceCodeURL))
|
||||
|
||||
private fun buildSendToIntent(url: String) = Intent().apply {
|
||||
action = Intent.ACTION_SENDTO
|
||||
@@ -82,7 +83,7 @@ class IntentFactory
|
||||
}
|
||||
|
||||
fun codeContributors(context: Context) =
|
||||
buildViewIntent(context.getString(R.string.codeContributorsURL))
|
||||
buildViewIntent(context.getString(R.string.codeContributorsURL))
|
||||
|
||||
private fun startEditActivity(context: Context): Intent {
|
||||
return Intent(context, EditHabitActivity::class.java)
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
|
||||
package org.isoron.uhabits.intents
|
||||
|
||||
import android.content.*
|
||||
import android.content.ContentUris.*
|
||||
import android.net.*
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import javax.inject.*
|
||||
import android.content.ContentUris.parseId
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
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.utils.DateUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class IntentParser
|
||||
@@ -37,13 +39,13 @@ class IntentParser
|
||||
}
|
||||
|
||||
fun copyIntentData(source: Intent, destination: Intent) {
|
||||
destination.data = source.data;
|
||||
destination.data = source.data
|
||||
destination.putExtra("timestamp", source.getLongExtra("timestamp", DateUtils.getTodayWithOffset().unixTime))
|
||||
}
|
||||
|
||||
private fun parseHabit(uri: Uri): Habit {
|
||||
val habit = habits.getById(parseId(uri)) ?:
|
||||
throw IllegalArgumentException("habit not found")
|
||||
val habit = habits.getById(parseId(uri))
|
||||
?: throw IllegalArgumentException("habit not found")
|
||||
return habit
|
||||
}
|
||||
|
||||
|
||||
@@ -19,37 +19,42 @@
|
||||
|
||||
package org.isoron.uhabits.intents
|
||||
|
||||
import android.app.*
|
||||
import android.app.AlarmManager.*
|
||||
import android.content.*
|
||||
import android.content.Context.*
|
||||
import android.os.Build.VERSION.*
|
||||
import android.os.Build.VERSION_CODES.*
|
||||
import android.util.*
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import java.util.*
|
||||
import javax.inject.*
|
||||
import android.app.AlarmManager
|
||||
import android.app.AlarmManager.RTC
|
||||
import android.app.AlarmManager.RTC_WAKEUP
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Context.ALARM_SERVICE
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.M
|
||||
import android.util.Log
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler.SchedulerResult
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler.SystemScheduler
|
||||
import org.isoron.uhabits.core.utils.DateFormats
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class IntentScheduler
|
||||
@Inject constructor(
|
||||
@AppContext context: Context,
|
||||
private val pendingIntents: PendingIntentFactory
|
||||
@AppContext context: Context,
|
||||
private val pendingIntents: PendingIntentFactory
|
||||
) : SystemScheduler {
|
||||
|
||||
private val manager =
|
||||
context.getSystemService(ALARM_SERVICE) as AlarmManager
|
||||
context.getSystemService(ALARM_SERVICE) as AlarmManager
|
||||
|
||||
private fun schedule(timestamp: Long, intent: PendingIntent, alarmType: Int): SchedulerResult {
|
||||
val now = System.currentTimeMillis()
|
||||
Log.d("IntentScheduler", "timestamp=$timestamp now=$now")
|
||||
if (timestamp < now) {
|
||||
Log.e("IntentScheduler",
|
||||
"Ignoring attempt to schedule intent in the past.")
|
||||
Log.e(
|
||||
"IntentScheduler",
|
||||
"Ignoring attempt to schedule intent in the past."
|
||||
)
|
||||
return SchedulerResult.IGNORED
|
||||
}
|
||||
if (SDK_INT >= M)
|
||||
@@ -59,9 +64,11 @@ class IntentScheduler
|
||||
return SchedulerResult.OK
|
||||
}
|
||||
|
||||
override fun scheduleShowReminder(reminderTime: Long,
|
||||
habit: Habit,
|
||||
timestamp: Long): SchedulerResult {
|
||||
override fun scheduleShowReminder(
|
||||
reminderTime: Long,
|
||||
habit: Habit,
|
||||
timestamp: Long
|
||||
): SchedulerResult {
|
||||
val intent = pendingIntents.showReminder(habit, reminderTime, timestamp)
|
||||
logReminderScheduled(habit, reminderTime)
|
||||
return schedule(reminderTime, intent, RTC_WAKEUP)
|
||||
@@ -81,7 +88,9 @@ class IntentScheduler
|
||||
val name = habit.name.substring(0, min)
|
||||
val df = DateFormats.getBackupDateFormat()
|
||||
val time = df.format(Date(reminderTime))
|
||||
Log.i("ReminderHelper",
|
||||
String.format("Setting alarm (%s): %s", time, name))
|
||||
Log.i(
|
||||
"ReminderHelper",
|
||||
String.format("Setting alarm (%s): %s", time, name)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,111 +19,138 @@
|
||||
|
||||
package org.isoron.uhabits.intents
|
||||
|
||||
import android.app.*
|
||||
import android.app.PendingIntent.*
|
||||
import android.content.*
|
||||
import android.net.*
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.receivers.*
|
||||
import javax.inject.*
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.PendingIntent.getBroadcast
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import org.isoron.uhabits.receivers.ReminderReceiver
|
||||
import org.isoron.uhabits.receivers.WidgetReceiver
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class PendingIntentFactory
|
||||
@Inject constructor(
|
||||
@AppContext private val context: Context,
|
||||
private val intentFactory: IntentFactory) {
|
||||
@AppContext private val context: Context,
|
||||
private val intentFactory: IntentFactory
|
||||
) {
|
||||
|
||||
fun addCheckmark(habit: Habit, timestamp: Timestamp?): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 1,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_ADD_REPETITION
|
||||
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
1,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_ADD_REPETITION
|
||||
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun dismissNotification(habit: Habit): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 0,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
||||
data = Uri.parse(habit.uriString)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
||||
data = Uri.parse(habit.uriString)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun removeRepetition(habit: Habit): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 3,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
||||
data = Uri.parse(habit.uriString)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
3,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
||||
data = Uri.parse(habit.uriString)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun showHabit(habit: Habit): PendingIntent =
|
||||
androidx.core.app.TaskStackBuilder
|
||||
.create(context)
|
||||
.addNextIntentWithParentStack(
|
||||
intentFactory.startShowHabitActivity(
|
||||
context, habit))
|
||||
.getPendingIntent(0, FLAG_UPDATE_CURRENT)!!
|
||||
|
||||
fun showReminder(habit: Habit,
|
||||
reminderTime: Long?,
|
||||
timestamp: Long): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
androidx.core.app.TaskStackBuilder
|
||||
.create(context)
|
||||
.addNextIntentWithParentStack(
|
||||
intentFactory.startShowHabitActivity(
|
||||
context,
|
||||
(habit.id!! % Integer.MAX_VALUE).toInt() + 1,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
action = ReminderReceiver.ACTION_SHOW_REMINDER
|
||||
data = Uri.parse(habit.uriString)
|
||||
putExtra("timestamp", timestamp)
|
||||
putExtra("reminderTime", reminderTime)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
habit
|
||||
)
|
||||
)
|
||||
.getPendingIntent(0, FLAG_UPDATE_CURRENT)!!
|
||||
|
||||
fun showReminder(
|
||||
habit: Habit,
|
||||
reminderTime: Long?,
|
||||
timestamp: Long
|
||||
): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
(habit.id!! % Integer.MAX_VALUE).toInt() + 1,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
action = ReminderReceiver.ACTION_SHOW_REMINDER
|
||||
data = Uri.parse(habit.uriString)
|
||||
putExtra("timestamp", timestamp)
|
||||
putExtra("reminderTime", reminderTime)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun snoozeNotification(habit: Habit): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 0,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
Intent(context, ReminderReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun toggleCheckmark(habit: Habit, timestamp: Long?): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 2,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun setNumericalValue(widgetContext: Context,
|
||||
habit: Habit,
|
||||
numericalValue: Int,
|
||||
timestamp: Long?):
|
||||
PendingIntent =
|
||||
fun setNumericalValue(
|
||||
widgetContext: Context,
|
||||
habit: Habit,
|
||||
numericalValue: Int,
|
||||
timestamp: Long?
|
||||
):
|
||||
PendingIntent =
|
||||
getBroadcast(
|
||||
widgetContext, 2,
|
||||
Intent(widgetContext, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_SET_NUMERICAL_VALUE
|
||||
putExtra("numericalValue", numericalValue);
|
||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
widgetContext,
|
||||
2,
|
||||
Intent(widgetContext, WidgetReceiver::class.java).apply {
|
||||
data = Uri.parse(habit.uriString)
|
||||
action = WidgetReceiver.ACTION_SET_NUMERICAL_VALUE
|
||||
putExtra("numericalValue", numericalValue)
|
||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
fun updateWidgets(): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, 0,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
||||
},
|
||||
FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
Intent(context, WidgetReceiver::class.java).apply {
|
||||
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
||||
},
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
package org.isoron.uhabits.io
|
||||
|
||||
import android.util.*
|
||||
import org.isoron.uhabits.core.io.*
|
||||
import android.util.Log
|
||||
import org.isoron.uhabits.core.io.Logger
|
||||
import org.isoron.uhabits.core.io.Logging
|
||||
|
||||
class AndroidLogging : Logging {
|
||||
override fun getLogger(name: String): Logger {
|
||||
@@ -44,5 +45,4 @@ class AndroidLogger(val name: String) : Logger {
|
||||
override fun error(exception: Exception) {
|
||||
Log.e(name, "Exception", exception)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,31 +19,37 @@
|
||||
|
||||
package org.isoron.uhabits.notifications
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.graphics.BitmapFactory.*
|
||||
import android.os.*
|
||||
import android.os.Build.VERSION.*
|
||||
import android.util.*
|
||||
import androidx.core.app.*
|
||||
import androidx.core.app.NotificationCompat.*
|
||||
import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory.decodeResource
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat.Action
|
||||
import androidx.core.app.NotificationCompat.Builder
|
||||
import androidx.core.app.NotificationCompat.WearableExtender
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.ui.NotificationTray
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import org.isoron.uhabits.intents.PendingIntentFactory
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class AndroidNotificationTray
|
||||
@Inject constructor(
|
||||
@AppContext private val context: Context,
|
||||
private val pendingIntents: PendingIntentFactory,
|
||||
private val preferences: Preferences,
|
||||
private val ringtoneManager: RingtoneManager
|
||||
@AppContext private val context: Context,
|
||||
private val pendingIntents: PendingIntentFactory,
|
||||
private val preferences: Preferences,
|
||||
private val ringtoneManager: RingtoneManager
|
||||
) : NotificationTray.SystemTray {
|
||||
private var active = HashSet<Int>()
|
||||
|
||||
@@ -57,10 +63,12 @@ class AndroidNotificationTray
|
||||
active.remove(id)
|
||||
}
|
||||
|
||||
override fun showNotification(habit: Habit,
|
||||
notificationId: Int,
|
||||
timestamp: Timestamp,
|
||||
reminderTime: Long) {
|
||||
override fun showNotification(
|
||||
habit: Habit,
|
||||
notificationId: Int,
|
||||
timestamp: Timestamp,
|
||||
reminderTime: Long
|
||||
) {
|
||||
val notificationManager = NotificationManagerCompat.from(context)
|
||||
val notification = buildNotification(habit, reminderTime, timestamp)
|
||||
createAndroidNotificationChannel(context)
|
||||
@@ -68,37 +76,45 @@ class AndroidNotificationTray
|
||||
notificationManager.notify(notificationId, notification)
|
||||
} catch (e: RuntimeException) {
|
||||
// Some Xiaomi phones produce a RuntimeException if custom notification sounds are used.
|
||||
Log.i("AndroidNotificationTray",
|
||||
"Failed to show notification. Retrying without sound.")
|
||||
val n = buildNotification(habit,
|
||||
reminderTime,
|
||||
timestamp,
|
||||
disableSound = true)
|
||||
Log.i(
|
||||
"AndroidNotificationTray",
|
||||
"Failed to show notification. Retrying without sound."
|
||||
)
|
||||
val n = buildNotification(
|
||||
habit,
|
||||
reminderTime,
|
||||
timestamp,
|
||||
disableSound = true
|
||||
)
|
||||
notificationManager.notify(notificationId, n)
|
||||
|
||||
}
|
||||
active.add(notificationId)
|
||||
}
|
||||
|
||||
fun buildNotification(habit: Habit,
|
||||
reminderTime: Long,
|
||||
timestamp: Timestamp,
|
||||
disableSound: Boolean = false): Notification {
|
||||
fun buildNotification(
|
||||
habit: Habit,
|
||||
reminderTime: Long,
|
||||
timestamp: Timestamp,
|
||||
disableSound: Boolean = false
|
||||
): Notification {
|
||||
|
||||
val addRepetitionAction = Action(
|
||||
R.drawable.ic_action_check,
|
||||
context.getString(R.string.yes),
|
||||
pendingIntents.addCheckmark(habit, timestamp))
|
||||
R.drawable.ic_action_check,
|
||||
context.getString(R.string.yes),
|
||||
pendingIntents.addCheckmark(habit, timestamp)
|
||||
)
|
||||
|
||||
val removeRepetitionAction = Action(
|
||||
R.drawable.ic_action_cancel,
|
||||
context.getString(R.string.no),
|
||||
pendingIntents.removeRepetition(habit))
|
||||
R.drawable.ic_action_cancel,
|
||||
context.getString(R.string.no),
|
||||
pendingIntents.removeRepetition(habit)
|
||||
)
|
||||
|
||||
val enterAction = Action(
|
||||
R.drawable.ic_action_check,
|
||||
context.getString(R.string.enter),
|
||||
pendingIntents.setNumericalValue(context, habit, 0, null))
|
||||
R.drawable.ic_action_check,
|
||||
context.getString(R.string.enter),
|
||||
pendingIntents.setNumericalValue(context, habit, 0, null)
|
||||
)
|
||||
|
||||
val wearableBg = decodeResource(context.resources, R.drawable.stripe)
|
||||
|
||||
@@ -109,26 +125,26 @@ class AndroidNotificationTray
|
||||
|
||||
val defaultText = context.getString(R.string.default_reminder_question)
|
||||
val builder = Builder(context, REMINDERS_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(habit.name)
|
||||
.setContentText(if(habit.question.isBlank()) defaultText else habit.question)
|
||||
.setContentIntent(pendingIntents.showHabit(habit))
|
||||
.setDeleteIntent(pendingIntents.dismissNotification(habit))
|
||||
.setSound(null)
|
||||
.setWhen(reminderTime)
|
||||
.setShowWhen(true)
|
||||
.setOngoing(preferences.shouldMakeNotificationsSticky())
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(habit.name)
|
||||
.setContentText(if (habit.question.isBlank()) defaultText else habit.question)
|
||||
.setContentIntent(pendingIntents.showHabit(habit))
|
||||
.setDeleteIntent(pendingIntents.dismissNotification(habit))
|
||||
.setSound(null)
|
||||
.setWhen(reminderTime)
|
||||
.setShowWhen(true)
|
||||
.setOngoing(preferences.shouldMakeNotificationsSticky())
|
||||
|
||||
if (habit.isNumerical) {
|
||||
wearableExtender.addAction(enterAction)
|
||||
builder.addAction(enterAction)
|
||||
} else {
|
||||
wearableExtender
|
||||
.addAction(addRepetitionAction)
|
||||
.addAction(removeRepetitionAction)
|
||||
.addAction(addRepetitionAction)
|
||||
.addAction(removeRepetitionAction)
|
||||
builder
|
||||
.addAction(addRepetitionAction)
|
||||
.addAction(removeRepetitionAction)
|
||||
.addAction(addRepetitionAction)
|
||||
.addAction(removeRepetitionAction)
|
||||
}
|
||||
|
||||
if (!disableSound)
|
||||
@@ -137,9 +153,11 @@ class AndroidNotificationTray
|
||||
if (preferences.shouldMakeNotificationsLed())
|
||||
builder.setLights(Color.RED, 1000, 1000)
|
||||
|
||||
val snoozeAction = Action(R.drawable.ic_action_snooze,
|
||||
context.getString(R.string.snooze),
|
||||
pendingIntents.snoozeNotification(habit))
|
||||
val snoozeAction = Action(
|
||||
R.drawable.ic_action_snooze,
|
||||
context.getString(R.string.snooze),
|
||||
pendingIntents.snoozeNotification(habit)
|
||||
)
|
||||
wearableExtender.addAction(snoozeAction)
|
||||
builder.addAction(snoozeAction)
|
||||
|
||||
@@ -151,14 +169,15 @@ class AndroidNotificationTray
|
||||
private const val REMINDERS_CHANNEL_ID = "REMINDERS"
|
||||
fun createAndroidNotificationChannel(context: Context) {
|
||||
val notificationManager = context.getSystemService(Activity.NOTIFICATION_SERVICE)
|
||||
as NotificationManager
|
||||
as NotificationManager
|
||||
if (SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(REMINDERS_CHANNEL_ID,
|
||||
context.resources.getString(R.string.reminder),
|
||||
NotificationManager.IMPORTANCE_DEFAULT)
|
||||
val channel = NotificationChannel(
|
||||
REMINDERS_CHANNEL_ID,
|
||||
context.resources.getString(R.string.reminder),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,22 +19,25 @@
|
||||
|
||||
package org.isoron.uhabits.notifications
|
||||
|
||||
import android.content.*
|
||||
import android.media.RingtoneManager.*
|
||||
import android.net.*
|
||||
import android.preference.*
|
||||
import android.provider.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.media.RingtoneManager.EXTRA_RINGTONE_PICKED_URI
|
||||
import android.media.RingtoneManager.getRingtone
|
||||
import android.net.Uri
|
||||
import android.preference.PreferenceManager
|
||||
import android.provider.Settings
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class RingtoneManager
|
||||
@Inject constructor(@AppContext private val context: Context) {
|
||||
|
||||
val prefs: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
fun getName(): String? {
|
||||
try {
|
||||
@@ -54,8 +57,10 @@ class RingtoneManager
|
||||
fun getURI(): Uri? {
|
||||
var ringtoneUri: Uri? = null
|
||||
val defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI
|
||||
val prefRingtoneUri = prefs.getString("pref_ringtone_uri",
|
||||
defaultRingtoneUri.toString())!!
|
||||
val prefRingtoneUri = prefs.getString(
|
||||
"pref_ringtone_uri",
|
||||
defaultRingtoneUri.toString()
|
||||
)!!
|
||||
if (prefRingtoneUri.isNotEmpty())
|
||||
ringtoneUri = Uri.parse(prefRingtoneUri)
|
||||
|
||||
|
||||
@@ -19,22 +19,23 @@
|
||||
|
||||
package org.isoron.uhabits.preferences
|
||||
|
||||
import android.content.*
|
||||
import android.preference.*
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.inject.*
|
||||
import javax.inject.*
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import javax.inject.Inject
|
||||
|
||||
@AppScope
|
||||
class SharedPreferencesStorage
|
||||
@Inject constructor(
|
||||
@AppContext context: Context
|
||||
@AppContext context: Context
|
||||
) : SharedPreferences.OnSharedPreferenceChangeListener, Preferences.Storage {
|
||||
|
||||
private val sharedPrefs: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
private var preferences: Preferences? = null
|
||||
|
||||
@@ -46,38 +47,40 @@ class SharedPreferencesStorage
|
||||
override fun clear() = sharedPrefs.edit().clear().apply()
|
||||
|
||||
override fun getBoolean(key: String, defValue: Boolean) =
|
||||
sharedPrefs.getBoolean(key, defValue)
|
||||
sharedPrefs.getBoolean(key, defValue)
|
||||
|
||||
override fun getInt(key: String, defValue: Int) =
|
||||
sharedPrefs.getInt(key, defValue)
|
||||
sharedPrefs.getInt(key, defValue)
|
||||
|
||||
override fun getLong(key: String, defValue: Long) =
|
||||
sharedPrefs.getLong(key, defValue)
|
||||
sharedPrefs.getLong(key, defValue)
|
||||
|
||||
override fun getString(key: String, defValue: String): String =
|
||||
sharedPrefs.getString(key, defValue)!!
|
||||
sharedPrefs.getString(key, defValue)!!
|
||||
|
||||
override fun onAttached(preferences: Preferences) {
|
||||
this.preferences = preferences
|
||||
}
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean) =
|
||||
sharedPrefs.edit().putBoolean(key, value).apply()
|
||||
sharedPrefs.edit().putBoolean(key, value).apply()
|
||||
|
||||
override fun putInt(key: String, value: Int) =
|
||||
sharedPrefs.edit().putInt(key, value).apply()
|
||||
sharedPrefs.edit().putInt(key, value).apply()
|
||||
|
||||
override fun putLong(key: String, value: Long) =
|
||||
sharedPrefs.edit().putLong(key, value).apply()
|
||||
sharedPrefs.edit().putLong(key, value).apply()
|
||||
|
||||
override fun putString(key: String, value: String) =
|
||||
sharedPrefs.edit().putString(key, value).apply()
|
||||
sharedPrefs.edit().putString(key, value).apply()
|
||||
|
||||
override fun remove(key: String) =
|
||||
sharedPrefs.edit().remove(key).apply()
|
||||
sharedPrefs.edit().remove(key).apply()
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences,
|
||||
key: String) {
|
||||
override fun onSharedPreferenceChanged(
|
||||
sharedPreferences: SharedPreferences,
|
||||
key: String
|
||||
) {
|
||||
val preferences = this.preferences ?: return
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
|
||||
when (key) {
|
||||
|
||||
@@ -19,12 +19,14 @@
|
||||
|
||||
package org.isoron.uhabits.sync
|
||||
|
||||
import android.content.*
|
||||
import android.net.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import org.isoron.uhabits.core.sync.NetworkManager
|
||||
|
||||
class AndroidNetworkManager(
|
||||
val context: Context,
|
||||
val context: Context,
|
||||
) : NetworkManager, ConnectivityManager.NetworkCallback() {
|
||||
|
||||
val listeners = mutableListOf<NetworkManager.Listener>()
|
||||
@@ -54,4 +56,4 @@ class AndroidNetworkManager(
|
||||
connected = false
|
||||
for (l in listeners) l.onNetworkLost()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,21 +19,32 @@
|
||||
|
||||
package org.isoron.uhabits.sync
|
||||
|
||||
import android.util.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.android.*
|
||||
import io.ktor.client.features.*
|
||||
import io.ktor.client.features.json.*
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.sync.*
|
||||
import android.util.Log
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.android.Android
|
||||
import io.ktor.client.features.ClientRequestException
|
||||
import io.ktor.client.features.ServerResponseException
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.put
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.invoke
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.sync.AbstractSyncServer
|
||||
import org.isoron.uhabits.core.sync.EditConflictException
|
||||
import org.isoron.uhabits.core.sync.GetDataVersionResponse
|
||||
import org.isoron.uhabits.core.sync.KeyNotFoundException
|
||||
import org.isoron.uhabits.core.sync.RegisterReponse
|
||||
import org.isoron.uhabits.core.sync.ServiceUnavailable
|
||||
import org.isoron.uhabits.core.sync.SyncData
|
||||
|
||||
class RemoteSyncServer(
|
||||
private val preferences: Preferences,
|
||||
private val httpClient: HttpClient = HttpClient(Android) {
|
||||
install(JsonFeature)
|
||||
}
|
||||
private val preferences: Preferences,
|
||||
private val httpClient: HttpClient = HttpClient(Android) {
|
||||
install(JsonFeature)
|
||||
}
|
||||
) : AbstractSyncServer {
|
||||
|
||||
override suspend fun register(): String = Dispatchers.IO {
|
||||
@@ -42,7 +53,7 @@ class RemoteSyncServer(
|
||||
Log.i("RemoteSyncServer", "POST $url")
|
||||
val response: RegisterReponse = httpClient.post(url)
|
||||
return@IO response.key
|
||||
} catch(e: ServerResponseException) {
|
||||
} catch (e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
}
|
||||
}
|
||||
@@ -59,8 +70,8 @@ class RemoteSyncServer(
|
||||
throw ServiceUnavailable()
|
||||
} catch (e: ClientRequestException) {
|
||||
Log.w("RemoteSyncServer", "ClientRequestException", e)
|
||||
if(e.message!!.contains("409")) throw EditConflictException()
|
||||
if(e.message!!.contains("404")) throw KeyNotFoundException()
|
||||
if (e.message!!.contains("409")) throw EditConflictException()
|
||||
if (e.message!!.contains("404")) throw KeyNotFoundException()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -85,7 +96,7 @@ class RemoteSyncServer(
|
||||
Log.i("RemoteSyncServer", "GET $url")
|
||||
val response: GetDataVersionResponse = httpClient.get(url)
|
||||
return@IO response.version
|
||||
} catch(e: ServerResponseException) {
|
||||
} catch (e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
} catch (e: ClientRequestException) {
|
||||
Log.w("RemoteSyncServer", "ClientRequestException", e)
|
||||
|
||||
@@ -55,4 +55,4 @@ object ColorUtils {
|
||||
val f2 = (color2 shr channel and 0xff).toFloat() * (1.0f - amount)
|
||||
return (fl + f2).toInt() and 0xff shl channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user