Compare commits

..

22 Commits

Author SHA1 Message Date
70dab74528 Bump version to 2.3.0 and update changelog 2025-06-23 22:23:55 -05:00
7e5d2fa207 Update Gradle and AGP versions to 8.11.1 and 8.9.2 2025-06-23 21:48:00 -05:00
0e432fb332 HistoryWidget: Increase padding; update widget images 2025-06-23 21:47:47 -05:00
897a236501 Widgets: Update test images 2025-03-23 19:41:31 -05:00
0cccecec77 Widgets: Update test images 2025-03-23 19:27:12 -05:00
f1ed875256 Further increase widget corner radius to match current Android style 2025-03-23 17:10:39 -05:00
e82bd47aab Increase minimum widget size to 50x50 and 100x100
Some Samsung phones were allowing graph widgets to occupy 1x2 or
2x1 grid cells, leading to very small text. This commit bumps up
the minimum widget size to 100x100 to ensure they always occupy at
least 2x2 cells. Tested on Pixel 4, Pixel 7 and Samsung Galaxy S24.

Closes #2118
2025-03-23 16:52:23 -05:00
e9517f7378 Bump targetSdk to 36 2025-03-23 07:22:36 -05:00
12cc70a51a Confetti: Always emit from checkmark, not popup button 2025-03-23 07:06:45 -05:00
fa670b19b7 HabitCardView: Fix confetti position in API 36+ 2025-03-22 23:03:59 -05:00
45b100aad9 build.sh: Make output dir 2025-03-22 15:26:28 -05:00
3c0c0b77ff build.sh: Update emulator path 2025-03-22 15:19:37 -05:00
66fa56ea62 Merge branch 'improve-gradle' into dev 2025-03-21 22:16:29 -05:00
951dabea8b Merge branch 'isse_1857_reset_measurable_entry' into dev 2025-03-21 21:41:55 -05:00
76b9dd8bd9 Checkmark popup: Minor changes to layout 2025-03-21 21:41:23 -05:00
f68510f860 Allow user to disable confetti animation 2025-03-21 21:29:49 -05:00
kalina559
cc720e3dcb Removed an unnecessary change 2025-02-18 23:19:20 +01:00
kalina559
6e3d06cff9 Corrected formatting in the layout file 2025-02-18 22:32:48 +01:00
kalina559
d458cbd47a COrrected formatting 2025-02-18 20:51:16 +01:00
kalina559
74ce269446 Added 'UNKNOWN' button for measurable habits 2025-02-18 20:47:12 +01:00
Jimly Asshiddiqy
9eb8624863 Migrate to KSP 2025-02-17 09:56:21 +07:00
Jimly Asshiddiqy
c4bc301fb2 Implement version catalog 2025-02-14 16:56:04 +07:00
49 changed files with 320 additions and 184 deletions

View File

