mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-24 03:42:15 +00:00
Move DownloadService to Coroutine (1/2)
This commit is contained in:
parent
0dd23c23b1
commit
e7184ecccc
@ -24,13 +24,13 @@ import com.looker.droidify.utility.extension.android.*
|
|||||||
import com.looker.droidify.utility.extension.resources.*
|
import com.looker.droidify.utility.extension.resources.*
|
||||||
import com.looker.droidify.utility.extension.text.*
|
import com.looker.droidify.utility.extension.text.*
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.core.Observable
|
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
class DownloadService : ConnectionService<DownloadService.Binder>() {
|
class DownloadService : ConnectionService<DownloadService.Binder>() {
|
||||||
@ -41,7 +41,8 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
private const val EXTRA_CACHE_FILE_NAME =
|
private const val EXTRA_CACHE_FILE_NAME =
|
||||||
"${BuildConfig.APPLICATION_ID}.intent.extra.CACHE_FILE_NAME"
|
"${BuildConfig.APPLICATION_ID}.intent.extra.CACHE_FILE_NAME"
|
||||||
|
|
||||||
private val downloadingSubject = PublishSubject.create<State.Downloading>()
|
private val mutableDownloadState = MutableSharedFlow<State.Downloading>()
|
||||||
|
private val downloadState = mutableDownloadState.asSharedFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
val scope = CoroutineScope(Dispatchers.Default)
|
val scope = CoroutineScope(Dispatchers.Default)
|
||||||
@ -81,15 +82,14 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
State(packageName, name)
|
State(packageName, name)
|
||||||
|
|
||||||
class Success(
|
class Success(
|
||||||
packageName: String, name: String, val release: Release,
|
packageName: String, name: String, val release: Release
|
||||||
val consume: () -> Unit,
|
|
||||||
) : State(packageName, name)
|
) : State(packageName, name)
|
||||||
|
|
||||||
class Error(packageName: String, name: String) : State(packageName, name)
|
class Error(packageName: String, name: String) : State(packageName, name)
|
||||||
class Cancel(packageName: String, name: String) : State(packageName, name)
|
class Cancel(packageName: String, name: String) : State(packageName, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val stateSubject = PublishSubject.create<State>()
|
private val mutableStateSubject = MutableSharedFlow<State>()
|
||||||
|
|
||||||
private class Task(
|
private class Task(
|
||||||
val packageName: String, val name: String, val release: Release,
|
val packageName: String, val name: String, val release: Release,
|
||||||
@ -106,9 +106,7 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
private var currentTask: CurrentTask? = null
|
private var currentTask: CurrentTask? = null
|
||||||
|
|
||||||
inner class Binder : android.os.Binder() {
|
inner class Binder : android.os.Binder() {
|
||||||
fun events(packageName: String): Observable<State> {
|
val stateSubject = mutableStateSubject.asSharedFlow()
|
||||||
return stateSubject.filter { it.packageName == packageName }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun enqueue(packageName: String, name: String, repository: Repository, release: Release) {
|
fun enqueue(packageName: String, name: String, repository: Repository, release: Release) {
|
||||||
val task = Task(
|
val task = Task(
|
||||||
@ -128,7 +126,7 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
if (currentTask == null) {
|
if (currentTask == null) {
|
||||||
handleDownload()
|
handleDownload()
|
||||||
} else {
|
} else {
|
||||||
stateSubject.onNext(State.Pending(packageName, name))
|
scope.launch { mutableStateSubject.emit(State.Pending(packageName, name)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,18 +136,11 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
cancelCurrentTask(packageName)
|
cancelCurrentTask(packageName)
|
||||||
handleDownload()
|
handleDownload()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getState(packageName: String): State? = currentTask
|
|
||||||
?.let { if (it.task.packageName == packageName) it.lastState else null }
|
|
||||||
?: tasks.find { it.packageName == packageName }
|
|
||||||
?.let { State.Pending(it.packageName, it.name) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val binder = Binder()
|
private val binder = Binder()
|
||||||
override fun onBind(intent: Intent): Binder = binder
|
override fun onBind(intent: Intent): Binder = binder
|
||||||
|
|
||||||
private var downloadingDisposable: Disposable? = null
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
@ -162,16 +153,14 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
.let(notificationManager::createNotificationChannel)
|
.let(notificationManager::createNotificationChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingDisposable = downloadingSubject
|
scope.launch {
|
||||||
.sample(500L, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
downloadState.collect { publishForegroundState(false, it) }
|
||||||
.subscribe { publishForegroundState(false, it) }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
downloadingDisposable?.dispose()
|
|
||||||
downloadingDisposable = null
|
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
cancelTasks(null)
|
cancelTasks(null)
|
||||||
cancelCurrentTask(null)
|
cancelCurrentTask(null)
|
||||||
@ -187,7 +176,7 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
private fun cancelTasks(packageName: String?) {
|
private fun cancelTasks(packageName: String?) {
|
||||||
tasks.removeAll {
|
tasks.removeAll {
|
||||||
(packageName == null || it.packageName == packageName) && run {
|
(packageName == null || it.packageName == packageName) && run {
|
||||||
stateSubject.onNext(State.Cancel(it.packageName, it.name))
|
scope.launch { mutableStateSubject.emit(State.Cancel(it.packageName, it.name)) }
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,7 +186,14 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
currentTask?.let {
|
currentTask?.let {
|
||||||
if (packageName == null || it.task.packageName == packageName) {
|
if (packageName == null || it.task.packageName == packageName) {
|
||||||
currentTask = null
|
currentTask = null
|
||||||
stateSubject.onNext(State.Cancel(it.task.packageName, it.task.name))
|
scope.launch {
|
||||||
|
mutableStateSubject.emit(
|
||||||
|
State.Cancel(
|
||||||
|
it.task.packageName,
|
||||||
|
it.task.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
it.disposable.dispose()
|
it.disposable.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,9 +303,10 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
|
|
||||||
private fun publishSuccess(task: Task) {
|
private fun publishSuccess(task: Task) {
|
||||||
var consumed = false
|
var consumed = false
|
||||||
stateSubject.onNext(State.Success(task.packageName, task.name, task.release) {
|
scope.launch {
|
||||||
|
mutableStateSubject.emit(State.Success(task.packageName, task.name, task.release))
|
||||||
consumed = true
|
consumed = true
|
||||||
})
|
}
|
||||||
if (!consumed) {
|
if (!consumed) {
|
||||||
if (rootInstallerEnabled) {
|
if (rootInstallerEnabled) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@ -414,7 +411,7 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
}
|
}
|
||||||
}::class
|
}::class
|
||||||
}.build())
|
}.build())
|
||||||
stateSubject.onNext(state)
|
scope.launch { mutableStateSubject.emit(state) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,14 +438,16 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
task.authentication
|
task.authentication
|
||||||
) { read, total ->
|
) { read, total ->
|
||||||
if (!disposable.isDisposed) {
|
if (!disposable.isDisposed) {
|
||||||
downloadingSubject.onNext(
|
scope.launch {
|
||||||
State.Downloading(
|
mutableDownloadState.emit(
|
||||||
task.packageName,
|
State.Downloading(
|
||||||
task.name,
|
task.packageName,
|
||||||
read,
|
task.name,
|
||||||
total
|
read,
|
||||||
|
total
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
@ -460,7 +459,14 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
task,
|
task,
|
||||||
if (result != null) ErrorType.Http else ErrorType.Network
|
if (result != null) ErrorType.Http else ErrorType.Network
|
||||||
)
|
)
|
||||||
stateSubject.onNext(State.Error(task.packageName, task.name))
|
scope.launch {
|
||||||
|
mutableStateSubject.emit(
|
||||||
|
State.Error(
|
||||||
|
task.packageName,
|
||||||
|
task.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val validationError = validatePackage(task, partialReleaseFile)
|
val validationError = validatePackage(task, partialReleaseFile)
|
||||||
if (validationError == null) {
|
if (validationError == null) {
|
||||||
@ -471,7 +477,14 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
|
|||||||
} else {
|
} else {
|
||||||
partialReleaseFile.delete()
|
partialReleaseFile.delete()
|
||||||
showNotificationError(task, ErrorType.Validation(validationError))
|
showNotificationError(task, ErrorType.Validation(validationError))
|
||||||
stateSubject.onNext(State.Error(task.packageName, task.name))
|
scope.launch {
|
||||||
|
mutableStateSubject.emit(
|
||||||
|
State.Error(
|
||||||
|
task.packageName,
|
||||||
|
task.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleDownload()
|
handleDownload()
|
||||||
|
@ -35,6 +35,8 @@ import io.reactivex.rxjava3.core.Observable
|
|||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -83,15 +85,12 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
|
|||||||
private var recyclerView: RecyclerView? = null
|
private var recyclerView: RecyclerView? = null
|
||||||
|
|
||||||
private var productDisposable: Disposable? = null
|
private var productDisposable: Disposable? = null
|
||||||
private var downloadDisposable: Disposable? = null
|
|
||||||
private val downloadConnection = Connection(DownloadService::class.java, onBind = { _, binder ->
|
private val downloadConnection = Connection(DownloadService::class.java, onBind = { _, binder ->
|
||||||
lifecycleScope.launch { updateDownloadState(binder.getState(packageName)) }
|
lifecycleScope.launch {
|
||||||
downloadDisposable = binder.events(packageName).subscribe {
|
binder.stateSubject.filter { it.packageName == packageName }.collect {
|
||||||
lifecycleScope.launch { updateDownloadState(it) }
|
updateDownloadState(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, onUnbind = { _, _ ->
|
|
||||||
downloadDisposable?.dispose()
|
|
||||||
downloadDisposable = null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@ -238,8 +237,6 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
|
|||||||
|
|
||||||
productDisposable?.dispose()
|
productDisposable?.dispose()
|
||||||
productDisposable = null
|
productDisposable = null
|
||||||
downloadDisposable?.dispose()
|
|
||||||
downloadDisposable = null
|
|
||||||
downloadConnection.unbind(requireContext())
|
downloadConnection.unbind(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +360,6 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
|
|||||||
(recyclerView?.adapter as? AppDetailAdapter)?.setStatus(status)
|
(recyclerView?.adapter as? AppDetailAdapter)?.setStatus(status)
|
||||||
if (state is DownloadService.State.Success && isResumed) {
|
if (state is DownloadService.State.Success && isResumed) {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
state.consume()
|
|
||||||
AppInstaller.getInstance(context)?.defaultInstaller?.install(state.release.cacheFileName)
|
AppInstaller.getInstance(context)?.defaultInstaller?.install(state.release.cacheFileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user