Compare commits
22 Commits
245b0eb4d6
...
release/2.
| Author | SHA1 | Date | |
|---|---|---|---|
| 70dab74528 | |||
| 7e5d2fa207 | |||
| 0e432fb332 | |||
| 897a236501 | |||
| 0cccecec77 | |||
| f1ed875256 | |||
| e82bd47aab | |||
| e9517f7378 | |||
| 12cc70a51a | |||
| fa670b19b7 | |||
| 45b100aad9 | |||
| 3c0c0b77ff | |||
| 66fa56ea62 | |||
| 951dabea8b | |||
| 76b9dd8bd9 | |||
| f68510f860 | |||
|
|
cc720e3dcb | ||
|
|
6e3d06cff9 | ||
|
|
d458cbd47a | ||
|
|
74ce269446 | ||
|
|
9eb8624863 | ||
|
|
c4bc301fb2 |
16
CHANGELOG.md
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
build.sh
@@ -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=$?
|
||||||
|
|||||||
@@ -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
@@ -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" }
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -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) }
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
@@ -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"/>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||