diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt index 4b7ccab9d..216457092 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/FrostTheme.kt @@ -27,6 +27,7 @@ import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext /** Main Frost compose theme. */ @@ -34,13 +35,14 @@ import androidx.compose.ui.platform.LocalContext fun FrostTheme( isDarkTheme: Boolean = isSystemInDarkTheme(), isDynamicColor: Boolean = true, + transparent: Boolean = true, modifier: Modifier = Modifier, content: @Composable () -> Unit ) { val context = LocalContext.current val dynamicColor = isDynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val colorScheme = - remember(dynamicColor, isDarkTheme) { + remember(dynamicColor, isDarkTheme, transparent) { when { dynamicColor && isDarkTheme -> { dynamicDarkColorScheme(context) @@ -53,5 +55,11 @@ fun FrostTheme( } } - MaterialTheme(colorScheme = colorScheme) { Surface(modifier = modifier, content = content) } + MaterialTheme(colorScheme = colorScheme) { + Surface( + modifier = modifier, + color = if (transparent) Color.Transparent else MaterialTheme.colorScheme.surface, + content = content, + ) + } } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt index 4af7739d3..958b18862 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/compose/webview/FrostWebCompose.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.core.widget.NestedScrollView import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.ext.WebTargetId -import com.pitchedapps.frost.view.NestedWebView +import com.pitchedapps.frost.view.FrostWebView import com.pitchedapps.frost.web.state.FrostWebStore import com.pitchedapps.frost.web.state.TabAction import com.pitchedapps.frost.web.state.TabAction.ResponseAction.LoadUrlResponseAction @@ -126,7 +126,7 @@ class FrostWebCompose( AndroidView( factory = { context -> val childView = - NestedWebView(context) + FrostWebView(context) .apply { onCreated(this) @@ -150,9 +150,6 @@ class FrostWebCompose( } .also { webView = it } - // Workaround a crash on certain devices that expect WebView to be - // wrapped in a ViewGroup. - // b/243567497 val parentLayout = NestedScrollView(context) parentLayout.layoutParams = FrameLayout.LayoutParams( @@ -176,17 +173,3 @@ class FrostWebCompose( private val logger = FluentLogger.forEnclosingClass() } } - - -// override fun onReceivedError( -// view: WebView, -// request: WebResourceRequest?, -// error: WebResourceError? -// ) { -// super.onReceivedError(view, request, error) -// -// if (error != null) { -// state.errorsForCurrentRequest.add(WebViewError(request, error)) -// } -// } -// } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt index 4d371f36e..8247397f5 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt @@ -24,7 +24,7 @@ const val WWW_FACEBOOK_COM = "www.$FACEBOOK_COM" const val WWW_MESSENGER_COM = "www.$MESSENGER_COM" const val HTTPS_FACEBOOK_COM = "https://$WWW_FACEBOOK_COM" const val HTTPS_MESSENGER_COM = "https://$WWW_MESSENGER_COM" -const val FACEBOOK_BASE_COM = "m.$FACEBOOK_COM" +const val FACEBOOK_BASE_COM = "touch.$FACEBOOK_COM" const val FB_URL_BASE = "https://$FACEBOOK_BASE_COM/" const val FACEBOOK_MBASIC_COM = "mbasic.$FACEBOOK_COM" const val FB_URL_MBASIC_BASE = "https://$FACEBOOK_MBASIC_COM/" diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt index 3a3e5af1a..6df9f6cc7 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/hilt/FrostModule.kt @@ -24,7 +24,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Qualifier -import javax.inject.Singleton @Qualifier annotation class Frost @@ -54,7 +53,7 @@ object FrostModule { "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0" private const val USER_AGENT_WINDOWS_FROST = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Safari/537.36" + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36" - @Provides @Singleton @Frost fun userAgent(): String = USER_AGENT_WINDOWS_FROST + @Provides @Frost fun userAgent(): String = USER_AGENT_WINDOWS_FROST } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt index 23192ccfc..a64f6053d 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainActivity.kt @@ -21,6 +21,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.core.view.WindowCompat import com.google.common.flogger.FluentLogger +import com.pitchedapps.frost.R import com.pitchedapps.frost.compose.FrostTheme import com.pitchedapps.frost.web.state.FrostWebStore import dagger.hilt.android.AndroidEntryPoint @@ -38,6 +39,8 @@ class MainActivity : ComponentActivity() { @Inject lateinit var store: FrostWebStore override fun onCreate(savedInstanceState: Bundle?) { + // TODO make configurable + setTheme(R.style.FrostTheme_Transparent) super.onCreate(savedInstanceState) logger.atInfo().log("onCreate main activity") diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt index c9da5835c..b148c0c7b 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/main/MainScreenWebView.kt @@ -41,6 +41,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.lifecycle.viewmodel.compose.viewModel import com.pitchedapps.frost.compose.webview.FrostWebCompose import com.pitchedapps.frost.ext.WebTargetId @@ -59,6 +60,7 @@ fun MainScreenWebView(modifier: Modifier = Modifier, homeTabs: List Scaffold( modifier = modifier, + containerColor = Color.Transparent, topBar = { MainTopBar(modifier = modifier) }, bottomBar = { MainBottomBar( diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt new file mode 100644 index 000000000..d90b669ab --- /dev/null +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/FrostWebView.kt @@ -0,0 +1,54 @@ +/* + * 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 . + */ +package com.pitchedapps.frost.view + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import com.pitchedapps.frost.hilt.Frost +import dagger.hilt.android.AndroidEntryPoint +import java.util.Optional +import javax.inject.Inject + +@AndroidEntryPoint +class FrostWebView +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + NestedWebView(context, attrs, defStyleAttr) { + + @Inject @Frost lateinit var userAgent: Optional + + init { + userAgent.ifPresent { + settings.userAgentString = it + println("Set user agent to $it") + } + with(settings) { + // noinspection SetJavaScriptEnabled + javaScriptEnabled = true + mediaPlaybackRequiresUserGesture = false // TODO check if we need this + allowFileAccess = true + // textZoom + domStorageEnabled = true + + setLayerType(LAYER_TYPE_HARDWARE, null) + setBackgroundColor(Color.TRANSPARENT) + // Download listener + // JS Interface + } + } +} diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt index dbc955871..6d160ec5f 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/view/NestedWebView.kt @@ -30,7 +30,7 @@ import androidx.core.view.ViewCompat * * Webview extension that handles nested scrolls */ -class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : +open class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : WebView(context, attrs, defStyleAttr), NestedScrollingChild3 { // No JvmOverloads due to hilt @@ -109,21 +109,17 @@ class NestedWebView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled - override fun startNestedScroll(axes: Int, type: Int): Boolean { - TODO("not implemented") - } + override fun startNestedScroll(axes: Int, type: Int): Boolean = + childHelper.startNestedScroll(axes, type) override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes) - override fun stopNestedScroll(type: Int) { - TODO("not implemented") - } + override fun stopNestedScroll(type: Int) = childHelper.stopNestedScroll(type) override fun stopNestedScroll() = childHelper.stopNestedScroll() - override fun hasNestedScrollingParent(type: Int): Boolean { - TODO("not implemented") - } + override fun hasNestedScrollingParent(type: Int): Boolean = + childHelper.hasNestedScrollingParent(type) override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent() diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt index a9bb3bc88..0b89c7556 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/FrostWebViewClients.kt @@ -17,6 +17,7 @@ package com.pitchedapps.frost.webview import android.graphics.Bitmap +import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView @@ -26,6 +27,7 @@ import com.pitchedapps.frost.ext.WebTargetId import com.pitchedapps.frost.facebook.FACEBOOK_BASE_COM import com.pitchedapps.frost.facebook.WWW_FACEBOOK_COM import com.pitchedapps.frost.facebook.isExplicitIntent +import com.pitchedapps.frost.facebook.isFacebookUrl import com.pitchedapps.frost.web.FrostWebHelper import com.pitchedapps.frost.web.state.FrostWebStore import com.pitchedapps.frost.web.state.TabAction @@ -132,21 +134,23 @@ class FrostWebViewClient( // refresh.offer(true) } - // private fun injectBackgroundColor() { - // web?.setBackgroundColor( - // when { - // isMain -> Color.TRANSPARENT - // web.url.isFacebookUrl -> themeProvider.bgColor.withAlpha(255) - // else -> Color.WHITE - // } - // ) - // } + // private fun WebView.injectBackgroundColor(url: String?) { + // setBackgroundColor( + // when { + // isMain -> Color.TRANSPARENT + // url.isFacebookUrl -> themeProvider.bgColor.withAlpha(255) + // else -> Color.WHITE + // } + // ) + // } override fun onPageCommitVisible(view: WebView, url: String?) { super.onPageCommitVisible(view, url) - frostJsInjectors.injectOnPageCommitVisible(view, url) + + when { + url.isFacebookUrl -> frostJsInjectors.facebookInjectOnPageCommitVisible(view, url) + } } - // injectBackgroundColor() // when { // url.isFacebookUrl -> { // v { "FB Page commit visible" } @@ -251,6 +255,14 @@ class FrostWebViewClient( return super.shouldOverrideUrlLoading(view, request) } + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError? + ) { + super.onReceivedError(view, request, error) + } + companion object { private val logger = FluentLogger.forEnclosingClass() } diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt index 0af5774b8..c19f4ae39 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/FrostJsInjectors.kt @@ -20,6 +20,8 @@ import android.content.Context import android.webkit.WebView import com.google.common.flogger.FluentLogger import com.pitchedapps.frost.webview.injection.assets.JsActions +import com.pitchedapps.frost.webview.injection.assets.JsAssets +import com.pitchedapps.frost.webview.injection.assets.inject import dagger.hilt.android.qualifiers.ApplicationContext import java.io.BufferedReader import java.io.FileNotFoundException @@ -37,9 +39,9 @@ internal constructor( @Volatile private var theme: JsInjector = JsInjector.EMPTY - fun injectOnPageCommitVisible(view: WebView, url: String?) { + fun facebookInjectOnPageCommitVisible(view: WebView, url: String?) { logger.atInfo().log("inject page commit visible %b", theme != JsInjector.EMPTY) - theme.inject(view) + listOf(theme, JsAssets.CLICK_A).inject(view) } private fun getTheme(): JsInjector { @@ -49,7 +51,8 @@ internal constructor( .open("frost/css/facebook/themes/material_glass.css") .bufferedReader() .use(BufferedReader::readText) - JsBuilder().css(content).build() + logger.atInfo().log("css %s", content) + JsBuilder().css(content).single("material_glass").build() } catch (e: FileNotFoundException) { logger.atSevere().withCause(e).log("CssAssets file not found") JsActions.EMPTY diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt index 28211e26d..9fa410974 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/JsInjector.kt @@ -75,7 +75,6 @@ class JsBuilder { 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');") @@ -89,16 +88,18 @@ class JsBuilder { append(js) } } - var content = builder.append("}()").toString() + var content = builder.toString() if (tag != null) { content = singleInjector(tag, content) } - return content + return wrapAnonymous(content) } + private fun wrapAnonymous(body: String) = "(function(){$body})();" + private fun singleInjector(tag: String, content: String) = """ - if (!window.hasOwnProperty("$tag") { + if (!window.hasOwnProperty("$tag")) { console.log("Registering $tag"); window.$tag = true; $content diff --git a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt index a911bbbcc..a4ad2e432 100644 --- a/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt +++ b/app-compose/src/main/kotlin/com/pitchedapps/frost/webview/injection/assets/JsAssets.kt @@ -75,3 +75,7 @@ enum class JsAssets(private val singleLoad: Boolean = true) : JsInjector { withContext(Dispatchers.IO) { JsAssets.values().forEach { it.load(context) } } } } + +fun List.inject(webView: WebView) { + forEach { it.inject(webView) } +}