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