Bugfixes and extension installation improvements
Cette révision appartient à :
Parent
a3c03e8ceb
révision
8e50ac67bc
11 fichiers modifiés avec 94 ajouts et 49 suppressions
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
Référencer dans un nouveau ticket