@@ -1,5 +1,21 @@
# Changelog # Changelog
## [2.3.0] -- 2025-06-23
### Added
- Add support for Android 15 and 16 (@iSoron)
- Show confetti animation (@gokulk16, @iSoron, #1743)
- Show streaks for measurable habits (@teckwarz, #2059)
- Allow user to unset measurable habits (@leontodd, @kalina559, #1899, #2109)
### Changed
- Change background widget color for habits with implicit checks (@wobbba, #1915)
### Fixed
- Fix notification when goal type is set to maximum (@manish99verma, #1931)
- Never mark "at most" habits as completed for the day (@kalina559, #2077)
- Increase minimum widget size (@iSoron, #2118)
- Improve Gradle configuration (@jimlyas, #2108)
## [2.2.0] -- 2024-01-30 ## [2.2.0] -- 2024-01-30
### Added ### Added
- Add support for Android 14 (@iSoron, @hiqua) - Add support for Android 14 (@iSoron, @hiqua)

View File

@@ -1,23 +1,11 @@
plugins { plugins {
val kotlinVersion = "2.1.10" alias(libs.plugins.agp) apply false
id("com.android.application") version "8.8.0" apply (false) alias(libs.plugins.kotlin.android) apply false
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false) alias(libs.plugins.ksp) apply false
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false) alias(libs.plugins.ktlint.plugin) apply false
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false) alias(libs.plugins.shadow) apply false
id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
} }
apply { apply {
from("translators.gradle.kts") from("gradle/translators.gradle.kts")
}
allprojects {
repositories {
google()
mavenCentral()
maven(url = "https://plugins.gradle.org/m2/")
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
maven(url = "https://jitpack.io")
maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers/") // Repository for kotlin-css-jvm old versions, now that the Gradle Plugin Portal no longer brings these in by mirroring JCenter
}
} }

View File

@@ -21,7 +21,7 @@ ADB="${ANDROID_HOME}/platform-tools/adb"
ANDROID_OUTPUTS_DIR="uhabits-android/build/outputs" ANDROID_OUTPUTS_DIR="uhabits-android/build/outputs"
AVDMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager" AVDMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager"
AVD_PREFIX="uhabitsTest" AVD_PREFIX="uhabitsTest"
EMULATOR="${ANDROID_HOME}/tools/emulator" EMULATOR="${ANDROID_HOME}/emulator/emulator"
GRADLE="./gradlew --stacktrace --quiet" GRADLE="./gradlew --stacktrace --quiet"
PACKAGE_NAME=org.isoron.uhabits PACKAGE_NAME=org.isoron.uhabits
SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager"
@@ -38,6 +38,11 @@ if [ ! -f "${ANDROID_HOME}/platform-tools/adb" ]; then
exit 1 exit 1
fi fi
if [ ! -f "$EMULATOR" ]; then
echo "Error: Not found: $EMULATOR"
exit 1
fi
# Logging # Logging
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@@ -217,6 +222,7 @@ android_test_parallel() {
for API in $*; do for API in $*; do
( (
LOG=build/android-test-$API.log LOG=build/android-test-$API.log
mkdir -p build
log_info "API $API: Running tests..." log_info "API $API: Running tests..."
android_test $API 1>$LOG 2>&1 android_test $API 1>$LOG 2>&1
ret_code=$? ret_code=$?

View File

@@ -6,3 +6,6 @@ android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false android.nonTransitiveRClass=false
android.nonFinalResIds=false android.nonFinalResIds=false
org.gradle.configureondemand=true
org.gradle.warning.mode=all
org.gradle.caching=true

102
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,102 @@
[versions]
agp = "8.9.2"
annotation = "1.9.1"
appcompat = "1.7.0"
appintro = "6.3.1"
commonsCodec = "1.16.0"
commonsIo = "1.3.2"
commonsLang3 = "3.14.0"
dagger = "2.55"
desugar = "2.1.4"
dexmaker = "2.28.3"
espresso = "3.6.1"
guava = "33.2.1-android"
hamcrest = "2.2"
jsr250 = "1.0"
jsr305 = "3.0.2"
junit = "1.2.1"
junitJupiter = "5.10.1"
junitVersion = "4.13.2"
konfetti-xml = "2.0.2"
kotlin = "2.1.10"
kotlinxCoroutinesCoreCommon = "1.3.8"
ksp = "2.1.10-1.0.30"
ktlint-plugin = "11.6.1"
ktor = "1.6.8"
ktxCoroutine = "1.10.1"
legacy-support = "1.0.0"
material = "1.12.0"
mockito-kotlin = "5.4.0"
opencsv = "5.9"
rules = "1.6.1"
shadow = "8.1.1"
sqliteJdbc = "3.45.1.0"
uiautomator = "2.3.0"
[libraries]
annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
appIntro = { group = "com.github.AppIntro", name = "AppIntro", version.ref = "appintro" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
commons-codec = { module = "commons-codec:commons-codec", version.ref = "commonsCodec" }
commons-io = { module = "org.apache.commons:commons-io", version.ref = "commonsIo" }
commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commonsLang3" }
dagger = { group = "com.google.dagger", name = "dagger", version.ref = "dagger" }
dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "dagger" }
desugar_jdk_libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugar" }
dexmaker-mockito = { group = "com.linkedin.dexmaker", name = "dexmaker-mockito", version.ref = "dexmaker" }
espresso-contrib = { group = "androidx.test.espresso", name = "espresso-contrib", version.ref = "espresso" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" }
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
jsr250-api = { group = "javax.annotation", name = "jsr250-api", version.ref = "jsr250" }
jsr305 = { group = "com.google.code.findbugs", name = "jsr305", version.ref = "jsr305" }
junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" }
junit-junit = { module = "junit:junit", version.ref = "junitVersion" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junitJupiter" }
konfetti-xml = { group = "nl.dionsegijn", name = "konfetti-xml", version.ref = "konfetti-xml" }
kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "ktxCoroutine" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "ktxCoroutine" }
kotlinx-coroutines-core-common = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common", version.ref = "kotlinxCoroutinesCoreCommon" }
kotlinx-coroutines-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", version.ref = "ktxCoroutine" }
ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" }
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
ktor-client-jackson = { group = "io.ktor", name = "ktor-client-jackson", version.ref = "ktor" }
ktor-client-json = { group = "io.ktor", name = "ktor-client-json", version.ref = "ktor" }
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor" }
ktor-jackson = { group = "io.ktor", name = "ktor-jackson", version.ref = "ktor" }
legacy-preference-v14 = { group = "androidx.legacy", name = "legacy-preference-v14", version.ref = "legacy-support" }
legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacy-support" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockito-kotlin" }
opencsv = { group = "com.opencsv", name = "opencsv", version.ref = "opencsv" }
rules = { group = "androidx.test", name = "rules", version.ref = "rules" }
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqliteJdbc" }
uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
[bundles]
androidTest = [
"annotation",
"dagger",
"dexmaker-mockito",
"espresso-contrib",
"espresso-core",
"junit",
"ktor-client-mock",
"ktor-jackson",
"mockito-kotlin",
"rules",
"uiautomator"
]
test = [
"dagger",
"junit-junit",
"mockito-kotlin",
]
[plugins]
agp = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
ktlint-plugin = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-plugin" }
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,13 +1,32 @@
pluginManagement { pluginManagement {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
google() google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
} }
resolutionStrategy.eachPlugin {
if (requested.id.id == "com.android.application") {
useModule("com.android.tools.build:gradle:${requested.version}")
} }
} }
} }
include(":uhabits-android", ":uhabits-core") include(":uhabits-android", ":uhabits-core")
dependencyResolutionManagement {
@Suppress("UnstableApiUsage")
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
maven(url = "https://plugins.gradle.org/m2/")
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
maven(url = "https://jitpack.io")
}
}
include(":uhabits-android", ":uhabits-core")

View File

@@ -18,10 +18,10 @@
*/ */
plugins { plugins {
id("com.android.application") version "8.8.0" alias(libs.plugins.agp)
id("org.jetbrains.kotlin.android") alias(libs.plugins.kotlin.android)
id("org.jetbrains.kotlin.kapt") alias(libs.plugins.ksp)
id("org.jlleitschuh.gradle.ktlint") alias(libs.plugins.ktlint.plugin)
} }
tasks.compileLint { tasks.compileLint {
@@ -40,16 +40,14 @@ kotlin {
} }
android { android {
namespace = "org.isoron.uhabits" namespace = "org.isoron.uhabits"
compileSdk = 35 compileSdk = 36
// compileSdkPreview = "VanillaIceCream"
defaultConfig { defaultConfig {
versionCode = 20200 versionCode = 20300
versionName = "2.2.0" versionName = "2.3.0"
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 36
applicationId = "org.isoron.uhabits" applicationId = "org.isoron.uhabits"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -66,7 +64,7 @@ android {
} }
buildTypes { buildTypes {
getByName("release") { release {
isMinifyEnabled = true isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.txt") proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.txt")
if (signingConfigs.findByName("release") != null) { if (signingConfigs.findByName("release") != null) {
@@ -74,8 +72,8 @@ android {
} }
} }
getByName("debug") { debug {
isTestCoverageEnabled = true enableUnitTestCoverage = true
} }
} }
@@ -84,64 +82,35 @@ android {
targetCompatibility(JavaVersion.VERSION_11) targetCompatibility(JavaVersion.VERSION_11)
sourceCompatibility(JavaVersion.VERSION_11) sourceCompatibility(JavaVersion.VERSION_11)
} }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
buildFeatures { kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
viewBinding = true buildFeatures.viewBinding = true
} lint.abortOnError = false
lint {
abortOnError = false
}
} }
dependencies { dependencies {
val daggerVersion = "2.51.1" compileOnly(libs.jsr250.api)
val kotlinVersion = "2.1.10" coreLibraryDesugaring(libs.desugar.jdk.libs)
val kxCoroutinesVersion = "1.10.1" implementation(libs.appIntro)
val ktorVersion = "1.6.8" implementation(libs.jsr305)
val espressoVersion = "3.6.1" implementation(libs.dagger)
implementation(libs.guava)
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion") implementation(libs.ktor.client.android)
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion") implementation(libs.ktor.client.core)
androidTestImplementation("com.google.dagger:dagger:$daggerVersion") implementation(libs.ktor.client.jackson)
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.3") implementation(libs.ktor.client.json)
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion") implementation(libs.kotlin.stdlib.jdk8)
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion") implementation(libs.kotlinx.coroutines.android)
androidTestImplementation("androidx.annotation:annotation:1.7.1") implementation(libs.kotlinx.coroutines.core)
androidTestImplementation("androidx.test.ext:junit:1.2.1") implementation(libs.appcompat)
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0") implementation(libs.legacy.preference.v14)
androidTestImplementation("androidx.test:rules:1.6.1") implementation(libs.legacy.support.v4)
androidTestImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0") implementation(libs.material)
compileOnly("javax.annotation:jsr250-api:1.0") implementation(libs.opencsv)
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") implementation(libs.konfetti.xml)
implementation("com.github.AppIntro:AppIntro:6.3.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.google.dagger:dagger:$daggerVersion")
implementation("com.google.guava:guava:33.1.0-android")
implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
implementation("io.ktor:ktor-client-json:$ktorVersion")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("com.google.android.material:material:1.12.0")
implementation("com.opencsv:opencsv:5.9")
implementation("nl.dionsegijn:konfetti-xml:2.0.2")
implementation(project(":uhabits-core")) implementation(project(":uhabits-core"))
kapt("com.google.dagger:dagger-compiler:$daggerVersion") ksp(libs.dagger.compiler)
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
testImplementation("com.google.dagger:dagger:$daggerVersion")
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
}
kapt { androidTestImplementation(libs.bundles.androidTest)
correctErrorTypes = true testImplementation(libs.bundles.test)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -33,11 +33,10 @@ 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.databinding.CheckmarkPopupBinding import org.isoron.uhabits.databinding.CheckmarkPopupBinding
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
import org.isoron.uhabits.utils.getCenter
import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.sres
class CheckmarkDialog : AppCompatDialogFragment() { class CheckmarkDialog : AppCompatDialogFragment() {
var onToggle: (Int, String, Float, Float) -> Unit = { _, _, _, _ -> } var onToggle: (Int, String) -> Unit = { _, _ -> }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val appComponent = (requireActivity().application as HabitsApplication).component val appComponent = (requireActivity().application as HabitsApplication).component
@@ -64,8 +63,7 @@ class CheckmarkDialog : AppCompatDialogFragment() {
} }
fun onClick(v: Int) { fun onClick(v: Int) {
val notes = view.notes.text.toString().trim() val notes = view.notes.text.toString().trim()
val location = view.yesBtn.getCenter() onToggle(v, notes)
onToggle(v, notes, location.x, location.y)
requireDialog().dismiss() requireDialog().dismiss()
} }
view.yesBtn.setOnClickListener { onClick(YES_MANUAL) } view.yesBtn.setOnClickListener { onClick(YES_MANUAL) }

View File

@@ -25,7 +25,7 @@ import java.text.ParseException
class NumberDialog : AppCompatDialogFragment() { class NumberDialog : AppCompatDialogFragment() {
var onToggle: (Double, String, Float, Float) -> Unit = { _, _, _, _ -> } var onToggle: (Double, String) -> Unit = { _, _ -> }
var onDismiss: () -> Unit = {} var onDismiss: () -> Unit = {}
private var originalNotes: String = "" private var originalNotes: String = ""
@@ -36,16 +36,17 @@ class NumberDialog : AppCompatDialogFragment() {
val appComponent = (requireActivity().application as HabitsApplication).component val appComponent = (requireActivity().application as HabitsApplication).component
val prefs = appComponent.preferences val prefs = appComponent.preferences
view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)) view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context))
arrayOf(view.yesBtn, view.skipBtn).forEach { arrayOf(view.yesBtn).forEach {
it.setTextColor(requireArguments().getInt("color")) it.setTextColor(requireArguments().getInt("color"))
} }
arrayOf(view.noBtn, view.unknownBtn).forEach { arrayOf(view.noBtn, view.unknownBtnNumber).forEach {
it.setTextColor(view.root.sres.getColor(R.attr.contrast60)) it.setTextColor(view.root.sres.getColor(R.attr.contrast60))
} }
arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach { arrayOf(view.yesBtn, view.noBtn, view.unknownBtnNumber).forEach {
it.typeface = InterfaceUtils.getFontAwesome(requireContext()) it.typeface = InterfaceUtils.getFontAwesome(requireContext())
} }
if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = View.GONE if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = View.GONE
if (!prefs.areQuestionMarksEnabled) view.unknownBtnNumber.visibility = View.GONE
view.numberButtons.visibility = View.VISIBLE view.numberButtons.visibility = View.VISIBLE
fixDecimalSeparator(view) fixDecimalSeparator(view)
originalNotes = requireArguments().getString("notes")!! originalNotes = requireArguments().getString("notes")!!
@@ -71,6 +72,12 @@ class NumberDialog : AppCompatDialogFragment() {
view.value.setText(DecimalFormat("#.###").format((Entry.SKIP.toDouble() / 1000))) view.value.setText(DecimalFormat("#.###").format((Entry.SKIP.toDouble() / 1000)))
save() save()
} }
view.unknownBtnNumber.setOnClickListener {
view.value.setText(DecimalFormat("#.###").format((Entry.UNKNOWN.toDouble() / 1000)))
save()
}
view.notes.setOnEditorActionListener { v, actionId, event -> view.notes.setOnEditorActionListener { v, actionId, event ->
save() save()
true true
@@ -115,7 +122,7 @@ class NumberDialog : AppCompatDialogFragment() {
} }
val notes = view.notes.text.toString() val notes = view.notes.text.toString()
val location = view.saveBtn.getCenter() val location = view.saveBtn.getCenter()
onToggle(value, notes, location.x, location.y) onToggle(value, notes)
requireDialog().dismiss() requireDialog().dismiss()
} }
} }

View File

@@ -180,7 +180,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
val timestamp = intent.extras?.getLong("timestamp") val timestamp = intent.extras?.getLong("timestamp")
if (habitId != null && timestamp != null) { if (habitId != null && timestamp != null) {
val habit = appComponent.habitList.getById(habitId)!! val habit = appComponent.habitList.getById(habitId)!!
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp)) component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp), 0f, 0f)
} }
} }
intent = null intent = null

