mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 05:31:51 +01:00
Minor updates to extension installs
downloadsStateFlow now uses pkgName instead of downloadId Suppress downloadId warnings ExtensionInstallJob now sets all pending extensions into "Pending" State On load, extensions list now gets the current status of updating/installing extensions Emit a finish to all pending extension on job cancel
This commit is contained in:
parent
b3d05d50fa
commit
0d67d49fc9
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager.ExtensionInfo
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -49,6 +50,8 @@ class ExtensionInstallService(
|
||||
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
|
||||
private var activeInstalls = listOf<String>()
|
||||
|
||||
/**
|
||||
* This method needs to be implemented, but it's not used/needed.
|
||||
*/
|
||||
@ -85,6 +88,11 @@ class ExtensionInstallService(
|
||||
) < it.versionCode
|
||||
}
|
||||
?: return START_NOT_STICKY
|
||||
|
||||
activeInstalls = list.map { it.pkgName }
|
||||
serviceScope.launch {
|
||||
list.forEach { extensionManager.setPending(it.pkgName) }
|
||||
}
|
||||
var installed = 0
|
||||
val installedExtensions = mutableListOf<ExtensionInfo>()
|
||||
job = serviceScope.launch {
|
||||
@ -143,6 +151,8 @@ class ExtensionInstallService(
|
||||
override fun onDestroy() {
|
||||
job?.cancel()
|
||||
serviceScope.cancel()
|
||||
activeInstalls.forEach { extensionManager.cleanUpInstallation(it) }
|
||||
extensionManager.downloadRelay.tryEmit("Finished" to (InstallStep.Installed to null))
|
||||
if (instance == this) {
|
||||
instance = null
|
||||
}
|
||||
@ -166,6 +176,8 @@ class ExtensionInstallService(
|
||||
context.stopService(Intent(context, ExtensionUpdateJob::class.java))
|
||||
}
|
||||
|
||||
fun activeInstalls(): List<String>? = instance?.activeInstalls
|
||||
|
||||
/**
|
||||
* Returns the status of the service.
|
||||
*
|
||||
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
|
||||
@ -54,7 +55,7 @@ class ExtensionManager(
|
||||
val downloadRelay
|
||||
get() = installer.downloadsStateFlow
|
||||
|
||||
fun getExtension(downloadId: Long): String? {
|
||||
private fun getExtension(downloadId: Long): String? {
|
||||
return installer.activeDownloads.entries.find { downloadId == it.value }?.key
|
||||
}
|
||||
|
||||
@ -266,7 +267,16 @@ class ExtensionManager(
|
||||
* @param result Whether the extension was installed or not.
|
||||
*/
|
||||
fun setInstallationResult(downloadId: Long, result: Boolean) {
|
||||
installer.setInstallationResult(downloadId, result)
|
||||
val pkgName = getExtension(downloadId) ?: return
|
||||
setInstallationResult(pkgName, result)
|
||||
}
|
||||
|
||||
fun cleanUpInstallation(pkgName: String) {
|
||||
installer.cleanUpInstallation(pkgName)
|
||||
}
|
||||
|
||||
fun setInstallationResult(pkgName: String, result: Boolean) {
|
||||
installer.setInstallationResult(pkgName, result)
|
||||
}
|
||||
|
||||
/** Sets the result of the installation of an extension.
|
||||
@ -284,7 +294,39 @@ class ExtensionManager(
|
||||
* @param downloadId The id of the download.
|
||||
*/
|
||||
fun setInstalling(downloadId: Long, sessionId: Int) {
|
||||
installer.setInstalling(downloadId, sessionId)
|
||||
val pkgName = getExtension(downloadId) ?: return
|
||||
setInstalling(pkgName, sessionId)
|
||||
}
|
||||
|
||||
fun setInstalling(pkgName: String, sessionId: Int) {
|
||||
installer.setInstalling(pkgName, sessionId)
|
||||
}
|
||||
|
||||
suspend fun setPending(pkgName: String) {
|
||||
installer.setPending(pkgName)
|
||||
}
|
||||
|
||||
fun getInstallInfo(pkgName: String): ExtensionIntallInfo? {
|
||||
val installStep = when {
|
||||
installer.downloadInstallerMap[pkgName] != null &&
|
||||
context.packageManager.packageInstaller
|
||||
.getSessionInfo(installer.downloadInstallerMap[pkgName] ?: 0) != null -> {
|
||||
InstallStep.Installing
|
||||
}
|
||||
installer.activeDownloads[pkgName] != null -> InstallStep.Downloading
|
||||
ExtensionInstallService.activeInstalls()
|
||||
?.contains(pkgName) == true -> InstallStep.Pending
|
||||
else -> return null
|
||||
}
|
||||
val sessionInfo = run {
|
||||
val sessionId = installer.downloadInstallerMap[pkgName]
|
||||
if (sessionId != null) {
|
||||
context.packageManager.packageInstaller.getSessionInfo(sessionId)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
return ExtensionIntallInfo(installStep, sessionInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.extension.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@ -63,10 +64,10 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
/**
|
||||
* StateFlow used to notify the installation step of every download.
|
||||
*/
|
||||
val downloadsStateFlow = MutableStateFlow(0L to ExtensionIntallInfo(InstallStep.Pending, null))
|
||||
val downloadsStateFlow = MutableStateFlow("" to ExtensionIntallInfo(InstallStep.Pending, null))
|
||||
|
||||
/** Map of download id to installer session id */
|
||||
val downloadInstallerMap = hashMapOf<Long, Int>()
|
||||
val downloadInstallerMap = hashMapOf<String, Int>()
|
||||
|
||||
/**
|
||||
* Adds the given extension to the downloads queue and returns a flow containing its
|
||||
@ -103,7 +104,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
scope.launch {
|
||||
flowOf(
|
||||
pollStatus(id),
|
||||
pollInstallStatus(id)
|
||||
pollInstallStatus(pkgName)
|
||||
).flattenMerge()
|
||||
.transformWhile {
|
||||
emit(it)
|
||||
@ -118,11 +119,11 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
deleteDownload(pkgName)
|
||||
}
|
||||
.collect {
|
||||
downloadsStateFlow.emit(id to it)
|
||||
downloadsStateFlow.emit(extension.pkgName to it)
|
||||
}
|
||||
}
|
||||
|
||||
return downloadsStateFlow.filter { it.first == id }.map { it.second }
|
||||
return downloadsStateFlow.filter { it.first == extension.pkgName }.map { it.second }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.transformWhile {
|
||||
emit(it)
|
||||
@ -139,6 +140,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
*
|
||||
* @param id The id of the download to poll.
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
private fun pollStatus(id: Long): Flow<ExtensionIntallInfo> {
|
||||
val query = DownloadManager.Query().setFilterById(id)
|
||||
|
||||
@ -176,12 +178,12 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
* Returns a flow that polls the given installer session for its status every half second, as the
|
||||
* manager doesn't have any notification system. This will only stop once
|
||||
*
|
||||
* @param id The id of the download mapped to the session to poll.
|
||||
* @param pkgName The pkgName of the download mapped to the session to poll.
|
||||
*/
|
||||
private fun pollInstallStatus(id: Long): Flow<ExtensionIntallInfo> {
|
||||
private fun pollInstallStatus(pkgName: String): Flow<ExtensionIntallInfo> {
|
||||
return flow {
|
||||
while (true) {
|
||||
val sessionId = downloadInstallerMap[id]
|
||||
val sessionId = downloadInstallerMap[pkgName]
|
||||
if (sessionId != null) {
|
||||
val session =
|
||||
context.packageManager.packageInstaller.getSessionInfo(sessionId)
|
||||
@ -191,7 +193,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
}
|
||||
}
|
||||
.takeWhile { info ->
|
||||
val sessionId = downloadInstallerMap[id]
|
||||
val sessionId = downloadInstallerMap[pkgName]
|
||||
if (sessionId != null) {
|
||||
info.second != null
|
||||
} else {
|
||||
@ -250,15 +252,29 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
*
|
||||
* @param downloadId The id of the download.
|
||||
*/
|
||||
fun setInstalling(downloadId: Long, sessionId: Int) {
|
||||
downloadsStateFlow.tryEmit(downloadId to ExtensionIntallInfo(InstallStep.Installing, null))
|
||||
downloadInstallerMap[downloadId] = sessionId
|
||||
fun setInstalling(pkgName: String, sessionId: Int) {
|
||||
downloadsStateFlow.tryEmit(pkgName to ExtensionIntallInfo(InstallStep.Installing, null))
|
||||
downloadInstallerMap[pkgName] = sessionId
|
||||
}
|
||||
|
||||
suspend fun setPending(pkgName: String) {
|
||||
downloadsStateFlow.emit(pkgName to ExtensionIntallInfo(InstallStep.Pending, null))
|
||||
}
|
||||
|
||||
fun cancelInstallation(sessionId: Int) {
|
||||
val downloadId = downloadInstallerMap.entries.find { it.value == sessionId }?.key ?: return
|
||||
setInstallationResult(downloadId, false)
|
||||
context.packageManager.packageInstaller.abandonSession(sessionId)
|
||||
try {
|
||||
context.packageManager.packageInstaller.abandonSession(sessionId)
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
|
||||
fun cleanUpInstallation(pkgName: String) {
|
||||
val sessionId = downloadInstallerMap[pkgName] ?: return
|
||||
downloadInstallerMap.remove(pkgName)
|
||||
try {
|
||||
context.packageManager.packageInstaller.abandonSession(sessionId)
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,10 +283,10 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
* @param downloadId The id of the download.
|
||||
* @param result Whether the extension was installed or not.
|
||||
*/
|
||||
fun setInstallationResult(downloadId: Long, result: Boolean) {
|
||||
fun setInstallationResult(pkgName: String, result: Boolean) {
|
||||
val step = if (result) InstallStep.Installed else InstallStep.Error
|
||||
downloadInstallerMap.remove(downloadId)
|
||||
downloadsStateFlow.tryEmit(downloadId to ExtensionIntallInfo(step, null))
|
||||
downloadInstallerMap.remove(pkgName)
|
||||
downloadsStateFlow.tryEmit(pkgName to ExtensionIntallInfo(step, null))
|
||||
}
|
||||
|
||||
fun softDeleteDownload(downloadId: Long) {
|
||||
@ -327,6 +343,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
* Called when a download event is received. It looks for the download in the current active
|
||||
* downloads and notifies its installation step.
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0) ?: return
|
||||
|
||||
@ -335,12 +352,13 @@ internal class ExtensionInstaller(private val context: Context) {
|
||||
|
||||
val uri = downloadManager.getUriForDownloadedFile(id)
|
||||
|
||||
val pkgName = activeDownloads.entries.find { id == it.value }?.key
|
||||
// Set next installation step
|
||||
if (uri != null) {
|
||||
downloadsStateFlow.tryEmit(id to ExtensionIntallInfo(InstallStep.Loading, null))
|
||||
} else {
|
||||
if (uri != null && pkgName != null) {
|
||||
downloadsStateFlow.tryEmit(pkgName to ExtensionIntallInfo(InstallStep.Loading, null))
|
||||
} else if (pkgName != null) {
|
||||
Timber.e("Couldn't locate downloaded APK")
|
||||
downloadsStateFlow.tryEmit(id to ExtensionIntallInfo(InstallStep.Error, null))
|
||||
downloadsStateFlow.tryEmit(pkgName to ExtensionIntallInfo(InstallStep.Error, null))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ class ExtensionBottomPresenter(
|
||||
private val sourceManager: SourceManager = Injekt.get()
|
||||
|
||||
private var selectedSource: Long? = null
|
||||
private var firstLoad = true
|
||||
private val db: DatabaseHelper = Injekt.get()
|
||||
|
||||
override fun onCreate() {
|
||||
@ -100,9 +101,21 @@ class ExtensionBottomPresenter(
|
||||
presenterScope.launch {
|
||||
extensionManager.downloadRelay
|
||||
.collect {
|
||||
val extPageName = extensionManager.getExtension(it.first)
|
||||
if (it.first == "Finished") {
|
||||
firstLoad = true
|
||||
currentDownloads.clear()
|
||||
extensions = toItems(
|
||||
Triple(
|
||||
extensionManager.installedExtensions,
|
||||
extensionManager.untrustedExtensions,
|
||||
extensionManager.availableExtensions
|
||||
)
|
||||
)
|
||||
withUIContext { bottomSheet.setExtensions(extensions) }
|
||||
return@collect
|
||||
}
|
||||
val extension = extensions.find { item ->
|
||||
extPageName == item.extension.pkgName
|
||||
it.first == item.extension.pkgName
|
||||
} ?: return@collect
|
||||
when (it.second.first) {
|
||||
InstallStep.Installed, InstallStep.Error -> {
|
||||
@ -183,6 +196,15 @@ class ExtensionBottomPresenter(
|
||||
|
||||
val items = mutableListOf<ExtensionItem>()
|
||||
|
||||
if (firstLoad) {
|
||||
val listOfExtensions = installed + untrusted + available
|
||||
listOfExtensions.forEach {
|
||||
val installInfo = extensionManager.getInstallInfo(it.pkgName) ?: return@forEach
|
||||
currentDownloads[it.pkgName] = installInfo
|
||||
}
|
||||
firstLoad = false
|
||||
}
|
||||
|
||||
val updatesSorted = installed.filter { it.hasUpdate && (showNsfwExtensions || !it.isNsfw) }.sortedBy { it.name }
|
||||
val sortOrder = InstalledExtensionsOrder.fromPreference(preferences)
|
||||
val installedSorted = installed
|
||||
|
Loading…
Reference in New Issue
Block a user