mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
document and improve the webview api
This commit is contained in:
parent
66c06a6fe5
commit
c3e48a331a
@ -7,7 +7,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.manager.plugin.downloader"
|
||||
compileSdk = 34
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
|
@ -5,6 +5,8 @@ import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import app.revanced.manager.plugin.downloader.DownloaderScope
|
||||
import app.revanced.manager.plugin.downloader.GetResult
|
||||
import app.revanced.manager.plugin.downloader.GetScope
|
||||
import app.revanced.manager.plugin.downloader.Scope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@ -14,32 +16,49 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URI
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal typealias PageLoadCallback<T> = suspend WebViewCallbackScope<T>.(url: String) -> Unit
|
||||
internal typealias DownloadCallback<T> = suspend WebViewCallbackScope<T>.(url: String, mimeType: String, userAgent: String) -> Unit
|
||||
internal typealias ReadyCallback<T> = suspend WebViewCallbackScope<T>.() -> Unit
|
||||
|
||||
@Parcelize
|
||||
data class DownloadUrl(val url: String, val mimeType: String, val userAgent: String) : Parcelable {
|
||||
/**
|
||||
* 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
|
||||
setRequestProperty("User-Agent", userAgent)
|
||||
userAgent?.let { setRequestProperty("User-Agent", it) }
|
||||
|
||||
connectTimeout = 10_000
|
||||
connect()
|
||||
|
||||
inputStream to getHeaderField("Content-Length").toLong()
|
||||
}
|
||||
}
|
||||
|
||||
interface WebViewCallbackScope<T : Parcelable> {
|
||||
suspend fun finish(result: GetResult<T>?)
|
||||
interface WebViewCallbackScope<T> : Scope {
|
||||
/**
|
||||
* Finishes the activity and returns the [result].
|
||||
*/
|
||||
suspend fun finish(result: T)
|
||||
|
||||
/**
|
||||
* Tells the WebView to load the specified [url].
|
||||
*/
|
||||
suspend fun load(url: String)
|
||||
}
|
||||
|
||||
class WebViewScope<T : Parcelable> internal constructor(
|
||||
class WebViewScope<T> internal constructor(
|
||||
coroutineScope: CoroutineScope,
|
||||
setResult: (GetResult<T>?) -> Unit
|
||||
) {
|
||||
private val scopeImpl: Scope,
|
||||
setResult: (T) -> Unit
|
||||
) : Scope by scopeImpl {
|
||||
private var onPageLoadCallback: PageLoadCallback<T> = {}
|
||||
private var onDownloadCallback: DownloadCallback<T> = { _, _, _ -> }
|
||||
private var onReadyCallback: ReadyCallback<T> =
|
||||
@ -76,8 +95,8 @@ class WebViewScope<T : Parcelable> internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val callbackScope = object : WebViewCallbackScope<T> {
|
||||
override suspend fun finish(result: GetResult<T>?) {
|
||||
private val callbackScope = object : WebViewCallbackScope<T>, Scope by scopeImpl {
|
||||
override suspend fun finish(result: T) {
|
||||
setResult(result)
|
||||
// Tell the WebViewActivity to finish
|
||||
webView.let { withContext(Dispatchers.IO) { it.finish() } }
|
||||
@ -89,38 +108,66 @@ class WebViewScope<T : Parcelable> internal constructor(
|
||||
|
||||
}
|
||||
|
||||
fun onDownload(block: DownloadCallback<T>) {
|
||||
/**
|
||||
* Called when the WebView attempts to navigate to a downloadable file.
|
||||
*/
|
||||
fun download(block: DownloadCallback<T>) {
|
||||
onDownloadCallback = block
|
||||
}
|
||||
|
||||
fun onPageLoad(block: PageLoadCallback<T>) {
|
||||
/**
|
||||
* Called when the WebView finishes loading a page.
|
||||
*/
|
||||
fun pageLoad(block: PageLoadCallback<T>) {
|
||||
onPageLoadCallback = block
|
||||
}
|
||||
|
||||
fun onReady(block: ReadyCallback<T>) {
|
||||
/**
|
||||
* Called when the WebView is ready. This should always call [WebViewCallbackScope.load].
|
||||
*/
|
||||
fun ready(block: ReadyCallback<T>) {
|
||||
onReadyCallback = block
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Parcelable> DownloaderScope<T>.webView(block: WebViewScope<T>.(packageName: String, version: String?) -> Unit) =
|
||||
get { pkgName, version ->
|
||||
var result: GetResult<T>? = null
|
||||
@JvmInline
|
||||
private value class Container<U>(val value: U)
|
||||
|
||||
private suspend fun <T> GetScope.runWebView(title: String, block: WebViewScope<T>.() -> Unit) =
|
||||
coroutineScope {
|
||||
val scope = WebViewScope(this) { result = it }
|
||||
scope.block(pkgName, version)
|
||||
var result by Delegates.notNull<Container<T>>()
|
||||
|
||||
val scope = WebViewScope<T>(this@coroutineScope, this@runWebView) { result = Container(it) }
|
||||
scope.block()
|
||||
|
||||
// Start the webview activity and wait until it finishes
|
||||
requestStartActivity(Intent().apply {
|
||||
putExtras(Bundle().apply {
|
||||
putBinder(WebViewActivity.BINDER_KEY, scope.binder)
|
||||
val pm = context.packageManager
|
||||
val label = pm.getPackageInfo(pluginPackageName, 0).applicationInfo.loadLabel(pm).toString()
|
||||
putString(WebViewActivity.TITLE_KEY, label)
|
||||
putString(WebViewActivity.TITLE_KEY, title)
|
||||
})
|
||||
setClassName(
|
||||
hostPackageName,
|
||||
WebViewActivity::class.qualifiedName!!
|
||||
)
|
||||
})
|
||||
|
||||
result.value
|
||||
}
|
||||
result
|
||||
|
||||
/**
|
||||
* Implements [DownloaderScope.get] using an [android.webkit.WebView]. Event handlers are defined in the provided [block].
|
||||
* The activity will keep running until it is cancelled or an event handler calls [WebViewCallbackScope.finish].
|
||||
*
|
||||
* @param title The title that will be shown in the WebView activity. The default value is the plugin application label.
|
||||
*/
|
||||
fun <T : Parcelable> DownloaderScope<T>.webView(
|
||||
title: String = context.applicationInfo.loadLabel(
|
||||
context.packageManager
|
||||
).toString(),
|
||||
block: WebViewScope<GetResult<T>?>.(packageName: String, version: String?) -> Unit
|
||||
) = get { pkgName, version ->
|
||||
runWebView(title) {
|
||||
block(pkgName, version)
|
||||
}
|
||||
}
|
@ -9,12 +9,12 @@ android {
|
||||
val packageName = "app.revanced.manager.plugin.downloader.example"
|
||||
|
||||
namespace = packageName
|
||||
compileSdk = 34
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = packageName
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
}
|
||||
|
@ -54,11 +54,15 @@ val installedAppDownloader = downloader<DownloadUrl> {
|
||||
build().toString()
|
||||
}
|
||||
|
||||
onDownload { url, mimeType, userAgent ->
|
||||
finish(DownloadUrl(url, mimeType, userAgent) to version)
|
||||
download { url, _, userAgent ->
|
||||
finish(DownloadUrl(url, userAgent) to version)
|
||||
}
|
||||
|
||||
onReady {
|
||||
pageLoad { url ->
|
||||
println(url)
|
||||
}
|
||||
|
||||
ready {
|
||||
load(startUrl)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user