View File

@@ -224,6 +224,8 @@ class ListHabitsScreen
} }
override fun showConfetti(color: PaletteColor, x: Float, y: Float) { override fun showConfetti(color: PaletteColor, x: Float, y: Float) {
if (x == 0f && y == 0f) return
if (preferences.isConfettiAnimationDisabled) return
val baseColor = themeSwitcher.currentTheme!!.color(color).toInt() val baseColor = themeSwitcher.currentTheme!!.color(color).toInt()
rootView.get().konfettiView.start( rootView.get().konfettiView.start(
Party( Party(
@@ -267,7 +269,7 @@ class ListHabitsScreen
putDouble("value", value) putDouble("value", value)
putString("notes", notes) putString("notes", notes)
} }
dialog.onToggle = { v, n, x, y -> callback.onNumberPicked(v, n, x, y) } dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) }
dialog.dismissCurrentAndShow(fm, "numberDialog") dialog.dismissCurrentAndShow(fm, "numberDialog")
} }
@@ -285,7 +287,7 @@ class ListHabitsScreen
putInt("value", selectedValue) putInt("value", selectedValue)
putString("notes", notes) putString("notes", notes)
} }
dialog.onToggle = { v, n, x, y -> callback.onNotesSaved(v, n, x, y) } dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) }
dialog.dismissCurrentAndShow(fm, "checkmarkDialog") dialog.dismissCurrentAndShow(fm, "checkmarkDialog")
} }

