diff --git a/uhabits-android/lint-baseline.xml b/uhabits-android/lint-baseline.xml
index 6de04b37..c55a3507 100644
--- a/uhabits-android/lint-baseline.xml
+++ b/uhabits-android/lint-baseline.xml
@@ -1578,17 +1578,6 @@
file="src/main/res/mipmap-anydpi-v26"/>
-
-
-
-
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-package org.isoron.uhabits.tasks
-
-import android.os.AsyncTask
-import kotlinx.coroutines.runBlocking
-import org.isoron.uhabits.core.tasks.Task
-import org.isoron.uhabits.core.tasks.TaskRunner
-import java.util.HashMap
-import java.util.LinkedList
-
-class AndroidTaskRunner : TaskRunner {
- private val activeTasks: LinkedList = LinkedList()
- private val taskToAsyncTask: HashMap = HashMap()
- private val listeners: LinkedList = LinkedList()
- override fun addListener(listener: TaskRunner.Listener) {
- listeners.add(listener)
- }
-
- override fun execute(task: Task) {
- task.onAttached(this)
- CustomAsyncTask(task).execute()
- }
-
- override val activeTaskCount: Int
- get() = activeTasks.size
-
- override fun publishProgress(task: Task, progress: Int) {
- val asyncTask = taskToAsyncTask[task] ?: return
- asyncTask.publish(progress)
- }
-
- override fun removeListener(listener: TaskRunner.Listener) {
- listeners.remove(listener)
- }
-
- private inner class CustomAsyncTask(val task: Task) : AsyncTask() {
-
- fun publish(progress: Int) {
- publishProgress(progress)
- }
-
- @Deprecated("Deprecated in Java")
- override fun doInBackground(vararg params: Void?): Void? {
- if (isCancelled) return null
- runBlocking { task.doInBackground() }
- return null
- }
-
- @Deprecated("Deprecated in Java")
- override fun onPostExecute(aVoid: Void?) {
- if (isCancelled) return
- task.onPostExecute()
- activeTasks.remove(this)
- taskToAsyncTask.remove(task)
- for (l in listeners) l.onTaskFinished(task)
- }
-
- @Deprecated("Deprecated in Java")
- override fun onPreExecute() {
- if (isCancelled) return
- for (l in listeners) l.onTaskStarted(task)
- activeTasks.add(this)
- taskToAsyncTask[task] = this
- task.onPreExecute()
- }
-
- @Deprecated("Deprecated in Java")
- override fun onProgressUpdate(vararg values: Int?) {
- values[0]?.let { task.onProgressUpdate(it) }
- }
- }
-}
diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt b/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt
index 68fe38df..cdcf7fe8 100644
--- a/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt
+++ b/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt
@@ -18,12 +18,14 @@
*/
package org.isoron.uhabits
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.isoron.platform.time.LocalDate
import org.isoron.platform.time.setToday
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.memory.MemoryModelFactory
-import org.isoron.uhabits.core.tasks.SingleThreadTaskRunner
+import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
+import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.test.HabitFixtures
import org.junit.After
import org.junit.Before
@@ -33,7 +35,7 @@ open class BaseAndroidJVMTest {
private lateinit var habitList: HabitList
protected lateinit var fixtures: HabitFixtures
private lateinit var modelFactory: MemoryModelFactory
- private lateinit var taskRunner: SingleThreadTaskRunner
+ private lateinit var taskRunner: TaskRunner
private lateinit var commandRunner: CommandRunner
@Before
@@ -42,7 +44,10 @@ open class BaseAndroidJVMTest {
modelFactory = MemoryModelFactory()
habitList = modelFactory.buildHabitList()
fixtures = HabitFixtures(modelFactory, habitList)
- taskRunner = SingleThreadTaskRunner()
+ taskRunner = CoroutineTaskRunner(
+ mainDispatcher = UnconfinedTestDispatcher(),
+ ioDispatcher = UnconfinedTestDispatcher()
+ )
commandRunner = CommandRunner(taskRunner)
}
diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunner.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunner.kt
similarity index 54%
rename from uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunner.kt
rename to uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunner.kt
index 36973188..3dbe442e 100644
--- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunner.kt
+++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunner.kt
@@ -18,33 +18,51 @@
*/
package org.isoron.uhabits.core.tasks
-import org.isoron.platform.runSuspend
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
-class SingleThreadTaskRunner : TaskRunner {
- override val activeTaskCount: Int
- get() = 0
+class CoroutineTaskRunner(
+ mainDispatcher: CoroutineDispatcher,
+ private val ioDispatcher: CoroutineDispatcher
+) : TaskRunner {
+
+ private val scope = CoroutineScope(SupervisorJob() + mainDispatcher)
+ private val listeners = mutableListOf()
+ private var activeCount = 0
+
+ override val activeTaskCount: Int get() = activeCount
- private val listeners: MutableList = mutableListOf()
override fun addListener(listener: TaskRunner.Listener) {
listeners.add(listener)
}
- override fun execute(task: Task) {
- for (l in listeners) l.onTaskStarted(task)
- if (!task.isCanceled()) {
- task.onAttached(this)
- task.onPreExecute()
- runSuspend { task.doInBackground() }
- task.onPostExecute()
- }
- for (l in listeners) l.onTaskFinished(task)
- }
-
- override fun publishProgress(task: Task, progress: Int) {
- task.onProgressUpdate(progress)
- }
-
override fun removeListener(listener: TaskRunner.Listener) {
listeners.remove(listener)
}
+
+ override fun execute(task: Task) {
+ task.onAttached(this)
+ scope.launch {
+ activeCount++
+ listeners.forEach { it.onTaskStarted(task) }
+ task.onPreExecute()
+ if (!task.isCanceled()) {
+ withContext(ioDispatcher) {
+ task.doInBackground()
+ }
+ }
+ task.onPostExecute()
+ activeCount--
+ listeners.forEach { it.onTaskFinished(task) }
+ }
+ }
+
+ override fun publishProgress(task: Task, progress: Int) {
+ scope.launch {
+ task.onProgressUpdate(progress)
+ }
+ }
}
diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt
index fb6c8481..47801c23 100644
--- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt
+++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt
@@ -33,6 +33,7 @@ import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.tasks.ExportCSVTask
+import org.isoron.uhabits.core.tasks.Task
import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.ui.callbacks.CheckMarkDialogCallback
import org.isoron.uhabits.core.ui.callbacks.NumberPickerCallback
@@ -107,10 +108,14 @@ open class ListHabitsBehavior(
}
open fun onRepairDB() {
- taskRunner.execute {
- habitList.repair()
- screen.showMessage(Message.DATABASE_REPAIRED)
- }
+ taskRunner.execute(object : Task {
+ override suspend fun doInBackground() {
+ habitList.repair()
+ }
+ override fun onPostExecute() {
+ screen.showMessage(Message.DATABASE_REPAIRED)
+ }
+ })
}
open fun onSendBugReport() {
diff --git a/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/BaseUnitTest.kt b/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/BaseUnitTest.kt
index 8951ada1..30e4fa6e 100644
--- a/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/BaseUnitTest.kt
+++ b/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/BaseUnitTest.kt
@@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.core
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.isoron.platform.io.Database
import org.isoron.platform.io.DatabaseOpener
import org.isoron.platform.io.FileOpener
@@ -31,7 +32,8 @@ import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.models.memory.MemoryModelFactory
-import org.isoron.uhabits.core.tasks.SingleThreadTaskRunner
+import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
+import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.test.HabitFixtures
import kotlin.test.BeforeTest
@@ -39,7 +41,7 @@ open class BaseUnitTest {
protected open lateinit var habitList: HabitList
protected lateinit var fixtures: HabitFixtures
protected lateinit var modelFactory: ModelFactory
- protected lateinit var taskRunner: SingleThreadTaskRunner
+ protected lateinit var taskRunner: TaskRunner
protected open lateinit var commandRunner: CommandRunner
protected val fileOpener: FileOpener = createTestFileOpener()
private var _databaseOpener: DatabaseOpener? = null
@@ -57,7 +59,10 @@ open class BaseUnitTest {
habitList = memoryModelFactory.buildHabitList()
fixtures = HabitFixtures(memoryModelFactory, habitList)
modelFactory = memoryModelFactory
- taskRunner = SingleThreadTaskRunner()
+ taskRunner = CoroutineTaskRunner(
+ mainDispatcher = UnconfinedTestDispatcher(),
+ ioDispatcher = UnconfinedTestDispatcher()
+ )
commandRunner = CommandRunner(taskRunner)
}
diff --git a/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt b/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunnerTest.kt
similarity index 81%
rename from uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt
rename to uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunnerTest.kt
index 03757a8c..0a0b4588 100644
--- a/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt
+++ b/uhabits-core/src/commonTest/kotlin/org/isoron/uhabits/core/tasks/CoroutineTaskRunnerTest.kt
@@ -21,18 +21,22 @@ package org.isoron.uhabits.core.tasks
import dev.mokkery.mock
import dev.mokkery.verify.VerifyMode.Companion.order
import dev.mokkery.verifySuspend
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.isoron.uhabits.core.BaseUnitTest
import kotlin.test.BeforeTest
import kotlin.test.Test
-class SingleThreadTaskRunnerTest : BaseUnitTest() {
- private lateinit var runner: SingleThreadTaskRunner
+class CoroutineTaskRunnerTest : BaseUnitTest() {
+ private lateinit var runner: CoroutineTaskRunner
private var task: Task = mock()
@BeforeTest
override fun setUp() {
super.setUp()
- runner = SingleThreadTaskRunner()
+ runner = CoroutineTaskRunner(
+ mainDispatcher = UnconfinedTestDispatcher(),
+ ioDispatcher = UnconfinedTestDispatcher()
+ )
}
@Test