Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
830e28ca8e Bump com.android.tools:desugar_jdk_libs from 1.2.2 to 2.0.3
Bumps [com.android.tools:desugar_jdk_libs](https://github.com/google/desugar_jdk_libs) from 1.2.2 to 2.0.3.
- [Release notes](https://github.com/google/desugar_jdk_libs/releases)
- [Changelog](https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/desugar_jdk_libs/commits)

---
updated-dependencies:
- dependency-name: com.android.tools:desugar_jdk_libs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 23:31:55 +00:00
99 changed files with 357 additions and 725 deletions

View File

@@ -4,7 +4,6 @@ updates:
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "github-actions"
directory: "/"
schedule:

View File

@@ -12,17 +12,17 @@ jobs:
timeout-minutes: 30
steps:
- name: Check out source code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Build project
run: ./build.sh build
- name: Run Android tests
run: ./build.sh android-tests-parallel 28 29 30 32 33 34
run: ./build.sh android-tests-parallel 28 29 30 31 32 33
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: build
path: |

1
.gitignore vendored
View File

@@ -17,4 +17,3 @@ node_modules
*xcuserdata*
*.sketch
crowdin.yml
kotlin-js-store

View File

@@ -1,22 +1,5 @@
# Changelog
## [2.2.0] -- 2024-01-30
### Added
- Add support for Android 14 (@iSoron, @hiqua)
- Allow user to change app language (@leondzn)
### Fixed
- Implement workaround to make notifications non-dismissible in Android 14 (@iSoron, #1872)
- Fix splash screen background color in dark mode (@SIKV, #1888)
## [2.1.3] -- 2023-08-28
### Fixed
- Use text input on Samsung devices (@iSoron, #1719)
- Prevent crash if alarm permission is revoked (@iSoron)
- Adjust widget colors (@iSoron)
- Fix bug preventing screens from updating at midnight (@iSoron)
- Fix skip button in locales that use comma instead of dot (@iSoron, #1721)
## [2.1.2] -- 2023-05-26
### Fixed
- Fix bug that caused widget to enter checkmark on wrong date (@iSoron, #1541)

View File

@@ -1,10 +1,11 @@
plugins {
val kotlinVersion = "1.9.22"
id("com.android.application") version "8.1.4" apply (false)
val kotlinVersion = "1.7.21"
id("com.android.application") version ("7.4.2") 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)
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
id("org.jlleitschuh.gradle.ktlint") version "11.4.2"
}
apply {

View File

@@ -181,7 +181,7 @@ android_test() {
OUT_INSTRUMENT=${ANDROID_OUTPUTS_DIR}/instrument-${API}.txt
OUT_LOGCAT=${ANDROID_OUTPUTS_DIR}/logcat-${API}.txt
FAILED_TESTS=""
for i in {1..10}; do
for i in {1..5}; do
log_info "Running $size instrumented tests (attempt $i)..."
$ADB shell am instrument \
-r -e coverage true -e size "$size" $FAILED_TESTS \

View File

@@ -3,6 +3,3 @@ org.gradle.daemon=true
org.gradle.jvmargs=-Xms2048m -Xmx2048m
android.useAndroidX=true
android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

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

View File

@@ -18,10 +18,11 @@
*/
plugins {
id("com.github.triplet.play") version "3.8.6"
id("com.android.application") version "8.1.4"
id("com.github.triplet.play") version "3.7.0"
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.kapt")
id("org.jetbrains.kotlin.android.extensions")
id("org.jlleitschuh.gradle.ktlint")
}
@@ -29,27 +30,15 @@ tasks.compileLint {
dependsOn("updateTranslators")
}
/*
Added on top of kotlinOptions to work around this issue:
https://youtrack.jetbrains.com/issue/KTIJ-24311/task-current-target-is-17-and-kaptGenerateStubsProductionDebugKotlin-task-current-target-is-1.8-jvm-target-compatibility-should#focus=Comments-27-6798448.0-0
Updating gradle might fix this, so try again in the future to remove this and run:
./gradlew --rerun-tasks :uhabits-android:kaptGenerateStubsReleaseKotlin
If this doesn't produce any warning, try to remove it.
*/
kotlin {
jvmToolchain(11)
}
android {
namespace = "org.isoron.uhabits"
compileSdk = 34
compileSdk = 32
defaultConfig {
versionCode = 20200
versionName = "2.2.0"
minSdk = 28
targetSdk = 34
targetSdk = 31
applicationId = "org.isoron.uhabits"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -81,11 +70,8 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
targetCompatibility(JavaVersion.VERSION_11)
sourceCompatibility(JavaVersion.VERSION_11)
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
targetCompatibility(JavaVersion.VERSION_1_8)
sourceCompatibility(JavaVersion.VERSION_1_8)
}
buildFeatures {
@@ -94,9 +80,9 @@ android {
}
dependencies {
val daggerVersion = "2.51.1"
val kotlinVersion = "1.9.22"
val kxCoroutinesVersion = "1.7.3"
val daggerVersion = "2.46"
val kotlinVersion = "1.7.21"
val kxCoroutinesVersion = "1.6.4"
val ktorVersion = "1.6.8"
val espressoVersion = "3.5.1"
@@ -106,17 +92,17 @@ dependencies {
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.3")
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
androidTestImplementation("androidx.annotation:annotation:1.7.1")
androidTestImplementation("androidx.annotation:annotation:1.5.0")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
androidTestImplementation("androidx.test:rules:1.5.0")
androidTestImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
compileOnly("javax.annotation:jsr250-api:1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
implementation("com.github.AppIntro:AppIntro:6.3.1")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
implementation("com.github.AppIntro:AppIntro:6.2.0")
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("com.google.guava:guava:31.1-android")
implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
@@ -124,18 +110,17 @@ dependencies {
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.6.1")
implementation("androidx.appcompat:appcompat:1.5.1")
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.11.0")
implementation("com.opencsv:opencsv:5.9")
implementation("nl.dionsegijn:konfetti-xml:2.0.2")
implementation("com.google.android.material:material:1.8.0")
implementation("com.opencsv:opencsv:5.7.1")
implementation(project(":uhabits-core"))
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
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.2.1")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
}
kapt {

View File

@@ -19,6 +19,7 @@
package org.isoron.uhabits
import com.nhaarman.mockitokotlin2.mock
import dagger.Component
import dagger.Module
import dagger.Provides
@@ -34,7 +35,6 @@ import org.isoron.uhabits.inject.ActivityScope
import org.isoron.uhabits.inject.HabitModule
import org.isoron.uhabits.inject.HabitsActivityModule
import org.isoron.uhabits.inject.HabitsApplicationComponent
import org.mockito.kotlin.mock
@Module
class TestModule {

View File

@@ -20,6 +20,7 @@ package org.isoron.uhabits.acceptance.steps
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso
@@ -32,11 +33,11 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers
import org.isoron.uhabits.BaseUserInterfaceTest
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.habits.list.ListHabitsActivity
import org.junit.Assert.assertTrue
object CommonSteps : BaseUserInterfaceTest() {
fun pressBack() {
@@ -132,7 +133,7 @@ object CommonSteps : BaseUserInterfaceTest() {
}
@Throws(Exception::class)
fun verifyOpensWebsite(url: String) {
fun verifyOpensWebsite(url: String?) {
var browserPkg = "org.chromium.webview_shell"
if (SDK_INT <= Build.VERSION_CODES.M) {
browserPkg = "com.android.browser"
@@ -147,19 +148,15 @@ object CommonSteps : BaseUserInterfaceTest() {
Screen.LIST_HABITS ->
Espresso.onView(ViewMatchers.withClassName(CoreMatchers.endsWith("ListHabitsRootView")))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Screen.SHOW_HABIT ->
Espresso.onView(ViewMatchers.withId(R.id.subtitleCard))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Screen.EDIT_HABIT ->
Espresso.onView(ViewMatchers.withId(R.id.questionInput))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Screen.SELECT_HABIT_TYPE ->
Espresso.onView(ViewMatchers.withText(R.string.yes_or_no_example))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
else -> throw IllegalStateException()
}
}

View File

@@ -21,9 +21,9 @@ package org.isoron.uhabits.acceptance.steps
import android.os.Build.VERSION.SDK_INT
import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.isoron.uhabits.BaseUserInterfaceTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
object WidgetSteps {
@Throws(Exception::class)

View File

@@ -18,7 +18,6 @@
*/
package org.isoron.uhabits.activities.common.views
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
@@ -53,8 +52,7 @@ class FrequencyChartTest : BaseViewTest() {
@Test
@Throws(Throwable::class)
fun testRender_withDataOffset() {
val e = MotionEvent.obtain(0, 0, 0, 0f, 0f, 0)
view.onScroll(e, e, -dpToPixels(150), 0f)
view.onScroll(null, null, -dpToPixels(150), 0f)
view.invalidate()
assertRenders(view, BASE_PATH + "renderDataOffset.png")
}

View File

@@ -18,7 +18,6 @@
*/
package org.isoron.uhabits.activities.common.views
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
@@ -64,8 +63,7 @@ class ScoreChartTest : BaseViewTest() {
@Test
@Throws(Throwable::class)
fun testRender_withDataOffset() {
val e = MotionEvent.obtain(0, 0, 0, 0f, 0f, 0)
view.onScroll(e, e, -dpToPixels(150), 0f)
view.onScroll(null, null, -dpToPixels(150), 0f)
view.invalidate()
assertRenders(view, BASE_PATH + "renderDataOffset.png")
}

View File

@@ -20,15 +20,15 @@ package org.isoron.uhabits.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.whenever
import org.isoron.uhabits.BaseViewTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@MediumTest

View File

@@ -20,6 +20,9 @@ package org.isoron.uhabits.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.BaseViewTest
@@ -27,9 +30,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.HintList
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@MediumTest

View File

@@ -16,13 +16,12 @@
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.isoron.uhabits">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".HabitsApplication"
@@ -30,7 +29,6 @@
android:backupAgent=".HabitsBackupAgent"
android:icon="@mipmap/ic_launcher"
android:label="@string/main_activity_title"
android:localeConfig="@xml/locales_config"
android:supportsRtl="true"
android:theme="@style/AppBaseTheme">

View File

@@ -22,6 +22,7 @@ import java.util.Locale;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;

View File

@@ -23,13 +23,14 @@ import android.graphics.Paint.*;
import android.os.*;
import androidx.core.view.*;
import androidx.core.view.accessibility.*;
import androidx.core.widget.*;
import android.text.format.*;
import android.view.*;
import android.view.accessibility.*;
import androidx.customview.widget.ExploreByTouchHelper;
import com.android.*;
import com.android.datetimepicker.*;
import com.android.datetimepicker.date.MonthAdapter.*;

View File

@@ -23,6 +23,7 @@ import android.graphics.Paint.*;
import android.util.*;
import android.view.*;
import com.android.*;
import com.android.datetimepicker.*;
import org.isoron.uhabits.R;

View File

@@ -28,6 +28,7 @@ import android.view.View.*;
import android.view.accessibility.*;
import android.widget.*;
import com.android.*;
import com.android.datetimepicker.*;
import org.isoron.uhabits.R;

View File

@@ -44,21 +44,21 @@ class AndroidDataView(
addUpdateListener(this@AndroidDataView)
}
override fun onTouchEvent(event: MotionEvent) = detector.onTouchEvent(event)
override fun onDown(e: MotionEvent) = true
override fun onShowPress(e: MotionEvent) = Unit
override fun onTouchEvent(event: MotionEvent?) = detector.onTouchEvent(event)
override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) = Unit
override fun onSingleTapUp(e: MotionEvent): Boolean {
override fun onSingleTapUp(e: MotionEvent?): Boolean {
return handleClick(e, true)
}
override fun onLongPress(e: MotionEvent) {
override fun onLongPress(e: MotionEvent?) {
handleClick(e)
}
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent,
e2: MotionEvent?,
dx: Float,
dy: Float
): Boolean {
@@ -80,7 +80,7 @@ class AndroidDataView(
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
e2: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
@@ -100,7 +100,7 @@ class AndroidDataView(
return false
}
override fun onAnimationUpdate(animation: ValueAnimator) {
override fun onAnimationUpdate(animation: ValueAnimator?) {
if (!scroller.isFinished) {
scroller.computeScrollOffset()
updateDataOffset()
@@ -127,11 +127,11 @@ class AndroidDataView(
}
}
private fun handleClick(e: MotionEvent, isSingleTap: Boolean = false): Boolean {
private fun handleClick(e: MotionEvent?, isSingleTap: Boolean = false): Boolean {
val x: Float
val y: Float
try {
val pointerId = e.getPointerId(0)
val pointerId = e!!.getPointerId(0)
x = e.getX(pointerId)
y = e.getY(pointerId)
} catch (ex: RuntimeException) {

View File

@@ -33,19 +33,17 @@ import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
import org.isoron.uhabits.utils.getCenter
import org.isoron.uhabits.utils.sres
class CheckmarkDialog : AppCompatDialogFragment() {
var onToggle: (Int, String, Float, Float) -> Unit = { _, _, _, _ -> }
var onToggle: (Int, String) -> Unit = { _, _ -> }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val appComponent = (requireActivity().application as HabitsApplication).component
val prefs = appComponent.preferences
val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context))
val color = requireArguments().getInt("color")
arrayOf(view.yesBtn, view.skipBtn).forEach {
it.setTextColor(color)
it.setTextColor(requireArguments().getInt("color"))
}
arrayOf(view.noBtn, view.unknownBtn).forEach {
it.setTextColor(view.root.sres.getColor(R.attr.contrast60))
@@ -64,8 +62,7 @@ class CheckmarkDialog : AppCompatDialogFragment() {
}
fun onClick(v: Int) {
val notes = view.notes.text.toString().trim()
val location = view.yesBtn.getCenter()
onToggle(v, notes, location.x, location.y)
onToggle(v, notes)
requireDialog().dismiss()
}
view.yesBtn.setOnClickListener { onClick(YES_MANUAL) }

View File

@@ -22,101 +22,97 @@ package org.isoron.uhabits.activities.common.dialogs
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import kotlinx.android.synthetic.main.frequency_picker_dialog.view.*
import org.isoron.uhabits.R
import org.isoron.uhabits.databinding.FrequencyPickerDialogBinding
class FrequencyPickerDialog(
var freqNumerator: Int,
var freqDenominator: Int
) : AppCompatDialogFragment() {
private var _binding: FrequencyPickerDialogBinding? = null
private val binding get() = _binding!!
lateinit var contentView: View
var onFrequencyPicked: (num: Int, den: Int) -> Unit = { _, _ -> }
constructor() : this(1, 1)
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = FrequencyPickerDialogBinding.inflate(LayoutInflater.from(requireActivity()))
val inflater = LayoutInflater.from(requireActivity())
contentView = inflater.inflate(R.layout.frequency_picker_dialog, null)
addBeforeAfterText(
this.getString(R.string.every_x_days),
binding.everyXDaysContainer
contentView.everyXDaysContainer
)
addBeforeAfterText(
this.getString(R.string.x_times_per_week),
binding.xTimesPerWeekContainer
contentView.xTimesPerWeekContainer
)
addBeforeAfterText(
this.getString(R.string.x_times_per_month),
binding.xTimesPerMonthContainer
contentView.xTimesPerMonthContainer
)
addBeforeAfterText(
this.getString(R.string.x_times_per_y_days),
binding.xTimesPerYDaysContainer
contentView.xTimesPerYDaysContainer
)
binding.everyDayRadioButton.setOnClickListener {
check(binding.everyDayRadioButton)
contentView.everyDayRadioButton.setOnClickListener {
check(contentView.everyDayRadioButton)
}
binding.everyXDaysRadioButton.setOnClickListener {
check(binding.everyXDaysRadioButton)
val everyXDaysTextView = binding.everyXDaysTextView
contentView.everyXDaysRadioButton.setOnClickListener {
check(contentView.everyXDaysRadioButton)
val everyXDaysTextView = contentView.everyXDaysTextView
selectInputField(everyXDaysTextView)
}
binding.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(binding.everyXDaysRadioButton)
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(contentView.everyXDaysRadioButton)
}
binding.xTimesPerWeekRadioButton.setOnClickListener {
check(binding.xTimesPerWeekRadioButton)
selectInputField(binding.xTimesPerWeekTextView)
contentView.xTimesPerWeekRadioButton.setOnClickListener {
check(contentView.xTimesPerWeekRadioButton)
selectInputField(contentView.xTimesPerWeekTextView)
}
binding.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(binding.xTimesPerWeekRadioButton)
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(contentView.xTimesPerWeekRadioButton)
}
binding.xTimesPerMonthRadioButton.setOnClickListener {
check(binding.xTimesPerMonthRadioButton)
selectInputField(binding.xTimesPerMonthTextView)
contentView.xTimesPerMonthRadioButton.setOnClickListener {
check(contentView.xTimesPerMonthRadioButton)
selectInputField(contentView.xTimesPerMonthTextView)
}
binding.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(binding.xTimesPerMonthRadioButton)
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(contentView.xTimesPerMonthRadioButton)
}
binding.xTimesPerYDaysRadioButton.setOnClickListener {
check(binding.xTimesPerYDaysRadioButton)
selectInputField(binding.xTimesPerYDaysXTextView)
contentView.xTimesPerYDaysRadioButton.setOnClickListener {
check(contentView.xTimesPerYDaysRadioButton)
selectInputField(contentView.xTimesPerYDaysXTextView)
}
binding.xTimesPerYDaysXTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(binding.xTimesPerYDaysRadioButton)
contentView.xTimesPerYDaysXTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(contentView.xTimesPerYDaysRadioButton)
}
binding.xTimesPerYDaysYTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(binding.xTimesPerYDaysRadioButton)
contentView.xTimesPerYDaysYTextView.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) check(contentView.xTimesPerYDaysRadioButton)
}
return AlertDialog.Builder(requireActivity())
.setView(binding.root)
.setView(contentView)
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
.create()
}
@@ -138,35 +134,31 @@ class FrequencyPickerDialog(
var numerator = 1
var denominator = 1
when {
binding.everyDayRadioButton.isChecked -> {
contentView.everyDayRadioButton.isChecked -> {
// NOP
}
binding.everyXDaysRadioButton.isChecked -> {
if (binding.everyXDaysTextView.text.isNotEmpty()) {
denominator = Integer.parseInt(binding.everyXDaysTextView.text.toString())
contentView.everyXDaysRadioButton.isChecked -> {
if (contentView.everyXDaysTextView.text.isNotEmpty()) {
denominator = Integer.parseInt(contentView.everyXDaysTextView.text.toString())
}
}
binding.xTimesPerWeekRadioButton.isChecked -> {
if (binding.xTimesPerWeekTextView.text.isNotEmpty()) {
numerator = Integer.parseInt(binding.xTimesPerWeekTextView.text.toString())
contentView.xTimesPerWeekRadioButton.isChecked -> {
if (contentView.xTimesPerWeekTextView.text.isNotEmpty()) {
numerator = Integer.parseInt(contentView.xTimesPerWeekTextView.text.toString())
denominator = 7
}
}
binding.xTimesPerYDaysRadioButton.isChecked -> {
if (binding.xTimesPerYDaysXTextView.text.isNotEmpty() && binding.xTimesPerYDaysYTextView.text.isNotEmpty()) {
contentView.xTimesPerYDaysRadioButton.isChecked -> {
if (contentView.xTimesPerYDaysXTextView.text.isNotEmpty() && contentView.xTimesPerYDaysYTextView.text.isNotEmpty()) {
numerator =
Integer.parseInt(binding.xTimesPerYDaysXTextView.text.toString())
Integer.parseInt(contentView.xTimesPerYDaysXTextView.text.toString())
denominator =
Integer.parseInt(binding.xTimesPerYDaysYTextView.text.toString())
Integer.parseInt(contentView.xTimesPerYDaysYTextView.text.toString())
}
}
else -> {
if (binding.xTimesPerMonthTextView.text.isNotEmpty()) {
numerator = Integer.parseInt(binding.xTimesPerMonthTextView.text.toString())
if (contentView.xTimesPerMonthTextView.text.isNotEmpty()) {
numerator = Integer.parseInt(contentView.xTimesPerMonthTextView.text.toString())
denominator = 30
}
}
@@ -193,27 +185,27 @@ class FrequencyPickerDialog(
private fun populateViews() {
uncheckAll()
if (freqDenominator == 30 || freqDenominator == 31) {
binding.xTimesPerMonthRadioButton.isChecked = true
binding.xTimesPerMonthTextView.setText(freqNumerator.toString())
selectInputField(binding.xTimesPerMonthTextView)
contentView.xTimesPerMonthRadioButton.isChecked = true
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
selectInputField(contentView.xTimesPerMonthTextView)
} else {
if (freqNumerator == 1) {
if (freqDenominator == 1) {
binding.everyDayRadioButton.isChecked = true
contentView.everyDayRadioButton.isChecked = true
} else {
binding.everyXDaysRadioButton.isChecked = true
binding.everyXDaysTextView.setText(freqDenominator.toString())
selectInputField(binding.everyXDaysTextView)
contentView.everyXDaysRadioButton.isChecked = true
contentView.everyXDaysTextView.setText(freqDenominator.toString())
selectInputField(contentView.everyXDaysTextView)
}
} else {
if (freqDenominator == 7) {
binding.xTimesPerWeekRadioButton.isChecked = true
binding.xTimesPerWeekTextView.setText(freqNumerator.toString())
selectInputField(binding.xTimesPerWeekTextView)
contentView.xTimesPerWeekRadioButton.isChecked = true
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
selectInputField(contentView.xTimesPerWeekTextView)
} else {
binding.xTimesPerYDaysRadioButton.isChecked = true
binding.xTimesPerYDaysXTextView.setText(freqNumerator.toString())
binding.xTimesPerYDaysYTextView.setText(freqDenominator.toString())
contentView.xTimesPerYDaysRadioButton.isChecked = true
contentView.xTimesPerYDaysXTextView.setText(freqNumerator.toString())
contentView.xTimesPerYDaysYTextView.setText(freqDenominator.toString())
}
}
}
@@ -224,10 +216,10 @@ class FrequencyPickerDialog(
}
private fun uncheckAll() {
binding.everyDayRadioButton.isChecked = false
binding.everyXDaysRadioButton.isChecked = false
binding.xTimesPerWeekRadioButton.isChecked = false
binding.xTimesPerMonthRadioButton.isChecked = false
binding.xTimesPerYDaysRadioButton.isChecked = false
contentView.everyDayRadioButton.isChecked = false
contentView.everyXDaysRadioButton.isChecked = false
contentView.xTimesPerWeekRadioButton.isChecked = false
contentView.xTimesPerMonthRadioButton.isChecked = false
contentView.xTimesPerYDaysRadioButton.isChecked = false
}
}

View File

@@ -2,20 +2,17 @@ package org.isoron.uhabits.activities.common.dialogs
import android.app.Dialog
import android.os.Bundle
import android.provider.Settings
import android.text.method.DigitsKeyListener
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AppCompatDialogFragment
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
import org.isoron.uhabits.utils.InterfaceUtils
import org.isoron.uhabits.utils.getCenter
import org.isoron.uhabits.utils.requestFocusWithKeyboard
import org.isoron.uhabits.utils.sres
import java.text.DecimalFormat
@@ -25,7 +22,7 @@ import java.text.ParseException
class NumberDialog : AppCompatDialogFragment() {
var onToggle: (Double, String, Float, Float) -> Unit = { _, _, _, _ -> }
var onToggle: (Double, String) -> Unit = { _, _ -> }
var onDismiss: () -> Unit = {}
private var originalNotes: String = ""
@@ -68,7 +65,7 @@ class NumberDialog : AppCompatDialogFragment() {
save()
}
view.skipBtnNumber.setOnClickListener {
view.value.setText(DecimalFormat("#.###").format((Entry.SKIP.toDouble() / 1000)))
view.value.setText((Entry.SKIP.toDouble() / 1000).toString())
save()
}
view.notes.setOnEditorActionListener { v, actionId, event ->
@@ -89,15 +86,6 @@ class NumberDialog : AppCompatDialogFragment() {
// https://stackoverflow.com/a/34256139
val separator = DecimalFormatSymbols.getInstance().decimalSeparator
view.value.keyListener = DigitsKeyListener.getInstance("0123456789$separator")
// https://github.com/flutter/flutter/issues/61175
val currKeyboard = Settings.Secure.getString(
requireContext().contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
)
if (currKeyboard.contains("swiftkey") || currKeyboard.contains("samsung")) {
view.value.inputType = EditorInfo.TYPE_CLASS_TEXT
}
}
fun save() {
@@ -105,17 +93,12 @@ class NumberDialog : AppCompatDialogFragment() {
try {
val numberFormat = NumberFormat.getInstance()
val valueStr = view.value.text.toString()
value = if (valueStr.isNotEmpty()) {
numberFormat.parse(valueStr)!!.toDouble()
} else {
Entry.UNKNOWN.toDouble() / 1000
}
value = numberFormat.parse(valueStr)!!.toDouble()
} catch (e: ParseException) {
// NOP
}
val notes = view.notes.text.toString()
val location = view.saveBtn.getCenter()
onToggle(value, notes, location.x, location.y)
onToggle(value, notes)
requireDialog().dismiss()
}
}

View File

@@ -58,7 +58,6 @@ class RingView : View {
private var em = 0f
private var text: String?
private var textSize: Float
private var isStrokedTextEnabled: Boolean = false
private var enableFontAwesome = false
private var internalDrawingCache: Bitmap? = null
private var cacheCanvas: Canvas? = null
@@ -132,10 +131,6 @@ class RingView : View {
invalidate()
}
fun setIsStrokedTextEnabled(isStroked: Boolean) {
this.isStrokedTextEnabled = isStroked
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val activeCanvas: Canvas?
@@ -164,12 +159,6 @@ class RingView : View {
pRing!!.xfermode = null
pRing!!.color = color
pRing!!.textSize = textSize
if (isStrokedTextEnabled) {
pRing!!.style = Paint.Style.STROKE
pRing!!.strokeWidth = textSize / 15f
}
if (enableFontAwesome) pRing!!.typeface = getFontAwesome(context)
activeCanvas.drawText(
text!!,

View File

@@ -65,7 +65,7 @@ abstract class ScrollableChart : View, GestureDetector.OnGestureListener, Animat
}
override fun onFling(
e1: MotionEvent?,
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
@@ -116,7 +116,7 @@ abstract class ScrollableChart : View, GestureDetector.OnGestureListener, Animat
return BundleSavedState(superState, bundle)
}
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dx: Float, dy: Float): Boolean {
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, dx: Float, dy: Float): Boolean {
var dx = dx
if (scrollerBucketSize == 0) return false
if (abs(dx) > abs(dy)) {

View File

@@ -35,6 +35,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import com.android.datetimepicker.time.RadialPickerLayout
import com.android.datetimepicker.time.TimePickerDialog
import kotlinx.android.synthetic.main.activity_edit_habit.nameInput
import kotlinx.android.synthetic.main.activity_edit_habit.notesInput
import kotlinx.android.synthetic.main.activity_edit_habit.questionInput
import kotlinx.android.synthetic.main.activity_edit_habit.targetInput
import kotlinx.android.synthetic.main.activity_edit_habit.unitInput
import org.isoron.platform.gui.toInt
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
@@ -266,9 +271,9 @@ class EditHabitActivity : AppCompatActivity() {
habit.copyFrom(original)
}
habit.name = binding.nameInput.text.trim().toString()
habit.question = binding.questionInput.text.trim().toString()
habit.description = binding.notesInput.text.trim().toString()
habit.name = nameInput.text.trim().toString()
habit.question = questionInput.text.trim().toString()
habit.description = notesInput.text.trim().toString()
habit.color = color
if (reminderHour >= 0) {
habit.reminder = Reminder(reminderHour, reminderMin, reminderDays)
@@ -278,9 +283,9 @@ class EditHabitActivity : AppCompatActivity() {
habit.frequency = Frequency(freqNum, freqDen)
if (habitType == HabitType.NUMERICAL) {
habit.targetValue = binding.targetInput.text.toString().toDouble()
habit.targetValue = targetInput.text.toString().toDouble()
habit.targetType = targetType
habit.unit = binding.unitInput.text.trim().toString()
habit.unit = unitInput.text.trim().toString()
}
habit.type = habitType
@@ -303,13 +308,13 @@ class EditHabitActivity : AppCompatActivity() {
private fun validate(): Boolean {
var isValid = true
if (binding.nameInput.text.isEmpty()) {
binding.nameInput.error = getFormattedValidationError(R.string.validation_cannot_be_blank)
if (nameInput.text.isEmpty()) {
nameInput.error = getFormattedValidationError(R.string.validation_cannot_be_blank)
isValid = false
}
if (habitType == HabitType.NUMERICAL) {
if (binding.targetInput.text.isEmpty()) {
binding.targetInput.error = getString(R.string.validation_cannot_be_blank)
if (targetInput.text.isEmpty()) {
targetInput.error = getString(R.string.validation_cannot_be_blank)
isValid = false
}
}

View File

@@ -19,17 +19,12 @@
package org.isoron.uhabits.activities.habits.list
import android.Manifest.permission.POST_NOTIFICATIONS
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.checkSelfPermission
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.isoron.uhabits.BaseExceptionHandler
@@ -61,16 +56,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
lateinit var midnightTimer: MidnightTimer
private val scope = CoroutineScope(Dispatchers.Main)
private var permissionAlreadyRequested = false
private val permissionLauncher =
registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
scheduleReminders()
} else {
Log.i("ListHabitsActivity", "POST_NOTIFICATIONS denied")
}
}
private lateinit var menu: ListHabitsMenu
override fun onQuestionMarksChanged() {
@@ -116,26 +101,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
screen.onAttached()
rootView.postInvalidate()
midnightTimer.onResume()
if (appComponent.reminderScheduler.hasHabitsWithReminders()) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
scheduleReminders()
} else {
if (checkSelfPermission(this, POST_NOTIFICATIONS) == PERMISSION_GRANTED) {
scheduleReminders()
} else {
// If we have not requested the permission yet, request it. Otherwide do
// nothing. This check is necessary to avoid an infinite onResume loop in case
// the user denies the permission.
if (!permissionAlreadyRequested) {
Log.i("ListHabitsActivity", "Requestion permission: POST_NOTIFICATIONS")
permissionLauncher.launch(POST_NOTIFICATIONS)
permissionAlreadyRequested = true
}
}
}
}
appComponent.reminderScheduler.scheduleAll()
taskRunner.run {
try {
AutoBackup(this@ListHabitsActivity).run()
@@ -151,10 +117,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
super.onResume()
}
private fun scheduleReminders() {
appComponent.reminderScheduler.scheduleAll()
}
override fun onCreateOptionsMenu(m: Menu): Boolean {
menu.onCreate(menuInflater, m)
return true
@@ -165,7 +127,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
return menu.onItemSelected(item)
}
@Deprecated("Deprecated in Java")
override fun onActivityResult(request: Int, result: Int, data: Intent?) {
super.onActivityResult(request, result, data)
screen.onResult(request, result, data)

View File

@@ -23,7 +23,6 @@ import android.content.Context
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import android.widget.RelativeLayout
import nl.dionsegijn.konfetti.xml.KonfettiView
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.views.ScrollableChart
import org.isoron.uhabits.activities.common.views.TaskProgressBar
@@ -70,9 +69,6 @@ class ListHabitsRootView @Inject constructor(
val listView: HabitCardListView = habitCardListViewFactory.create()
val llEmpty = EmptyListView(context)
val tbar = buildToolbar()
val konfettiView = KonfettiView(context).apply {
translationZ = 10f
}
val progressBar = TaskProgressBar(context, runner)
val hintView: HintView
val header = HeaderView(context, preferences, midnightTimer)
@@ -84,7 +80,6 @@ class ListHabitsRootView @Inject constructor(
val rootView = RelativeLayout(context).apply {
background = sres.getDrawable(R.attr.windowBackgroundColor)
addAtTop(konfettiView)
addAtTop(tbar)
addBelow(header, tbar)
addBelow(listView, header, height = MATCH_PARENT)

View File

@@ -25,9 +25,6 @@ import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dagger.Lazy
import nl.dionsegijn.konfetti.core.Party
import nl.dionsegijn.konfetti.core.Position
import nl.dionsegijn.konfetti.core.emitter.Emitter
import org.isoron.platform.gui.toInt
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
@@ -66,7 +63,6 @@ import org.isoron.uhabits.intents.IntentFactory
import org.isoron.uhabits.tasks.ExportDBTaskFactory
import org.isoron.uhabits.tasks.ImportDataTask
import org.isoron.uhabits.tasks.ImportDataTaskFactory
import org.isoron.uhabits.utils.ColorUtils
import org.isoron.uhabits.utils.copyTo
import org.isoron.uhabits.utils.currentTheme
import org.isoron.uhabits.utils.dismissCurrentAndShow
@@ -76,7 +72,6 @@ import org.isoron.uhabits.utils.showSendEmailScreen
import org.isoron.uhabits.utils.showSendFileScreen
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
const val RESULT_IMPORT_DATA = 101
@@ -223,28 +218,6 @@ class ListHabitsScreen
activity.showSendFileScreen(filename)
}
override fun showConfetti(color: PaletteColor, x: Float, y: Float) {
val baseColor = themeSwitcher.currentTheme!!.color(color).toInt()
rootView.get().konfettiView.start(
Party(
speed = 0f,
maxSpeed = 16f,
damping = 0.9f,
spread = 360,
angle = 0,
colors = listOf(
ColorUtils.changeHue(baseColor, 180f),
ColorUtils.changeHue(baseColor, 20f),
ColorUtils.changeHue(baseColor, -20f),
baseColor
),
position = Position.Absolute(x, y),
emitter = Emitter(duration = 25, TimeUnit.MILLISECONDS).max(25),
timeToLive = 0
)
)
}
override fun showSettingsScreen() {
val intent = intentFactory.startSettingsActivity(activity)
activity.startActivityForResult(intent, REQUEST_SETTINGS)
@@ -267,7 +240,7 @@ class ListHabitsScreen
putDouble("value", value)
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")
}
@@ -285,7 +258,7 @@ class ListHabitsScreen
putInt("value", selectedValue)
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")
}

View File

@@ -20,7 +20,6 @@
package org.isoron.uhabits.activities.habits.list.views
import android.content.Context
import android.graphics.PointF
import android.graphics.text.LineBreaker.BREAK_STRATEGY_BALANCED
import android.os.Build
import android.os.Build.VERSION.SDK_INT
@@ -155,17 +154,7 @@ class HabitCardView(
checkmarkPanel = checkmarkPanelFactory.create().apply {
onToggle = { timestamp, value, notes ->
triggerRipple(timestamp)
val location = getAbsoluteButtonLocation(timestamp)
habit?.let {
behavior.onToggle(
it,
timestamp,
value,
notes,
location.x,
location.y
)
}
habit?.let { behavior.onToggle(it, timestamp, value, notes) }
}
onEdit = { timestamp ->
triggerRipple(timestamp)
@@ -217,27 +206,12 @@ class HabitCardView(
}
fun triggerRipple(timestamp: Timestamp) {
val location = getRelativeButtonLocation(timestamp)
triggerRipple(location.x, location.y)
}
private fun getRelativeButtonLocation(timestamp: Timestamp): PointF {
val today = DateUtils.getTodayWithOffset()
val offset = timestamp.daysUntil(today) - dataOffset
val button = checkmarkPanel.buttons[offset]
val y = button.height / 2.0f
val x = checkmarkPanel.x + button.x + (button.width / 2).toFloat()
return PointF(x, y)
}
private fun getAbsoluteButtonLocation(timestamp: Timestamp): PointF {
val containerLocation = IntArray(2)
this.getLocationOnScreen(containerLocation)
val relButtonLocation = getRelativeButtonLocation(timestamp)
return PointF(
containerLocation[0].toFloat() + relButtonLocation.x,
containerLocation[1].toFloat() - relButtonLocation.y
)
triggerRipple(x, y)
}
override fun onAttachedToWindow() {

View File

@@ -179,7 +179,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
putDouble("value", value)
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")
}
@@ -196,7 +196,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
putInt("value", selectedValue)
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")
}

View File

@@ -38,7 +38,7 @@ class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context,
fun setState(state: BarCardState) {
val androidColor = state.theme.color(state.color).toInt()
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.getDefault())).apply {
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.US)).apply {
series = mutableListOf(state.entries.map { it.value / 1000.0 })
colors = mutableListOf(theme.color(state.color.paletteIndex))
axis = state.entries.map { it.timestamp.toLocalDate() }

View File

@@ -53,8 +53,6 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
private var ringtoneManager: RingtoneManager? = null
private lateinit var prefs: Preferences
private var widgetUpdater: WidgetUpdater? = null
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == RINGTONE_REQUEST_CODE) {
ringtoneManager!!.update(data)

View File

@@ -25,7 +25,6 @@ import android.app.AlarmManager.RTC_WAKEUP
import android.app.PendingIntent
import android.content.Context
import android.content.Context.ALARM_SERVICE
import android.os.Build
import android.util.Log
import org.isoron.uhabits.core.AppScope
import org.isoron.uhabits.core.models.Habit
@@ -57,10 +56,6 @@ class IntentScheduler
)
return SchedulerResult.IGNORED
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !manager.canScheduleExactAlarms()) {
Log.e("IntentScheduler", "No permission to schedule exact alarms")
return SchedulerResult.IGNORED
}
manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent)
return SchedulerResult.OK
}

View File

@@ -66,13 +66,7 @@ class ReminderController @Inject constructor(
}
fun onDismiss(habit: Habit) {
if (preferences.shouldMakeNotificationsSticky()) {
// This is a workaround to keep sticky notifications non-dismissible in Android 14+.
// If the notification is dismissed, we immediately reshow it.
notificationTray.reshow(habit)
} else {
notificationTray.cancel(habit)
}
notificationTray.cancel(habit)
}
private fun showSnoozeDelayPicker(habit: Habit, context: Context) {

View File

@@ -60,14 +60,12 @@ class AndroidTaskRunner : TaskRunner {
publishProgress(progress)
}
@Deprecated("Deprecated in Java")
override fun doInBackground(vararg params: Void?): Void? {
if (isCancelled) return null
task.doInBackground()
return null
}
@Deprecated("Deprecated in Java")
override fun onPostExecute(aVoid: Void?) {
if (isCancelled) return
task.onPostExecute()
@@ -76,7 +74,6 @@ class AndroidTaskRunner : TaskRunner {
for (l in listeners) l.onTaskFinished(task)
}
@Deprecated("Deprecated in Java")
override fun onPreExecute() {
if (isCancelled) return
for (l in listeners) l.onTaskStarted(task)
@@ -85,7 +82,6 @@ class AndroidTaskRunner : TaskRunner {
task.onPreExecute()
}
@Deprecated("Deprecated in Java")
override fun onProgressUpdate(vararg values: Int?) {
values[0]?.let { task.onProgressUpdate(it) }
}

View File

@@ -36,13 +36,6 @@ object ColorUtils {
return a or r or g or b
}
fun changeHue(color: Int, delta: Float): Int {
val hsv = FloatArray(3)
Color.colorToHSV(color, hsv)
hsv[0] = (hsv[0] + delta).mod(360f)
return Color.HSVToColor(hsv)
}
@JvmStatic
fun setAlpha(color: Int, newAlpha: Float): Int {
val intAlpha = (newAlpha * 255).toInt()

View File

@@ -26,7 +26,6 @@ import android.content.Intent
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.graphics.drawable.ColorDrawable
import android.os.Handler
import android.os.SystemClock
@@ -136,11 +135,7 @@ fun Activity.startActivitySafely(intent: Intent) {
}
}
fun Activity.showSendEmailScreen(
@StringRes toId: Int,
@StringRes subjectId: Int,
content: String?
) {
fun Activity.showSendEmailScreen(@StringRes toId: Int, @StringRes subjectId: Int, content: String?) {
val to = this.getString(toId)
val subject = this.getString(subjectId)
this.startActivity(
@@ -237,11 +232,3 @@ fun View.requestFocusWithKeyboard() {
dispatchTouchEvent(MotionEvent.obtain(time, time, MotionEvent.ACTION_UP, 0f, 0f, 0))
}, 250)
}
fun View.getCenter(): PointF {
val viewLocation = IntArray(2)
this.getLocationOnScreen(viewLocation)
viewLocation[0] += this.width / 2
viewLocation[1] -= this.height / 2
return PointF(viewLocation[0].toFloat(), viewLocation[1].toFloat())
}

View File

@@ -68,13 +68,13 @@ class CheckmarkWidgetView : HabitWidgetView {
val fgColor: Int
setShadowAlpha(0x4f)
when (entryState) {
YES_MANUAL, SKIP, YES_AUTO -> {
YES_MANUAL, SKIP -> {
bgColor = activeColor
fgColor = res.getColor(R.attr.contrast0)
backgroundPaint!!.color = bgColor
frame!!.setBackgroundDrawable(background)
}
NO, UNKNOWN -> {
YES_AUTO, NO, UNKNOWN -> {
bgColor = res.getColor(R.attr.cardBgColor)
fgColor = res.getColor(R.attr.contrast60)
}
@@ -87,23 +87,12 @@ class CheckmarkWidgetView : HabitWidgetView {
ring.setColor(fgColor)
ring.setBackgroundColor(bgColor)
ring.setText(text)
ring.setIsStrokedTextEnabled(strokedTextEnabled)
label.text = name
label.setTextColor(fgColor)
requestLayout()
postInvalidate()
}
private val strokedTextEnabled: Boolean
get() = if (isNumerical) {
false
} else {
when (entryState) {
YES_AUTO -> true
else -> false
}
}
private val text: String
get() = if (isNumerical) {
(max(0, entryValue) / 1000.0).toShortString()

View File

@@ -36,7 +36,7 @@
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:inputType="textCapSentences|textMultiLine"
android:inputType="textCapSentences"
android:textSize="@dimen/smallTextSize"
android:padding="4dp"
android:background="@color/transparent"

View File

@@ -44,7 +44,6 @@
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="@dimen/smallTextSize"
android:maxLines="2"
android:textColor="@color/white"/>
</LinearLayout>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<resources>
<color name="color_background">@color/grey_900</color>
</resources>

View File

@@ -89,5 +89,4 @@
</array>
<color name="ic_launcher_background">#1976D2</color>
<color name="color_background">@color/grey_200</color>
</resources>

View File

@@ -61,7 +61,6 @@
<item name="widgetShadowAlpha">0.25</item>
<item name="windowActionModeOverlay">true</item>
<item name="windowBackgroundColor">@color/grey_200</item>
<item name="android:colorBackground">@color/color_background</item>
<item name="android:textColorAlertDialogListItem">@color/grey_800</item>
<item name="singleLineTitle">false</item>
<item name="dialogFormLabelColor">@color/white</item>

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" /> <!-- English -->
<locale android:name="af-ZA" /> <!-- Afrikaans -->
<locale android:name="ar-SA" /> <!-- Arabic -->
<locale android:name="bg-BG" /> <!-- Bulgarian -->
<locale android:name="ca-ES" /> <!-- Catalan -->
<locale android:name="cs-CZ" /> <!-- Czech -->
<locale android:name="da-DK" /> <!-- Danish -->
<locale android:name="de-DE" /> <!-- German -->
<locale android:name="el-GR" /> <!-- Greek -->
<locale android:name="eo-UY" /> <!-- Esperanto -->
<locale android:name="es-ES" /> <!-- Spanish -->
<locale android:name="eu-ES" /> <!-- Basque -->
<locale android:name="fa-IR" /> <!-- Farsi -->
<locale android:name="fi-FI" /> <!-- Finnish -->
<locale android:name="fr-FR" /> <!-- French -->
<locale android:name="hi-IN" /> <!-- Hindi -->
<locale android:name="hr-HR" /> <!-- Croatian -->
<locale android:name="hu-HU" /> <!-- Hungarian -->
<locale android:name="hy-AM" /> <!-- Armenian -->
<locale android:name="in-ID" /> <!-- Indonesian -->
<locale android:name="it-IT" /> <!-- Italian -->
<locale android:name="iw-IL" /> <!-- Hebrew -->
<locale android:name="ja-JP" /> <!-- Japanese -->
<locale android:name="ko-KR" /> <!-- Korean -->
<locale android:name="nl-NL" /> <!-- Dutch -->
<locale android:name="no-NO" /> <!-- Norwegian -->
<locale android:name="pl-PL" /> <!-- Polish -->
<locale android:name="pt-BR" /> <!-- Portuguese (Brazil) -->
<locale android:name="pt-PT" /> <!-- Portuguese (Portugal) -->
<locale android:name="ro-RO" /> <!-- Romanian -->
<locale android:name="ru-RU" /> <!-- Russian -->
<locale android:name="sk-SK" /> <!-- Slovak -->
<locale android:name="sl-SL" /> <!-- Slovenian -->
<locale android:name="sr-CS" /> <!-- Serbian (Latin) -->
<locale android:name="sr-SP" /> <!-- Serbian (Cyrillic) -->
<locale android:name="sv-SE" /> <!-- Swedish -->
<locale android:name="ta-IN" /> <!-- Tamil -->
<locale android:name="te-IN" /> <!-- Telugu -->
<locale android:name="tr-TR" /> <!-- Turkish -->
<locale android:name="ug-CN" /> <!-- Uyghur -->
<locale android:name="uk-UA" /> <!-- Ukrainian -->
<locale android:name="vi-VN" /> <!-- Vietnamese -->
<locale android:name="zh-CN" /> <!-- Chinese (Simplified) -->
<locale android:name="zh-TW" /> <!-- Chinese (Traditional) -->
</locale-config>

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits
import com.nhaarman.mockitokotlin2.spy
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.memory.MemoryModelFactory
@@ -30,7 +31,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.spy
@RunWith(MockitoJUnitRunner::class)
open class BaseAndroidJVMTest {

View File

@@ -18,6 +18,9 @@
*/
package org.isoron.uhabits.receivers
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import org.isoron.uhabits.BaseAndroidJVMTest
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.Timestamp
@@ -25,9 +28,6 @@ import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.reminders.ReminderScheduler
import org.isoron.uhabits.core.ui.NotificationTray
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
class ReminderControllerTest : BaseAndroidJVMTest() {
private lateinit var controller: ReminderController

View File

@@ -24,7 +24,6 @@ plugins {
kotlin {
jvm().withJava()
jvmToolchain(11)
sourceSets {
val commonMain by getting {
@@ -44,14 +43,14 @@ kotlin {
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-jdk8"))
compileOnly("com.google.dagger:dagger:2.51.1")
implementation("com.google.guava:guava:33.1.0-android")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3")
implementation("androidx.annotation:annotation:1.7.1")
compileOnly("com.google.dagger:dagger:2.46")
implementation("com.google.guava:guava:31.1-android")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4")
implementation("androidx.annotation:annotation:1.5.0")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.opencsv:opencsv:5.9")
implementation("commons-codec:commons-codec:1.16.0")
implementation("org.apache.commons:commons-lang3:3.14.0")
implementation("com.opencsv:opencsv:5.7.1")
implementation("commons-codec:commons-codec:1.15")
implementation("org.apache.commons:commons-lang3:3.12.0")
}
}
@@ -59,11 +58,10 @@ kotlin {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-junit"))
implementation("org.xerial:sqlite-jdbc:3.45.1.0")
implementation("org.xerial:sqlite-jdbc:3.40.0.0")
implementation("org.hamcrest:hamcrest:2.2")
implementation("org.apache.commons:commons-io:1.3.2")
implementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
implementation("org.junit.jupiter:junit-jupiter:5.10.1")
implementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
}
}
}

View File

@@ -82,8 +82,8 @@ class JavaUserFile(val path: Path) : UserFile {
@Suppress("NewApi")
class JavaFileOpener : FileOpener {
override fun openUserFile(path: String): UserFile {
val resolvedPath = Paths.get("/tmp/$path")
return JavaUserFile(resolvedPath)
val path = Paths.get("/tmp/$path")
return JavaUserFile(path)
}
override fun openResourceFile(path: String): ResourceFile {

View File

@@ -45,14 +45,14 @@ internal class Tokenizer(
if (s == null || s.isEmpty()) {
return false
}
if (s[0].code != mCurrent) {
if (s[0].toInt() != mCurrent) {
return false
}
val len = s.length
mStream.mark(len - 1)
for (n in 1 until len) {
val value = mStream.read()
if (value != s[n].code) {
if (value != s[n].toInt()) {
mStream.reset()
return false
}
@@ -68,9 +68,10 @@ object SQLParser {
private const val STATE_COMMENT_BLOCK = 3
fun parse(stream: InputStream): List<String> {
val buffer = BufferedInputStream(stream)
val commands: MutableList<String> = ArrayList()
val sb = StringBuffer()
BufferedInputStream(stream).use { buffer ->
buffer.use { buffer ->
val tokenizer = Tokenizer(buffer)
var state = STATE_NONE
while (tokenizer.hasNext()) {

View File

@@ -61,7 +61,7 @@ data class Habit(
return if (isNumerical) {
when (targetType) {
NumericalHabitType.AT_LEAST -> value / 1000.0 >= targetValue
NumericalHabitType.AT_MOST -> value != Entry.UNKNOWN && value / 1000.0 <= targetValue
NumericalHabitType.AT_MOST -> value / 1000.0 <= targetValue
}
} else {
value != Entry.NO && value != Entry.UNKNOWN

View File

@@ -115,11 +115,6 @@ class ReminderScheduler @Inject constructor(
for (habit in reminderHabits) schedule(habit)
}
@Synchronized
fun hasHabitsWithReminders(): Boolean {
return !habitList.getFiltered(HabitMatcher.WITH_ALARM).isEmpty
}
@Synchronized
fun startListening() {
commandRunner.addListener(this)

View File

@@ -25,5 +25,5 @@ fun interface Task {
fun onAttached(runner: TaskRunner) {}
fun onPostExecute() {}
fun onPreExecute() {}
fun onProgressUpdate(currentPosition: Int) {}
fun onProgressUpdate(value: Int) {}
}

View File

@@ -89,12 +89,6 @@ class NotificationTray @Inject constructor(
}
}
fun reshow(habit: Habit) {
active[habit]?.let {
taskRunner.execute(ShowNotificationTask(habit, it))
}
}
interface SystemTray {
fun removeNotification(notificationId: Int)
fun showNotification(

View File

@@ -20,12 +20,9 @@ package org.isoron.uhabits.core.ui.screens.habits.list
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitType
import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST
import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.preferences.Preferences
@@ -55,16 +52,8 @@ open class ListHabitsBehavior @Inject constructor(
val entry = habit.computedEntries.get(timestamp!!)
if (habit.type == HabitType.NUMERICAL) {
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()
if (newValue != oldValue) {
if (
(habit.targetType == AT_LEAST && newValue >= habit.targetValue) ||
(habit.targetType == AT_MOST && newValue <= habit.targetValue)
) {
screen.showConfetti(habit.color, x, y)
}
}
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value, newNotes))
}
} else {
@@ -72,8 +61,7 @@ open class ListHabitsBehavior @Inject constructor(
entry.value,
entry.notes,
habit.color
) { newValue: Int, newNotes: String, x: Float, y: Float ->
if (newValue != entry.value && newValue == YES_MANUAL) screen.showConfetti(habit.color, x, y)
) { newValue, newNotes ->
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes))
}
}
@@ -129,11 +117,10 @@ open class ListHabitsBehavior @Inject constructor(
if (prefs.isFirstRun) onFirstRun()
}
fun onToggle(habit: Habit, timestamp: Timestamp, value: Int, notes: String, x: Float, y: Float) {
fun onToggle(habit: Habit, timestamp: Timestamp, value: Int, notes: String) {
commandRunner.run(
CreateRepetitionCommand(habitList, habit, timestamp, value, notes)
)
if (value == YES_MANUAL) screen.showConfetti(habit.color, x, y)
}
enum class Message {
@@ -157,22 +144,12 @@ open class ListHabitsBehavior @Inject constructor(
}
fun interface NumberPickerCallback {
fun onNumberPicked(
newValue: Double,
notes: String,
x: Float,
y: Float
)
fun onNumberPicked(newValue: Double, notes: String)
fun onNumberPickerDismissed() {}
}
fun interface CheckMarkDialogCallback {
fun onNotesSaved(
value: Int,
notes: String,
x: Float,
y: Float
)
fun onNotesSaved(value: Int, notes: String)
fun onNotesDismissed() {}
}
@@ -193,6 +170,5 @@ open class ListHabitsBehavior @Inject constructor(
)
fun showSendBugReportToDeveloperScreen(log: String)
fun showSendFileScreen(filename: String)
fun showConfetti(color: PaletteColor, x: Float, y: Float)
}
}

View File

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

View File

@@ -49,7 +49,7 @@ class HabitListHeader(
repeat(nButtons) { index ->
val date = today.minus(nButtons - index - 1)
val name = fmt.shortWeekdayName(date).uppercase()
val name = fmt.shortWeekdayName(date).toUpperCase()
val number = date.day.toString()
val x = width - (index + 1) * buttonSize + buttonSize / 2

View File

@@ -125,30 +125,4 @@ class WidgetTheme : LightTheme() {
override val highContrastTextColor = Color.WHITE
override val mediumContrastTextColor = Color.WHITE.withAlpha(0.50)
override val lowContrastTextColor = Color.WHITE.withAlpha(0.10)
override fun color(paletteIndex: Int): Color {
return when (paletteIndex) {
0 -> Color(0xD32F2F)
1 -> Color(0xE64A19)
2 -> Color(0xF57C00)
3 -> Color(0xFF8F00)
4 -> Color(0xF9A825)
5 -> Color(0xAFB42B)
6 -> Color(0x7CB342)
7 -> Color(0x388E3C)
8 -> Color(0x00897B)
9 -> Color(0x00ACC1)
10 -> Color(0x039BE5)
11 -> Color(0x1976D2)
12 -> Color(0x6275f0)
13 -> Color(0x5E35B1)
14 -> Color(0x8E24AA)
15 -> Color(0xD81B60)
16 -> Color(0x5D4037)
17 -> Color(0x757575)
18 -> Color(0x757575)
19 -> Color(0x9E9E9E)
else -> Color(0x000000)
}
}
}

View File

@@ -227,7 +227,7 @@ abstract class DateUtils {
fun getStartOfTodayWithOffset(): Long = getStartOfDayWithOffset(getLocalTime())
@JvmStatic
fun millisecondsUntilTomorrowWithOffset(): Long = getStartOfTomorrowWithOffset() - applyTimezone(getLocalTime())
fun millisecondsUntilTomorrowWithOffset(): Long = getStartOfTomorrowWithOffset() - getLocalTime()
@JvmStatic
fun getStartOfTodayCalendar(): GregorianCalendar = getCalendar(getStartOfToday())

View File

@@ -19,7 +19,6 @@
package org.isoron.uhabits.core.utils
import org.isoron.uhabits.core.AppScope
import org.isoron.uhabits.core.io.Logging
import java.util.LinkedList
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
@@ -30,10 +29,9 @@ import javax.inject.Inject
* A class that emits events when a new day starts.
*/
@AppScope
open class MidnightTimer @Inject constructor(logging: Logging) {
open class MidnightTimer @Inject constructor() {
private val listeners: MutableList<MidnightListener> = LinkedList()
private lateinit var executor: ScheduledExecutorService
private val logger = logging.getLogger("MidnightTimer")
@Synchronized
fun addListener(listener: MidnightListener) {
@@ -41,10 +39,7 @@ open class MidnightTimer @Inject constructor(logging: Logging) {
}
@Synchronized
fun onPause(): MutableList<Runnable>? {
logger.info("Pausing timer")
return executor.shutdownNow()
}
fun onPause(): MutableList<Runnable>? = executor.shutdownNow()
@Synchronized
fun onResume(
@@ -52,11 +47,9 @@ open class MidnightTimer @Inject constructor(logging: Logging) {
testExecutor: ScheduledExecutorService? = null
) {
executor = testExecutor ?: Executors.newSingleThreadScheduledExecutor()
val initialDelay = DateUtils.millisecondsUntilTomorrowWithOffset() + delayOffsetInMillis
logger.info("Scheduling refresh for $initialDelay ms from now")
executor.scheduleAtFixedRate(
{ notifyListeners() },
initialDelay,
DateUtils.millisecondsUntilTomorrowWithOffset() + delayOffsetInMillis,
DateUtils.DAY_LENGTH,
TimeUnit.MILLISECONDS
)
@@ -67,7 +60,6 @@ open class MidnightTimer @Inject constructor(logging: Logging) {
@Synchronized
private fun notifyListeners() {
logger.info("Midnight refresh")
for (l in listeners) {
l.atMidnight()
}

View File

@@ -19,9 +19,9 @@
package org.isoron.platform.gui
import junit.framework.Assert.fail
import kotlinx.coroutines.runBlocking
import org.isoron.platform.io.JavaFileOpener
import org.junit.Assert.fail
import org.junit.Test
import java.awt.image.BufferedImage
import java.awt.image.BufferedImage.TYPE_INT_ARGB

View File

@@ -18,6 +18,8 @@
*/
package org.isoron.uhabits.core
import com.nhaarman.mockitokotlin2.spy
import com.nhaarman.mockitokotlin2.validateMockitoUsage
import org.apache.commons.io.IOUtils
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.database.Database
@@ -38,8 +40,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.spy
import org.mockito.kotlin.validateMockitoUsage
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream

View File

@@ -18,12 +18,12 @@
*/
package org.isoron.uhabits.core.commands
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.junit.Before
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class ArchiveHabitsCommandTest : BaseUnitTest() {
private lateinit var command: ArchiveHabitsCommand

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.commands
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
@@ -26,7 +27,6 @@ import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList
import org.junit.Before
import org.junit.Test
import kotlin.test.assertTrue
class CreateHabitCommandTest : BaseUnitTest() {
private lateinit var command: CreateHabitCommand

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.commands
import junit.framework.Assert.assertEquals
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.models.Habit
@@ -25,7 +26,6 @@ import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
class CreateRepetitionCommandTest : BaseUnitTest() {
private lateinit var command: CreateRepetitionCommand

View File

@@ -23,13 +23,18 @@ import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.util.*
import org.junit.rules.ExpectedException
import java.util.LinkedList
class DeleteHabitsCommandTest : BaseUnitTest() {
private lateinit var command: DeleteHabitsCommand
private lateinit var selected: LinkedList<Habit>
@get:Rule
var thrown = ExpectedException.none()!!
@Before
@Throws(Exception::class)
override fun setUp() {

View File

@@ -18,12 +18,12 @@
*/
package org.isoron.uhabits.core.commands
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.junit.Before
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class UnarchiveHabitsCommandTest : BaseUnitTest() {
private lateinit var command: UnarchiveHabitsCommand

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.database
import junit.framework.Assert.assertNull
import org.apache.commons.lang3.builder.EqualsBuilder
import org.apache.commons.lang3.builder.HashCodeBuilder
import org.apache.commons.lang3.builder.ToStringBuilder
@@ -26,7 +27,6 @@ import org.hamcrest.core.IsEqual.equalTo
import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Before
import org.junit.Test
import kotlin.test.assertNull
class RepositoryTest : BaseUnitTest() {
private lateinit var repository: Repository<ThingRecord>

View File

@@ -27,10 +27,13 @@ import org.isoron.uhabits.core.database.Database
import org.isoron.uhabits.core.database.MigrationHelper
import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
import org.isoron.uhabits.core.test.HabitFixtures
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.rules.ExpectedException
class Version22Test : BaseUnitTest() {
@get:Rule
var exception = ExpectedException.none()!!
private lateinit var db: Database
private lateinit var helper: MigrationHelper
@@ -73,8 +76,8 @@ class Version22Test : BaseUnitTest() {
@Throws(Exception::class)
fun testDisallowNewRepsWithInvalidRef() {
helper.migrateTo(22)
val exception = assertThrows(java.lang.RuntimeException::class.java) { db.execute("insert into Repetitions(habit, timestamp, value) values (99999, 100, 2)") }
assertThat(exception.message, Matchers.containsString("SQLITE_CONSTRAINT"))
exception.expectMessage(Matchers.containsString("SQLITE_CONSTRAINT"))
db.execute("insert into Repetitions(habit, timestamp, value) values (99999, 100, 2)")
}
@Test
@@ -94,12 +97,8 @@ class Version22Test : BaseUnitTest() {
@Throws(Exception::class)
fun testDisallowNullTimestamp() {
helper.migrateTo(22)
val exception = assertThrows(java.lang.RuntimeException::class.java) {
db.execute("insert into Repetitions(habit, value) " + "values (0, 2)")
}
assertThat(exception.message, Matchers.containsString("SQLITE_CONSTRAINT"))
exception.expectMessage(Matchers.containsString("SQLITE_CONSTRAINT"))
db.execute("insert into Repetitions(habit, value) " + "values (0, 2)")
}
@Test
@@ -119,12 +118,8 @@ class Version22Test : BaseUnitTest() {
@Throws(Exception::class)
fun testDisallowNullHabit() {
helper.migrateTo(22)
val exception = assertThrows(java.lang.RuntimeException::class.java) {
db.execute("insert into Repetitions(timestamp, value) " + "values (5, 2)")
}
assertThat(exception.message, Matchers.containsString("SQLITE_CONSTRAINT"))
exception.expectMessage(Matchers.containsString("SQLITE_CONSTRAINT"))
db.execute("insert into Repetitions(timestamp, value) " + "values (5, 2)")
}
@Test
@@ -147,11 +142,7 @@ class Version22Test : BaseUnitTest() {
fun testDisallowNewDuplicateTimestamps() {
helper.migrateTo(22)
db.execute("insert into repetitions(habit, timestamp, value)values (0, 100, 2)")
val exception = assertThrows(java.lang.RuntimeException::class.java) {
db.execute("insert into repetitions(habit, timestamp, value)values (0, 100, 5)")
}
assertThat(exception.message, Matchers.containsString("SQLITE_CONSTRAINT"))
exception.expectMessage(Matchers.containsString("SQLITE_CONSTRAINT"))
db.execute("insert into repetitions(habit, timestamp, value)values (0, 100, 5)")
}
}

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.io
import junit.framework.Assert.assertTrue
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import org.isoron.uhabits.core.BaseUnitTest
@@ -28,9 +29,8 @@ import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.nio.file.Files
import java.util.*
import java.util.LinkedList
import java.util.zip.ZipFile
import kotlin.test.assertTrue
class HabitsCSVExporterTest : BaseUnitTest() {
private lateinit var baseDir: File
@@ -102,8 +102,9 @@ class HabitsCSVExporterTest : BaseUnitTest() {
private fun assertAbsolutePathExists(s: String) {
val file = File(s)
assertTrue(
String.format("File %s should exist", file.absolutePath)
) { file.exists() }
String.format("File %s should exist", file.absolutePath),
file.exists()
)
}
private fun assertFileAndReferenceAreEqual(s: String) {

View File

@@ -18,6 +18,8 @@
*/
package org.isoron.uhabits.core.io
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.isoron.uhabits.core.BaseUnitTest
@@ -32,8 +34,6 @@ import org.junit.Before
import org.junit.Test
import java.io.File
import java.io.IOException
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class ImportTest : BaseUnitTest() {
@Before

View File

@@ -18,19 +18,23 @@
*/
package org.isoron.uhabits.core.models
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.not
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import java.io.IOException
import java.io.StringWriter
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import java.util.ArrayList
class HabitListTest : BaseUnitTest() {
@get:Rule
var thrown = ExpectedException.none()!!
private lateinit var habitsArray: ArrayList<Habit>
private lateinit var activeHabits: HabitList
private lateinit var reminderHabits: HabitList
@@ -169,9 +173,8 @@ class HabitListTest : BaseUnitTest() {
fun testReorder_withInvalidArguments() {
val h1 = habitsArray[0]
val h2 = fixtures.createEmptyHabit()
assertThrows(IllegalArgumentException::class.java) {
habitList.reorder(h1, h2)
}
thrown.expect(IllegalArgumentException::class.java)
habitList.reorder(h1, h2)
}
@Test
@@ -232,17 +235,15 @@ class HabitListTest : BaseUnitTest() {
@Test
@Throws(Exception::class)
fun testAdd_withFilteredList() {
assertThrows(IllegalStateException::class.java) {
activeHabits.add(fixtures.createEmptyHabit())
}
thrown.expect(IllegalStateException::class.java)
activeHabits.add(fixtures.createEmptyHabit())
}
@Test
@Throws(Exception::class)
fun testRemove_onFilteredList() {
assertThrows(IllegalStateException::class.java) {
activeHabits.remove(fixtures.createEmptyHabit())
}
thrown.expect(IllegalStateException::class.java)
activeHabits.remove(fixtures.createEmptyHabit())
}
@Test
@@ -250,9 +251,8 @@ class HabitListTest : BaseUnitTest() {
fun testReorder_onFilteredList() {
val h1 = fixtures.createEmptyHabit()
val h2 = fixtures.createEmptyHabit()
assertThrows(IllegalStateException::class.java) {
activeHabits.reorder(h1, h2)
}
thrown.expect(IllegalStateException::class.java)
activeHabits.reorder(h1, h2)
}
@Test
@@ -261,8 +261,7 @@ class HabitListTest : BaseUnitTest() {
habitList.primaryOrder = HabitList.Order.BY_SCORE_DESC
val h1 = habitsArray[1]
val h2 = habitsArray[2]
assertThrows(IllegalStateException::class.java) {
habitList.reorder(h1, h2)
}
thrown.expect(IllegalStateException::class.java)
habitList.reorder(h1, h2)
}
}

View File

@@ -18,18 +18,21 @@
*/
package org.isoron.uhabits.core.models
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.rules.ExpectedException
class HabitTest : BaseUnitTest() {
@get:Rule
val exception = ExpectedException.none()!!
@Throws(Exception::class)
override fun setUp() {
@@ -52,7 +55,7 @@ class HabitTest : BaseUnitTest() {
model.reminder = Reminder(8, 30, WeekdayList(1))
val habit = modelFactory.buildHabit()
habit.copyFrom(model)
assertEquals(habit.isArchived, model.isArchived)
assertTrue(habit.isArchived == model.isArchived)
assertThat(habit.isArchived, `is`(model.isArchived))
assertThat(habit.color, `is`(model.color))
assertThat(habit.frequency, equalTo(model.frequency))

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.models
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.number.IsCloseTo
import org.hamcrest.number.OrderingComparison
@@ -27,7 +28,6 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Before
import org.junit.Test
import java.util.ArrayList
import kotlin.test.assertTrue
open class BaseScoreListTest : BaseUnitTest() {
protected lateinit var habit: Habit

View File

@@ -18,6 +18,8 @@
*/
package org.isoron.uhabits.core.models
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.greaterThan
@@ -25,8 +27,6 @@ import org.hamcrest.Matchers.lessThan
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class TimestampTest : BaseUnitTest() {
@Test

View File

@@ -18,12 +18,12 @@
*/
package org.isoron.uhabits.core.models
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class WeekdayListTest : BaseUnitTest() {
@Test

View File

@@ -19,6 +19,8 @@
package org.isoron.uhabits.core.models.sqlite
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNull
import org.isoron.uhabits.core.BaseUnitTest.Companion.buildMemoryDatabase
import org.isoron.uhabits.core.database.Repository
import org.isoron.uhabits.core.models.Entry
@@ -28,8 +30,6 @@ import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
import org.isoron.uhabits.core.utils.DateUtils
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class SQLiteEntryListTest {

View File

@@ -18,6 +18,9 @@
*/
package org.isoron.uhabits.core.models.sqlite
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import junit.framework.Assert.assertNull
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
@@ -31,14 +34,14 @@ import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
import org.isoron.uhabits.core.test.HabitFixtures
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.junit.rules.ExpectedException
import java.util.ArrayList
import kotlin.test.assertNull
class SQLiteHabitListTest : BaseUnitTest() {
@get:Rule
var exception = ExpectedException.none()!!
private lateinit var repository: Repository<HabitRecord>
private var listener: ModelObservable.Listener = mock()
private lateinit var habitsArray: ArrayList<Habit>
@@ -87,9 +90,8 @@ class SQLiteHabitListTest : BaseUnitTest() {
val habit = modelFactory.buildHabit()
habitList.add(habit)
verify(listener).onModelChange()
assertThrows(IllegalArgumentException::class.java) {
habitList.add(habit)
}
exception.expect(IllegalArgumentException::class.java)
habitList.add(habit)
}
@Test

View File

@@ -18,6 +18,10 @@
*/
package org.isoron.uhabits.core.preferences
import com.nhaarman.mockitokotlin2.mock
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
@@ -26,11 +30,7 @@ import org.isoron.uhabits.core.models.Timestamp.Companion.ZERO
import org.isoron.uhabits.core.ui.ThemeSwitcher
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import java.io.File
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
class PreferencesTest : BaseUnitTest() {
private lateinit var prefs: Preferences

View File

@@ -18,6 +18,9 @@
*/
package org.isoron.uhabits.core.preferences
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
@@ -25,9 +28,6 @@ import org.junit.Before
import org.junit.Test
import java.io.File
import java.util.Arrays
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class PropertiesStorageTest : BaseUnitTest() {
private lateinit var storage: PropertiesStorage

View File

@@ -18,6 +18,10 @@
*/
package org.isoron.uhabits.core.reminders
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.Reminder
@@ -33,10 +37,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.Calendar
import java.util.TimeZone

View File

@@ -18,12 +18,12 @@
*/
package org.isoron.uhabits.core.tasks
import com.nhaarman.mockitokotlin2.inOrder
import com.nhaarman.mockitokotlin2.mock
import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
@RunWith(JUnit4::class)
class SingleThreadTaskRunnerTest : BaseUnitTest() {

View File

@@ -18,6 +18,10 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.list
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.reset
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
@@ -26,10 +30,6 @@ import org.isoron.uhabits.core.commands.DeleteHabitsCommand
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
class HabitCardListCacheTest : BaseUnitTest() {
private lateinit var cache: HabitCardListCache

View File

@@ -18,6 +18,12 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.list
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.isoron.uhabits.core.BaseUnitTest
@@ -25,12 +31,6 @@ import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
class HintListTest : BaseUnitTest() {
private lateinit var hintList: HintList

View File

@@ -18,6 +18,16 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.list
import com.nhaarman.mockitokotlin2.KArgumentCaptor
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.clearInvocations
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.apache.commons.io.FileUtils
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
@@ -29,18 +39,8 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.io.IOException
import java.nio.file.Files
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class ListHabitsBehaviorTest : BaseUnitTest() {
private val dirFinder: ListHabitsBehavior.DirFinder = mock()
@@ -84,7 +84,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
eq(""),
picker.capture()
)
picker.lastValue.onNumberPicked(100.0, "", 0f, 0f)
picker.lastValue.onNumberPicked(100.0, "")
val today = getTodayWithOffset()
assertThat(habit2.computedEntries.get(today).value, equalTo(100000))
}
@@ -168,9 +168,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
habit = habit1,
timestamp = getToday(),
value = Entry.NO,
notes = "",
x = 0f,
y = 0f
notes = ""
)
assertFalse(habit1.isCompletedToday())
}

View File

@@ -18,6 +18,17 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.list
import com.nhaarman.mockitokotlin2.KArgumentCaptor
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.clearInvocations
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.whenever
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.isoron.uhabits.core.BaseUnitTest
@@ -26,17 +37,6 @@ import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.ui.ThemeSwitcher
import org.junit.Test
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class ListHabitsMenuBehaviorTest : BaseUnitTest() {
private lateinit var behavior: ListHabitsMenuBehavior

View File

@@ -18,6 +18,15 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.list
import com.nhaarman.mockitokotlin2.KArgumentCaptor
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.isoron.uhabits.core.BaseUnitTest
@@ -26,15 +35,6 @@ import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
import org.junit.Test
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
class ListHabitsSelectionMenuBehaviorTest : BaseUnitTest() {
private val screen: ListHabitsSelectionMenuBehavior.Screen = mock()

View File

@@ -18,15 +18,15 @@
*/
package org.isoron.uhabits.core.ui.screens.habits.show
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import org.apache.commons.io.FileUtils
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.nio.file.Files
class ShowHabitMenuPresenterTest : BaseUnitTest() {

View File

@@ -19,6 +19,10 @@
package org.isoron.uhabits.core.ui.views
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.reset
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import kotlinx.coroutines.runBlocking
import org.isoron.platform.gui.assertRenders
import org.isoron.platform.time.DayOfWeek
@@ -31,10 +35,6 @@ import org.isoron.uhabits.core.ui.views.HistoryChart.Square.HATCHED
import org.isoron.uhabits.core.ui.views.HistoryChart.Square.OFF
import org.isoron.uhabits.core.ui.views.HistoryChart.Square.ON
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import java.util.Locale
class HistoryChartTest {

View File

@@ -18,6 +18,11 @@
*/
package org.isoron.uhabits.core.ui.widgets
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.reset
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
import org.isoron.uhabits.core.models.Entry
@@ -29,11 +34,6 @@ import org.isoron.uhabits.core.ui.NotificationTray
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.reset
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever
class WidgetBehaviorTest : BaseUnitTest() {
private lateinit var notificationTray: NotificationTray
@@ -61,7 +61,7 @@ class WidgetBehaviorTest : BaseUnitTest() {
CreateRepetitionCommand(habitList, habit, today, Entry.YES_MANUAL, "")
)
verify(notificationTray).cancel(habit)
verifyNoInteractions(preferences)
verifyZeroInteractions(preferences)
}
@Test
@@ -71,7 +71,7 @@ class WidgetBehaviorTest : BaseUnitTest() {
CreateRepetitionCommand(habitList, habit, today, Entry.NO, "")
)
verify(notificationTray).cancel(habit)
verifyNoInteractions(preferences)
verifyZeroInteractions(preferences)
}
@Test
@@ -113,7 +113,7 @@ class WidgetBehaviorTest : BaseUnitTest() {
CreateRepetitionCommand(habitList, habit, today, 600, "")
)
verify(notificationTray).cancel(habit)
verifyNoInteractions(preferences)
verifyZeroInteractions(preferences)
}
@Test
@@ -126,6 +126,6 @@ class WidgetBehaviorTest : BaseUnitTest() {
CreateRepetitionCommand(habitList, habit, today, 400, "")
)
verify(notificationTray).cancel(habit)
verifyNoInteractions(preferences)
verifyZeroInteractions(preferences)
}
}

View File

@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core.utils
import junit.framework.Assert.assertEquals
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.isoron.uhabits.core.BaseUnitTest
@@ -39,7 +40,6 @@ import java.util.Calendar
import java.util.GregorianCalendar
import java.util.Locale
import java.util.TimeZone
import kotlin.test.assertEquals
class DateUtilsTest : BaseUnitTest() {
var firstWeekday = Calendar.SUNDAY

View File

@@ -4,7 +4,6 @@ import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.io.StandardLogging
import org.junit.Test
import java.util.Calendar
import java.util.TimeZone
@@ -35,7 +34,7 @@ class MidnightTimerTest : BaseUnitTest() {
)
val suspendedListener = suspendCoroutine<Boolean> { continuation ->
MidnightTimer(StandardLogging()).apply {
MidnightTimer().apply {
addListener { continuation.resume(true) }
// When
onResume(1, executor)

View File

@@ -22,11 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
application
id("kotlin")
id("com.github.johnrengelman.shadow") version "8.1.1"
}
kotlin {
jvmToolchain(17)
id("com.github.johnrengelman.shadow") version "7.1.2"
}
@@ -38,9 +34,11 @@ application {
dependencies {
val ktorVersion = "1.6.8"
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22")
val kotlinVersion = "1.7.21"
val logbackVersion = "1.4.5"
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("ch.qos.logback:logback-classic:1.4.14")
implementation("ch.qos.logback:logback-classic:$logbackVersion")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-html-builder:$ktorVersion")
implementation("io.ktor:ktor-jackson:$ktorVersion")
@@ -49,7 +47,7 @@ dependencies {
implementation("io.prometheus:simpleclient_httpserver:0.16.0")
implementation("io.prometheus:simpleclient_hotspot:0.16.0")
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
testImplementation(kotlin("test"))
testImplementation(kotlin("test-junit"))
}
@@ -59,4 +57,3 @@ tasks.withType<ShadowJar> {
archiveClassifier.set("")
archiveVersion.set("")
}

View File

@@ -19,7 +19,7 @@
package org.isoron.uhabits.sync.app
import org.mockito.kotlin.mock
import com.nhaarman.mockitokotlin2.mock
import io.ktor.application.*
import org.isoron.uhabits.sync.server.*

View File

@@ -19,7 +19,7 @@
package org.isoron.uhabits.sync.app
import org.mockito.kotlin.whenever
import com.nhaarman.mockitokotlin2.whenever
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod

View File

@@ -19,7 +19,7 @@
package org.isoron.uhabits.sync.app
import org.mockito.kotlin.whenever
import com.nhaarman.mockitokotlin2.whenever
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.handleRequest

View File

@@ -19,8 +19,8 @@
package org.isoron.uhabits.sync.app
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import io.ktor.http.*
import io.ktor.server.testing.*
import kotlinx.coroutines.*