View File

@@ -169,7 +169,8 @@ class HabitCardView(
} }
onEdit = { timestamp -> onEdit = { timestamp ->
triggerRipple(timestamp) triggerRipple(timestamp)
habit?.let { behavior.onEdit(it, timestamp) } val location = getAbsoluteButtonLocation(timestamp)
habit?.let { behavior.onEdit(it, timestamp, location.x, location.y) }
} }
} }
@@ -177,7 +178,8 @@ class HabitCardView(
visibility = GONE visibility = GONE
onEdit = { timestamp -> onEdit = { timestamp ->
triggerRipple(timestamp) triggerRipple(timestamp)
habit?.let { behavior.onEdit(it, timestamp) } val location = getAbsoluteButtonLocation(timestamp)
habit?.let { behavior.onEdit(it, timestamp, location.x, location.y) }
} }
} }
@@ -224,9 +226,13 @@ class HabitCardView(
private fun getRelativeButtonLocation(timestamp: Timestamp): PointF { private fun getRelativeButtonLocation(timestamp: Timestamp): PointF {
val today = DateUtils.getTodayWithOffset() val today = DateUtils.getTodayWithOffset()
val offset = timestamp.daysUntil(today) - dataOffset val offset = timestamp.daysUntil(today) - dataOffset
val button = checkmarkPanel.buttons[offset] val panel = when (habit!!.isNumerical) {
true -> numberPanel
false -> checkmarkPanel
}
val button = panel.buttons[offset]
val y = button.height / 2.0f val y = button.height / 2.0f
val x = checkmarkPanel.x + button.x + (button.width / 2).toFloat() val x = panel.x + button.x + (button.width / 2).toFloat()
return PointF(x, y) return PointF(x, y)
} }
@@ -234,9 +240,15 @@ class HabitCardView(
val containerLocation = IntArray(2) val containerLocation = IntArray(2)
this.getLocationOnScreen(containerLocation) this.getLocationOnScreen(containerLocation)
val relButtonLocation = getRelativeButtonLocation(timestamp) val relButtonLocation = getRelativeButtonLocation(timestamp)
val windowInsets = rootWindowInsets
val statusBarHeight = if (SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
windowInsets?.systemWindowInsetTop ?: 0
} else {
0
}
return PointF( return PointF(
containerLocation[0].toFloat() + relButtonLocation.x, containerLocation[0].toFloat() + relButtonLocation.x,
containerLocation[1].toFloat() - relButtonLocation.y containerLocation[1].toFloat() + relButtonLocation.y - statusBarHeight
) )
} }

