update docs and stuff

This commit is contained in:
Ax333l 2024-12-16 20:07:21 +01:00
parent 33fe3248f4
commit 56ff299fa0
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
10 changed files with 142 additions and 84 deletions

View File

@ -7,7 +7,7 @@ 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.network.downloader.LoadedDownloaderPlugin
import app.revanced.manager.plugin.downloader.DownloadScope
import app.revanced.manager.plugin.downloader.OutputDownloadScope
import app.revanced.manager.util.PM
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.channelFlow
@ -52,7 +52,7 @@ class DownloadedAppRepository(
val downloadedBytes = AtomicLong(0)
channelFlow {
val scope = object : DownloadScope {
val scope = object : OutputDownloadScope {
override val pluginPackageName = plugin.packageName
override val hostPackageName = app.packageName
override suspend fun reportSize(size: Long) {

View File

@ -1,7 +1,7 @@
package app.revanced.manager.network.downloader
import android.os.Parcelable
import app.revanced.manager.plugin.downloader.DownloadScope
import app.revanced.manager.plugin.downloader.OutputDownloadScope
import app.revanced.manager.plugin.downloader.GetScope
import java.io.OutputStream
@ -10,6 +10,6 @@ class LoadedDownloaderPlugin(
val name: String,
val version: String,
val get: suspend GetScope.(packageName: String, version: String?) -> Pair<Parcelable, String?>?,
val download: suspend DownloadScope.(data: Parcelable, outputStream: OutputStream) -> Unit,
val download: suspend OutputDownloadScope.(data: Parcelable, outputStream: OutputStream) -> Unit,
val classLoader: ClassLoader
)

View File

@ -5,6 +5,32 @@ public final class app/revanced/manager/plugin/downloader/ConstantsKt {
public static final field PLUGIN_HOST_PERMISSION Ljava/lang/String;
}
public final class app/revanced/manager/plugin/downloader/DownloadUrl : android/os/Parcelable {
public static final field CREATOR Landroid/os/Parcelable$Creator;
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/Map;
public final fun copy (Ljava/lang/String;Ljava/util/Map;)Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public static synthetic fun copy$default (Lapp/revanced/manager/plugin/downloader/DownloadUrl;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public final fun describeContents ()I
public fun equals (Ljava/lang/Object;)Z
public final fun getHeaders ()Ljava/util/Map;
public final fun getUrl ()Ljava/lang/String;
public fun hashCode ()I
public final fun toDownloadResult ()Lkotlin/Pair;
public fun toString ()Ljava/lang/String;
public final fun writeToParcel (Landroid/os/Parcel;I)V
}
public final class app/revanced/manager/plugin/downloader/DownloadUrl$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/DownloadUrl;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public final class app/revanced/manager/plugin/downloader/Downloader {
}
@ -94,31 +120,6 @@ public final class app/revanced/manager/plugin/downloader/webview/APIKt {
public static final fun runWebView (Lapp/revanced/manager/plugin/downloader/GetScope;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class app/revanced/manager/plugin/downloader/webview/DownloadUrl : android/os/Parcelable {
public static final field CREATOR Landroid/os/Parcelable$Creator;
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/manager/plugin/downloader/webview/DownloadUrl;
public static synthetic fun copy$default (Lapp/revanced/manager/plugin/downloader/webview/DownloadUrl;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lapp/revanced/manager/plugin/downloader/webview/DownloadUrl;
public final fun describeContents ()I
public fun equals (Ljava/lang/Object;)Z
public final fun getUrl ()Ljava/lang/String;
public final fun getUserAgent ()Ljava/lang/String;
public fun hashCode ()I
public final fun toResult ()Lkotlin/Pair;
public fun toString ()Ljava/lang/String;
public final fun writeToParcel (Landroid/os/Parcel;I)V
}
public final class app/revanced/manager/plugin/downloader/webview/DownloadUrl$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/webview/DownloadUrl;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/webview/DownloadUrl;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public abstract interface class app/revanced/manager/plugin/downloader/webview/IWebView : android/os/IInterface {
public static final field DESCRIPTOR Ljava/lang/String;
public abstract fun finish ()V
@ -162,12 +163,19 @@ public abstract class app/revanced/manager/plugin/downloader/webview/IWebViewEve
}
public final class app/revanced/manager/plugin/downloader/webview/WebViewActivity : androidx/activity/ComponentActivity {
public static final field BINDER_KEY Ljava/lang/String;
public static final field TITLE_KEY Ljava/lang/String;
public static final field KEY Ljava/lang/String;
public fun <init> ()V
public fun onOptionsItemSelected (Landroid/view/MenuItem;)Z
}
public final class app/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lapp/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lapp/revanced/manager/plugin/downloader/webview/WebViewActivity$Parameters;
public synthetic fun newArray (I)[Ljava/lang/Object;
}
public abstract interface class app/revanced/manager/plugin/downloader/webview/WebViewCallbackScope : app/revanced/manager/plugin/downloader/Scope {
public abstract fun finish (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun load (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@ -176,7 +184,9 @@ public abstract interface class app/revanced/manager/plugin/downloader/webview/W
public final class app/revanced/manager/plugin/downloader/webview/WebViewScope : app/revanced/manager/plugin/downloader/Scope {
public final fun download (Lkotlin/jvm/functions/Function5;)V
public fun getHostPackageName ()Ljava/lang/String;
public final fun getJsEnabled ()Z
public fun getPluginPackageName ()Ljava/lang/String;
public final fun pageLoad (Lkotlin/jvm/functions/Function3;)V
public final fun setJsEnabled (Z)V
}

View File

@ -1,3 +1,7 @@
package app.revanced.manager.plugin.downloader
/**
* The permission ID of the special plugin host permission. Only ReVanced Manager will have this permission.
* Plugin UI activities and internal services can be protected using this permission.
*/
const val PLUGIN_HOST_PERMISSION = "app.revanced.manager.permission.PLUGIN_HOST"

View File

@ -40,7 +40,7 @@ interface Scope {
*/
interface GetScope : Scope {
/**
* Ask the user to perform some required interaction contained in the activity specified by the provided [Intent].
* Ask the user to perform some required interaction in the activity specified by the provided [Intent].
* This function returns normally with the resulting [Intent] when the activity finishes with code [Activity.RESULT_OK].
*
* @throws UserInteractionException.RequestDenied User decided to skip this plugin.
@ -74,7 +74,7 @@ class DownloaderScope<T : Parcelable> internal constructor(
private val inputDownloadScopeImpl = object : InputDownloadScope, Scope by scopeImpl {}
/**
* Define the download function for this plugin.
* Define the download block of the plugin.
*/
fun download(block: suspend InputDownloadScope.(data: T) -> DownloadResult) {
download = { app, outputStream ->
@ -88,7 +88,8 @@ class DownloaderScope<T : Parcelable> internal constructor(
}
/**
* Define the get function for this plugin.
* Define the get block of the plugin.
* The block should return null if the app cannot be found. The version in the result must match the version argument unless it is null.
*/
fun get(block: suspend GetScope.(packageName: String, version: String?) -> GetResult<T>?) {
get = block
@ -139,14 +140,25 @@ class Downloader<T : Parcelable> internal constructor(
@property:PluginHostApi val download: suspend OutputDownloadScope.(data: T, outputStream: OutputStream) -> Unit
)
/**
* Define a downloader plugin.
*/
fun <T : Parcelable> Downloader(block: DownloaderScope<T>.() -> Unit) = DownloaderBuilder(block)
/**
* @see GetScope.requestStartActivity
*/
sealed class UserInteractionException(message: String) : Exception(message) {
class RequestDenied @PluginHostApi constructor() :
UserInteractionException("Request was denied")
UserInteractionException("Request denied by user")
sealed class Activity(message: String) : UserInteractionException(message) {
class Cancelled @PluginHostApi constructor() : Activity("Interaction was cancelled")
class Cancelled @PluginHostApi constructor() : Activity("Interaction cancelled")
/**
* @param resultCode The result code of the activity.
* @param intent The [Intent] of the activity.
*/
class NotCompleted @PluginHostApi constructor(val resultCode: Int, val intent: Intent?) :
Activity("Unexpected activity result code: $resultCode")
}

View File

@ -8,7 +8,7 @@ import android.os.Parcelable
import java.io.OutputStream
/**
* The scope of [DownloaderScope.download].
* The scope of the [OutputStream] version of [DownloaderScope.download].
*/
interface OutputDownloadScope : BaseDownloadScope {
suspend fun reportSize(size: Long)
@ -16,6 +16,7 @@ interface OutputDownloadScope : BaseDownloadScope {
/**
* A replacement for [DownloaderScope.download] that uses [OutputStream].
* The provided [OutputStream] does not need to be closed manually.
*/
fun <T : Parcelable> DownloaderScope<T>.download(block: suspend OutputDownloadScope.(T, OutputStream) -> Unit) {
download = block

View File

@ -1,7 +0,0 @@
package app.revanced.manager.plugin.downloader
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class Package(val name: String, val version: String) : Parcelable

View File

@ -0,0 +1,39 @@
package app.revanced.manager.plugin.downloader
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import java.net.HttpURLConnection
import java.net.URI
/**
* A simple parcelable data class for storing a package name and version.
* This can be used as the data type for plugins that only need a name and version to implement their [DownloaderScope.download] function.
*
* @param name The package name.
* @param version The version.
*/
@Parcelize
data class Package(val name: String, val version: String) : Parcelable
/**
* A data class for storing a download URL.
*
* @param url The download URL.
* @param headers The headers to use for the request.
*/
@Parcelize
data class DownloadUrl(val url: String, val headers: Map<String, String> = emptyMap()) : Parcelable {
/**
* Converts this into a [DownloadResult].
*/
fun toDownloadResult(): DownloadResult = with(URI.create(url).toURL().openConnection() as HttpURLConnection) {
useCaches = false
allowUserInteraction = false
headers.forEach(::setRequestProperty)
connectTimeout = 10_000
connect()
inputStream to getHeaderField("Content-Length").toLong()
}
}

View File

@ -2,7 +2,7 @@ package app.revanced.manager.plugin.downloader.webview
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import app.revanced.manager.plugin.downloader.DownloadUrl
import app.revanced.manager.plugin.downloader.DownloaderScope
import app.revanced.manager.plugin.downloader.GetScope
import app.revanced.manager.plugin.downloader.Scope
@ -14,35 +14,12 @@ import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import java.net.HttpURLConnection
import java.net.URI
import kotlin.properties.Delegates
typealias InitialUrl = String
typealias PageLoadCallback<T> = suspend WebViewCallbackScope<T>.(url: String) -> Unit
typealias DownloadCallback<T> = suspend WebViewCallbackScope<T>.(url: String, mimeType: String, userAgent: String) -> Unit
@Parcelize
/**
* A data class for storing a download
*/
data class DownloadUrl(val url: String, val userAgent: String?) : Parcelable {
/**
* Converts this into a [app.revanced.manager.plugin.downloader.DownloadResult].
*/
fun toResult() = with(URI.create(url).toURL().openConnection() as HttpURLConnection) {
useCaches = false
allowUserInteraction = false
userAgent?.let { setRequestProperty("User-Agent", it) }
connectTimeout = 10_000
connect()
inputStream to getHeaderField("Content-Length").toLong()
}
}
interface WebViewCallbackScope<T> : Scope {
/**
* Finishes the activity and returns the [result].
@ -68,6 +45,12 @@ class WebViewScope<T> internal constructor(
private lateinit var webView: IWebView
internal lateinit var initialUrl: String
/**
* Controls whether JavaScript is enabled in the WebView. The default value is false.
* Changing this after the WebView has been launched has no effect.
*/
var jsEnabled = false
internal val binder = object : IWebViewEvents.Stub() {
override fun ready(iface: IWebView?) {
coroutineScope.launch(dispatcher) {
@ -107,7 +90,7 @@ class WebViewScope<T> internal constructor(
}
/**
* Called when the WebView attempts to navigate to a downloadable file.
* Called when the WebView attempts to download a file to disk.
*/
fun download(block: DownloadCallback<T>) {
onDownloadCallback = block
@ -127,9 +110,10 @@ private value class Container<U>(val value: U)
/**
* Run a [android.webkit.WebView] Activity controlled by the provided code block.
* The activity will keep running until it is cancelled or an event handler calls [WebViewCallbackScope.finish].
* The [block] defines the event handlers and returns the initial URL.
*
* @param title The string displayed in the action bar
* @param block Defines event handlers and returns an initial URL
* @param title The string displayed in the action bar.
* @param block The control block.
*/
suspend fun <T> GetScope.runWebView(
title: String,
@ -140,12 +124,12 @@ 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
// Start the webview activity and wait until it finishes.
requestStartActivity(Intent().apply {
putExtras(Bundle().apply {
putBinder(WebViewActivity.BINDER_KEY, scope.binder)
putString(WebViewActivity.TITLE_KEY, title)
})
putExtra(
WebViewActivity.KEY,
WebViewActivity.Parameters(title, scope.jsEnabled, scope.binder)
)
setClassName(
hostPackageName,
WebViewActivity::class.qualifiedName!!
@ -174,7 +158,14 @@ fun WebViewDownloader(block: suspend WebViewScope<DownloadUrl>.(packageName: Str
try {
runWebView(label) {
download { url, _, userAgent -> finish(DownloadUrl(url, userAgent)) }
download { url, _, userAgent ->
finish(
DownloadUrl(
url,
mapOf("User-Agent" to userAgent)
)
)
}
block(this@runWebView, packageName, version) ?: throw ReturnNull()
} to version
@ -184,6 +175,6 @@ fun WebViewDownloader(block: suspend WebViewScope<DownloadUrl>.(packageName: Str
}
download {
it.toResult()
it.toDownloadResult()
}
}

View File

@ -2,6 +2,8 @@ package app.revanced.manager.plugin.downloader.webview
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.IBinder
import android.os.Parcelable
import android.view.MenuItem
import android.webkit.CookieManager
import android.webkit.WebSettings
@ -21,6 +23,7 @@ import app.revanced.manager.plugin.downloader.R
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
class WebViewActivity : ComponentActivity() {
@SuppressLint("SetJavaScriptEnabled")
@ -36,22 +39,23 @@ class WebViewActivity : ComponentActivity() {
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val params = intent.getParcelableExtra<Parameters>(KEY)!!
actionBar?.apply {
title = intent.getStringExtra(TITLE_KEY)
title = intent.getStringExtra(params.title)
setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel)
setDisplayHomeAsUpEnabled(true)
}
val events = IWebViewEvents.Stub.asInterface(intent.extras!!.getBinder(BINDER_KEY))!!
val events = IWebViewEvents.Stub.asInterface(params.events)!!
vm.setup(events)
val webView = findViewById<WebView>(R.id.content).apply {
settings.apply {
cacheMode = WebSettings.LOAD_NO_CACHE
databaseEnabled = false
allowContentAccess = true
allowContentAccess = false
domStorageEnabled = false
javaScriptEnabled = true
javaScriptEnabled = params.jsEnabled
}
webViewClient = vm.webViewClient
@ -82,9 +86,13 @@ class WebViewActivity : ComponentActivity() {
true
} else super.onOptionsItemSelected(item)
@Parcelize
internal class Parameters(
val title: String, val jsEnabled: Boolean, val events: IBinder
) : Parcelable
internal companion object {
const val BINDER_KEY = "EVENTS"
const val TITLE_KEY = "TITLE"
const val KEY = "params"
}
}