* feat: (wip) torrent

credit to kuukiyomi

* fix: extensions -> addons

* fix: unified loader

* feat: (wip) modularity

* fix: addon ui

* feat: addon install/uninstall

---------

Co-authored-by: aayush262 <aayushthakur262006@gmail.com>
This commit is contained in:
rebel onion
2024-04-19 04:08:20 -05:00
committed by GitHub
parent 3d1040b280
commit 670d16bd8e
66 changed files with 1923 additions and 427 deletions

View File

@@ -39,6 +39,12 @@ object Notifications {
const val GROUP_NEW_CHAPTERS = "eu.kanade.tachiyomi.NEW_CHAPTERS"
const val GROUP_NEW_EPISODES = "eu.kanade.tachiyomi.NEW_EPISODES"
/**
* Notification channel and ids used by the torrent server.
*/
const val ID_TORRENT_SERVER = -1100
const val CHANNEL_TORRENT_SERVER = "dantotsu_torrent_server"
/**
* Notification channel used for Incognito Mode
*/
@@ -154,6 +160,9 @@ object Notifications {
buildNotificationChannel(CHANNEL_INCOGNITO_MODE, IMPORTANCE_LOW) {
setName("Incognito Mode")
},
buildNotificationChannel(CHANNEL_TORRENT_SERVER, IMPORTANCE_LOW) {
setName("Torrent Server")
},
buildNotificationChannel(CHANNEL_COMMENTS, IMPORTANCE_HIGH) {
setName("Comments")
setGroup(GROUP_COMMENTS)

View File

@@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.data.torrentServer.model
import kotlinx.serialization.Serializable
@Serializable
data class Torrent(
var title: String,
var poster: String? = null,
var data: String? = null,
var timestamp: Long? = null,
var name: String? = null,
var hash: String? = null,
var stat: Int? = null,
var stat_string: String? = null,
var loaded_size: Long? = null,
var torrent_size: Long? = null,
var preloaded_bytes: Long? = null,
var preload_size: Long? = null,
var download_speed: Double? = null,
var upload_speed: Double? = null,
var total_peers: Int? = null,
var pending_peers: Int? = null,
var active_peers: Int? = null,
var connected_seeders: Int? = null,
var half_open_peers: Int? = null,
var bytes_written: Long? = null,
var bytes_written_data: Long? = null,
var bytes_read: Long? = null,
var bytes_read_data: Long? = null,
var bytes_read_useful_data: Long? = null,
var chunks_written: Long? = null,
var chunks_read: Long? = null,
var chunks_read_useful: Long? = null,
var chunks_read_wasted: Long? = null,
var pieces_dirtied_good: Long? = null,
var pieces_dirtied_bad: Long? = null,
var duration_seconds: Double? = null,
var bit_rate: String? = null,
var file_stats: List<FileStat>? = null,
)
@Serializable
data class FileStat(
var id: Int? = null,
var path: String,
var length: Long,
)

View File

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.anime
import android.content.Context
import android.graphics.drawable.Drawable
import ani.dantotsu.media.MediaType
import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.domain.source.service.SourcePreferences
@@ -206,7 +207,8 @@ class AnimeExtensionManager(
* @param extension The anime extension to be installed.
*/
fun installExtension(extension: AnimeExtension.Available): Observable<InstallStep> {
return installer.downloadAndInstall(api.getAnimeApkUrl(extension), extension)
return installer.downloadAndInstall(api.getAnimeApkUrl(extension), extension.pkgName,
extension.name, MediaType.ANIME)
}
/**

View File

@@ -8,7 +8,11 @@ import android.content.IntentFilter
import android.net.Uri
import androidx.annotation.CallSuper
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.media.AddonType
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.Type
import ani.dantotsu.parsers.novel.NovelExtensionManager
import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
@@ -25,6 +29,8 @@ abstract class Installer(private val service: Service) {
private val animeExtensionManager: AnimeExtensionManager by injectLazy()
private val mangaExtensionManager: MangaExtensionManager by injectLazy()
private val novelExtensionManager: NovelExtensionManager by injectLazy()
private val torrentAddonManager: TorrentAddonManager by injectLazy()
private val downloadAddonManager: DownloadAddonManager by injectLazy()
private var waitingInstall = AtomicReference<Entry>(null)
private val queue = Collections.synchronizedList(mutableListOf<Entry>())
@@ -49,7 +55,7 @@ abstract class Installer(private val service: Service) {
* @param downloadId Download ID as known by [ExtensionManager]
* @param uri Uri of APK to install
*/
fun addToQueue(type: MediaType, downloadId: Long, uri: Uri) {
fun addToQueue(type: Type, downloadId: Long, uri: Uri) {
queue.add(Entry(type, downloadId, uri))
checkQueue()
}
@@ -63,10 +69,17 @@ abstract class Installer(private val service: Service) {
*/
@CallSuper
open fun processEntry(entry: Entry) {
when (entry.type) {
MediaType.ANIME -> animeExtensionManager.setInstalling(entry.downloadId)
MediaType.MANGA -> mangaExtensionManager.setInstalling(entry.downloadId)
MediaType.NOVEL -> novelExtensionManager.setInstalling(entry.downloadId)
if (entry.type is MediaType) {
when (entry.type) {
MediaType.ANIME -> animeExtensionManager.setInstalling(entry.downloadId)
MediaType.MANGA -> mangaExtensionManager.setInstalling(entry.downloadId)
MediaType.NOVEL -> novelExtensionManager.setInstalling(entry.downloadId)
}
} else {
when (entry.type) {
AddonType.TORRENT -> torrentAddonManager.setInstalling(entry.downloadId)
AddonType.DOWNLOAD -> downloadAddonManager.setInstalling(entry.downloadId)
}
}
}
@@ -90,17 +103,34 @@ abstract class Installer(private val service: Service) {
fun continueQueue(resultStep: InstallStep) {
val completedEntry = waitingInstall.getAndSet(null)
if (completedEntry != null) {
when (completedEntry.type) {
MediaType.ANIME -> {
animeExtensionManager.updateInstallStep(completedEntry.downloadId, resultStep)
}
if (completedEntry.type is MediaType) {
when (completedEntry.type) {
MediaType.ANIME -> animeExtensionManager.updateInstallStep(
completedEntry.downloadId,
resultStep
)
MediaType.MANGA -> {
mangaExtensionManager.updateInstallStep(completedEntry.downloadId, resultStep)
}
MediaType.MANGA -> mangaExtensionManager.updateInstallStep(
completedEntry.downloadId,
resultStep
)
MediaType.NOVEL -> {
novelExtensionManager.updateInstallStep(completedEntry.downloadId, resultStep)
MediaType.NOVEL -> novelExtensionManager.updateInstallStep(
completedEntry.downloadId,
resultStep
)
}
} else {
when (completedEntry.type) {
AddonType.TORRENT -> torrentAddonManager.updateInstallStep(
completedEntry.downloadId,
resultStep
)
AddonType.DOWNLOAD -> downloadAddonManager.updateInstallStep(
completedEntry.downloadId,
resultStep
)
}
}
checkQueue()
@@ -113,7 +143,7 @@ abstract class Installer(private val service: Service) {
*
* @see ready
*/
fun checkQueue() {
private fun checkQueue() {
if (!ready) {
return
}
@@ -135,15 +165,35 @@ abstract class Installer(private val service: Service) {
open fun onDestroy() {
LocalBroadcastManager.getInstance(service).unregisterReceiver(cancelReceiver)
queue.forEach {
when (it.type) {
MediaType.ANIME -> {
animeExtensionManager.updateInstallStep(it.downloadId, InstallStep.Error)
if (it.type is MediaType) {
when (it.type) {
MediaType.ANIME -> animeExtensionManager.updateInstallStep(
it.downloadId,
InstallStep.Error
)
MediaType.MANGA -> mangaExtensionManager.updateInstallStep(
it.downloadId,
InstallStep.Error
)
MediaType.NOVEL -> novelExtensionManager.updateInstallStep(
it.downloadId,
InstallStep.Error
)
}
MediaType.MANGA -> {
mangaExtensionManager.updateInstallStep(it.downloadId, InstallStep.Error)
}
MediaType.NOVEL -> {
novelExtensionManager.updateInstallStep(it.downloadId, InstallStep.Error)
} else {
when (it.type) {
AddonType.TORRENT -> torrentAddonManager.updateInstallStep(
it.downloadId,
InstallStep.Error
)
AddonType.DOWNLOAD -> downloadAddonManager.updateInstallStep(
it.downloadId,
InstallStep.Error
)
}
}
}
@@ -168,15 +218,34 @@ abstract class Installer(private val service: Service) {
this.waitingInstall.set(null)
checkQueue()
}
when (toCancel.type) {
MediaType.ANIME -> {
animeExtensionManager.updateInstallStep(downloadId, InstallStep.Idle)
if (toCancel.type is MediaType) {
when (toCancel.type) {
MediaType.ANIME -> animeExtensionManager.updateInstallStep(
downloadId,
InstallStep.Idle
)
MediaType.MANGA -> mangaExtensionManager.updateInstallStep(
downloadId,
InstallStep.Idle
)
MediaType.NOVEL -> novelExtensionManager.updateInstallStep(
downloadId,
InstallStep.Idle
)
}
MediaType.MANGA -> {
mangaExtensionManager.updateInstallStep(downloadId, InstallStep.Idle)
}
MediaType.NOVEL -> {
novelExtensionManager.updateInstallStep(downloadId, InstallStep.Idle)
} else {
when (toCancel.type) {
AddonType.TORRENT -> torrentAddonManager.updateInstallStep(
downloadId,
InstallStep.Idle
)
AddonType.DOWNLOAD -> downloadAddonManager.updateInstallStep(
downloadId,
InstallStep.Idle
)
}
}
}
@@ -188,7 +257,7 @@ abstract class Installer(private val service: Service) {
* @param downloadId Download ID as known by [ExtensionManager]
* @param uri Uri of APK to install
*/
data class Entry(val type: MediaType, val downloadId: Long, val uri: Uri)
data class Entry(val type: Type, val downloadId: Long, val uri: Uri)
init {
val filter = IntentFilter(ACTION_CANCEL_QUEUE)

View File

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.manga
import android.content.Context
import android.graphics.drawable.Drawable
import ani.dantotsu.media.MediaType
import ani.dantotsu.snackString
import ani.dantotsu.util.Logger
import eu.kanade.domain.source.service.SourcePreferences
@@ -203,7 +204,8 @@ class MangaExtensionManager(
* @param extension The extension to be installed.
*/
fun installExtension(extension: MangaExtension.Available): Observable<InstallStep> {
return installer.downloadAndInstall(api.getMangaApkUrl(extension), extension)
return installer.downloadAndInstall(api.getMangaApkUrl(extension), extension.pkgName,
extension.name, MediaType.MANGA)
}
/**

View File

@@ -5,6 +5,9 @@ import android.os.Bundle
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.media.AddonType
import ani.dantotsu.media.MediaType
import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.themes.ThemeManager
@@ -29,7 +32,8 @@ class ExtensionInstallActivity : AppCompatActivity() {
private var ignoreResult = false
private var hasIgnoredResult = false
private var type: MediaType? = null
private var mediaType: MediaType? = null
private var addonType: AddonType? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -37,7 +41,9 @@ class ExtensionInstallActivity : AppCompatActivity() {
ThemeManager(this).applyTheme()
if (intent.hasExtra(ExtensionInstaller.EXTRA_EXTENSION_TYPE))
type = intent.getSerializableExtraCompat<MediaType>(ExtensionInstaller.EXTRA_EXTENSION_TYPE)
mediaType = intent.getSerializableExtraCompat<MediaType>(ExtensionInstaller.EXTRA_EXTENSION_TYPE)
if (intent.hasExtra(ExtensionInstaller.EXTRA_ADDON_TYPE))
addonType = intent.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE)
@Suppress("DEPRECATION")
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
@@ -85,17 +91,34 @@ class ExtensionInstallActivity : AppCompatActivity() {
RESULT_CANCELED -> InstallStep.Idle
else -> InstallStep.Error
}
when (type) {
MediaType.ANIME -> {
Injekt.get<AnimeExtensionManager>().updateInstallStep(downloadId, newStep)
if (mediaType != null) {
when (mediaType) {
MediaType.ANIME -> {
Injekt.get<AnimeExtensionManager>().updateInstallStep(downloadId, newStep)
}
MediaType.MANGA -> {
Injekt.get<MangaExtensionManager>().updateInstallStep(downloadId, newStep)
}
MediaType.NOVEL -> {
Injekt.get<NovelExtensionManager>().updateInstallStep(downloadId, newStep)
}
null -> {}
}
MediaType.MANGA -> {
Injekt.get<MangaExtensionManager>().updateInstallStep(downloadId, newStep)
} else {
when (addonType) {
AddonType.TORRENT -> {
Injekt.get<TorrentAddonManager>().updateInstallStep(downloadId, newStep)
}
AddonType.DOWNLOAD -> {
Injekt.get<DownloadAddonManager>().updateInstallStep(downloadId, newStep)
}
null -> {}
}
MediaType.NOVEL -> {
Injekt.get<NovelExtensionManager>().updateInstallStep(downloadId, newStep)
}
null -> { }
}
}
}

View File

@@ -50,18 +50,6 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
return this
}
/**
* Returns the intent filter this receiver should subscribe to.
*/
private val filter
get() = IntentFilter().apply {
priority = 100
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package")
}
/**
* Called when one of the events of the [filter] is received. When the package is an extension,
* it's loaded in background and it notifies the [listener] when finished.
@@ -136,21 +124,13 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
}
}
/**
* Returns true if this package is performing an update.
*
* @param intent The intent that triggered the event.
*/
private fun isReplacing(intent: Intent): Boolean {
return intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
}
/**
* Returns the extension triggered by the given intent.
*
* @param context The application context.
* @param intent The intent containing the package name of the extension.
*/
@OptIn(DelicateCoroutinesApi::class)
private suspend fun getAnimeExtensionFromIntent(context: Context, intent: Intent?): AnimeLoadResult {
val pkgName = getPackageNameFromIntent(intent)
if (pkgName == null) {
@@ -180,12 +160,6 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
}.await()
}
/**
* Returns the package name of the installed, updated or removed application.
*/
private fun getPackageNameFromIntent(intent: Intent?): String? {
return intent?.data?.encodedSchemeSpecificPart ?: return null
}
/**
* Listener that receives extension installation events.
@@ -203,4 +177,36 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
fun onExtensionUntrusted(extension: MangaExtension.Untrusted)
fun onPackageUninstalled(pkgName: String)
}
companion object {
/**
* Returns the intent filter this receiver should subscribe to.
*/
val filter
get() = IntentFilter().apply {
priority = 100
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REPLACED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package")
}
/**
* Returns true if this package is performing an update.
*
* @param intent The intent that triggered the event.
*/
fun isReplacing(intent: Intent): Boolean {
return intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
}
/**
* Returns the package name of the installed, updated or removed application.
*/
fun getPackageNameFromIntent(intent: Intent?): String? {
return intent?.data?.encodedSchemeSpecificPart ?: return null
}
}
}

View File

@@ -8,7 +8,9 @@ import android.net.Uri
import android.os.Build
import android.os.IBinder
import ani.dantotsu.R
import ani.dantotsu.media.AddonType
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.Type
import ani.dantotsu.util.Logger
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.notification.Notifications
@@ -45,12 +47,13 @@ class ExtensionInstallService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.data
val type = intent?.getSerializableExtraCompat<MediaType>(EXTRA_EXTENSION_TYPE)
val mediaType = intent?.getSerializableExtraCompat<MediaType>(EXTRA_EXTENSION_TYPE)
val addonType = intent?.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE)
val id = intent?.getLongExtra(EXTRA_DOWNLOAD_ID, -1)?.takeIf { it != -1L }
val installerUsed = intent?.getSerializableExtraCompat<BasePreferences.ExtensionInstaller>(
EXTRA_INSTALLER
)
if (uri == null || type == null || id == null || installerUsed == null) {
if (uri == null || (mediaType == null && addonType == null) || id == null || installerUsed == null) {
stopSelf()
return START_NOT_STICKY
}
@@ -68,7 +71,7 @@ class ExtensionInstallService : Service() {
}
}
}
installer!!.addToQueue(type, id, uri)
installer!!.addToQueue(mediaType ?: addonType!!, id, uri)
return START_NOT_STICKY
}
@@ -84,16 +87,21 @@ class ExtensionInstallService : Service() {
fun getIntent(
context: Context,
type: MediaType,
type: Type,
downloadId: Long,
uri: Uri,
installer: BasePreferences.ExtensionInstaller,
): Intent {
return Intent(context, ExtensionInstallService::class.java)
val intent = Intent(context, ExtensionInstallService::class.java)
.setDataAndType(uri, ExtensionInstaller.APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
.putExtra(EXTRA_EXTENSION_TYPE, type)
.putExtra(EXTRA_INSTALLER, installer)
if (type is MediaType) {
intent.putExtra(EXTRA_EXTENSION_TYPE, type)
} else if (type is AddonType) {
intent.putExtra(ExtensionInstaller.EXTRA_ADDON_TYPE, type)
}
return intent
}
}
}

View File

@@ -11,7 +11,9 @@ import android.os.Environment
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.net.toUri
import ani.dantotsu.media.AddonType
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.Type
import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.util.Logger
import com.jakewharton.rxrelay.PublishRelay
@@ -33,7 +35,7 @@ import java.util.concurrent.TimeUnit
*
* @param context The application context.
*/
internal class ExtensionInstaller(private val context: Context) {
class ExtensionInstaller(private val context: Context) {
/**
* The system's download manager
@@ -65,27 +67,24 @@ internal class ExtensionInstaller(private val context: Context) {
* @param url The url of the apk.
* @param extension The extension to install.
*/
fun downloadAndInstall(url: String, extension: AnimeExtension): Observable<InstallStep> = Observable.defer {
val pkgName = extension.pkgName
fun <T : Type> downloadAndInstall(url: String, pkgName: String, name: String, type: T): Observable<InstallStep> = Observable.defer {
val oldDownload = activeDownloads[pkgName]
if (oldDownload != null) {
deleteDownload(pkgName)
}
// Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register()
val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name)
.setTitle(name)
.setMimeType(APK_MIME)
.setDestinationInExternalFilesDir(
context,
Environment.DIRECTORY_DOWNLOADS,
downloadUri.lastPathSegment
)
.setDescription(MediaType.ANIME.asText())
.setDescription(type.asText())
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val id = downloadManager.enqueue(request)
@@ -93,91 +92,12 @@ internal class ExtensionInstaller(private val context: Context) {
downloadsRelay.filter { it.first == id }
.map { it.second }
// Poll download status
.mergeWith(pollStatus(id))
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
}
fun downloadAndInstall(url: String, extension: MangaExtension): Observable<InstallStep> = Observable.defer {
val pkgName = extension.pkgName
val oldDownload = activeDownloads[pkgName]
if (oldDownload != null) {
deleteDownload(pkgName)
}
// Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register()
val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name)
.setMimeType(APK_MIME)
.setDestinationInExternalFilesDir(
context,
Environment.DIRECTORY_DOWNLOADS,
downloadUri.lastPathSegment
)
.setDescription(MediaType.MANGA.asText())
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val id = downloadManager.enqueue(request)
activeDownloads[pkgName] = id
downloadsRelay.filter { it.first == id }
.map { it.second }
// Poll download status
.mergeWith(pollStatus(id))
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
}
fun downloadAndInstall(url: String, extension: NovelExtension) = Observable.defer {
val pkgName = extension.pkgName
val oldDownload = activeDownloads[pkgName]
if (oldDownload != null) {
deleteDownload(pkgName)
}
// Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register()
val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name)
.setMimeType(APK_MIME)
.setDestinationInExternalFilesDir(
context,
Environment.DIRECTORY_DOWNLOADS,
downloadUri.lastPathSegment
)
.setDescription(MediaType.MANGA.asText())
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val id = downloadManager.enqueue(request)
activeDownloads[pkgName] = id
downloadsRelay.filter { it.first == id }
.map { it.second }
// Poll download status
.mergeWith(pollStatus(id))
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
}
/**
* Returns an observable that polls the given download id for its status every second, as the
@@ -215,14 +135,18 @@ internal class ExtensionInstaller(private val context: Context) {
*
* @param uri The uri of the extension to install.
*/
fun installApk(type: MediaType, downloadId: Long, uri: Uri) {
fun installApk(type: Type, downloadId: Long, uri: Uri) {
when (val installer = extensionInstaller.get()) {
BasePreferences.ExtensionInstaller.LEGACY -> {
val intent = Intent(context, ExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_EXTENSION_TYPE, type)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (type is MediaType) {
intent.putExtra(EXTRA_EXTENSION_TYPE, type)
} else if (type is AddonType) {
intent.putExtra(EXTRA_ADDON_TYPE, type)
}
context.startActivity(intent)
}
@@ -342,7 +266,9 @@ internal class ExtensionInstaller(private val context: Context) {
).removePrefix(FILE_SCHEME)
val type = MediaType.fromText(cursor.getString(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION),
))
)) ?: AddonType.fromText(cursor.getString(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION),
)) ?: return
installApk(type, id, File(localUri).getUriCompat(context))
}
@@ -354,6 +280,7 @@ internal class ExtensionInstaller(private val context: Context) {
const val APK_MIME = "application/vnd.android.package-archive"
const val EXTRA_DOWNLOAD_ID = "ExtensionInstaller.extra.DOWNLOAD_ID"
const val EXTRA_EXTENSION_TYPE = "ExtensionInstaller.extra.EXTENSION_TYPE"
const val EXTRA_ADDON_TYPE = "ExtensionInstaller.extra.ADDON_TYPE"
const val FILE_SCHEME = "file://"
}
}

View File

@@ -60,7 +60,7 @@ internal object ExtensionLoader {
const val MANGA_LIB_VERSION_MIN = 1.2
const val MANGA_LIB_VERSION_MAX = 1.5
private val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or
val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or
PackageManager.GET_META_DATA or
@Suppress ("DEPRECATION") PackageManager.GET_SIGNATURES or
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)