diff --git a/build.gradle.kts b/build.gradle.kts index 8b4bfc046..675df891e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { val kotlinVersion = "1.5.0" - id("com.android.application") version ("7.0.2") apply (false) + id("com.android.application") version ("7.0.3") apply (false) id("org.jetbrains.kotlin.android") version kotlinVersion apply (false) id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false) id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false) diff --git a/uhabits-android/build.gradle.kts b/uhabits-android/build.gradle.kts index 451e27583..3d97312e4 100644 --- a/uhabits-android/build.gradle.kts +++ b/uhabits-android/build.gradle.kts @@ -86,7 +86,7 @@ android { } dependencies { - val daggerVersion = "2.39" + val daggerVersion = "2.40" val kotlinVersion = "1.5.31" val kxCoroutinesVersion = "1.5.2" val ktorVersion = "1.6.4" diff --git a/uhabits-android/src/androidTest/assets/views/habits/list/CheckmarkButtonView/render_implicit_check.png b/uhabits-android/src/androidTest/assets/views/habits/list/CheckmarkButtonView/render_implicit_check.png index 86e6a5406..c45314e03 100644 Binary files a/uhabits-android/src/androidTest/assets/views/habits/list/CheckmarkButtonView/render_implicit_check.png and b/uhabits-android/src/androidTest/assets/views/habits/list/CheckmarkButtonView/render_implicit_check.png differ diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.kt index 171a9ed41..bfbdf3bb8 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.kt @@ -55,6 +55,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @LargeTest class HabitsTest : BaseUserInterfaceTest() { + @Test @Throws(Exception::class) fun shouldCreateHabit() { @@ -180,6 +181,8 @@ class HabitsTest : BaseUserInterfaceTest() { longPressCheckmarks("Wake up early", count = 2) clickText("Wake up early") verifyShowsScreen(SHOW_HABIT) + // TODO: find a better way than sleeping in tests + Thread.sleep(2001L) verifyDisplaysText("10%") } @@ -194,6 +197,8 @@ class HabitsTest : BaseUserInterfaceTest() { verifyDoesNotDisplayText("Track time") verifyDisplaysText("Wake up early") longPressCheckmarks("Wake up early", count = 1) + // TODO: find a better way than sleeping in tests + Thread.sleep(2001L) verifyDoesNotDisplayText("Wake up early") clickMenu(TOGGLE_COMPLETED) verifyDisplaysText("Track time") diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt index f8d1ef477..22fd1b42e 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt @@ -53,6 +53,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener { override fun onQuestionMarksChanged() { invalidateOptionsMenu() + menu.behavior.onPreferencesChanged() } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt index c92d0dd3f..a70008d13 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt @@ -39,7 +39,7 @@ class ListHabitsMenu @Inject constructor( @ActivityContext context: Context, private val preferences: Preferences, private val themeSwitcher: ThemeSwitcher, - private val behavior: ListHabitsMenuBehavior + val behavior: ListHabitsMenuBehavior ) { val activity = (context as AppCompatActivity) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt index 498b80ab6..9108e5123 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt @@ -37,9 +37,9 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.inject.ActivityContext -import org.isoron.uhabits.utils.dim import org.isoron.uhabits.utils.drawNotesIndicator import org.isoron.uhabits.utils.getFontAwesome +import org.isoron.uhabits.utils.sp import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.toMeasureSpec import javax.inject.Inject @@ -154,6 +154,11 @@ class CheckmarkButtonView( } else -> R.string.fa_check } + paint.textSize = when { + id == R.string.fa_question -> sp(12.0f) + value == YES_AUTO -> sp(13.0f) + else -> sp(14.0f) + } if (value == YES_AUTO) { paint.strokeWidth = 5f paint.style = Paint.Style.STROKE @@ -162,11 +167,6 @@ class CheckmarkButtonView( paint.style = Paint.Style.FILL } - paint.textSize = when (id) { - UNKNOWN -> dim(R.dimen.smallerTextSize) - else -> dim(R.dimen.smallTextSize) - } - val label = resources.getString(id) val em = paint.measureText("m") diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt index b04a92477..34479beb6 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt @@ -36,6 +36,7 @@ import android.widget.TextView import org.isoron.platform.gui.toInt import org.isoron.uhabits.R import org.isoron.uhabits.activities.common.views.RingView +import org.isoron.uhabits.activities.habits.list.views.HabitCardView.Companion.delay import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.ModelObservable import org.isoron.uhabits.core.models.Timestamp @@ -150,7 +151,11 @@ class HabitCardView( checkmarkPanel = checkmarkPanelFactory.create().apply { onToggle = { timestamp, value -> triggerRipple(timestamp) - habit?.let { behavior.onToggle(it, timestamp, value) } + habit?.let { + { + behavior.onToggle(it, timestamp, value) + }.delay(TOGGLE_DELAY_MILLIS) + } } onEdit = { timestamp -> triggerRipple(timestamp) @@ -274,4 +279,12 @@ class HabitCardView( } innerFrame.setBackgroundResource(background) } + + companion object { + const val TOGGLE_DELAY_MILLIS = 2000L + + fun (() -> Unit).delay(delayInMillis: Long) { + Handler(Looper.getMainLooper()).postDelayed(this, delayInMillis) + } + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt index c40335545..6361397ef 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt @@ -23,17 +23,17 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.activities.AndroidThemeSwitcher -import org.isoron.uhabits.core.models.HabitMatcherBuilder +import org.isoron.uhabits.core.models.HabitMatcher class EditSettingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val app = applicationContext as HabitsApplication val habits = app.component.habitList.getFiltered( - HabitMatcherBuilder() - .setArchivedAllowed(false) - .setCompletedAllowed(true) - .build() + HabitMatcher( + isArchivedAllowed = false, + isCompletedAllowed = true, + ) ) AndroidThemeSwitcher(this, app.component.preferences).apply() diff --git a/uhabits-core/assets/test/csv_export/001 Meditate/Checkmarks.csv b/uhabits-core/assets/test/csv_export/001 Meditate/Checkmarks.csv new file mode 100644 index 000000000..e69de29bb diff --git a/uhabits-core/assets/test/csv_export/001 Meditate/Scores.csv b/uhabits-core/assets/test/csv_export/001 Meditate/Scores.csv new file mode 100644 index 000000000..6004f60bc --- /dev/null +++ b/uhabits-core/assets/test/csv_export/001 Meditate/Scores.csv @@ -0,0 +1 @@ +2015-01-25,0.0000 diff --git a/uhabits-core/assets/test/csv_export/002 Wake up early/Checkmarks.csv b/uhabits-core/assets/test/csv_export/002 Wake up early/Checkmarks.csv new file mode 100644 index 000000000..89f788f68 --- /dev/null +++ b/uhabits-core/assets/test/csv_export/002 Wake up early/Checkmarks.csv @@ -0,0 +1,10 @@ +2015-01-25,2 +2015-01-24,0 +2015-01-23,1 +2015-01-22,2 +2015-01-21,2 +2015-01-20,2 +2015-01-19,1 +2015-01-18,1 +2015-01-17,2 +2015-01-16,2 diff --git a/uhabits-core/assets/test/csv_export/002 Wake up early/Scores.csv b/uhabits-core/assets/test/csv_export/002 Wake up early/Scores.csv new file mode 100644 index 000000000..7f4fa4780 --- /dev/null +++ b/uhabits-core/assets/test/csv_export/002 Wake up early/Scores.csv @@ -0,0 +1,10 @@ +2015-01-25,0.2557 +2015-01-24,0.2226 +2015-01-23,0.1991 +2015-01-22,0.1746 +2015-01-21,0.1379 +2015-01-20,0.0995 +2015-01-19,0.0706 +2015-01-18,0.0515 +2015-01-17,0.0315 +2015-01-16,0.0107 diff --git a/uhabits-core/assets/test/csv_export/Checkmarks.csv b/uhabits-core/assets/test/csv_export/Checkmarks.csv new file mode 100644 index 000000000..c0788570b --- /dev/null +++ b/uhabits-core/assets/test/csv_export/Checkmarks.csv @@ -0,0 +1,11 @@ +Date,Meditate,Wake up early, +2015-01-25,-1,2, +2015-01-24,-1,0, +2015-01-23,-1,1, +2015-01-22,-1,2, +2015-01-21,-1,2, +2015-01-20,-1,2, +2015-01-19,-1,1, +2015-01-18,-1,1, +2015-01-17,-1,2, +2015-01-16,-1,2, diff --git a/uhabits-core/assets/test/csv_export/Habits.csv b/uhabits-core/assets/test/csv_export/Habits.csv new file mode 100644 index 000000000..672189f33 --- /dev/null +++ b/uhabits-core/assets/test/csv_export/Habits.csv @@ -0,0 +1,3 @@ +Position,Name,Question,Description,NumRepetitions,Interval,Color +001,Meditate,Did you meditate this morning?,,1,1,#FF8F00 +002,Wake up early,Did you wake up before 6am?,,2,3,#00897B diff --git a/uhabits-core/assets/test/csv_export/Scores.csv b/uhabits-core/assets/test/csv_export/Scores.csv new file mode 100644 index 000000000..931c6c41c --- /dev/null +++ b/uhabits-core/assets/test/csv_export/Scores.csv @@ -0,0 +1,11 @@ +Date,Meditate,Wake up early, +2015-01-25,0.0000,0.2557, +2015-01-24,0.0000,0.2226, +2015-01-23,0.0000,0.1991, +2015-01-22,0.0000,0.1746, +2015-01-21,0.0000,0.1379, +2015-01-20,0.0000,0.0995, +2015-01-19,0.0000,0.0706, +2015-01-18,0.0000,0.0515, +2015-01-17,0.0000,0.0315, +2015-01-16,0.0000,0.0107, diff --git a/uhabits-core/build.gradle.kts b/uhabits-core/build.gradle.kts index ae3a8b44f..c846a8888 100644 --- a/uhabits-core/build.gradle.kts +++ b/uhabits-core/build.gradle.kts @@ -43,7 +43,7 @@ kotlin { val jvmMain by getting { dependencies { implementation(kotlin("stdlib-jdk8")) - compileOnly("com.google.dagger:dagger:2.39") + compileOnly("com.google.dagger:dagger:2.40") implementation("com.google.guava:guava:31.0.1-android") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2") diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt index 341b6716c..35e459a27 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt @@ -167,7 +167,7 @@ class HabitsCSVExporter( checksWriter.write(sb.toString()) scoresWriter.write(sb.toString()) for (j in selectedHabits.indices) { - checksWriter.write(checkmarks[j][i].toString()) + checksWriter.write(checkmarks[j][i].value.toString()) checksWriter.write(delimiter) val score = String.format(Locale.US, "%.4f", scores[j][i].value) scoresWriter.write(score) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt index 20c3d7949..68bbabc08 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt @@ -68,17 +68,10 @@ data class Habit( } } - fun isFailedToday(): Boolean { + fun isEnteredToday(): Boolean { val today = DateUtils.getTodayWithOffset() val value = computedEntries.get(today).value - return if (isNumerical) { - when (targetType) { - NumericalHabitType.AT_LEAST -> value / 1000.0 < targetValue - NumericalHabitType.AT_MOST -> value / 1000.0 > targetValue - } - } else { - value == Entry.NO - } + return value != Entry.UNKNOWN } fun recompute() { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt index 69b738dda..74ee0a700 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt @@ -43,7 +43,7 @@ abstract class HabitList : Iterable { */ constructor() { observable = ModelObservable() - filter = HabitMatcherBuilder().setArchivedAllowed(true).build() + filter = HabitMatcher(isArchivedAllowed = true) } protected constructor(filter: HabitMatcher) { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcher.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcher.kt index f26e00006..ed6bc023b 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcher.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcher.kt @@ -22,19 +22,21 @@ data class HabitMatcher( val isArchivedAllowed: Boolean = false, val isReminderRequired: Boolean = false, val isCompletedAllowed: Boolean = true, + val isEnteredAllowed: Boolean = true, ) { fun matches(habit: Habit): Boolean { if (!isArchivedAllowed && habit.isArchived) return false if (isReminderRequired && !habit.hasReminder()) return false - if (!isCompletedAllowed && (habit.isCompletedToday() || habit.isFailedToday())) return false + if (!isCompletedAllowed && habit.isCompletedToday()) return false + if (!isEnteredAllowed && habit.isEnteredToday()) return false return true } companion object { @JvmField - val WITH_ALARM = HabitMatcherBuilder() - .setArchivedAllowed(true) - .setReminderRequired(true) - .build() + val WITH_ALARM = HabitMatcher( + isArchivedAllowed = true, + isReminderRequired = true, + ) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcherBuilder.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcherBuilder.kt deleted file mode 100644 index c9f1a7f7e..000000000 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitMatcherBuilder.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016-2021 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Loop Habit Tracker is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ -package org.isoron.uhabits.core.models - -class HabitMatcherBuilder { - private var archivedAllowed = false - private var reminderRequired = false - private var completedAllowed = true - - fun build(): HabitMatcher { - return HabitMatcher( - isArchivedAllowed = archivedAllowed, - isReminderRequired = reminderRequired, - isCompletedAllowed = completedAllowed, - ) - } - - fun setArchivedAllowed(archivedAllowed: Boolean): HabitMatcherBuilder { - this.archivedAllowed = archivedAllowed - return this - } - - fun setCompletedAllowed(completedAllowed: Boolean): HabitMatcherBuilder { - this.completedAllowed = completedAllowed - return this - } - - fun setReminderRequired(reminderRequired: Boolean): HabitMatcherBuilder { - this.reminderRequired = reminderRequired - return this - } -} diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt index d538f1950..deb42e384 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt @@ -20,7 +20,6 @@ package org.isoron.uhabits.core.ui.screens.habits.list import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitMatcher -import org.isoron.uhabits.core.models.HabitMatcherBuilder import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.ThemeSwitcher import javax.inject.Inject @@ -33,6 +32,7 @@ class ListHabitsMenuBehavior @Inject constructor( ) { private var showCompleted: Boolean private var showArchived: Boolean + fun onCreateHabit() { screen.showSelectHabitTypeDialog() } @@ -97,13 +97,26 @@ class ListHabitsMenuBehavior @Inject constructor( screen.applyTheme() } + fun onPreferencesChanged() { + updateAdapterFilter() + } + private fun updateAdapterFilter() { - adapter.setFilter( - HabitMatcherBuilder() - .setArchivedAllowed(showArchived) - .setCompletedAllowed(showCompleted) - .build() - ) + if (preferences.areQuestionMarksEnabled) { + adapter.setFilter( + HabitMatcher( + isArchivedAllowed = showArchived, + isEnteredAllowed = showCompleted, + ) + ) + } else { + adapter.setFilter( + HabitMatcher( + isArchivedAllowed = showArchived, + isCompletedAllowed = showCompleted, + ) + ) + } adapter.refresh() } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt index 8af545504..7c2c0a4fd 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt @@ -77,11 +77,11 @@ abstract class DateUtils { } @JvmStatic - fun getLocalTime(): Long { + fun getLocalTime(utcTimeInMillis: Long? = null): Long { if (fixedLocalTime != null) return fixedLocalTime as Long val tz = getTimeZone() - val now = Date().time + val now = utcTimeInMillis ?: Date().time return now + tz.getOffset(now) } @@ -100,7 +100,7 @@ abstract class DateUtils { format: Int, firstWeekDay: Int ): Array { - val calendar = GregorianCalendar() + val calendar = GregorianCalendar(getLocale()) calendar.set(DAY_OF_WEEK, firstWeekDay) val daysNullable = ArrayList() @@ -149,7 +149,7 @@ abstract class DateUtils { */ @JvmStatic fun getFirstWeekdayNumberAccordingToLocale(): Int { - return GregorianCalendar().firstDayOfWeek + return GregorianCalendar(getLocale()).firstDayOfWeek } /** @@ -214,13 +214,7 @@ abstract class DateUtils { @JvmStatic fun getStartOfTodayCalendarWithOffset(): GregorianCalendar = getCalendar(getStartOfTodayWithOffset()) - @JvmStatic - fun getTimeZone(): TimeZone { - return fixedTimeZone ?: TimeZone.getDefault() - } - - @JvmStatic - fun getTimezone(): TimeZone { + private fun getTimeZone(): TimeZone { return fixedTimeZone ?: TimeZone.getDefault() } @@ -236,8 +230,7 @@ abstract class DateUtils { startDayMinuteOffset = minuteOffset } - @JvmStatic - fun getLocale(): Locale { + private fun getLocale(): Locale { return fixedLocale ?: Locale.getDefault() } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.kt index c6655bba6..c56bfe622 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/io/HabitsCSVExporterTest.kt @@ -34,6 +34,7 @@ import java.util.zip.ZipFile class HabitsCSVExporterTest : BaseUnitTest() { private lateinit var baseDir: File + @Before @Throws(Exception::class) override fun setUp() { @@ -41,12 +42,7 @@ class HabitsCSVExporterTest : BaseUnitTest() { habitList.add(fixtures.createShortHabit()) habitList.add(fixtures.createEmptyHabit()) baseDir = Files.createTempDirectory("csv").toFile() - } - - @Throws(Exception::class) - override fun tearDown() { - FileUtils.deleteDirectory(baseDir) - super.tearDown() + baseDir.deleteOnExit() } @Test @@ -63,14 +59,20 @@ class HabitsCSVExporterTest : BaseUnitTest() { assertAbsolutePathExists(filename) val archive = File(filename) unzip(archive) - assertPathExists("Habits.csv") - assertPathExists("001 Meditate/Checkmarks.csv") - assertPathExists("001 Meditate/Scores.csv") - assertPathExists("002 Wake up early") - assertPathExists("002 Wake up early/Checkmarks.csv") - assertPathExists("002 Wake up early/Scores.csv") - assertPathExists("Checkmarks.csv") - assertPathExists("Scores.csv") + val filesToCheck = arrayOf( + "001 Meditate/Checkmarks.csv", + "001 Meditate/Scores.csv", + "002 Wake up early/Checkmarks.csv", + "002 Wake up early/Scores.csv", + "Checkmarks.csv", + "Habits.csv", + "Scores.csv" + ) + + for (file in filesToCheck) { + assertPathExists(file) + assertFileAndReferenceAreEqual(file) + } } @Throws(IOException::class) @@ -104,4 +106,18 @@ class HabitsCSVExporterTest : BaseUnitTest() { file.exists() ) } + + private fun assertFileAndReferenceAreEqual(s: String) { + val assetFilename = String.format("csv_export/%s", s) + val file = File.createTempFile("asset", "") + file.deleteOnExit() + copyAssetToFile(assetFilename, file) + + assertTrue( + FileUtils.contentEquals( + file, + File(String.format("%s/%s", baseDir.absolutePath, s)) + ) + ) + } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitListTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitListTest.kt index 7f67d3adf..0d2314be5 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitListTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitListTest.kt @@ -53,12 +53,12 @@ class HabitListTest : BaseUnitTest() { habitsArray[1].isArchived = true habitsArray[4].isArchived = true habitsArray[7].isArchived = true - activeHabits = habitList.getFiltered(HabitMatcherBuilder().build()) + activeHabits = habitList.getFiltered(HabitMatcher()) reminderHabits = habitList.getFiltered( - HabitMatcherBuilder() - .setArchivedAllowed(true) - .setReminderRequired(true) - .build() + HabitMatcher( + isArchivedAllowed = true, + isReminderRequired = true, + ) ) } @@ -181,10 +181,10 @@ class HabitListTest : BaseUnitTest() { fun testOrder_inherit() { habitList.primaryOrder = HabitList.Order.BY_COLOR_ASC val filteredList = habitList.getFiltered( - HabitMatcherBuilder() - .setArchivedAllowed(false) - .setCompletedAllowed(false) - .build() + HabitMatcher( + isArchivedAllowed = false, + isCompletedAllowed = false, + ) ) assertEquals(filteredList.primaryOrder, HabitList.Order.BY_COLOR_ASC) } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitTest.kt index 5ba6ced1f..da568adf2 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/HabitTest.kt @@ -82,12 +82,12 @@ class HabitTest : BaseUnitTest() { @Test @Throws(Exception::class) - fun test_isFailed() { + fun test_isEntered() { val h = modelFactory.buildHabit() - assertFalse(h.isFailedToday()) + assertFalse(h.isEnteredToday()) h.originalEntries.add(Entry(getToday(), Entry.NO)) h.recompute() - assertTrue(h.isFailedToday()) + assertTrue(h.isEnteredToday()) } @Test @@ -119,35 +119,6 @@ class HabitTest : BaseUnitTest() { assertTrue(h.isCompletedToday()) } - @Test - @Throws(Exception::class) - fun test_isFailedNumerical() { - val h = modelFactory.buildHabit() - h.type = HabitType.NUMERICAL - h.targetType = NumericalHabitType.AT_LEAST - h.targetValue = 100.0 - assertTrue(h.isFailedToday()) - h.originalEntries.add(Entry(getToday(), 200000)) - h.recompute() - assertFalse(h.isFailedToday()) - h.originalEntries.add(Entry(getToday(), 100000)) - h.recompute() - assertFalse(h.isFailedToday()) - h.originalEntries.add(Entry(getToday(), 50000)) - h.recompute() - assertTrue(h.isFailedToday()) - h.targetType = NumericalHabitType.AT_MOST - h.originalEntries.add(Entry(getToday(), 200000)) - h.recompute() - assertTrue(h.isFailedToday()) - h.originalEntries.add(Entry(getToday(), 100000)) - h.recompute() - assertFalse(h.isFailedToday()) - h.originalEntries.add(Entry(getToday(), 50000)) - h.recompute() - assertFalse(h.isFailedToday()) - } - @Test @Throws(Exception::class) fun testURI() { diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt index 59ee7a585..d5a551012 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt @@ -28,7 +28,7 @@ import org.isoron.uhabits.core.database.Database import org.isoron.uhabits.core.database.Repository import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.HabitList -import org.isoron.uhabits.core.models.HabitMatcherBuilder +import org.isoron.uhabits.core.models.HabitMatcher import org.isoron.uhabits.core.models.ModelObservable import org.isoron.uhabits.core.models.Reminder import org.isoron.uhabits.core.models.WeekdayList @@ -69,12 +69,12 @@ class SQLiteHabitListTest : BaseUnitTest() { habitsArray[4].isArchived = true habitsArray[7].isArchived = true habitList.update(habitsArray) - activeHabits = habitList.getFiltered(HabitMatcherBuilder().build()) + activeHabits = habitList.getFiltered(HabitMatcher()) reminderHabits = habitList.getFiltered( - HabitMatcherBuilder() - .setArchivedAllowed(true) - .setReminderRequired(true) - .build() + HabitMatcher( + isArchivedAllowed = true, + isReminderRequired = true, + ) ) habitList.observable.addListener(listener) } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt index 1b440279e..75280ab9b 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt @@ -37,6 +37,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.truncate import org.junit.Before import org.junit.Test import java.util.Calendar +import java.util.GregorianCalendar import java.util.Locale import java.util.TimeZone @@ -58,6 +59,129 @@ class DateUtilsTest : BaseUnitTest() { assertThat(formatted, equalTo("Thu\n31")) } + @Test + fun testGetLocalTime() { + setFixedLocalTime(null) + setFixedTimeZone(TimeZone.getTimeZone("Australia/Sydney")) + val utcTestTimeInMillis = unixTime(2015, Calendar.JANUARY, 11) + val localTimeInMillis = DateUtils.getLocalTime(utcTestTimeInMillis) + val expectedUnixTimeOffsetForSydney = 11 * 60 * 60 * 1000 + val expectedUnixTimeForSydney = utcTestTimeInMillis + expectedUnixTimeOffsetForSydney + assertThat(expectedUnixTimeForSydney, equalTo(localTimeInMillis)) + } + + @Test + fun testGetWeekdaySequence() { + val weekdaySequence = DateUtils.getWeekdaySequence(3) + assertThat(arrayOf(3, 4, 5, 6, 7, 1, 2), equalTo(weekdaySequence)) + } + + @Test + fun testGetFirstWeekdayNumberAccordingToLocale_germany() { + setFixedLocale(Locale.GERMANY) + val firstWeekdayNumber = DateUtils.getFirstWeekdayNumberAccordingToLocale() + assertThat(2, equalTo(firstWeekdayNumber)) + } + + @Test + fun testGetFirstWeekdayNumberAccordingToLocale_us() { + setFixedLocale(Locale.US) + val firstWeekdayNumber = DateUtils.getFirstWeekdayNumberAccordingToLocale() + assertThat(1, equalTo(firstWeekdayNumber)) + } + + @Test + fun testGetLongWeekdayNames_germany() { + setFixedLocale(Locale.GERMANY) + val longWeekdayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY) + assertThat(arrayOf("Samstag", "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"), equalTo(longWeekdayNames)) + } + + @Test + fun testGetLongWeekdayNames_us() { + setFixedLocale(Locale.US) + val longWeekdayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY) + assertThat(arrayOf("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), equalTo(longWeekdayNames)) + } + + @Test + fun testGetShortWeekdayNames_germany() { + setFixedLocale(Locale.GERMANY) + val longWeekdayNames = DateUtils.getShortWeekdayNames(Calendar.SATURDAY) + assertThat(arrayOf("Sa.", "So.", "Mo.", "Di.", "Mi.", "Do.", "Fr."), equalTo(longWeekdayNames)) + } + + @Test + fun testGetShortWeekdayNames_us() { + setFixedLocale(Locale.US) + val longWeekdayNames = DateUtils.getShortWeekdayNames(Calendar.SATURDAY) + assertThat(arrayOf("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"), equalTo(longWeekdayNames)) + } + + @Test + fun testGetToday() { + setFixedLocalTime(FIXED_LOCAL_TIME) + val today = DateUtils.getToday() + assertThat(Timestamp(FIXED_LOCAL_TIME), equalTo(today)) + } + + @Test + fun testGetStartOfDay() { + val expectedStartOfDayUtc = fixedStartOfToday() + val laterInTheDayUtc = fixedStartOfTodayWithOffset(20) + val startOfDay = DateUtils.getStartOfDay(laterInTheDayUtc) + assertThat(expectedStartOfDayUtc, equalTo(startOfDay)) + } + + @Test + fun testGetStartOfToday() { + val expectedStartOfDayUtc = fixedStartOfToday() + val laterInTheDayUtc = fixedStartOfTodayWithOffset(20) + setFixedLocalTime(laterInTheDayUtc) + val startOfToday = DateUtils.getStartOfToday() + assertThat(expectedStartOfDayUtc, equalTo(startOfToday)) + } + + @Test + fun testGetStartOfTomorrowWithOffset_priorToOffset() { + val priorToOffset = HOUR_OFFSET - 1 + testGetStartOfTomorrowWithOffset(priorToOffset) + } + + @Test + fun testGetStartOfTomorrowWithOffset_afterOffset() { + val afterOffset = HOUR_OFFSET + 1 - HOURS_IN_ONE_DAY + testGetStartOfTomorrowWithOffset(afterOffset) + } + + private fun testGetStartOfTomorrowWithOffset(startOfTodayOffset: Int) { + configureOffsetTest(startOfTodayOffset) + assertThat( + fixedStartOfTodayWithOffset(HOUR_OFFSET), + equalTo(DateUtils.getStartOfTomorrowWithOffset()) + ) + } + + @Test + fun testGetStartOfTodayWithOffset_priorToOffset() { + val priorToOffset = HOURS_IN_ONE_DAY + HOUR_OFFSET - 1 + testGetStartOfTodayWithOffset(priorToOffset) + } + + @Test + fun testGetStartOfTodayWithOffset_afterOffset() { + val afterOffset = HOUR_OFFSET + 1 + testGetStartOfTodayWithOffset(afterOffset) + } + + private fun testGetStartOfTodayWithOffset(startOfTodayOffset: Int) { + configureOffsetTest(startOfTodayOffset) + assertThat( + fixedStartOfToday(), + equalTo(DateUtils.getStartOfTodayWithOffset()) + ) + } + @Test fun testTruncate_dayOfWeek() { val field = DateUtils.TruncateField.WEEK_NUMBER @@ -142,22 +266,39 @@ class DateUtilsTest : BaseUnitTest() { assertThat(truncate(field, t2, firstWeekday), equalTo(expected)) } + @Test + fun testTruncate_timestamp() { + val field = DateUtils.TruncateField.YEAR + val nonTruncatedDate = unixTime(2016, Calendar.MAY, 30) + val expected = Timestamp(unixTime(2016, Calendar.JANUARY, 1)) + assertThat(expected, equalTo(truncate(field, Timestamp(nonTruncatedDate), firstWeekday))) + } + + @Test + fun testGetUpcomingTimeInMillis() { + setFixedLocalTime(FIXED_LOCAL_TIME) + setFixedTimeZone(TimeZone.getTimeZone("GMT")) + val expected = unixTime(2015, Calendar.JANUARY, 25, 10, 1) + val upcomingTimeMillis = DateUtils.getUpcomingTimeInMillis(10, 1) + assertThat(expected, equalTo(upcomingTimeMillis)) + } + @Test @Throws(Exception::class) fun testMillisecondsUntilTomorrow() { setFixedTimeZone(TimeZone.getTimeZone("GMT")) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59)) assertThat(millisecondsUntilTomorrowWithOffset(), equalTo(DateUtils.MINUTE_LENGTH)) - setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 20, 0)) + setFixedLocalTime(fixedStartOfTodayWithOffset(20)) assertThat( millisecondsUntilTomorrowWithOffset(), equalTo(4 * DateUtils.HOUR_LENGTH) ) - setStartDayOffset(3, 30) + setStartDayOffset(HOUR_OFFSET, 30) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59)) assertThat( millisecondsUntilTomorrowWithOffset(), - equalTo(3 * DateUtils.HOUR_LENGTH + 31 * DateUtils.MINUTE_LENGTH) + equalTo(HOUR_OFFSET * DateUtils.HOUR_LENGTH + 31 * DateUtils.MINUTE_LENGTH) ) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 2, 1, 0)) assertThat( @@ -166,6 +307,52 @@ class DateUtilsTest : BaseUnitTest() { ) } + @Test + fun testGetStartOfTodayCalendar() { + setFixedLocalTime(FIXED_LOCAL_TIME) + setFixedLocale(Locale.GERMANY) + val expectedStartOfDay = unixTime(2015, Calendar.JANUARY, 25, 0, 0) + val expectedCalendar = GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.GERMANY) + expectedCalendar.timeInMillis = expectedStartOfDay + val startOfTodayCalendar = DateUtils.getStartOfTodayCalendar() + assertThat(expectedCalendar, equalTo(startOfTodayCalendar)) + } + + @Test + fun testGetStartOfTodayCalendarWithOffset_priorToOffset() { + val priorToOffset = HOUR_OFFSET - 1 + testGetStartOfTodayCalendarWithOffset(priorToOffset) + } + + @Test + fun testGetStartOfTodayCalendarWithOffset_afterOffset() { + val afterOffset = HOUR_OFFSET + 1 + testGetStartOfTodayCalendarWithOffset(afterOffset) + } + + private fun testGetStartOfTodayCalendarWithOffset(startOfTodayOffset: Int) { + configureOffsetTest(startOfTodayOffset) + setFixedLocale(Locale.GERMANY) + val expectedCalendar = GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.GERMANY) + expectedCalendar.timeInMillis = fixedStartOfToday() + assertThat( + expectedCalendar, + equalTo(DateUtils.getStartOfTodayCalendar()) + ) + } + + private fun configureOffsetTest(startOfTodayOffset: Int) { + setStartDayOffset(HOUR_OFFSET, 0) + setFixedTimeZone(TimeZone.getTimeZone("GMT")) + setFixedLocalTime(fixedStartOfTodayWithOffset(startOfTodayOffset)) + } + + private fun fixedStartOfToday() = fixedStartOfTodayWithOffset(0) + + private fun fixedStartOfTodayWithOffset(hourOffset: Int): Long { + return unixTime(2017, Calendar.JANUARY, 1, hourOffset, 0) + } + @Test @Throws(Exception::class) fun testGetTodayWithOffset() { @@ -469,4 +656,9 @@ class DateUtilsTest : BaseUnitTest() { unixTime(2018, Calendar.APRIL, 1, 18, 0) ) } + + companion object { + const val HOUR_OFFSET = 3 + const val HOURS_IN_ONE_DAY = 24 + } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/FileExtensionsTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/FileExtensionsTest.kt new file mode 100644 index 000000000..627422608 --- /dev/null +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/FileExtensionsTest.kt @@ -0,0 +1,17 @@ +package org.isoron.uhabits.core.utils + +import org.isoron.uhabits.core.BaseUnitTest +import org.junit.Test +import java.io.File +import kotlin.test.assertTrue + +class FileExtensionsTest : BaseUnitTest() { + + @Test + fun testIsSQLite3File() { + val file = File.createTempFile("asset", "") + copyAssetToFile("loop.db", file) + val isSqlite3File = file.isSQLite3File() + assertTrue(isSqlite3File) + } +} diff --git a/uhabits-core/src/jvmTest/resources/databases/021.db b/uhabits-core/src/jvmTest/resources/databases/021.db deleted file mode 100644 index 850479ca0..000000000 Binary files a/uhabits-core/src/jvmTest/resources/databases/021.db and /dev/null differ diff --git a/uhabits-core/src/jvmTest/resources/databases/022.db b/uhabits-core/src/jvmTest/resources/databases/022.db deleted file mode 100644 index 01237dfc8..000000000 Binary files a/uhabits-core/src/jvmTest/resources/databases/022.db and /dev/null differ diff --git a/uhabits-core/src/jvmTest/resources/habitbull.csv b/uhabits-core/src/jvmTest/resources/habitbull.csv deleted file mode 100644 index 977a8e8df..000000000 --- a/uhabits-core/src/jvmTest/resources/habitbull.csv +++ /dev/null @@ -1,19 +0,0 @@ -HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText -Breed dragons,with love and fire,Diet & Food,2016-03-18,1, -Breed dragons,with love and fire,Diet & Food,2016-03-19,1, -Breed dragons,with love and fire,Diet & Food,2016-03-21,1, -Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1, -Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1, -Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1, -Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1, -No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1, -Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1, -Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1, -Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1, diff --git a/uhabits-core/src/jvmTest/resources/loop.db b/uhabits-core/src/jvmTest/resources/loop.db deleted file mode 100644 index 25f0f32cd..000000000 Binary files a/uhabits-core/src/jvmTest/resources/loop.db and /dev/null differ diff --git a/uhabits-core/src/jvmTest/resources/rewire.db b/uhabits-core/src/jvmTest/resources/rewire.db deleted file mode 100644 index 21f55032f..000000000 Binary files a/uhabits-core/src/jvmTest/resources/rewire.db and /dev/null differ diff --git a/uhabits-core/src/jvmTest/resources/tickmate.db b/uhabits-core/src/jvmTest/resources/tickmate.db deleted file mode 100644 index a4bec769c..000000000 Binary files a/uhabits-core/src/jvmTest/resources/tickmate.db and /dev/null differ diff --git a/uhabits-server/build.gradle.kts b/uhabits-server/build.gradle.kts index 8f0fc2554..2dd787bb4 100644 --- a/uhabits-server/build.gradle.kts +++ b/uhabits-server/build.gradle.kts @@ -22,7 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { application id("kotlin") - id("com.github.johnrengelman.shadow") version "7.0.0" + id("com.github.johnrengelman.shadow") version "7.1.0" }