From 2ec60748d3a6fa8fab7a45e5b7c7acec53ca76b3 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 25 Jun 2020 07:45:55 -0500 Subject: [PATCH] Use coroutines in TaskListViewModel --- app/build.gradle.kts | 3 +- app/licenses.yml | 12 ++--- app/src/main/assets/licenses.json | 28 +++++----- .../java/com/todoroo/astrid/dao/TaskDao.kt | 23 +++++--- .../java/org/tasks/ui/TaskListViewModel.kt | 52 ++++++++----------- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4cbda365d..38f915df3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -150,7 +150,8 @@ dependencies { implementation("androidx.hilt:hilt-work:${Versions.hilt_androidx}") implementation("androidx.hilt:hilt-lifecycle-viewmodel:${Versions.hilt_androidx}") - implementation("androidx.room:room-runtime:${Versions.room}") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0") + implementation("androidx.room:room-ktx:${Versions.room}") kapt("androidx.room:room-compiler:${Versions.room}") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("io.reactivex.rxjava2:rxandroid:2.1.1") diff --git a/app/licenses.yml b/app/licenses.yml index 87868b3b8..907273359 100644 --- a/app/licenses.yml +++ b/app/licenses.yml @@ -549,12 +549,6 @@ license: The Apache Software License, Version 2.0 licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt url: https://developer.android.com/topic/libraries/architecture/index.html -- artifact: org.jetbrains.kotlinx:kotlinx-coroutines-core-common:+ - name: kotlinx-coroutines-core-common - copyrightHolder: JetBrains s.r.o. - license: The Apache Software License, Version 2.0 - licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt - url: https://github.com/Kotlin/kotlinx.coroutines - artifact: org.conscrypt:conscrypt-android:+ name: org.conscrypt:conscrypt-android copyrightHolder: Android Open Source Project @@ -726,3 +720,9 @@ license: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 licenseUrl: https://glassfish.dev.java.net/public/CDDLv1.0.html url: http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html +- artifact: androidx.room:room-ktx:+ + name: room-ktx + copyrightHolder: Android Open Source Project + license: The Apache Software License, Version 2.0 + licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt + url: https://developer.android.com/topic/libraries/architecture/index.html diff --git a/app/src/main/assets/licenses.json b/app/src/main/assets/licenses.json index 713b71cae..8dbf888e5 100644 --- a/app/src/main/assets/licenses.json +++ b/app/src/main/assets/licenses.json @@ -1298,20 +1298,6 @@ "url": "https://developer.android.com/topic/libraries/architecture/index.html", "libraryName": "Android Paging-Common" }, - { - "artifactId": { - "name": "kotlinx-coroutines-core-common", - "group": "org.jetbrains.kotlinx", - "version": "+" - }, - "copyrightHolder": "JetBrains s.r.o.", - "copyrightStatement": "Copyright © JetBrains s.r.o. All rights reserved.", - "license": "The Apache Software License, Version 2.0", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt", - "normalizedLicense": "apache2", - "url": "https://github.com/Kotlin/kotlinx.coroutines", - "libraryName": "kotlinx-coroutines-core-common" - }, { "artifactId": { "name": "conscrypt-android", @@ -1730,6 +1716,20 @@ "normalizedLicense": "cddl1", "url": "http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html", "libraryName": "jsr250-api" + }, + { + "artifactId": { + "name": "room-ktx", + "group": "androidx.room", + "version": "+" + }, + "copyrightHolder": "Android Open Source Project", + "copyrightStatement": "Copyright © Android Open Source Project. All rights reserved.", + "license": "The Apache Software License, Version 2.0", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt", + "normalizedLicense": "apache2", + "url": "https://developer.android.com/topic/libraries/architecture/index.html", + "libraryName": "room-ktx" } ] } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt index 79e9b6adc..493b28f25 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt +++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt @@ -11,12 +11,14 @@ import androidx.sqlite.db.SimpleSQLiteQuery import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.Field import com.todoroo.andlib.sql.Functions +import com.todoroo.andlib.utility.AndroidUtilities.assertNotMainThread import com.todoroo.andlib.utility.DateUtilities import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.PermaSql import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task.Companion.NO_ID import com.todoroo.astrid.helper.UUIDHelper +import kotlinx.coroutines.runBlocking import org.tasks.BuildConfig import org.tasks.data.Place import org.tasks.data.SubtaskInfo @@ -116,11 +118,15 @@ abstract class TaskDao(private val database: Database) { @Transaction open fun fetchTasks(callback: (SubtaskInfo) -> List): List { - return fetchTasks(callback, getSubtaskInfo()) + return runBlocking { + fetchTasks(callback, getSubtaskInfo()) + } } @Transaction open fun fetchTasks(callback: (SubtaskInfo) -> List, subtasks: SubtaskInfo): List { + assertNotMainThread() + val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 val queries = callback.invoke(subtasks) val db = database.openHelper.writableDatabase @@ -145,11 +151,16 @@ abstract class TaskDao(private val database: Database) { @RawQuery abstract fun count(query: SimpleSQLiteQuery): Int - @Query("SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks," - + "EXISTS(SELECT 1 FROM google_tasks " - + " INNER JOIN tasks ON gt_task = _id " - + " WHERE deleted = 0 AND gt_parent > 0 AND gt_deleted = 0) AS hasGoogleSubtasks") - abstract fun getSubtaskInfo(): SubtaskInfo + @Query(""" +SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks, + EXISTS(SELECT 1 + FROM google_tasks + INNER JOIN tasks ON gt_task = _id + WHERE deleted = 0 + AND gt_parent > 0 + AND gt_deleted = 0) AS hasGoogleSubtasks + """) + abstract suspend fun getSubtaskInfo(): SubtaskInfo @RawQuery(observedEntities = [Place::class]) abstract fun getTaskFactory( diff --git a/app/src/main/java/org/tasks/ui/TaskListViewModel.kt b/app/src/main/java/org/tasks/ui/TaskListViewModel.kt index cfd9164a6..12465702d 100644 --- a/app/src/main/java/org/tasks/ui/TaskListViewModel.kt +++ b/app/src/main/java/org/tasks/ui/TaskListViewModel.kt @@ -9,11 +9,9 @@ import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.DateUtilities import com.todoroo.astrid.api.Filter import com.todoroo.astrid.dao.TaskDao -import io.reactivex.Completable -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.tasks.BuildConfig import org.tasks.data.SubtaskInfo import org.tasks.data.TaskContainer @@ -28,7 +26,6 @@ class TaskListViewModel @ViewModelInject constructor( private var tasks = MutableLiveData>() private var filter: Filter? = null private var manualSortFilter = false - private val disposable = CompositeDisposable() private var internal: LiveData>? = null fun setFilter(filter: Filter) { @@ -58,26 +55,27 @@ class TaskListViewModel @ViewModelInject constructor( if (filter == null) { return } - disposable.add( - Single.fromCallable { taskDao.getSubtaskInfo() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { subtasks: SubtaskInfo -> - if (manualSortFilter || !preferences.usePagedQueries()) { - performNonPagedQuery(subtasks) - } else { - performPagedListQuery() - } - }) { t: Throwable? -> Timber.e(t) }) + try { + viewModelScope.launch { + val subtasks = taskDao.getSubtaskInfo() + if (manualSortFilter || !preferences.usePagedQueries()) { + performNonPagedQuery(subtasks) + } else { + performPagedListQuery() + } + } + } catch (e: Exception) { + Timber.e(e) + } } - private fun performNonPagedQuery(subtasks: SubtaskInfo) = - disposable.add( - Single.fromCallable { taskDao.fetchTasks({ s: SubtaskInfo? -> getQuery(preferences, filter!!, s!!) }, subtasks) } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ value: List -> tasks.postValue(value) }) { t: Throwable? -> Timber.e(t) }) + private suspend fun performNonPagedQuery(subtasks: SubtaskInfo) { + tasks.value = withContext(Dispatchers.IO) { + taskDao.fetchTasks( + { s: SubtaskInfo? -> getQuery(preferences, filter!!, s!!) }, + subtasks) + } + } private fun performPagedListQuery() { val queries = getQuery(preferences, filter!!, SubtaskInfo()) @@ -97,14 +95,11 @@ class TaskListViewModel @ViewModelInject constructor( } if (BuildConfig.DEBUG) { builder.setFetchExecutor { command: Runnable -> - Completable.fromAction { - AndroidUtilities.assertNotMainThread() + viewModelScope.launch(Dispatchers.IO) { val start = DateUtilities.now() command.run() Timber.d("*** paged list execution took %sms", DateUtilities.now() - start) } - .subscribeOn(Schedulers.io()) - .subscribe() } } internal = builder.build() @@ -112,7 +107,6 @@ class TaskListViewModel @ViewModelInject constructor( } override fun onCleared() { - disposable.dispose() removeObserver() }