View File

@@ -181,7 +181,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
putDouble("value", value) putDouble("value", value)
putString("notes", notes) putString("notes", notes)
} }
dialog.onToggle = { v, n, x, y -> callback.onNumberPicked(v, n, x, y) } dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) }
dialog.dismissCurrentAndShow(supportFragmentManager, "numberDialog") dialog.dismissCurrentAndShow(supportFragmentManager, "numberDialog")
} }
@@ -198,7 +198,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
putInt("value", selectedValue) putInt("value", selectedValue)
putString("notes", notes) putString("notes", notes)
} }
dialog.onToggle = { v, n, x, y -> callback.onNotesSaved(v, n, x, y) } dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) }
dialog.dismissCurrentAndShow(supportFragmentManager, "checkmarkDialog") dialog.dismissCurrentAndShow(supportFragmentManager, "checkmarkDialog")
} }

View File

@@ -75,7 +75,8 @@ class HistoryWidget(
firstWeekday = prefs.firstWeekday, firstWeekday = prefs.firstWeekday,
series = listOf(), series = listOf(),
defaultSquare = HistoryChart.Square.OFF, defaultSquare = HistoryChart.Square.OFF,
notesIndicators = listOf() notesIndicators = listOf(),
padding = 2.5
) )
} }
).apply { ).apply {

View File

@@ -69,7 +69,7 @@ abstract class HabitWidgetView : FrameLayout {
val shadowRadius = dpToPixels(context, 2f).toInt() val shadowRadius = dpToPixels(context, 2f).toInt()
val shadowOffset = dpToPixels(context, 1f).toInt() val shadowOffset = dpToPixels(context, 1f).toInt()
val shadowColor = Color.argb(shadowAlpha, 0, 0, 0) val shadowColor = Color.argb(shadowAlpha, 0, 0, 0)
val cornerRadius = dpToPixels(context, 12f) val cornerRadius = dpToPixels(context, 18f)
val radii = FloatArray(8) val radii = FloatArray(8)
Arrays.fill(radii, cornerRadius) Arrays.fill(radii, cornerRadius)
val shape = RoundRectShape(radii, null, null) val shape = RoundRectShape(radii, null, null)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -28,7 +28,7 @@
<item android:id="@android:id/mask"> <item android:id="@android:id/mask">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<corners android:radius="12dp"/> <corners android:radius="18dp"/>
<solid android:color="?android:colorPrimary"/> <solid android:color="?android:colorPrimary"/>
</shape> </shape>
<color android:color="@color/white"/> <color android:color="@color/white"/>

View File

@@ -23,32 +23,32 @@
android:id="@+id/container" android:id="@+id/container"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="128dp" android:background="@drawable/checkmark_dialog_bg"
android:minWidth="208dp" android:minWidth="208dp"
app:divider="@drawable/checkmark_dialog_divider" android:minHeight="128dp"
app:showDividers="middle"
android:orientation="vertical" android:orientation="vertical"
android:background="@drawable/checkmark_dialog_bg"> app:divider="@drawable/checkmark_dialog_divider"
app:showDividers="middle">
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/notes" android:id="@+id/notes"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center"
android:inputType="textCapSentences|textMultiLine"
android:textSize="@dimen/smallTextSize"
android:padding="4dp"
android:background="@color/transparent" android:background="@color/transparent"
android:gravity="center"
android:hint="@string/notes" android:hint="@string/notes"
android:text="" /> android:inputType="textCapSentences|textMultiLine"
android:padding="4dp"
android:text=""
android:textSize="@dimen/smallTextSize" />
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/booleanButtons" android:id="@+id/booleanButtons"
android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:orientation="horizontal" android:orientation="horizontal"
android:visibility="gone"
app:divider="@drawable/checkmark_dialog_divider" app:divider="@drawable/checkmark_dialog_divider"
app:showDividers="middle"> app:showDividers="middle">
@@ -75,10 +75,10 @@
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/numberButtons" android:id="@+id/numberButtons"
android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:orientation="horizontal" android:orientation="horizontal"
android:visibility="gone"
app:divider="@drawable/checkmark_dialog_divider" app:divider="@drawable/checkmark_dialog_divider"
app:showDividers="middle"> app:showDividers="middle">
@@ -86,21 +86,26 @@
android:id="@+id/value" android:id="@+id/value"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="2"
android:background="@color/transparent" android:background="@color/transparent"
android:textAlignment="center"
android:inputType="numberDecimal" android:inputType="numberDecimal"
android:selectAllOnFocus="true" android:selectAllOnFocus="true"
android:textAlignment="center"
android:textSize="@dimen/smallTextSize" /> android:textSize="@dimen/smallTextSize" />
<TextView
android:id="@+id/saveBtn"
style="@style/NumericalPopupBtn"
android:text="@string/save" />
<TextView <TextView
android:id="@+id/skipBtnNumber" android:id="@+id/skipBtnNumber"
style="@style/NumericalPopupBtn" style="@style/NumericalPopupBtn"
android:text="@string/skip_day" /> android:text="@string/skip_day" />
<TextView <TextView
android:id="@+id/saveBtn" android:id="@+id/unknownBtnNumber"
style="@style/NumericalPopupBtn" style="@style/CheckmarkPopupBtn"
android:text="@string/save" /> android:text="@string/fa_question" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>

View File

@@ -233,4 +233,6 @@
<string name="activity_not_found">No app was found to support this action</string> <string name="activity_not_found">No app was found to support this action</string>
<string name="pref_midnight_delay_title">Extend day a few hours past midnight</string> <string name="pref_midnight_delay_title">Extend day a few hours past midnight</string>
<string name="pref_midnight_delay_description">Wait until 3:00 AM to show a new day. Useful if you typically go to sleep after midnight. Requires app restart.</string> <string name="pref_midnight_delay_description">Wait until 3:00 AM to show a new day. Useful if you typically go to sleep after midnight. Requires app restart.</string>
<string name="pref_animations_title">Disable animations</string>
<string name="pref_animations_description">Disable confetti animation after adding a checkmark.</string>
</resources> </resources>

View File

@@ -67,6 +67,13 @@
android:title="@string/use_pure_black" android:title="@string/use_pure_black"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_disable_animation"
android:summary="@string/pref_animations_description"
android:title="@string/pref_animations_title"
app:iconSpaceReserved="false" />
<ListPreference <ListPreference
android:defaultValue="255" android:defaultValue="255"
android:entries="@array/widget_opacity_entries" android:entries="@array/widget_opacity_entries"

View File

@@ -19,8 +19,8 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="40dp" android:minHeight="50dp"
android:minWidth="40dp" android:minWidth="50dp"
android:initialLayout="@layout/widget_wrapper" android:initialLayout="@layout/widget_wrapper"
android:previewImage="@drawable/widget_preview_checkmark" android:previewImage="@drawable/widget_preview_checkmark"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,10 +19,10 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="80dp" android:minHeight="100dp"
android:minWidth="80dp" android:minWidth="100dp"
android:minResizeWidth="80dp" android:minResizeWidth="100dp"
android:minResizeHeight="80dp" android:minResizeHeight="100dp"
android:initialLayout="@layout/widget_graph" android:initialLayout="@layout/widget_graph"
android:previewImage="@drawable/widget_preview_frequency" android:previewImage="@drawable/widget_preview_frequency"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,10 +19,10 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="80dp" android:minHeight="100dp"
android:minWidth="80dp" android:minWidth="100dp"
android:minResizeWidth="80dp" android:minResizeWidth="100dp"
android:minResizeHeight="80dp" android:minResizeHeight="100dp"
android:initialLayout="@layout/widget_graph" android:initialLayout="@layout/widget_graph"
android:previewImage="@drawable/widget_preview_history" android:previewImage="@drawable/widget_preview_history"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,10 +19,10 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="80dp" android:minHeight="100dp"
android:minWidth="80dp" android:minWidth="100dp"
android:minResizeWidth="80dp" android:minResizeWidth="100dp"
android:minResizeHeight="80dp" android:minResizeHeight="100dp"
android:initialLayout="@layout/widget_graph" android:initialLayout="@layout/widget_graph"
android:previewImage="@drawable/widget_preview_score" android:previewImage="@drawable/widget_preview_score"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,10 +19,10 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="80dp" android:minHeight="100dp"
android:minWidth="80dp" android:minWidth="100dp"
android:minResizeWidth="80dp" android:minResizeWidth="100dp"
android:minResizeHeight="80dp" android:minResizeHeight="100dp"
android:initialLayout="@layout/widget_graph" android:initialLayout="@layout/widget_graph"
android:previewImage="@drawable/widget_preview_streaks" android:previewImage="@drawable/widget_preview_streaks"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,10 +19,10 @@
--> -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="80dp" android:minHeight="100dp"
android:minWidth="80dp" android:minWidth="100dp"
android:minResizeWidth="80dp" android:minResizeWidth="100dp"
android:minResizeHeight="80dp" android:minResizeHeight="100dp"
android:initialLayout="@layout/widget_graph" android:initialLayout="@layout/widget_graph"
android:previewImage="@drawable/widget_preview_target" android:previewImage="@drawable/widget_preview_target"
android:resizeMode="vertical|horizontal" android:resizeMode="vertical|horizontal"

View File

@@ -19,7 +19,7 @@
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("org.jlleitschuh.gradle.ktlint") alias(libs.plugins.ktlint.plugin)
} }
kotlin { kotlin {
@@ -30,7 +30,7 @@ kotlin {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation(kotlin("stdlib-common")) implementation(kotlin("stdlib-common"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.8") implementation(libs.kotlinx.coroutines.core.common)
} }
} }
@@ -44,14 +44,14 @@ kotlin {
val jvmMain by getting { val jvmMain by getting {
dependencies { dependencies {
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
compileOnly("com.google.dagger:dagger:2.51.1") compileOnly(libs.dagger)
implementation("com.google.guava:guava:33.1.0-android") implementation(libs.guava)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1") implementation(libs.kotlinx.coroutines.core.jvm)
implementation("androidx.annotation:annotation:1.7.1") implementation(libs.annotation)
implementation("com.google.code.findbugs:jsr305:3.0.2") implementation(libs.jsr305)
implementation("com.opencsv:opencsv:5.9") implementation(libs.opencsv)
implementation("commons-codec:commons-codec:1.16.0") implementation(libs.commons.codec)
implementation("org.apache.commons:commons-lang3:3.14.0") implementation(libs.commons.lang3)
} }
} }
@@ -59,19 +59,16 @@ kotlin {
dependencies { dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation(kotlin("test-junit")) implementation(kotlin("test-junit"))
implementation("org.xerial:sqlite-jdbc:3.45.1.0") implementation(libs.sqlite.jdbc)
implementation("org.hamcrest:hamcrest:2.2") implementation(libs.hamcrest)
implementation("org.apache.commons:commons-io:1.3.2") implementation(libs.commons.io)
implementation("org.mockito.kotlin:mockito-kotlin:5.4.0") implementation(libs.mockito.kotlin)
implementation("org.junit.jupiter:junit-jupiter:5.10.1") implementation(libs.junit.jupiter)
} }
} }
} }
} }
tasks.named<org.gradle.language.jvm.tasks.ProcessResources>("jvmProcessResources") { tasks.withType<ProcessResources> {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
tasks.named<org.gradle.language.jvm.tasks.ProcessResources>("jvmTestProcessResources") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
} }

