mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-19 02:39:16 +01:00
Bugfixes and extension installation improvements
This commit is contained in:
parent
a3c03e8ceb
commit
8e50ac67bc
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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) {
|
||||||
|
@ -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))
|
.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"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -14,25 +14,22 @@
|
|||||||
|
|
||||||
<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"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@mipmap/ic_launcher_round" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
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"
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
|
|
||||||
<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"
|
||||||
tools:src="@mipmap/ic_launcher_round"/>
|
tools:src="@mipmap/ic_launcher_round" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/ext_title"
|
android:id="@+id/ext_title"
|
||||||
|
Loading…
Reference in New Issue
Block a user