Bugfixes and extension installation improvements

This commit is contained in:
inorichi 2018-03-02 18:10:10 +01:00
parent a3c03e8ceb
commit 8e50ac67bc
11 changed files with 94 additions and 49 deletions

View File

@ -102,7 +102,7 @@ android {
dependencies {
// Modified dependencies
implementation 'com.github.inorichi:subsampling-scale-image-view:c19b883'
implementation 'com.github.inorichi:subsampling-scale-image-view:81b9d68'
implementation 'com.github.inorichi:junrar-android:634c1f5'
// Android support library

View File

@ -52,6 +52,9 @@
android:scheme="tachiyomi" />
</intent-filter>
</activity>
<activity
android:name=".extension.util.ExtensionInstallActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
<provider
android:name="android.support.v4.content.FileProvider"

View File

@ -201,6 +201,16 @@ class ExtensionManager(
return installExtension(availableExt)
}
/**
* Sets the result of the installation of an extension.
*
* @param downloadId The id of the download.
* @param result Whether the extension was installed or not.
*/
fun setInstallationResult(downloadId: Long, result: Boolean) {
installer.setInstallationResult(downloadId, result)
}
/**
* Uninstalls the extension that matches the given package name.
*
@ -295,17 +305,14 @@ class ExtensionManager(
override fun onExtensionInstalled(extension: Extension.Installed) {
registerNewExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
}
override fun onExtensionUpdated(extension: Extension.Installed) {
registerUpdatedExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
}
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
untrustedExtensions += extension
installer.onApkInstalled(extension.pkgName)
}
override fun onPackageUninstalled(pkgName: String) {

View File

@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.extension.util
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import eu.kanade.tachiyomi.extension.ExtensionManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Activity used to install extensions, because we can only receive the result of the installation
* with [startActivityForResult], which we need to update the UI.
*/
class ExtensionInstallActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
.setDataAndType(intent.data, intent.type)
.putExtra(Intent.EXTRA_RETURN_RESULT, true)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(installIntent, INSTALL_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == INSTALL_REQUEST_CODE) {
checkInstallationResult(resultCode)
}
finish()
}
private fun checkInstallationResult(resultCode: Int) {
val downloadId = intent.extras.getLong(ExtensionInstaller.EXTRA_DOWNLOAD_ID)
val success = resultCode == RESULT_OK
val extensionManager = Injekt.get<ExtensionManager>()
extensionManager.setInstallationResult(downloadId, success)
}
private companion object {
const val INSTALL_REQUEST_CODE = 500
}
}

View File

@ -77,8 +77,6 @@ internal class ExtensionInstaller(private val context: Context) {
.mergeWith(pollStatus(id))
// Force an error if the download takes more than 3 minutes
.mergeWith(Observable.timer(3, TimeUnit.MINUTES).map { InstallStep.Error })
// Force an error if the install process takes more than 10 seconds
.flatMap { Observable.just(it).mergeWith(timeoutWhenInstalling(it)) }
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
@ -118,27 +116,15 @@ internal class ExtensionInstaller(private val context: Context) {
}
}
/**
* Returns an observable that timeouts the installation after a specified time when the apk has
* been downloaded.
*
* @param currentStep The current step of the installation process.
*/
private fun timeoutWhenInstalling(currentStep: InstallStep): Observable<InstallStep> {
return Observable.just(currentStep)
.filter { it == InstallStep.Installing }
.delay(10, TimeUnit.SECONDS)
.map { InstallStep.Error }
}
/**
* Starts an intent to install the extension at the given uri.
*
* @param uri The uri of the extension to install.
*/
fun installApk(uri: Uri) {
val intent = Intent(Intent.ACTION_VIEW)
fun installApk(downloadId: Long, uri: Uri) {
val intent = Intent(context, ExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(intent)
@ -158,13 +144,14 @@ internal class ExtensionInstaller(private val context: Context) {
}
/**
* Called when an extension is installed, allowing to update its installation step.
* Sets the result of the installation of an extension.
*
* @param pkgName The package name of the installed application.
* @param downloadId The id of the download.
* @param result Whether the extension was installed or not.
*/
fun onApkInstalled(pkgName: String) {
val id = activeDownloads[pkgName] ?: return
downloadsRelay.call(id to InstallStep.Installed)
fun setInstallationResult(downloadId: Long, result: Boolean) {
val step = if (result) InstallStep.Installed else InstallStep.Error
downloadsRelay.call(downloadId to step)
}
/**
@ -243,17 +230,18 @@ internal class ExtensionInstaller(private val context: Context) {
@Suppress("DEPRECATION")
val uriCompat = File(cursor.getString(cursor.getColumnIndex(
DownloadManager.COLUMN_LOCAL_FILENAME))).getUriCompat(context)
installApk(uriCompat)
installApk(id, uriCompat)
}
}
} else {
installApk(uri)
installApk(id, uri)
}
}
}
private companion object {
companion object {
const val APK_MIME = "application/vnd.android.package-archive"
const val EXTRA_DOWNLOAD_ID = "ExtensionInstaller.extra.DOWNLOAD_ID"
}
}

View File

@ -57,7 +57,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
override fun onViewCreated(view: View) {
super.onViewCreated(view)
val extension = presenter.extension
val extension = presenter.extension ?: return
val context = view.context
extension_title.text = extension.name

View File

@ -12,7 +12,7 @@ class ExtensionDetailsPresenter(
private val extensionManager: ExtensionManager = Injekt.get()
) : BasePresenter<ExtensionDetailsController>() {
val extension = extensionManager.installedExtensions.first { it.pkgName == pkgName }
val extension = extensionManager.installedExtensions.find { it.pkgName == pkgName }
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@ -33,6 +33,7 @@ class ExtensionDetailsPresenter(
}
fun uninstallExtension() {
val extension = extension ?: return
extensionManager.uninstallExtension(extension.pkgName)
}
}

View File

@ -148,7 +148,10 @@ class MainActivity : BaseActivity() {
SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates)
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read)
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues)
SHORTCUT_MANGA -> router.setRoot(RouterTransaction.with(MangaController(intent.extras)))
SHORTCUT_MANGA -> {
val extras = intent.extras ?: return false
router.setRoot(RouterTransaction.with(MangaController(extras)))
}
SHORTCUT_DOWNLOADS -> {
if (router.backstack.none { it.controller() is DownloadController }) {
setSelectedDrawerItem(R.id.nav_drawer_downloads)

View File

@ -39,7 +39,7 @@ import java.util.*
class MangaController : RxController, TabbedController {
constructor(manga: Manga?, fromCatalogue: Boolean = false) : super(Bundle().apply {
putLong(MANGA_EXTRA, manga?.id!!)
putLong(MANGA_EXTRA, manga?.id ?: 0)
putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue)
}) {
this.manga = manga

View File

@ -14,25 +14,22 @@
<ImageView
android:id="@+id/image"
android:layout_width="48dp"
android:layout_height="56dp"
android:clickable="true"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:paddingRight="0dp"
android:paddingEnd="0dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintLeft_toLeftOf="parent"
tools:src="@mipmap/ic_launcher_round"/>
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingRight="8dp"
android:paddingEnd="8dp"
android:ellipsize="end"
@ -65,4 +62,4 @@
</android.support.constraint.ConstraintLayout>
</FrameLayout>
</FrameLayout>

View File

@ -14,14 +14,15 @@
<ImageView
android:id="@+id/image"
android:layout_width="64dp"
android:layout_height="match_parent"
android:padding="16dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round"/>
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/ext_title"