diff --git a/src/main/kotlin/com/looker/droidify/database/dao/RepositoryDao.kt b/src/main/kotlin/com/looker/droidify/database/dao/RepositoryDao.kt index 5c32459d..a704bf1c 100644 --- a/src/main/kotlin/com/looker/droidify/database/dao/RepositoryDao.kt +++ b/src/main/kotlin/com/looker/droidify/database/dao/RepositoryDao.kt @@ -5,6 +5,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import com.looker.droidify.database.entity.Repository +import kotlinx.coroutines.flow.Flow @Dao interface RepositoryDao : BaseDao { @@ -27,6 +28,9 @@ interface RepositoryDao : BaseDao { @Query("SELECT * FROM repository WHERE _id = :id") fun getLive(id: Long): LiveData + @Query("SELECT * FROM repository ORDER BY _id ASC") + fun getAllRepositories(): Flow> + @get:Query("SELECT * FROM repository ORDER BY _id ASC") val all: List @@ -38,7 +42,7 @@ interface RepositoryDao : BaseDao { // TODO clean up products and other tables afterwards @Query("DELETE FROM repository WHERE _id = :id") - fun deleteById(id: Long): Int + fun deleteById(id: Long) @Query("SELECT MAX(_id) FROM repository") fun latestAddedId(): Long diff --git a/src/main/kotlin/com/looker/droidify/service/SyncService.kt b/src/main/kotlin/com/looker/droidify/service/SyncService.kt index 6ec2bf85..b4704f2a 100644 --- a/src/main/kotlin/com/looker/droidify/service/SyncService.kt +++ b/src/main/kotlin/com/looker/droidify/service/SyncService.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.lang.ref.WeakReference import kotlin.math.roundToInt @@ -117,7 +118,7 @@ class SyncService : ConnectionService() { fun installApps(products: List) = batchUpdate(products, true) fun sync(request: SyncRequest) { - GlobalScope.launch { + scope.launch { val ids = db.repositoryDao.all.filter { it.enabled }.map { it.id }.toList() sync(ids, request) } @@ -143,29 +144,30 @@ class SyncService : ConnectionService() { } } - fun setEnabled(repository: Repository, enabled: Boolean): Boolean { - db.repositoryDao.put(repository.enable(enabled)) - if (enabled) { - if (repository.id != currentTask?.task?.repositoryId && !tasks.any { it.repositoryId == repository.id }) { - synchronized(tasks) { tasks += Task(repository.id, true) } - handleNextTask(false) + suspend fun setEnabled(repository: Repository, enabled: Boolean): Boolean = + withContext(Dispatchers.IO) { + db.repositoryDao.put(repository.enable(enabled)) + if (enabled) { + if (repository.id != currentTask?.task?.repositoryId && !tasks.any { it.repositoryId == repository.id }) { + synchronized(tasks) { tasks += Task(repository.id, true) } + handleNextTask(false) + } + } else { + cancelTasks { it.repositoryId == repository.id } + synchronized(tasks) { db.cleanUp(setOf(Pair(repository.id, false))) } + val cancelledTask = cancelCurrentTask { it.task?.repositoryId == repository.id } + handleNextTask(cancelledTask?.hasUpdates == true) } - } else { - cancelTasks { it.repositoryId == repository.id } - synchronized(tasks) { db.cleanUp(setOf(Pair(repository.id, false))) } - val cancelledTask = cancelCurrentTask { it.task?.repositoryId == repository.id } - handleNextTask(cancelledTask?.hasUpdates == true) + true } - return true + + suspend fun isCurrentlySyncing(repositoryId: Long): Boolean = withContext(Dispatchers.IO) { + currentTask?.task?.repositoryId == repositoryId } - fun isCurrentlySyncing(repositoryId: Long): Boolean { - return currentTask?.task?.repositoryId == repositoryId - } - - fun deleteRepository(repositoryId: Long): Boolean { + suspend fun deleteRepository(repositoryId: Long): Boolean = withContext(Dispatchers.IO) { val repository = db.repositoryDao.get(repositoryId) - return repository != null && run { + repository != null && run { setEnabled(repository, false) db.repositoryDao.deleteById(repository.id) true @@ -325,7 +327,7 @@ class SyncService : ConnectionService() { private fun handleNextTask(hasUpdates: Boolean) { if (currentTask == null) { - GlobalScope.launch { + scope.launch { if (tasks.isNotEmpty()) { val task = tasks.removeAt(0) val repository = db.repositoryDao.get(task.repositoryId) @@ -425,7 +427,7 @@ class SyncService : ConnectionService() { * @see SyncService.displayUpdatesNotification */ private fun batchUpdate(productItems: List, install: Boolean = false) { - if (Preferences[Preferences.Key.InstallAfterSync]) GlobalScope.launch { + if (Preferences[Preferences.Key.InstallAfterSync]) scope.launch { // run startUpdate on every item productItems.map { productItem -> Triple( diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/pages/settings/repository/RepositoryPage.kt b/src/main/kotlin/com/looker/droidify/ui/compose/pages/settings/repository/RepositoryPage.kt index a137fdcc..ca7a5f75 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/pages/settings/repository/RepositoryPage.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/pages/settings/repository/RepositoryPage.kt @@ -29,7 +29,7 @@ fun RepositoryPage(viewModel: RepositoriesViewModelX) { } } ) { - val sortedRepoList = remember { repos.sortedBy { !it.enabled } } + val sortedRepoList = remember(repos) { repos.sortedBy { !it.enabled } } RepositoriesRecycler( repositoriesList = sortedRepoList, onClick = { viewModel.toggleRepository(it, it.enabled) }, diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/EditRepositorySheetX.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/EditRepositorySheetX.kt index b902d7aa..ae01909b 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/EditRepositorySheetX.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/EditRepositorySheetX.kt @@ -15,6 +15,7 @@ import androidx.appcompat.app.AlertDialog import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.looker.droidify.EXTRA_REPOSITORY_ID import com.looker.droidify.R @@ -33,9 +34,8 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.schedulers.Schedulers -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request @@ -138,12 +138,14 @@ class EditRepositorySheetX() : FullscreenBottomSheetDialogFragment(), RepoManage .show(childFragmentManager) } - GlobalScope.launch { - val list = viewModel.db.repositoryDao.all - takenAddresses = list.asSequence().filter { it.id != repositoryId } - .flatMap { (it.mirrors + it.address).asSequence() } - .map { it.withoutKnownPath }.toSet() - MainScope().launch { invalidateAddress() } + lifecycleScope.launchWhenStarted { + val reposFlow = viewModel.db.repositoryDao.getAllRepositories() + reposFlow.collectLatest { list -> + takenAddresses = list.asSequence().filter { it.id != repositoryId } + .flatMap { (it.mirrors + it.address).asSequence() } + .map { it.withoutKnownPath }.toSet() + MainScope().launch { invalidateAddress() } + } } } @@ -389,7 +391,7 @@ class EditRepositorySheetX() : FullscreenBottomSheetDialogFragment(), RepoManage address: String, fingerprint: String, authentication: String, - ) = GlobalScope.launch { + ) = lifecycleScope.launch { val binder = syncConnection.binder if (binder != null) { if (binder.isCurrentlySyncing(repositoryId)) { @@ -435,7 +437,7 @@ class EditRepositorySheetX() : FullscreenBottomSheetDialogFragment(), RepoManage } override fun onDeleteConfirm() { - GlobalScope.launch(Dispatchers.IO) { + lifecycleScope.launch { if (syncConnection.binder?.deleteRepository(repositoryId) == true) dismissAllowingStateLoss() } diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt index f25a2403..276cbecb 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt @@ -32,7 +32,7 @@ class PrefsRepositoriesFragment : BaseNavFragment() { savedInstanceState: Bundle?, ): View { super.onCreate(savedInstanceState) - lifecycleScope.launchWhenCreated { + lifecycleScope.launchWhenStarted { viewModel.showSheet.collectLatest { it?.let { RepositorySheetX(it).showNow(childFragmentManager, "Repository $it") diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt index 736f7f7f..a40acecb 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import com.looker.droidify.EXTRA_REPOSITORY_ID import com.looker.droidify.R import com.looker.droidify.RepoManager @@ -19,8 +20,6 @@ import com.looker.droidify.service.SyncService import com.looker.droidify.ui.activities.PrefsActivityX import com.looker.droidify.ui.viewmodels.RepositoryViewModelX import com.looker.droidify.utility.extension.resources.getColorFromAttr -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import java.util.* @@ -140,7 +139,7 @@ class RepositorySheetX() : FullscreenBottomSheetDialogFragment(), RepoManager { } override fun onDeleteConfirm() { - GlobalScope.launch(Dispatchers.IO) { + lifecycleScope.launch { if (syncConnection.binder?.deleteRepository(repositoryId) == true) dismissAllowingStateLoss() } diff --git a/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoriesViewModelX.kt b/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoriesViewModelX.kt index 9a1ab55e..d98b438b 100644 --- a/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoriesViewModelX.kt +++ b/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoriesViewModelX.kt @@ -13,6 +13,7 @@ import com.looker.droidify.service.SyncService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -30,7 +31,9 @@ class RepositoriesViewModelX(val repositoryDao: RepositoryDao) : ViewModel() { init { viewModelScope.launch(Dispatchers.IO) { - _repositories.emit(repositoryDao.all) + repositoryDao.getAllRepositories().collectLatest { + _repositories.emit(it) + } } toLaunch.value = null } @@ -40,13 +43,11 @@ class RepositoriesViewModelX(val repositoryDao: RepositoryDao) : ViewModel() { } fun showRepositorySheet(repositoryId: Long) { - viewModelScope.launch { - _showSheet.emit(repositoryId) - } + viewModelScope.launch { _showSheet.emit(repositoryId) } } fun toggleRepository(repository: Repository, isEnabled: Boolean) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch { syncConnection.binder?.setEnabled(repository, isEnabled) } }