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 { dependencies {
// Modified 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' implementation 'com.github.inorichi:junrar-android:634c1f5'
// Android support library // Android support library

View File

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

View File

@ -201,6 +201,16 @@ class ExtensionManager(
return installExtension(availableExt) 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. * Uninstalls the extension that matches the given package name.
* *
@ -295,17 +305,14 @@ class ExtensionManager(
override fun onExtensionInstalled(extension: Extension.Installed) { override fun onExtensionInstalled(extension: Extension.Installed) {
registerNewExtension(extension.withUpdateCheck()) registerNewExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
} }
override fun onExtensionUpdated(extension: Extension.Installed) { override fun onExtensionUpdated(extension: Extension.Installed) {
registerUpdatedExtension(extension.withUpdateCheck()) registerUpdatedExtension(extension.withUpdateCheck())
installer.onApkInstalled(extension.pkgName)
} }
override fun onExtensionUntrusted(extension: Extension.Untrusted) { override fun onExtensionUntrusted(extension: Extension.Untrusted) {
untrustedExtensions += extension untrustedExtensions += extension
installer.onApkInstalled(extension.pkgName)
} }
override fun onPackageUninstalled(pkgName: String) { 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)) .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 })
// 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 // Stop when the application is installed or errors
.takeUntil { it.isCompleted() } .takeUntil { it.isCompleted() }
// Always notify on main thread // 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. * Starts an intent to install the extension at the given uri.
* *
* @param uri The uri of the extension to install. * @param uri The uri of the extension to install.
*/ */
fun installApk(uri: Uri) { fun installApk(downloadId: Long, uri: Uri) {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(context, ExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME) .setDataAndType(uri, APK_MIME)
.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)
@ -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) { fun setInstallationResult(downloadId: Long, result: Boolean) {
val id = activeDownloads[pkgName] ?: return val step = if (result) InstallStep.Installed else InstallStep.Error
downloadsRelay.call(id to InstallStep.Installed) downloadsRelay.call(downloadId to step)
} }
/** /**
@ -243,17 +230,18 @@ internal class ExtensionInstaller(private val context: Context) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
val uriCompat = File(cursor.getString(cursor.getColumnIndex( val uriCompat = File(cursor.getString(cursor.getColumnIndex(
DownloadManager.COLUMN_LOCAL_FILENAME))).getUriCompat(context) DownloadManager.COLUMN_LOCAL_FILENAME))).getUriCompat(context)
installApk(uriCompat) installApk(id, uriCompat)
} }
} }
} else { } else {
installApk(uri) installApk(id, uri)
} }
} }
} }
private 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"
} }
} }

View File

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

View File

@ -12,7 +12,7 @@ class ExtensionDetailsPresenter(
private val extensionManager: ExtensionManager = Injekt.get() private val extensionManager: ExtensionManager = Injekt.get()
) : BasePresenter<ExtensionDetailsController>() { ) : BasePresenter<ExtensionDetailsController>() {
val extension = extensionManager.installedExtensions.first { it.pkgName == pkgName } val extension = extensionManager.installedExtensions.find { it.pkgName == pkgName }
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
@ -33,6 +33,7 @@ class ExtensionDetailsPresenter(
} }
fun uninstallExtension() { fun uninstallExtension() {
val extension = extension ?: return
extensionManager.uninstallExtension(extension.pkgName) 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_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates)
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read) SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read)
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues) 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 -> { SHORTCUT_DOWNLOADS -> {
if (router.backstack.none { it.controller() is DownloadController }) { if (router.backstack.none { it.controller() is DownloadController }) {
setSelectedDrawerItem(R.id.nav_drawer_downloads) setSelectedDrawerItem(R.id.nav_drawer_downloads)

View File

@ -39,7 +39,7 @@ import java.util.*
class MangaController : RxController, TabbedController { class MangaController : RxController, TabbedController {
constructor(manga: Manga?, fromCatalogue: Boolean = false) : super(Bundle().apply { 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) putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue)
}) { }) {
this.manga = manga this.manga = manga

View File

@ -14,16 +14,13 @@
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="48dp" android:layout_width="0dp"
android:layout_height="56dp" android:layout_height="0dp"
android:clickable="true" android:padding="8dp"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:paddingRight="0dp"
android:paddingEnd="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" /> tools:src="@mipmap/ic_launcher_round" />
<TextView <TextView
@ -31,8 +28,8 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:paddingLeft="16dp" android:paddingLeft="0dp"
android:paddingStart="16dp" android:paddingStart="0dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:ellipsize="end" android:ellipsize="end"

View File

@ -14,10 +14,11 @@
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="64dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:padding="16dp" android:padding="12dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"