Merge branch 'dev' into feat-1074

pull/1103/head
Alinson S. Xavier 4 years ago
commit 4972257635
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -1,6 +1,6 @@
plugins { plugins {
val kotlinVersion = "1.5.0" 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.android") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false) id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false) id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)

@ -86,7 +86,7 @@ android {
} }
dependencies { dependencies {
val daggerVersion = "2.39" val daggerVersion = "2.40"
val kotlinVersion = "1.5.31" val kotlinVersion = "1.5.31"
val kxCoroutinesVersion = "1.5.2" val kxCoroutinesVersion = "1.5.2"
val ktorVersion = "1.6.4" val ktorVersion = "1.6.4"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -55,6 +55,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
class HabitsTest : BaseUserInterfaceTest() { class HabitsTest : BaseUserInterfaceTest() {
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun shouldCreateHabit() { fun shouldCreateHabit() {
@ -180,6 +181,8 @@ class HabitsTest : BaseUserInterfaceTest() {
longPressCheckmarks("Wake up early", count = 2) longPressCheckmarks("Wake up early", count = 2)
clickText("Wake up early") clickText("Wake up early")
verifyShowsScreen(SHOW_HABIT) verifyShowsScreen(SHOW_HABIT)
// TODO: find a better way than sleeping in tests
Thread.sleep(2001L)
verifyDisplaysText("10%") verifyDisplaysText("10%")
} }
@ -194,6 +197,8 @@ class HabitsTest : BaseUserInterfaceTest() {
verifyDoesNotDisplayText("Track time") verifyDoesNotDisplayText("Track time")
verifyDisplaysText("Wake up early") verifyDisplaysText("Wake up early")
longPressCheckmarks("Wake up early", count = 1) longPressCheckmarks("Wake up early", count = 1)
// TODO: find a better way than sleeping in tests
Thread.sleep(2001L)
verifyDoesNotDisplayText("Wake up early") verifyDoesNotDisplayText("Wake up early")
clickMenu(TOGGLE_COMPLETED) clickMenu(TOGGLE_COMPLETED)
verifyDisplaysText("Track time") verifyDisplaysText("Track time")

@ -53,6 +53,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
override fun onQuestionMarksChanged() { override fun onQuestionMarksChanged() {
invalidateOptionsMenu() invalidateOptionsMenu()
menu.behavior.onPreferencesChanged()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

@ -39,7 +39,7 @@ class ListHabitsMenu @Inject constructor(
@ActivityContext context: Context, @ActivityContext context: Context,
private val preferences: Preferences, private val preferences: Preferences,
private val themeSwitcher: ThemeSwitcher, private val themeSwitcher: ThemeSwitcher,
private val behavior: ListHabitsMenuBehavior val behavior: ListHabitsMenuBehavior
) { ) {
val activity = (context as AppCompatActivity) val activity = (context as AppCompatActivity)

@ -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.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.inject.ActivityContext
import org.isoron.uhabits.utils.dim
import org.isoron.uhabits.utils.drawNotesIndicator import org.isoron.uhabits.utils.drawNotesIndicator
import org.isoron.uhabits.utils.getFontAwesome import org.isoron.uhabits.utils.getFontAwesome
import org.isoron.uhabits.utils.sp
import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.sres
import org.isoron.uhabits.utils.toMeasureSpec import org.isoron.uhabits.utils.toMeasureSpec
import javax.inject.Inject import javax.inject.Inject
@ -154,6 +154,11 @@ class CheckmarkButtonView(
} }
else -> R.string.fa_check 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) { if (value == YES_AUTO) {
paint.strokeWidth = 5f paint.strokeWidth = 5f
paint.style = Paint.Style.STROKE paint.style = Paint.Style.STROKE
@ -162,11 +167,6 @@ class CheckmarkButtonView(
paint.style = Paint.Style.FILL 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 label = resources.getString(id)
val em = paint.measureText("m") val em = paint.measureText("m")

@ -36,6 +36,7 @@ import android.widget.TextView
import org.isoron.platform.gui.toInt import org.isoron.platform.gui.toInt
import org.isoron.uhabits.R import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.views.RingView 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.Habit
import org.isoron.uhabits.core.models.ModelObservable import org.isoron.uhabits.core.models.ModelObservable
import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.models.Timestamp
@ -150,7 +151,11 @@ class HabitCardView(
checkmarkPanel = checkmarkPanelFactory.create().apply { checkmarkPanel = checkmarkPanelFactory.create().apply {
onToggle = { timestamp, value -> onToggle = { timestamp, value ->
triggerRipple(timestamp) triggerRipple(timestamp)
habit?.let { behavior.onToggle(it, timestamp, value) } habit?.let {
{
behavior.onToggle(it, timestamp, value)
}.delay(TOGGLE_DELAY_MILLIS)
}
} }
onEdit = { timestamp -> onEdit = { timestamp ->
triggerRipple(timestamp) triggerRipple(timestamp)
@ -274,4 +279,12 @@ class HabitCardView(
} }
innerFrame.setBackgroundResource(background) innerFrame.setBackgroundResource(background)
} }
companion object {
const val TOGGLE_DELAY_MILLIS = 2000L
fun (() -> Unit).delay(delayInMillis: Long) {
Handler(Looper.getMainLooper()).postDelayed(this, delayInMillis)
}
}
} }

@ -23,17 +23,17 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.activities.AndroidThemeSwitcher import org.isoron.uhabits.activities.AndroidThemeSwitcher
import org.isoron.uhabits.core.models.HabitMatcherBuilder import org.isoron.uhabits.core.models.HabitMatcher
class EditSettingActivity : AppCompatActivity() { class EditSettingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val app = applicationContext as HabitsApplication val app = applicationContext as HabitsApplication
val habits = app.component.habitList.getFiltered( val habits = app.component.habitList.getFiltered(
HabitMatcherBuilder() HabitMatcher(
.setArchivedAllowed(false) isArchivedAllowed = false,
.setCompletedAllowed(true) isCompletedAllowed = true,
.build() )
) )
AndroidThemeSwitcher(this, app.component.preferences).apply() AndroidThemeSwitcher(this, app.component.preferences).apply()

@ -0,0 +1 @@
2015-01-25,0.0000
1 2015-01-25 0.0000

@ -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
1 2015-01-25 2
2 2015-01-24 0
3 2015-01-23 1
4 2015-01-22 2
5 2015-01-21 2
6 2015-01-20 2
7 2015-01-19 1
8 2015-01-18 1
9 2015-01-17 2
10 2015-01-16 2

@ -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
1 2015-01-25 0.2557
2 2015-01-24 0.2226
3 2015-01-23 0.1991
4 2015-01-22 0.1746
5 2015-01-21 0.1379
6 2015-01-20 0.0995
7 2015-01-19 0.0706
8 2015-01-18 0.0515
9 2015-01-17 0.0315
10 2015-01-16 0.0107

@ -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,
1 Date Meditate Wake up early
2 2015-01-25 -1 2
3 2015-01-24 -1 0
4 2015-01-23 -1 1
5 2015-01-22 -1 2
6 2015-01-21 -1 2
7 2015-01-20 -1 2
8 2015-01-19 -1 1
9 2015-01-18 -1 1
10 2015-01-17 -1 2
11 2015-01-16 -1 2

@ -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
1 Position Name Question Description NumRepetitions Interval Color
2 001 Meditate Did you meditate this morning? 1 1 #FF8F00
3 002 Wake up early Did you wake up before 6am? 2 3 #00897B

@ -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,
1 Date Meditate Wake up early
2 2015-01-25 0.0000 0.2557
3 2015-01-24 0.0000 0.2226
4 2015-01-23 0.0000 0.1991
5 2015-01-22 0.0000 0.1746
6 2015-01-21 0.0000 0.1379
7 2015-01-20 0.0000 0.0995
8 2015-01-19 0.0000 0.0706
9 2015-01-18 0.0000 0.0515
10 2015-01-17 0.0000 0.0315
11 2015-01-16 0.0000 0.0107

@ -43,7 +43,7 @@ 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.39") compileOnly("com.google.dagger:dagger:2.40")
implementation("com.google.guava:guava:31.0.1-android") implementation("com.google.guava:guava:31.0.1-android")
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2")

@ -167,7 +167,7 @@ class HabitsCSVExporter(
checksWriter.write(sb.toString()) checksWriter.write(sb.toString())
scoresWriter.write(sb.toString()) scoresWriter.write(sb.toString())
for (j in selectedHabits.indices) { for (j in selectedHabits.indices) {
checksWriter.write(checkmarks[j][i].toString()) checksWriter.write(checkmarks[j][i].value.toString())
checksWriter.write(delimiter) checksWriter.write(delimiter)
val score = String.format(Locale.US, "%.4f", scores[j][i].value) val score = String.format(Locale.US, "%.4f", scores[j][i].value)
scoresWriter.write(score) scoresWriter.write(score)

@ -68,17 +68,10 @@ data class Habit(
} }
} }
fun isFailedToday(): Boolean { fun isEnteredToday(): Boolean {
val today = DateUtils.getTodayWithOffset() val today = DateUtils.getTodayWithOffset()
val value = computedEntries.get(today).value val value = computedEntries.get(today).value
return if (isNumerical) { return value != Entry.UNKNOWN
when (targetType) {
NumericalHabitType.AT_LEAST -> value / 1000.0 < targetValue
NumericalHabitType.AT_MOST -> value / 1000.0 > targetValue
}
} else {
value == Entry.NO
}
} }
fun recompute() { fun recompute() {

@ -43,7 +43,7 @@ abstract class HabitList : Iterable<Habit> {
*/ */
constructor() { constructor() {
observable = ModelObservable() observable = ModelObservable()
filter = HabitMatcherBuilder().setArchivedAllowed(true).build() filter = HabitMatcher(isArchivedAllowed = true)
} }
protected constructor(filter: HabitMatcher) { protected constructor(filter: HabitMatcher) {

@ -22,19 +22,21 @@ data class HabitMatcher(
val isArchivedAllowed: Boolean = false, val isArchivedAllowed: Boolean = false,
val isReminderRequired: Boolean = false, val isReminderRequired: Boolean = false,
val isCompletedAllowed: Boolean = true, val isCompletedAllowed: Boolean = true,
val isEnteredAllowed: Boolean = true,
) { ) {
fun matches(habit: Habit): Boolean { fun matches(habit: Habit): Boolean {
if (!isArchivedAllowed && habit.isArchived) return false if (!isArchivedAllowed && habit.isArchived) return false
if (isReminderRequired && !habit.hasReminder()) 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 return true
} }
companion object { companion object {
@JvmField @JvmField
val WITH_ALARM = HabitMatcherBuilder() val WITH_ALARM = HabitMatcher(
.setArchivedAllowed(true) isArchivedAllowed = true,
.setReminderRequired(true) isReminderRequired = true,
.build() )
} }
} }

@ -1,48 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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
}
}

@ -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.HabitList
import org.isoron.uhabits.core.models.HabitMatcher 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.preferences.Preferences
import org.isoron.uhabits.core.ui.ThemeSwitcher import org.isoron.uhabits.core.ui.ThemeSwitcher
import javax.inject.Inject import javax.inject.Inject
@ -33,6 +32,7 @@ class ListHabitsMenuBehavior @Inject constructor(
) { ) {
private var showCompleted: Boolean private var showCompleted: Boolean
private var showArchived: Boolean private var showArchived: Boolean
fun onCreateHabit() { fun onCreateHabit() {
screen.showSelectHabitTypeDialog() screen.showSelectHabitTypeDialog()
} }
@ -97,13 +97,26 @@ class ListHabitsMenuBehavior @Inject constructor(
screen.applyTheme() screen.applyTheme()
} }
fun onPreferencesChanged() {
updateAdapterFilter()
}
private fun updateAdapterFilter() { private fun updateAdapterFilter() {
if (preferences.areQuestionMarksEnabled) {
adapter.setFilter(
HabitMatcher(
isArchivedAllowed = showArchived,
isEnteredAllowed = showCompleted,
)
)
} else {
adapter.setFilter( adapter.setFilter(
HabitMatcherBuilder() HabitMatcher(
.setArchivedAllowed(showArchived) isArchivedAllowed = showArchived,
.setCompletedAllowed(showCompleted) isCompletedAllowed = showCompleted,
.build()
) )
)
}
adapter.refresh() adapter.refresh()
} }

@ -77,11 +77,11 @@ abstract class DateUtils {
} }
@JvmStatic @JvmStatic
fun getLocalTime(): Long { fun getLocalTime(utcTimeInMillis: Long? = null): Long {
if (fixedLocalTime != null) return fixedLocalTime as Long if (fixedLocalTime != null) return fixedLocalTime as Long
val tz = getTimeZone() val tz = getTimeZone()
val now = Date().time val now = utcTimeInMillis ?: Date().time
return now + tz.getOffset(now) return now + tz.getOffset(now)
} }
@ -100,7 +100,7 @@ abstract class DateUtils {
format: Int, format: Int,
firstWeekDay: Int firstWeekDay: Int
): Array<String> { ): Array<String> {
val calendar = GregorianCalendar() val calendar = GregorianCalendar(getLocale())
calendar.set(DAY_OF_WEEK, firstWeekDay) calendar.set(DAY_OF_WEEK, firstWeekDay)
val daysNullable = ArrayList<String>() val daysNullable = ArrayList<String>()
@ -149,7 +149,7 @@ abstract class DateUtils {
*/ */
@JvmStatic @JvmStatic
fun getFirstWeekdayNumberAccordingToLocale(): Int { fun getFirstWeekdayNumberAccordingToLocale(): Int {
return GregorianCalendar().firstDayOfWeek return GregorianCalendar(getLocale()).firstDayOfWeek
} }
/** /**
@ -214,13 +214,7 @@ abstract class DateUtils {
@JvmStatic @JvmStatic
fun getStartOfTodayCalendarWithOffset(): GregorianCalendar = getCalendar(getStartOfTodayWithOffset()) fun getStartOfTodayCalendarWithOffset(): GregorianCalendar = getCalendar(getStartOfTodayWithOffset())
@JvmStatic private fun getTimeZone(): TimeZone {
fun getTimeZone(): TimeZone {
return fixedTimeZone ?: TimeZone.getDefault()
}
@JvmStatic
fun getTimezone(): TimeZone {
return fixedTimeZone ?: TimeZone.getDefault() return fixedTimeZone ?: TimeZone.getDefault()
} }
@ -236,8 +230,7 @@ abstract class DateUtils {
startDayMinuteOffset = minuteOffset startDayMinuteOffset = minuteOffset
} }
@JvmStatic private fun getLocale(): Locale {
fun getLocale(): Locale {
return fixedLocale ?: Locale.getDefault() return fixedLocale ?: Locale.getDefault()
} }

@ -34,6 +34,7 @@ import java.util.zip.ZipFile
class HabitsCSVExporterTest : BaseUnitTest() { class HabitsCSVExporterTest : BaseUnitTest() {
private lateinit var baseDir: File private lateinit var baseDir: File
@Before @Before
@Throws(Exception::class) @Throws(Exception::class)
override fun setUp() { override fun setUp() {
@ -41,12 +42,7 @@ class HabitsCSVExporterTest : BaseUnitTest() {
habitList.add(fixtures.createShortHabit()) habitList.add(fixtures.createShortHabit())
habitList.add(fixtures.createEmptyHabit()) habitList.add(fixtures.createEmptyHabit())
baseDir = Files.createTempDirectory("csv").toFile() baseDir = Files.createTempDirectory("csv").toFile()
} baseDir.deleteOnExit()
@Throws(Exception::class)
override fun tearDown() {
FileUtils.deleteDirectory(baseDir)
super.tearDown()
} }
@Test @Test
@ -63,14 +59,20 @@ class HabitsCSVExporterTest : BaseUnitTest() {
assertAbsolutePathExists(filename) assertAbsolutePathExists(filename)
val archive = File(filename) val archive = File(filename)
unzip(archive) unzip(archive)
assertPathExists("Habits.csv") val filesToCheck = arrayOf(
assertPathExists("001 Meditate/Checkmarks.csv") "001 Meditate/Checkmarks.csv",
assertPathExists("001 Meditate/Scores.csv") "001 Meditate/Scores.csv",
assertPathExists("002 Wake up early") "002 Wake up early/Checkmarks.csv",
assertPathExists("002 Wake up early/Checkmarks.csv") "002 Wake up early/Scores.csv",
assertPathExists("002 Wake up early/Scores.csv") "Checkmarks.csv",
assertPathExists("Checkmarks.csv") "Habits.csv",
assertPathExists("Scores.csv") "Scores.csv"
)
for (file in filesToCheck) {
assertPathExists(file)
assertFileAndReferenceAreEqual(file)
}
} }
@Throws(IOException::class) @Throws(IOException::class)
@ -104,4 +106,18 @@ class HabitsCSVExporterTest : BaseUnitTest() {
file.exists() 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))
)
)
}
} }

@ -53,12 +53,12 @@ class HabitListTest : BaseUnitTest() {
habitsArray[1].isArchived = true habitsArray[1].isArchived = true
habitsArray[4].isArchived = true habitsArray[4].isArchived = true
habitsArray[7].isArchived = true habitsArray[7].isArchived = true
activeHabits = habitList.getFiltered(HabitMatcherBuilder().build()) activeHabits = habitList.getFiltered(HabitMatcher())
reminderHabits = habitList.getFiltered( reminderHabits = habitList.getFiltered(
HabitMatcherBuilder() HabitMatcher(
.setArchivedAllowed(true) isArchivedAllowed = true,
.setReminderRequired(true) isReminderRequired = true,
.build() )
) )
} }
@ -181,10 +181,10 @@ class HabitListTest : BaseUnitTest() {
fun testOrder_inherit() { fun testOrder_inherit() {
habitList.primaryOrder = HabitList.Order.BY_COLOR_ASC habitList.primaryOrder = HabitList.Order.BY_COLOR_ASC
val filteredList = habitList.getFiltered( val filteredList = habitList.getFiltered(
HabitMatcherBuilder() HabitMatcher(
.setArchivedAllowed(false) isArchivedAllowed = false,
.setCompletedAllowed(false) isCompletedAllowed = false,
.build() )
) )
assertEquals(filteredList.primaryOrder, HabitList.Order.BY_COLOR_ASC) assertEquals(filteredList.primaryOrder, HabitList.Order.BY_COLOR_ASC)
} }

@ -82,12 +82,12 @@ class HabitTest : BaseUnitTest() {
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun test_isFailed() { fun test_isEntered() {
val h = modelFactory.buildHabit() val h = modelFactory.buildHabit()
assertFalse(h.isFailedToday()) assertFalse(h.isEnteredToday())
h.originalEntries.add(Entry(getToday(), Entry.NO)) h.originalEntries.add(Entry(getToday(), Entry.NO))
h.recompute() h.recompute()
assertTrue(h.isFailedToday()) assertTrue(h.isEnteredToday())
} }
@Test @Test
@ -119,35 +119,6 @@ class HabitTest : BaseUnitTest() {
assertTrue(h.isCompletedToday()) 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 @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testURI() { fun testURI() {

@ -28,7 +28,7 @@ import org.isoron.uhabits.core.database.Database
import org.isoron.uhabits.core.database.Repository import org.isoron.uhabits.core.database.Repository
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList 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.ModelObservable
import org.isoron.uhabits.core.models.Reminder import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList import org.isoron.uhabits.core.models.WeekdayList
@ -69,12 +69,12 @@ class SQLiteHabitListTest : BaseUnitTest() {
habitsArray[4].isArchived = true habitsArray[4].isArchived = true
habitsArray[7].isArchived = true habitsArray[7].isArchived = true
habitList.update(habitsArray) habitList.update(habitsArray)
activeHabits = habitList.getFiltered(HabitMatcherBuilder().build()) activeHabits = habitList.getFiltered(HabitMatcher())
reminderHabits = habitList.getFiltered( reminderHabits = habitList.getFiltered(
HabitMatcherBuilder() HabitMatcher(
.setArchivedAllowed(true) isArchivedAllowed = true,
.setReminderRequired(true) isReminderRequired = true,
.build() )
) )
habitList.observable.addListener(listener) habitList.observable.addListener(listener)
} }

@ -37,6 +37,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.truncate
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.util.Calendar import java.util.Calendar
import java.util.GregorianCalendar
import java.util.Locale import java.util.Locale
import java.util.TimeZone import java.util.TimeZone
@ -58,6 +59,129 @@ class DateUtilsTest : BaseUnitTest() {
assertThat(formatted, equalTo("Thu\n31")) 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 @Test
fun testTruncate_dayOfWeek() { fun testTruncate_dayOfWeek() {
val field = DateUtils.TruncateField.WEEK_NUMBER val field = DateUtils.TruncateField.WEEK_NUMBER
@ -142,22 +266,39 @@ class DateUtilsTest : BaseUnitTest() {
assertThat(truncate(field, t2, firstWeekday), equalTo(expected)) 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 @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testMillisecondsUntilTomorrow() { fun testMillisecondsUntilTomorrow() {
setFixedTimeZone(TimeZone.getTimeZone("GMT")) setFixedTimeZone(TimeZone.getTimeZone("GMT"))
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59)) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59))
assertThat(millisecondsUntilTomorrowWithOffset(), equalTo(DateUtils.MINUTE_LENGTH)) assertThat(millisecondsUntilTomorrowWithOffset(), equalTo(DateUtils.MINUTE_LENGTH))
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 20, 0)) setFixedLocalTime(fixedStartOfTodayWithOffset(20))
assertThat( assertThat(
millisecondsUntilTomorrowWithOffset(), millisecondsUntilTomorrowWithOffset(),
equalTo(4 * DateUtils.HOUR_LENGTH) equalTo(4 * DateUtils.HOUR_LENGTH)
) )
setStartDayOffset(3, 30) setStartDayOffset(HOUR_OFFSET, 30)
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59)) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59))
assertThat( assertThat(
millisecondsUntilTomorrowWithOffset(), 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)) setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 2, 1, 0))
assertThat( 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 @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testGetTodayWithOffset() { fun testGetTodayWithOffset() {
@ -469,4 +656,9 @@ class DateUtilsTest : BaseUnitTest() {
unixTime(2018, Calendar.APRIL, 1, 18, 0) unixTime(2018, Calendar.APRIL, 1, 18, 0)
) )
} }
companion object {
const val HOUR_OFFSET = 3
const val HOURS_IN_ONE_DAY = 24
}
} }

@ -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)
}
}

@ -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,
1 HabitName HabitDescription HabitCategory CalendarDate Value CommentText
2 Breed dragons with love and fire Diet & Food 2016-03-18 1
3 Breed dragons with love and fire Diet & Food 2016-03-19 1
4 Breed dragons with love and fire Diet & Food 2016-03-21 1
5 Reduce sleep only 2 hours per day Time Management 2016-03-15 1
6 Reduce sleep only 2 hours per day Time Management 2016-03-16 1
7 Reduce sleep only 2 hours per day Time Management 2016-03-17 1
8 Reduce sleep only 2 hours per day Time Management 2016-03-21 1
9 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
10 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
11 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
12 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
13 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
14 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
15 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
16 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
17 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-15 1
18 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-17 1
19 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-21 1

@ -22,7 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins { plugins {
application application
id("kotlin") id("kotlin")
id("com.github.johnrengelman.shadow") version "7.0.0" id("com.github.johnrengelman.shadow") version "7.1.0"
} }

Loading…
Cancel
Save