View File

@@ -135,6 +135,12 @@ open class Preferences(private val storage: Storage) {
storage.putBoolean("pref_short_toggle", enabled) storage.putBoolean("pref_short_toggle", enabled)
} }
var isConfettiAnimationDisabled: Boolean
get() = storage.getBoolean("pref_disable_animation", false)
set(enabled) {
storage.putBoolean("pref_disable_animation", enabled)
}
fun removeListener(listener: Listener) { fun removeListener(listener: Listener) {
listeners.remove(listener) listeners.remove(listener)
} }

View File

@@ -51,11 +51,11 @@ open class ListHabitsBehavior @Inject constructor(
screen.showHabitScreen(h) screen.showHabitScreen(h)
} }
fun onEdit(habit: Habit, timestamp: Timestamp?) { fun onEdit(habit: Habit, timestamp: Timestamp?, x: Float, y: Float) {
val entry = habit.computedEntries.get(timestamp!!) val entry = habit.computedEntries.get(timestamp!!)
if (habit.type == HabitType.NUMERICAL) { if (habit.type == HabitType.NUMERICAL) {
val oldValue = entry.value.toDouble() / 1000 val oldValue = entry.value.toDouble() / 1000
screen.showNumberPopup(oldValue, entry.notes) { newValue: Double, newNotes: String, x: Float, y: Float -> screen.showNumberPopup(oldValue, entry.notes) { newValue: Double, newNotes: String ->
val value = (newValue * 1000).roundToInt() val value = (newValue * 1000).roundToInt()
if (newValue != oldValue) { if (newValue != oldValue) {
if ( if (
@@ -72,7 +72,7 @@ open class ListHabitsBehavior @Inject constructor(
entry.value, entry.value,
entry.notes, entry.notes,
habit.color habit.color
) { newValue: Int, newNotes: String, x: Float, y: Float -> ) { newValue: Int, newNotes: String ->
if (newValue != entry.value && newValue == YES_MANUAL) screen.showConfetti(habit.color, x, y) if (newValue != entry.value && newValue == YES_MANUAL) screen.showConfetti(habit.color, x, y)
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes)) commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes))
} }
@@ -159,9 +159,7 @@ open class ListHabitsBehavior @Inject constructor(
fun interface NumberPickerCallback { fun interface NumberPickerCallback {
fun onNumberPicked( fun onNumberPicked(
newValue: Double, newValue: Double,
notes: String, notes: String
x: Float,
y: Float
) )
fun onNumberPickerDismissed() {} fun onNumberPickerDismissed() {}
} }
@@ -169,9 +167,7 @@ open class ListHabitsBehavior @Inject constructor(
fun interface CheckMarkDialogCallback { fun interface CheckMarkDialogCallback {
fun onNotesSaved( fun onNotesSaved(
value: Int, value: Int,
notes: String, notes: String
x: Float,
y: Float
) )
fun onNotesDismissed() {} fun onNotesDismissed() {}
} }

View File

@@ -98,7 +98,7 @@ class HistoryCardPresenter(
entry.value, entry.value,
entry.notes, entry.notes,
habit.color habit.color
) { newValue, newNotes, _: Float, _: Float -> ) { newValue, newNotes ->
commandRunner.run( commandRunner.run(
CreateRepetitionCommand( CreateRepetitionCommand(
habitList, habitList,
@@ -135,7 +135,7 @@ class HistoryCardPresenter(
screen.showNumberPopup( screen.showNumberPopup(
value = oldValue / 1000.0, value = oldValue / 1000.0,
notes = entry.notes notes = entry.notes
) { newValue: Double, newNotes: String, _: Float, _: Float -> ) { newValue: Double, newNotes: String ->
val thousands = (newValue * 1000).roundToInt() val thousands = (newValue * 1000).roundToInt()
commandRunner.run( commandRunner.run(
CreateRepetitionCommand( CreateRepetitionCommand(

View File

@@ -78,13 +78,13 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
@Test @Test
fun testOnEdit() { fun testOnEdit() {
behavior.onEdit(habit2, getToday()) behavior.onEdit(habit2, getToday(), 0f, 0f)
verify(screen).showNumberPopup( verify(screen).showNumberPopup(
eq(0.1), eq(0.1),
eq(""), eq(""),
picker.capture() picker.capture()
) )
picker.lastValue.onNumberPicked(100.0, "", 0f, 0f) picker.lastValue.onNumberPicked(100.0, "")
val today = getTodayWithOffset() val today = getTodayWithOffset()
assertThat(habit2.computedEntries.get(today).value, equalTo(100000)) assertThat(habit2.computedEntries.get(today).value, equalTo(100000))
} }