Updating Extension installer from upstream

Also adding an onerror handle
This commit is contained in:
Jay 2020-05-15 02:02:08 -04:00
parent e0e072546f
commit 652e045acf

View File

@ -6,7 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Environment
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.extension.model.InstallStep
@ -63,28 +63,28 @@ internal class ExtensionInstaller(private val context: Context) {
// Register the receiver after removing (and unregistering) the previous download // Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register() downloadReceiver.register()
val request = DownloadManager.Request(Uri.parse(url)) val downloadUri = Uri.parse(url)
.setTitle(extension.name) val request = DownloadManager.Request(downloadUri)
.setMimeType(APK_MIME) .setTitle(extension.name)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) .setMimeType(APK_MIME)
.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, downloadUri.lastPathSegment)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val id = downloadManager.enqueue(request) val id = downloadManager.enqueue(request)
activeDownloads[pkgName] = id activeDownloads[pkgName] = id
downloadsRelay.filter { it.first == id } downloadsRelay.filter { it.first == id }
.map { it.second } .map { it.second }
// Poll download status // Poll download status
.mergeWith(pollStatus(id)) .mergeWith(pollStatus(id))
// Force an error if the download takes more than 3 minutes // Force an error if the download takes more than 3 minutes
.mergeWith(Observable.timer(3, TimeUnit.MINUTES).map { InstallStep.Error }) .mergeWith(Observable.timer(3, TimeUnit.MINUTES).map { InstallStep.Error })
// Stop when the application is installed or errors // Stop when the application is installed or errors
.takeUntil { it.isCompleted() } .takeUntil { it.isCompleted() }
// Always notify on main thread // Always notify on main thread
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed // Always remove the download when unsubscribed
.doOnUnsubscribe { .doOnUnsubscribe { deleteDownload(pkgName) }
deleteDownload(pkgName)
}
} }
/** /**
@ -97,25 +97,28 @@ internal class ExtensionInstaller(private val context: Context) {
val query = DownloadManager.Query().setFilterById(id) val query = DownloadManager.Query().setFilterById(id)
return Observable.interval(0, 1, TimeUnit.SECONDS) return Observable.interval(0, 1, TimeUnit.SECONDS)
// Get the current download status // Get the current download status
.map { .map {
downloadManager.query(query).use { cursor -> downloadManager.query(query).use { cursor ->
cursor.moveToFirst() cursor.moveToFirst()
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
}
} }
// Ignore duplicate results }
.distinctUntilChanged() // Ignore duplicate results
// Stop polling when the download fails or finishes .distinctUntilChanged()
.takeUntil { it == DownloadManager.STATUS_SUCCESSFUL || it == DownloadManager.STATUS_FAILED } // Stop polling when the download fails or finishes
// Map to our model .takeUntil { it == DownloadManager.STATUS_SUCCESSFUL || it == DownloadManager.STATUS_FAILED }
.flatMap { status -> // Map to our model
when (status) { .flatMap { status ->
DownloadManager.STATUS_PENDING -> Observable.just(InstallStep.Pending) when (status) {
DownloadManager.STATUS_RUNNING -> Observable.just(InstallStep.Downloading) DownloadManager.STATUS_PENDING -> Observable.just(InstallStep.Pending)
else -> Observable.empty() DownloadManager.STATUS_RUNNING -> Observable.just(InstallStep.Downloading)
} else -> Observable.empty()
} }
}
.doOnError {
Timber.e(it)
}
} }
/** /**
@ -125,9 +128,9 @@ internal class ExtensionInstaller(private val context: Context) {
*/ */
fun installApk(downloadId: Long, uri: Uri) { fun installApk(downloadId: Long, uri: Uri) {
val intent = Intent(context, ExtensionInstallActivity::class.java) val intent = Intent(context, ExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME) .setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId) .putExtra(EXTRA_DOWNLOAD_ID, downloadId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(intent) context.startActivity(intent)
} }
@ -161,7 +164,7 @@ internal class ExtensionInstaller(private val context: Context) {
* *
* @param pkgName The package name of the download to delete. * @param pkgName The package name of the download to delete.
*/ */
fun deleteDownload(pkgName: String) { private fun deleteDownload(pkgName: String) {
val downloadId = activeDownloads.remove(pkgName) val downloadId = activeDownloads.remove(pkgName)
if (downloadId != null) { if (downloadId != null) {
downloadManager.remove(downloadId) downloadManager.remove(downloadId)
@ -223,20 +226,15 @@ internal class ExtensionInstaller(private val context: Context) {
return return
} }
// Due to a bug in Android versions prior to N, the installer can't open files that do val query = DownloadManager.Query().setFilterById(id)
// not contain the extension in the path, even if you specify the correct MIME. downloadManager.query(query).use { cursor ->
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { if (cursor.moveToFirst()) {
val query = DownloadManager.Query().setFilterById(id) val localUri = cursor.getString(
downloadManager.query(query).use { cursor -> cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
if (cursor.moveToFirst()) { ).removePrefix(FILE_SCHEME)
@Suppress("DEPRECATION")
val uriCompat = File(cursor.getString(cursor.getColumnIndex( installApk(id, File(localUri).getUriCompat(context))
DownloadManager.COLUMN_LOCAL_FILENAME))).getUriCompat(context)
installApk(id, uriCompat)
}
} }
} else {
installApk(id, uri)
} }
} }
} }
@ -244,5 +242,6 @@ internal class ExtensionInstaller(private val context: Context) {
companion object { companion object {
const val APK_MIME = "application/vnd.android.package-archive" const val APK_MIME = "application/vnd.android.package-archive"
const val EXTRA_DOWNLOAD_ID = "ExtensionInstaller.extra.DOWNLOAD_ID" const val EXTRA_DOWNLOAD_ID = "ExtensionInstaller.extra.DOWNLOAD_ID"
const val FILE_SCHEME = "file://"
} }
} }