mirror of
https://github.com/AllanWang/Frost-for-Facebook.git
synced 2024-11-09 12:32:30 +01:00
Add most injectors
This commit is contained in:
parent
5ed9556b87
commit
c3023b0da9
@ -206,6 +206,10 @@ dependencies {
|
||||
implementation("com.google.protobuf:protobuf-kotlin-lite:3.23.3")
|
||||
|
||||
implementation("app.cash.sqldelight:android-driver:2.0.0-rc01")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.commons/commons-text
|
||||
implementation("org.apache.commons:commons-text:1.10.0")
|
||||
|
||||
}
|
||||
|
||||
protobuf {
|
||||
|
@ -21,15 +21,20 @@ import android.app.Application
|
||||
import android.os.Bundle
|
||||
import com.google.common.flogger.FluentLogger
|
||||
import com.pitchedapps.frost.hilt.FrostComponents
|
||||
import com.pitchedapps.frost.webview.injection.FrostJsInjectors
|
||||
import com.pitchedapps.frost.webview.injection.assets.JsAssets
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** Frost Application. */
|
||||
@HiltAndroidApp
|
||||
class FrostApp : Application() {
|
||||
|
||||
@Inject lateinit var componentsProvider: Provider<FrostComponents>
|
||||
@Inject lateinit var frostJsInjectors: Provider<FrostJsInjectors>
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@ -57,6 +62,13 @@ class FrostApp : Application() {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
MainScope().launch { setup() }
|
||||
}
|
||||
|
||||
private suspend fun setup() {
|
||||
JsAssets.load(this)
|
||||
frostJsInjectors.get().load()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -18,7 +18,7 @@ package com.pitchedapps.frost
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.common.flogger.FluentLogger
|
||||
import com.pitchedapps.frost.components.FrostDataStore
|
||||
@ -46,7 +46,7 @@ import kotlinx.coroutines.withContext
|
||||
* and will launch another activity without history after doing initialization work.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class StartActivity : AppCompatActivity() {
|
||||
class StartActivity : ComponentActivity() {
|
||||
|
||||
@Inject lateinit var frostDb: FrostDb
|
||||
|
||||
@ -65,14 +65,14 @@ class StartActivity : AppCompatActivity() {
|
||||
// TODO load real tabs
|
||||
store.dispatch(TabListAction.SetHomeTabs(data = listOf(FbItem.Feed, FbItem.Menu)))
|
||||
// Test something scrollable
|
||||
store.dispatch(
|
||||
TabAction(
|
||||
tabId = HomeTabSessionState.homeTabId(0),
|
||||
TabAction.ContentAction.UpdateUrlAction(
|
||||
"https://github.com/AllanWang/Frost-for-Facebook"
|
||||
),
|
||||
),
|
||||
)
|
||||
// store.dispatch(
|
||||
// TabAction(
|
||||
// tabId = HomeTabSessionState.homeTabId(0),
|
||||
// TabAction.ContentAction.UpdateUrlAction(
|
||||
// "https://github.com/AllanWang/Frost-for-Facebook"
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
store.dispatch(
|
||||
TabAction(
|
||||
tabId = HomeTabSessionState.homeTabId(1),
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2023 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.web
|
||||
|
||||
import com.pitchedapps.frost.components.FrostDataStore
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Singleton
|
||||
|
||||
/** Snapshot of UI options based on user preferences */
|
||||
interface FrostWebUiOptions {
|
||||
|
||||
val theme: Theme
|
||||
|
||||
enum class Theme {
|
||||
Original,
|
||||
Light,
|
||||
Dark,
|
||||
Amoled,
|
||||
Glass // Custom
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton to provide snapshots of [FrostWebUiOptions].
|
||||
*
|
||||
* This is a mutable class, and does not provide change listeners. We will update activities
|
||||
* manually when needed.
|
||||
*/
|
||||
@Singleton
|
||||
class FrostWebUiSnapshot(private val dataStore: FrostDataStore) {
|
||||
|
||||
@Volatile
|
||||
var options: FrostWebUiOptions = defaultOptions()
|
||||
private set
|
||||
|
||||
private val stale = AtomicBoolean(true)
|
||||
|
||||
private fun defaultOptions(): FrostWebUiOptions =
|
||||
object : FrostWebUiOptions {
|
||||
override val theme: FrostWebUiOptions.Theme = FrostWebUiOptions.Theme.Original
|
||||
}
|
||||
|
||||
/** Fetch new snapshot and update other singletons */
|
||||
suspend fun reload() {
|
||||
if (!stale.getAndSet(false)) return
|
||||
// todo load
|
||||
}
|
||||
|
||||
fun markAsStale() {
|
||||
stale.set(true)
|
||||
}
|
||||
}
|
@ -28,10 +28,26 @@ class FrostLoggerMiddleware : FrostWebMiddleware {
|
||||
next: (FrostWebAction) -> Unit,
|
||||
action: FrostWebAction
|
||||
) {
|
||||
logger.atInfo().log("FrostWebAction: %s - %s", action::class.simpleName, action)
|
||||
if (logInfo(action)) {
|
||||
logger.atInfo().log("FrostWebAction: %s", action)
|
||||
} else {
|
||||
logger.atFine().log("FrostWebAction: %s", action)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
|
||||
private fun logInfo(action: FrostWebAction): Boolean {
|
||||
when (action) {
|
||||
is TabAction ->
|
||||
when (action.action) {
|
||||
is TabAction.ContentAction.UpdateProgressAction -> return false
|
||||
else -> {}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = FluentLogger.forEnclosingClass()
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import android.webkit.WebViewClient
|
||||
import com.pitchedapps.frost.ext.WebTargetId
|
||||
import com.pitchedapps.frost.web.FrostWebHelper
|
||||
import com.pitchedapps.frost.web.state.FrostWebStore
|
||||
import com.pitchedapps.frost.webview.injection.FrostJsInjectors
|
||||
import dagger.BindsInstance
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@ -62,8 +63,9 @@ internal object FrostWebModule {
|
||||
fun client(
|
||||
@FrostWeb tabId: WebTargetId,
|
||||
store: FrostWebStore,
|
||||
webHelper: FrostWebHelper
|
||||
): WebViewClient = FrostWebViewClient(tabId, store, webHelper)
|
||||
webHelper: FrostWebHelper,
|
||||
frostJsInjectors: FrostJsInjectors,
|
||||
): WebViewClient = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@ import com.pitchedapps.frost.compose.webview.FrostWebCompose
|
||||
import com.pitchedapps.frost.ext.WebTargetId
|
||||
import com.pitchedapps.frost.web.FrostWebHelper
|
||||
import com.pitchedapps.frost.web.state.FrostWebStore
|
||||
import com.pitchedapps.frost.webview.injection.FrostJsInjectors
|
||||
import javax.inject.Inject
|
||||
|
||||
class FrostWebComposer
|
||||
@ -27,10 +28,11 @@ class FrostWebComposer
|
||||
internal constructor(
|
||||
private val store: FrostWebStore,
|
||||
private val webHelper: FrostWebHelper,
|
||||
private val frostJsInjectors: FrostJsInjectors,
|
||||
) {
|
||||
|
||||
fun create(tabId: WebTargetId): FrostWebCompose {
|
||||
val client = FrostWebViewClient(tabId, store, webHelper)
|
||||
val client = FrostWebViewClient(tabId, store, webHelper, frostJsInjectors)
|
||||
val chromeClient = FrostChromeClient(tabId, store)
|
||||
return FrostWebCompose(tabId, store, client, chromeClient)
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import com.pitchedapps.frost.web.state.TabAction
|
||||
import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateNavigationAction
|
||||
import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateProgressAction
|
||||
import com.pitchedapps.frost.web.state.TabAction.ContentAction.UpdateTitleAction
|
||||
import com.pitchedapps.frost.webview.injection.FrostJsInjectors
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
/**
|
||||
@ -63,7 +64,8 @@ abstract class BaseWebViewClient : WebViewClient() {
|
||||
class FrostWebViewClient(
|
||||
private val tabId: WebTargetId,
|
||||
private val store: FrostWebStore,
|
||||
override val webHelper: FrostWebHelper
|
||||
override val webHelper: FrostWebHelper,
|
||||
private val frostJsInjectors: FrostJsInjectors,
|
||||
) : BaseWebViewClient() {
|
||||
|
||||
private fun FrostWebStore.dispatch(action: TabAction.Action) {
|
||||
@ -140,8 +142,10 @@ class FrostWebViewClient(
|
||||
// )
|
||||
// }
|
||||
|
||||
// override fun onPageCommitVisible(view: WebView, url: String?) {
|
||||
// super.onPageCommitVisible(view, url)
|
||||
override fun onPageCommitVisible(view: WebView, url: String?) {
|
||||
super.onPageCommitVisible(view, url)
|
||||
frostJsInjectors.injectOnPageCommitVisible(view, url)
|
||||
}
|
||||
// injectBackgroundColor()
|
||||
// when {
|
||||
// url.isFacebookUrl -> {
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2023 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection
|
||||
|
||||
import android.content.Context
|
||||
import android.webkit.WebView
|
||||
import com.google.common.flogger.FluentLogger
|
||||
import com.pitchedapps.frost.webview.injection.assets.JsActions
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Singleton
|
||||
class FrostJsInjectors
|
||||
@Inject
|
||||
internal constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) {
|
||||
|
||||
@Volatile private var theme: JsInjector = JsInjector.EMPTY
|
||||
|
||||
fun injectOnPageCommitVisible(view: WebView, url: String?) {
|
||||
logger.atInfo().log("inject page commit visible %b", theme != JsInjector.EMPTY)
|
||||
theme.inject(view)
|
||||
}
|
||||
|
||||
fun getTheme(): JsInjector {
|
||||
return try {
|
||||
val content =
|
||||
context.assets
|
||||
.open("frostcore/css/facebook/themes/material_glass.css")
|
||||
.bufferedReader()
|
||||
.use(BufferedReader::readText)
|
||||
JsBuilder().css(content).build()
|
||||
} catch (e: FileNotFoundException) {
|
||||
logger.atSevere().withCause(e).log("CssAssets file not found")
|
||||
JsActions.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun load() {
|
||||
withContext(Dispatchers.IO) { theme = getTheme() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = FluentLogger.forEnclosingClass()
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2023 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection
|
||||
|
||||
import android.webkit.WebView
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
|
||||
interface JsInjector {
|
||||
|
||||
fun inject(webView: WebView)
|
||||
|
||||
companion object {
|
||||
|
||||
val EMPTY: JsInjector = EmptyJsInjector
|
||||
|
||||
operator fun invoke(content: String): JsInjector =
|
||||
object : JsInjector {
|
||||
override fun inject(webView: WebView) {
|
||||
webView.evaluateJavascript(content, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object EmptyJsInjector : JsInjector {
|
||||
override fun inject(webView: WebView) {
|
||||
// Noop
|
||||
}
|
||||
}
|
||||
|
||||
data class OneShotJsInjector(val tag: String, val injector: JsInjector) : JsInjector {
|
||||
override fun inject(webView: WebView) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
class JsBuilder {
|
||||
private val css = StringBuilder()
|
||||
private val js = StringBuilder()
|
||||
|
||||
private var tag: String? = null
|
||||
|
||||
fun css(css: String): JsBuilder {
|
||||
this.css.append(StringEscapeUtils.escapeEcmaScript(css))
|
||||
return this
|
||||
}
|
||||
|
||||
fun js(content: String): JsBuilder {
|
||||
this.js.append(content)
|
||||
return this
|
||||
}
|
||||
|
||||
fun single(tag: String): JsBuilder {
|
||||
this.tag = tag // TODO TagObfuscator.obfuscateTag(tag)
|
||||
return this
|
||||
}
|
||||
|
||||
fun build() = JsInjector(toString())
|
||||
|
||||
override fun toString(): String {
|
||||
val tag = this.tag
|
||||
val builder =
|
||||
StringBuilder().apply {
|
||||
append("!function(){")
|
||||
if (css.isNotBlank()) {
|
||||
val cssMin = css.replace(Regex("\\s*\n\\s*"), "")
|
||||
append("var a=document.createElement('style');")
|
||||
append("a.innerHTML='$cssMin';")
|
||||
if (tag != null) {
|
||||
append("a.id='$tag';")
|
||||
}
|
||||
append("document.head.appendChild(a);")
|
||||
}
|
||||
if (js.isNotBlank()) {
|
||||
append(js)
|
||||
}
|
||||
}
|
||||
var content = builder.append("}()").toString()
|
||||
if (tag != null) {
|
||||
content = singleInjector(tag, content)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
private fun singleInjector(tag: String, content: String) =
|
||||
"""
|
||||
if (!window.hasOwnProperty("$tag") {
|
||||
console.log("Registering $tag");
|
||||
window.$tag = true;
|
||||
$content
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection.assets
|
||||
|
||||
import android.webkit.WebView
|
||||
import com.pitchedapps.frost.webview.injection.JsBuilder
|
||||
import com.pitchedapps.frost.webview.injection.JsInjector
|
||||
|
||||
/** Small misc inline css assets */
|
||||
enum class CssActions(private val content: String) : JsInjector {
|
||||
FullSizeImage(
|
||||
"div._4prr[style*=\"max-width\"][style*=\"max-height\"]{max-width:none !important;max-height:none !important}",
|
||||
);
|
||||
|
||||
private val injector: JsInjector =
|
||||
JsBuilder().css(content).single("css-small-assets-$name").build()
|
||||
|
||||
override fun inject(webView: WebView) {
|
||||
injector.inject(webView)
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection.assets
|
||||
|
||||
import android.webkit.WebView
|
||||
import com.pitchedapps.frost.webview.injection.JsBuilder
|
||||
import com.pitchedapps.frost.webview.injection.JsInjector
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-31.
|
||||
*
|
||||
* List of elements to hide
|
||||
*/
|
||||
enum class CssHider(private vararg val items: String) : JsInjector {
|
||||
CORE("[data-sigil=m_login_upsell]", "[role=progressbar]"),
|
||||
HEADER(
|
||||
"#header:not(.mFuturePageHeader):not(.titled)",
|
||||
"#mJewelNav",
|
||||
"[data-sigil=MTopBlueBarHeader]",
|
||||
"#header-notices",
|
||||
"[data-sigil*=m-promo-jewel-header]",
|
||||
),
|
||||
ADS("article[data-xt*=sponsor]", "article[data-store*=sponsor]", "article[data-ft*=sponsor]"),
|
||||
PEOPLE_YOU_MAY_KNOW("article._d2r"),
|
||||
SUGGESTED_GROUPS("article[data-ft*=\"ei\":]"),
|
||||
|
||||
// Is it really this simple?
|
||||
SUGGESTED_POSTS("article[data-store*=recommendation]", "article[data-ft*=recommendation]"),
|
||||
COMPOSER("#MComposer"),
|
||||
MESSENGER("._s15", "[data-testid=info_panel]", "js_i"),
|
||||
NON_RECENT("article:not([data-store*=actor_name])"),
|
||||
STORIES(
|
||||
"#MStoriesTray",
|
||||
// Sub element with just the tray; title is not a part of this
|
||||
"[data-testid=story_tray]",
|
||||
),
|
||||
POST_ACTIONS("footer [data-sigil=\"ufi-inline-actions\"]"),
|
||||
POST_REACTIONS("footer [data-sigil=\"reactions-bling-bar\"]");
|
||||
|
||||
private val injector: JsInjector =
|
||||
JsBuilder()
|
||||
.css("${items.joinToString(separator = ",")}{display:none !important}")
|
||||
.single("css-hider-$name")
|
||||
.build()
|
||||
|
||||
override fun inject(webView: WebView) {
|
||||
injector.inject(webView)
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection.assets
|
||||
|
||||
import android.webkit.WebView
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
import com.pitchedapps.frost.webview.injection.JsInjector
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-31.
|
||||
*
|
||||
* Collection of short js functions that are embedded directly
|
||||
*/
|
||||
enum class JsActions(body: String) : JsInjector {
|
||||
/**
|
||||
* Redirects to login activity if create account is found see
|
||||
* [com.pitchedapps.frost.web.FrostJSI.loadLogin]
|
||||
*/
|
||||
LOGIN_CHECK("document.getElementById('signup-button')&&Frost.loadLogin();"),
|
||||
BASE_HREF("""document.write("<base href='$FB_URL_BASE'/>");"""),
|
||||
FETCH_BODY(
|
||||
"""setTimeout(function(){var e=document.querySelector("main");e||(e=document.querySelector("body")),Frost.handleHtml(e.outerHTML)},1e2);""",
|
||||
),
|
||||
RETURN_BODY("return(document.getElementsByTagName('html')[0].innerHTML);"),
|
||||
CREATE_POST(clickBySelector("#MComposer [onclick]")),
|
||||
// CREATE_MSG(clickBySelector("a[rel=dialog]")),
|
||||
/** Used as a pseudoinjector for maybe functions */
|
||||
EMPTY("");
|
||||
|
||||
val function = "(function(){$body})();"
|
||||
|
||||
private val injector: JsInjector = JsInjector(function)
|
||||
|
||||
override fun inject(webView: WebView) {
|
||||
injector.inject(webView)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun clickBySelector(selector: String): String =
|
||||
"""document.querySelector("$selector").click()"""
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.webview.injection.assets
|
||||
|
||||
import android.content.Context
|
||||
import android.webkit.WebView
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.google.common.flogger.FluentLogger
|
||||
import com.pitchedapps.frost.webview.injection.JsBuilder
|
||||
import com.pitchedapps.frost.webview.injection.JsInjector
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-31. Mapping of the available assets The enum name must match the
|
||||
* js file name
|
||||
*/
|
||||
enum class JsAssets(private val singleLoad: Boolean = true) : JsInjector {
|
||||
CLICK_A,
|
||||
CONTEXT_A,
|
||||
MEDIA,
|
||||
HEADER_BADGES,
|
||||
TEXTAREA_LISTENER,
|
||||
NOTIF_MSG,
|
||||
DOCUMENT_WATCHER,
|
||||
HORIZONTAL_SCROLLING,
|
||||
AUTO_RESIZE_TEXTAREA(singleLoad = false),
|
||||
SCROLL_STOP,
|
||||
;
|
||||
|
||||
@VisibleForTesting internal val file = "${name.lowercase(Locale.CANADA)}.js"
|
||||
|
||||
private fun injectorBlocking(context: Context): JsInjector {
|
||||
return try {
|
||||
val content =
|
||||
context.assets.open("frostcore/js/$file").bufferedReader().use(BufferedReader::readText)
|
||||
JsBuilder().js(content).run { if (singleLoad) single(name) else this }.build()
|
||||
} catch (e: FileNotFoundException) {
|
||||
logger.atWarning().withCause(e).log("JsAssets file not found")
|
||||
JsInjector.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
private var injector: JsInjector = JsInjector.EMPTY
|
||||
|
||||
override fun inject(webView: WebView) {
|
||||
injector.inject(webView)
|
||||
}
|
||||
|
||||
private suspend fun load(context: Context) {
|
||||
withContext(Dispatchers.IO) { injector = injectorBlocking(context) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = FluentLogger.forEnclosingClass()
|
||||
|
||||
suspend fun load(context: Context) =
|
||||
withContext(Dispatchers.IO) { JsAssets.values().forEach { it.load(context) } }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user