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 {
|
android {
|
||||||
namespace = "app.revanced.manager.plugin.downloader"
|
namespace = "app.revanced.manager.plugin.downloader"
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
|
@ -5,6 +5,8 @@ import android.os.Bundle
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import app.revanced.manager.plugin.downloader.DownloaderScope
|
import app.revanced.manager.plugin.downloader.DownloaderScope
|
||||||
import app.revanced.manager.plugin.downloader.GetResult
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
@ -14,32 +16,49 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
internal typealias PageLoadCallback<T> = suspend WebViewCallbackScope<T>.(url: String) -> Unit
|
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 DownloadCallback<T> = suspend WebViewCallbackScope<T>.(url: String, mimeType: String, userAgent: String) -> Unit
|
||||||
internal typealias ReadyCallback<T> = suspend WebViewCallbackScope<T>.() -> Unit
|
internal typealias ReadyCallback<T> = suspend WebViewCallbackScope<T>.() -> Unit
|
||||||
|
|
||||||
@Parcelize
|
@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) {
|
fun toResult() = with(URI.create(url).toURL().openConnection() as HttpURLConnection) {
|
||||||
useCaches = false
|
useCaches = false
|
||||||
allowUserInteraction = false
|
allowUserInteraction = false
|
||||||
setRequestProperty("User-Agent", userAgent)
|
userAgent?.let { setRequestProperty("User-Agent", it) }
|
||||||
|
|
||||||
connectTimeout = 10_000
|
connectTimeout = 10_000
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
inputStream to getHeaderField("Content-Length").toLong()
|
inputStream to getHeaderField("Content-Length").toLong()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebViewCallbackScope<T : Parcelable> {
|
interface WebViewCallbackScope<T> : Scope {
|
||||||
suspend fun finish(result: GetResult<T>?)
|
/**
|
||||||
|
* 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)
|
suspend fun load(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebViewScope<T : Parcelable> internal constructor(
|
class WebViewScope<T> internal constructor(
|
||||||
coroutineScope: CoroutineScope,
|
coroutineScope: CoroutineScope,
|
||||||
setResult: (GetResult<T>?) -> Unit
|
private val scopeImpl: Scope,
|
||||||
) {
|
setResult: (T) -> Unit
|
||||||
|
) : Scope by scopeImpl {
|
||||||
private var onPageLoadCallback: PageLoadCallback<T> = {}
|
private var onPageLoadCallback: PageLoadCallback<T> = {}
|
||||||
private var onDownloadCallback: DownloadCallback<T> = { _, _, _ -> }
|
private var onDownloadCallback: DownloadCallback<T> = { _, _, _ -> }
|
||||||
private var onReadyCallback: ReadyCallback<T> =
|
private var onReadyCallback: ReadyCallback<T> =
|
||||||
@ -76,8 +95,8 @@ class WebViewScope<T : Parcelable> internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callbackScope = object : WebViewCallbackScope<T> {
|
private val callbackScope = object : WebViewCallbackScope<T>, Scope by scopeImpl {
|
||||||
override suspend fun finish(result: GetResult<T>?) {
|
override suspend fun finish(result: T) {
|
||||||
setResult(result)
|
setResult(result)
|
||||||
// Tell the WebViewActivity to finish
|
// Tell the WebViewActivity to finish
|
||||||
webView.let { withContext(Dispatchers.IO) { it.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
|
onDownloadCallback = block
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onPageLoad(block: PageLoadCallback<T>) {
|
/**
|
||||||
|
* Called when the WebView finishes loading a page.
|
||||||
|
*/
|
||||||
|
fun pageLoad(block: PageLoadCallback<T>) {
|
||||||
onPageLoadCallback = block
|
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
|
onReadyCallback = block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Parcelable> DownloaderScope<T>.webView(block: WebViewScope<T>.(packageName: String, version: String?) -> Unit) =
|
@JvmInline
|
||||||
get { pkgName, version ->
|
private value class Container<U>(val value: U)
|
||||||
var result: GetResult<T>? = null
|
|
||||||
|
|
||||||
|
private suspend fun <T> GetScope.runWebView(title: String, block: WebViewScope<T>.() -> Unit) =
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
val scope = WebViewScope(this) { result = it }
|
var result by Delegates.notNull<Container<T>>()
|
||||||
scope.block(pkgName, version)
|
|
||||||
|
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 {
|
requestStartActivity(Intent().apply {
|
||||||
putExtras(Bundle().apply {
|
putExtras(Bundle().apply {
|
||||||
putBinder(WebViewActivity.BINDER_KEY, scope.binder)
|
putBinder(WebViewActivity.BINDER_KEY, scope.binder)
|
||||||
val pm = context.packageManager
|
putString(WebViewActivity.TITLE_KEY, title)
|
||||||
val label = pm.getPackageInfo(pluginPackageName, 0).applicationInfo.loadLabel(pm).toString()
|
|
||||||
putString(WebViewActivity.TITLE_KEY, label)
|
|
||||||
})
|
})
|
||||||
setClassName(
|
setClassName(
|
||||||
hostPackageName,
|
hostPackageName,
|
||||||
WebViewActivity::class.qualifiedName!!
|
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"
|
val packageName = "app.revanced.manager.plugin.downloader.example"
|
||||||
|
|
||||||
namespace = packageName
|
namespace = packageName
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = packageName
|
applicationId = packageName
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
}
|
}
|
||||||
|
@ -54,11 +54,15 @@ val installedAppDownloader = downloader<DownloadUrl> {
|
|||||||
build().toString()
|
build().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
onDownload { url, mimeType, userAgent ->
|
download { url, _, userAgent ->
|
||||||
finish(DownloadUrl(url, mimeType, userAgent) to version)
|
finish(DownloadUrl(url, userAgent) to version)
|
||||||
}
|
}
|
||||||
|
|
||||||
onReady {
|
pageLoad { url ->
|
||||||
|
println(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
ready {
|
||||||
load(startUrl)
|
load(startUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user