From 82f79a7d368caad4068d97051ad8deddd05b4f7e Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 3 Jan 2019 23:10:53 -0500 Subject: Add asset preloader (#1311) --- .../com/pitchedapps/frost/injectors/CssAssets.kt | 33 ++++++++++++++--- .../com/pitchedapps/frost/injectors/CssHider.kt | 5 +-- .../com/pitchedapps/frost/injectors/JsActions.kt | 4 +- .../com/pitchedapps/frost/injectors/JsAssets.kt | 19 ++++++++-- .../com/pitchedapps/frost/injectors/JsInjector.kt | 43 ++++------------------ 5 files changed, 53 insertions(+), 51 deletions(-) (limited to 'app/src/main/kotlin/com/pitchedapps/frost/injectors') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt index de19f99c..77cb4de1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt @@ -16,6 +16,7 @@ */ package com.pitchedapps.frost.injectors +import android.content.Context import android.graphics.Color import android.webkit.WebView import ca.allanwang.kau.kotlin.lazyContext @@ -27,6 +28,8 @@ import ca.allanwang.kau.utils.use import ca.allanwang.kau.utils.withAlpha import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.FileNotFoundException import java.util.Locale @@ -36,12 +39,16 @@ import java.util.Locale * Mapping of the available assets * The enum name must match the css file name */ -enum class CssAssets(val folder: String = "themes") : InjectorContract { +enum class CssAssets(val folder: String = THEME_FOLDER) : InjectorContract { MATERIAL_LIGHT, MATERIAL_DARK, MATERIAL_AMOLED, MATERIAL_GLASS, CUSTOM, ROUND_ICONS("components") ; - var file = "${name.toLowerCase(Locale.CANADA)}.css" - var injector = lazyContext { + private val file = "${name.toLowerCase(Locale.CANADA)}.css" + + /** + * Note that while this can be loaded from any thread, it is typically done through [load] + */ + private val injector = lazyContext { try { var content = it.assets.open("css/$folder/$file").bufferedReader().use(BufferedReader::readText) if (this == CUSTOM) { @@ -72,11 +79,25 @@ enum class CssAssets(val folder: String = "themes") : InjectorContract { } } - override fun inject(webView: WebView, callback: (() -> Unit)?) { - injector(webView.context).inject(webView, callback) - } + override fun inject(webView: WebView) = + injector(webView.context).inject(webView) fun reset() { injector.invalidate() } + + companion object { + // Ensures that all non themes and the selected theme are loaded + suspend fun load(context: Context) { + withContext(Dispatchers.IO) { + val currentTheme = Prefs.t.injector as? CssAssets + val (themes, others) = CssAssets.values().partition { it.folder == THEME_FOLDER } + themes.filter { it != currentTheme }.forEach { it.reset() } + currentTheme?.injector?.invoke(context) + others.forEach { it.injector.invoke(context) } + } + } + } } + +private const val THEME_FOLDER = "themes" diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt index 7da6295f..4fd46ae4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt @@ -45,7 +45,6 @@ enum class CssHider(vararg val items: String) : InjectorContract { .single(name).build() } - override fun inject(webView: WebView, callback: (() -> Unit)?) { - injector.inject(webView, callback) - } + override fun inject(webView: WebView) = + injector.inject(webView) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt index e64d4faa..44fd01da 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt @@ -42,8 +42,8 @@ enum class JsActions(body: String) : InjectorContract { val function = "(function(){$body})();" - override fun inject(webView: WebView, callback: (() -> Unit)?) = - JsInjector(function).inject(webView, callback) + override fun inject(webView: WebView) = + JsInjector(function).inject(webView) } @Suppress("NOTHING_TO_INLINE") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt index 0dccc751..4b1bde43 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt @@ -16,9 +16,12 @@ */ package com.pitchedapps.frost.injectors +import android.content.Context import android.webkit.WebView import ca.allanwang.kau.kotlin.lazyContext import com.pitchedapps.frost.utils.L +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.FileNotFoundException import java.util.Locale @@ -33,8 +36,8 @@ enum class JsAssets : InjectorContract { DOCUMENT_WATCHER ; - var file = "${name.toLowerCase(Locale.CANADA)}.js" - var injector = lazyContext { + private val file = "${name.toLowerCase(Locale.CANADA)}.js" + private val injector = lazyContext { try { val content = it.assets.open("js/$file").bufferedReader().use(BufferedReader::readText) JsBuilder().js(content).single(name).build() @@ -44,7 +47,15 @@ enum class JsAssets : InjectorContract { } } - override fun inject(webView: WebView, callback: (() -> Unit)?) { - injector(webView.context).inject(webView, callback) + override fun inject(webView: WebView) = + injector(webView.context).inject(webView) + + companion object { + // Ensures that all non themes and the selected theme are loaded + suspend fun load(context: Context) { + withContext(Dispatchers.IO) { + JsAssets.values().forEach { it.injector.invoke(context) } + } + } } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt index 8ae3a2f4..00c7bcfc 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -17,12 +17,7 @@ package com.pitchedapps.frost.injectors import android.webkit.WebView -import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.web.FrostWebViewClient -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.subjects.SingleSubject import org.apache.commons.text.StringEscapeUtils import java.util.Locale @@ -81,8 +76,7 @@ class JsBuilder { * Contract for all injectors to allow it to interact properly with a webview */ interface InjectorContract { - fun inject(webView: WebView) = inject(webView, null) - fun inject(webView: WebView, callback: (() -> Unit)?) + fun inject(webView: WebView) /** * Toggle the injector (usually through Prefs * If false, will fallback to an empty action @@ -93,41 +87,18 @@ interface InjectorContract { /** * Helper method to inject multiple functions simultaneously with a single callback */ -fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Int) -> Unit)? = null): Disposable? { - val validInjectors = injectors.filter { it != JsActions.EMPTY } - if (validInjectors.isEmpty()) { - callback?.invoke(0) - return null +fun WebView.jsInject(vararg injectors: InjectorContract) { + injectors.filter { it != JsActions.EMPTY }.forEach { + it.inject(this) } - L.d { "Injecting ${validInjectors.size} items" } - if (callback == null) { - validInjectors.forEach { it.inject(this) } - return null - } - val observables = Array(validInjectors.size) { SingleSubject.create() } - val disposable = Single.zip(observables.asList()) { it.size } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { res, _ -> - callback(res) - } - (0 until validInjectors.size).forEach { i -> - validInjectors[i].inject(this) { - observables[i].onSuccess(Unit) - } - } - return disposable } -fun FrostWebViewClient.jsInject( - vararg injectors: InjectorContract, - callback: ((Int) -> Unit)? = null -) = web.jsInject(*injectors, callback = callback) +fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract) = web.jsInject(*injectors) /** * Wrapper class to convert a function into an injector */ class JsInjector(val function: String) : InjectorContract { - override fun inject(webView: WebView, callback: (() -> Unit)?) { - webView.evaluateJavascript(function) { callback?.invoke() } - } + override fun inject(webView: WebView) = + webView.evaluateJavascript(function, null) } -- cgit v1.2.3