mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
feat: Add support for writing downloader UI with fragments (#2998)
This commit is contained in:
@@ -4,7 +4,6 @@ plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
`maven-publish`
|
||||
signing
|
||||
@@ -15,8 +14,7 @@ group = "app.revanced"
|
||||
dependencies {
|
||||
implementation(libs.androidx.ktx)
|
||||
implementation(libs.runtime.ktx)
|
||||
implementation(libs.activity.compose)
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.fragment.ktx)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
@@ -6,13 +6,17 @@ import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.jvm.java
|
||||
|
||||
@RequiresOptIn(
|
||||
level = RequiresOptIn.Level.ERROR,
|
||||
@@ -34,6 +38,11 @@ interface Scope {
|
||||
* The package name of the downloader.
|
||||
*/
|
||||
val downloaderPackageName: String
|
||||
|
||||
/**
|
||||
* A data directory for this downloader package.
|
||||
*/
|
||||
val dataDir: File
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +58,25 @@ interface GetScope : Scope {
|
||||
* @throws UserInteractionException.Activity.NotCompleted The activity finished with an unknown result code.
|
||||
*/
|
||||
suspend fun requestStartActivity(intent: Intent): Intent?
|
||||
|
||||
/**
|
||||
* Starts an [Activity] using [requestStartActivity] which loads the specified [Fragment].
|
||||
* The fragment may reside in the downloader package.
|
||||
*
|
||||
* @param clazz The class of the fragment to launch.
|
||||
* @param args The fragment arguments.
|
||||
*/
|
||||
suspend fun requestStartFragment(clazz: Class<out Fragment>, args: Bundle?) =
|
||||
requestStartActivity(Intent().apply {
|
||||
setClassName(hostPackageName, "app.revanced.manager.DownloaderActivity")
|
||||
// We shouldn't use the downloader's resources if it is launching a fragment that resides in manager itself.
|
||||
if (clazz.classLoader != GetScope::class.java.classLoader) putExtra(
|
||||
"DOWNLOADER_NAME",
|
||||
downloaderPackageName
|
||||
)
|
||||
putExtra("FRAGMENT_CLASS_NAME", clazz.name)
|
||||
putExtra("FRAGMENT_ARGS", args)
|
||||
})
|
||||
}
|
||||
|
||||
interface BaseDownloadScope : Scope
|
||||
|
||||
@@ -3,8 +3,10 @@ package app.revanced.manager.downloader
|
||||
import android.app.Activity
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import androidx.fragment.app.Fragment
|
||||
import java.io.OutputStream
|
||||
|
||||
/**
|
||||
@@ -31,6 +33,15 @@ suspend inline fun <reified ACTIVITY : Activity> GetScope.requestStartActivity()
|
||||
Intent().apply { setClassName(downloaderPackageName, ACTIVITY::class.qualifiedName!!) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Starts an [Activity] using [GetScope.requestStartActivity] which loads the specified [Fragment].
|
||||
* The fragment may reside in the downloader package.
|
||||
*
|
||||
* @param args The fragment arguments.
|
||||
*/
|
||||
suspend inline fun <reified T : Fragment> GetScope.requestStartFragment(args: Bundle?) =
|
||||
requestStartFragment(T::class.java, args)
|
||||
|
||||
/**
|
||||
* Performs [DownloaderScope.useService] with an [Intent] created using the type information of [SERVICE].
|
||||
* @see [DownloaderScope.useService]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.manager.downloader.webview
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import app.revanced.manager.downloader.DownloadUrl
|
||||
import app.revanced.manager.downloader.DownloaderScope
|
||||
@@ -8,6 +9,7 @@ import app.revanced.manager.downloader.GetScope
|
||||
import app.revanced.manager.downloader.Scope
|
||||
import app.revanced.manager.downloader.Downloader
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import app.revanced.manager.downloader.requestStartFragment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -121,15 +123,11 @@ suspend fun <T> GetScope.runWebView(
|
||||
val scope = WebViewScope<T>(this@supervisorScope, this@runWebView) { result = Container(it) }
|
||||
scope.initialUrl = scope.block()
|
||||
|
||||
// Start the webview activity and wait until it finishes.
|
||||
requestStartActivity(Intent().apply {
|
||||
putExtra(
|
||||
WebViewActivity.KEY,
|
||||
WebViewActivity.Parameters(title, scope.binder)
|
||||
)
|
||||
setClassName(
|
||||
hostPackageName,
|
||||
WebViewActivity::class.qualifiedName!!
|
||||
// Start the webview and wait until it finishes.
|
||||
requestStartFragment<WebViewFragment>(Bundle().apply {
|
||||
putParcelable(
|
||||
WebViewFragment.KEY,
|
||||
WebViewFragment.Parameters(title, scope.binder)
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
package app.revanced.manager.downloader.webview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -29,34 +35,59 @@ import kotlinx.parcelize.Parcelize
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
@DownloaderHostApi
|
||||
class WebViewActivity : ComponentActivity() {
|
||||
class WebViewFragment : Fragment(R.layout.webview_fragment) {
|
||||
private val vm by viewModels<WebViewModel>()
|
||||
lateinit var webView: WebView
|
||||
private val args by lazy {
|
||||
arguments?.getParcelable<Parameters>(KEY)!!
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
requireActivity().apply {
|
||||
enableEdgeToEdge()
|
||||
onBackPressedDispatcher.addCallback {
|
||||
if (webView.canGoBack()) webView.goBack()
|
||||
else cancelActivity()
|
||||
}
|
||||
actionBar?.apply {
|
||||
title = args.title
|
||||
setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel)
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
addMenuProvider(
|
||||
object : MenuProvider {
|
||||
override fun onCreateMenu(
|
||||
menu: Menu,
|
||||
menuInflater: MenuInflater
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem) =
|
||||
if (menuItem.itemId == android.R.id.home) {
|
||||
cancelActivity()
|
||||
|
||||
true
|
||||
} else false
|
||||
},
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val vm by viewModels<WebViewModel>()
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_webview)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view.findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
val webView = findViewById<WebView>(R.id.webview)
|
||||
onBackPressedDispatcher.addCallback {
|
||||
if (webView.canGoBack()) webView.goBack()
|
||||
else cancelActivity()
|
||||
}
|
||||
webView = view.findViewById<WebView>(R.id.webview)
|
||||
|
||||
val params = intent.getParcelableExtra<Parameters>(KEY)!!
|
||||
actionBar?.apply {
|
||||
title = params.title
|
||||
setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel)
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
val events = IWebViewEvents.Stub.asInterface(params.events)!!
|
||||
val events = IWebViewEvents.Stub.asInterface(args.events)!!
|
||||
vm.setup(events)
|
||||
|
||||
webView.apply {
|
||||
@@ -73,13 +104,14 @@ class WebViewActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
val activity = requireActivity()
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
vm.commands.collect {
|
||||
when (it) {
|
||||
is WebViewModel.Command.Finish -> {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
activity.setResult(Activity.RESULT_OK)
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
is WebViewModel.Command.Load -> webView.loadUrl(it.url)
|
||||
@@ -90,16 +122,11 @@ class WebViewActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
private fun cancelActivity() {
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
val activity = requireActivity()
|
||||
activity.setResult(Activity.RESULT_CANCELED)
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) {
|
||||
cancelActivity()
|
||||
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
|
||||
@Parcelize
|
||||
internal class Parameters(
|
||||
val title: String, val events: IBinder
|
||||
@@ -1 +0,0 @@
|
||||
<resources></resources>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.WebViewActivity" parent="Theme.AppCompat.DayNight">
|
||||
<item name="android:windowActionBar">true</item>
|
||||
<item name="android:windowNoTitle">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -49,7 +49,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".downloader.webview.WebViewActivity" android:exported="false" android:theme="@style/Theme.WebViewActivity" />
|
||||
<activity android:name=".DownloaderActivity" android:exported="false" android:theme="@style/Theme.DownloaderActivity" />
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||
|
||||
62
app/src/main/java/app/revanced/manager/DownloaderActivity.kt
Normal file
62
app/src/main/java/app/revanced/manager/DownloaderActivity.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
package app.revanced.manager
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentContainerView
|
||||
import androidx.fragment.app.commit
|
||||
import app.revanced.manager.domain.repository.DownloaderRepository
|
||||
import app.revanced.manager.network.downloader.DownloaderPackageState
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class DownloaderActivity : FragmentActivity() {
|
||||
private val downloaderRepository: DownloaderRepository by inject()
|
||||
var downloaderPackageName = ""
|
||||
|
||||
private val downloaderPkgState
|
||||
get() = downloaderRepository
|
||||
.downloaderPackageStates
|
||||
.value[downloaderPackageName]
|
||||
?.let { it as? DownloaderPackageState.Loaded }
|
||||
|
||||
private var res: Resources? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val view = FragmentContainerView(this).apply {
|
||||
// The fragment manager requires an ID to work.
|
||||
id = R.id.fragment_container
|
||||
}
|
||||
setContentView(
|
||||
view,
|
||||
ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||
)
|
||||
|
||||
downloaderPackageName = intent.getStringExtra("DOWNLOADER_NAME").orEmpty()
|
||||
val fragmentClassName = intent.getStringExtra("FRAGMENT_CLASS_NAME")!!
|
||||
val args = intent.getBundleExtra("FRAGMENT_ARGS")
|
||||
|
||||
res =
|
||||
downloaderPkgState?.context?.createConfigurationContext(super.resources.configuration)?.resources
|
||||
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val fragmentClass = classLoader!!.loadClass(fragmentClassName) as Class<Fragment>
|
||||
|
||||
supportFragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
add(R.id.fragment_container, fragmentClass, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClassLoader(): ClassLoader? =
|
||||
downloaderPkgState?.classLoader ?: super.classLoader
|
||||
|
||||
override fun getResources(): Resources? = res ?: super.resources
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import android.os.Parcelable
|
||||
import app.revanced.manager.data.room.AppDatabase
|
||||
import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
|
||||
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import app.revanced.manager.network.downloader.LoadedDownloader
|
||||
import app.revanced.manager.downloader.OutputDownloadScope
|
||||
import app.revanced.manager.downloader.Scope
|
||||
import app.revanced.manager.util.PM
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
@@ -21,7 +23,7 @@ import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
class DownloadedAppRepository(
|
||||
private val app: Application,
|
||||
app: Application,
|
||||
db: AppDatabase,
|
||||
private val pm: PM
|
||||
) {
|
||||
@@ -35,6 +37,7 @@ class DownloadedAppRepository(
|
||||
|
||||
private fun getApkFileForDir(directory: File) = directory.listFiles()!!.first()
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
suspend fun download(
|
||||
downloader: LoadedDownloader,
|
||||
data: Parcelable,
|
||||
@@ -54,9 +57,7 @@ class DownloadedAppRepository(
|
||||
val downloadedBytes = AtomicLong(0)
|
||||
|
||||
channelFlow {
|
||||
val scope = object : OutputDownloadScope {
|
||||
override val downloaderPackageName = downloader.packageName
|
||||
override val hostPackageName = app.packageName
|
||||
val scope = object : OutputDownloadScope, Scope by downloader.scopeImpl {
|
||||
override suspend fun reportSize(size: Long) {
|
||||
require(size > 0) { "Size must be greater than zero" }
|
||||
require(
|
||||
@@ -87,7 +88,7 @@ class DownloadedAppRepository(
|
||||
)
|
||||
}
|
||||
}
|
||||
downloader.download(scope, data, stream)
|
||||
downloader.impl.download(scope, data, stream)
|
||||
}
|
||||
}
|
||||
.conflate()
|
||||
|
||||
@@ -2,6 +2,7 @@ package app.revanced.manager.domain.repository
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
@@ -24,6 +25,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
@@ -50,6 +52,8 @@ class DownloaderRepository(
|
||||
installed subtract acknowledged
|
||||
}
|
||||
|
||||
private val downloadersDir = app.getDir("downloaders", Context.MODE_PRIVATE)
|
||||
|
||||
suspend fun reload() {
|
||||
val downloaderPackages =
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -65,8 +69,12 @@ class DownloaderRepository(
|
||||
acknowledgedDownloader subtract installedDownloaderPackageNames.value
|
||||
if (uninstalledDownloader.isNotEmpty()) {
|
||||
Log.d(tag, "Uninstalled downloader: ${uninstalledDownloader.joinToString(", ")}")
|
||||
|
||||
this@DownloaderRepository.acknowledgedPackageNames.update(acknowledgedDownloader subtract uninstalledDownloader)
|
||||
trustDao.removeAll(uninstalledDownloader)
|
||||
withContext(Dispatchers.IO) {
|
||||
uninstalledDownloader.forEach { downloadersDir.resolve(it).deleteRecursively() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +117,8 @@ class DownloaderRepository(
|
||||
val scopeImpl = object : Scope {
|
||||
override val hostPackageName = app.packageName
|
||||
override val downloaderPackageName = downloaderContext.packageName
|
||||
override val dataDir =
|
||||
downloadersDir.resolve(downloaderPackageName).also(File::mkdirs)
|
||||
}
|
||||
|
||||
DownloaderPackageState.Loaded(
|
||||
@@ -126,11 +136,12 @@ class DownloaderRepository(
|
||||
className,
|
||||
downloaderContext.getString(downloader.name),
|
||||
packageInfo.versionName!!,
|
||||
downloader.get,
|
||||
downloader.download
|
||||
scopeImpl,
|
||||
downloader
|
||||
)
|
||||
},
|
||||
classLoader,
|
||||
downloaderContext,
|
||||
with(pm) { packageInfo.label() }
|
||||
)
|
||||
} catch (e: CancellationException) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package app.revanced.manager.network.downloader
|
||||
|
||||
import android.content.Context
|
||||
|
||||
sealed interface DownloaderPackageState {
|
||||
data object Untrusted : DownloaderPackageState
|
||||
|
||||
data class Loaded(
|
||||
val downloaders: List<LoadedDownloader>,
|
||||
val classLoader: ClassLoader,
|
||||
val context: Context,
|
||||
val name: String
|
||||
) : DownloaderPackageState
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package app.revanced.manager.network.downloader
|
||||
|
||||
import android.os.Parcelable
|
||||
import app.revanced.manager.downloader.OutputDownloadScope
|
||||
import app.revanced.manager.downloader.GetScope
|
||||
import java.io.OutputStream
|
||||
import app.revanced.manager.downloader.Downloader
|
||||
import app.revanced.manager.downloader.Scope
|
||||
|
||||
class LoadedDownloader(
|
||||
val packageName: String,
|
||||
val className: String,
|
||||
val name: String,
|
||||
val version: String,
|
||||
val get: suspend GetScope.(packageName: String, version: String?) -> Pair<Parcelable, String?>?,
|
||||
val download: suspend OutputDownloadScope.(data: Parcelable, outputStream: OutputStream) -> Unit
|
||||
val scopeImpl: Scope,
|
||||
val impl: Downloader<Parcelable>
|
||||
)
|
||||
@@ -38,6 +38,7 @@ import app.revanced.manager.patcher.runtime.ProcessRuntime
|
||||
import app.revanced.manager.patcher.toRemoteError
|
||||
import app.revanced.manager.downloader.GetScope
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import app.revanced.manager.downloader.Scope
|
||||
import app.revanced.manager.downloader.UserInteractionException
|
||||
import app.revanced.manager.ui.model.SelectedApp
|
||||
import app.revanced.manager.util.Options
|
||||
@@ -185,11 +186,7 @@ class PatcherWorker(
|
||||
downloaderRepository.loadedDownloadersFlow.first()
|
||||
.firstNotNullOfOrNull { downloader ->
|
||||
try {
|
||||
val getScope = object : GetScope {
|
||||
override val downloaderPackageName = downloader.packageName
|
||||
override val hostPackageName =
|
||||
applicationContext.packageName
|
||||
|
||||
val getScope = object : GetScope, Scope by downloader.scopeImpl {
|
||||
override suspend fun requestStartActivity(intent: Intent): Intent? {
|
||||
val result =
|
||||
args.handleStartActivityRequest(downloader, intent)
|
||||
@@ -204,7 +201,7 @@ class PatcherWorker(
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
downloader.get(
|
||||
downloader.impl.get(
|
||||
getScope,
|
||||
selectedApp.packageName,
|
||||
selectedApp.version
|
||||
|
||||
@@ -34,6 +34,7 @@ import app.revanced.manager.network.downloader.ParceledDownloaderData
|
||||
import app.revanced.manager.patcher.patch.PatchBundleInfo.Extensions.requiredOptionsSet
|
||||
import app.revanced.manager.downloader.GetScope
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import app.revanced.manager.downloader.Scope
|
||||
import app.revanced.manager.downloader.UserInteractionException
|
||||
import app.revanced.manager.ui.model.SelectedApp
|
||||
import app.revanced.manager.ui.model.navigation.Patcher
|
||||
@@ -192,9 +193,7 @@ class SelectedAppInfoViewModel(
|
||||
cancelDownloaderAction()
|
||||
downloaderAction = downloader to viewModelScope.launch {
|
||||
try {
|
||||
val scope = object : GetScope {
|
||||
override val hostPackageName = app.packageName
|
||||
override val downloaderPackageName = downloader.packageName
|
||||
val scope = object : GetScope, Scope by downloader.scopeImpl {
|
||||
override suspend fun requestStartActivity(intent: Intent) =
|
||||
withContext(Dispatchers.Main) {
|
||||
if (launchedActivity != null) error("Previous activity has not finished")
|
||||
@@ -219,7 +218,7 @@ class SelectedAppInfoViewModel(
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
downloader.get(scope, packageName, desiredVersion)
|
||||
downloader.impl.get(scope, packageName, desiredVersion)
|
||||
}?.let { (data, version) ->
|
||||
if (desiredVersion != null && version != desiredVersion) {
|
||||
app.toast(app.getString(R.string.downloader_invalid_version))
|
||||
|
||||
4
app/src/main/res/values/id.xml
Normal file
4
app/src/main/res/values/id.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item type="id" name="fragment_container" />
|
||||
</resources>
|
||||
@@ -6,4 +6,9 @@
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.DownloaderActivity" parent="Theme.AppCompat.DayNight">
|
||||
<item name="android:windowActionBar">true</item>
|
||||
<item name="android:windowNoTitle">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
[versions]
|
||||
ktx = "1.17.0"
|
||||
ktx = "1.18.0"
|
||||
material3 = "1.5.0-alpha14"
|
||||
ui-tooling = "1.10.0"
|
||||
viewmodel-lifecycle = "2.10.0"
|
||||
splash-screen = "1.2.0"
|
||||
activity = "1.12.2"
|
||||
appcompat = "1.7.1"
|
||||
fragment = "1.8.9"
|
||||
preferences-datastore = "1.2.0"
|
||||
work-runtime = "2.11.0"
|
||||
compose-bom = "2025.12.01"
|
||||
@@ -13,15 +14,15 @@ navigation = "2.9.7"
|
||||
accompanist = "0.37.3"
|
||||
placeholder = "1.0.12"
|
||||
reorderable = "3.0.0"
|
||||
serialization = "1.9.0"
|
||||
serialization = "1.10.0"
|
||||
collection = "0.4.0"
|
||||
datetime = "0.7.1"
|
||||
room-version = "2.8.4"
|
||||
revanced-patcher = "22.0.0"
|
||||
revanced-library = "4.0.0"
|
||||
koin = "4.1.1"
|
||||
ktor = "3.3.3"
|
||||
markdown-renderer = "0.39.0"
|
||||
ktor = "3.4.1"
|
||||
markdown-renderer = "0.39.2"
|
||||
fading-edges = "1.0.4"
|
||||
kotlin = "2.3.10"
|
||||
android-gradle-plugin = "8.13.2"
|
||||
@@ -37,7 +38,7 @@ kotlin-process = "1.5.1"
|
||||
hidden-api-stub = "4.4.0"
|
||||
binary-compatibility-validator = "0.18.1"
|
||||
semver-parser = "3.0.0"
|
||||
ackpine = "0.19.1"
|
||||
ackpine = "0.20.6"
|
||||
|
||||
[libraries]
|
||||
# AndroidX Core
|
||||
@@ -46,6 +47,7 @@ runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", ve
|
||||
runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "viewmodel-lifecycle" }
|
||||
splash-screen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splash-screen" }
|
||||
activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" }
|
||||
fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment" }
|
||||
work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work-runtime" }
|
||||
preferences-datastore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "preferences-datastore" }
|
||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
|
||||
Reference in New Issue
Block a user