Browse Source

Video & Prefs improvements

Carles Sentis 10 months ago
parent
commit
756b8ced5a
22 changed files with 432 additions and 123 deletions
  1. 2 2
      app/build.gradle.kts
  2. BIN
      app/release/app-release.aab
  3. 0 1
      app/src/main/AndroidManifest.xml
  4. 19 0
      app/src/main/java/com/codeskraps/sbrowser/MainActivity.kt
  5. 40 25
      app/src/main/java/com/codeskraps/sbrowser/MediaWebViewPreferences.kt
  6. 54 10
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/SettingsViewModel.kt
  7. 13 7
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/CheckPreference.kt
  8. 46 22
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/ListPreference.kt
  9. 49 24
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/SettingsScreen.kt
  10. 12 3
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsEvent.kt
  11. 14 4
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsState.kt
  12. 1 0
      app/src/main/java/com/codeskraps/sbrowser/feature/video/VideoViewModel.kt
  13. 9 7
      app/src/main/java/com/codeskraps/sbrowser/feature/video/components/VideoScreen.kt
  14. 1 0
      app/src/main/java/com/codeskraps/sbrowser/feature/video/mvi/VideoEvent.kt
  15. 4 2
      app/src/main/java/com/codeskraps/sbrowser/feature/video/mvi/VideoState.kt
  16. 17 0
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/ClearCookies.kt
  17. 34 11
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebView.kt
  18. 35 2
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebViewClient.kt
  19. 30 0
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/TextSize.kt
  20. 29 0
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/UserAgent.kt
  21. 14 0
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/WebScriptInterface.kt
  22. 9 3
      app/src/main/java/com/codeskraps/sbrowser/util/Constants.kt

+ 2 - 2
app/build.gradle.kts

@@ -14,8 +14,8 @@ android {
         applicationId = "com.codeskraps.sbrowser_new"
         minSdk = 26
         targetSdk = 34
-        versionCode = 2
-        versionName = "3.0"
+        versionCode = 3
+        versionName = "3.1"
 
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
         vectorDrawables {

BIN
app/release/app-release.aab


+ 0 - 1
app/src/main/AndroidManifest.xml

@@ -20,7 +20,6 @@
         <activity
             android:name=".MainActivity"
             android:exported="true"
-            android:label="@string/app_name"
             android:theme="@style/Theme.SBrowser">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />

+ 19 - 0
app/src/main/java/com/codeskraps/sbrowser/MainActivity.kt

@@ -23,12 +23,22 @@ import com.codeskraps.sbrowser.feature.video.VideoViewModel
 import com.codeskraps.sbrowser.feature.video.components.VideoScreen
 import com.codeskraps.sbrowser.feature.webview.MediaWebViewModel
 import com.codeskraps.sbrowser.feature.webview.components.WebViewScreen
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.MediaWebView
 import com.codeskraps.sbrowser.navigation.Screen
 import com.codeskraps.sbrowser.ui.theme.SBrowserTheme
 import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
 
 @AndroidEntryPoint
 class MainActivity : ComponentActivity() {
+
+    @Inject
+    lateinit var mediaWebView: MediaWebView
+
+    @Inject
+    lateinit var mediaWebViewPreferences: MediaWebViewPreferences
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
@@ -101,4 +111,13 @@ class MainActivity : ComponentActivity() {
             }
         }
     }
+
+    override fun onDestroy() {
+        when (mediaWebViewPreferences.clearCookies) {
+            ClearCookies.OFF -> {}
+            ClearCookies.SESSION_COOKIES -> mediaWebView.cookieManager.removeSessionCookies {}
+            ClearCookies.ALL_COOKIES -> mediaWebView.cookieManager.removeAllCookies {}
+        }
+        super.onDestroy()
+    }
 }

+ 40 - 25
app/src/main/java/com/codeskraps/sbrowser/MediaWebViewPreferences.kt

@@ -1,7 +1,9 @@
 package com.codeskraps.sbrowser
 
 import android.content.Context
-import android.webkit.WebSettings.PluginState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.TextSize
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 import com.codeskraps.sbrowser.util.Constants
 import javax.inject.Inject
 
@@ -12,8 +14,12 @@ class MediaWebViewPreferences @Inject constructor(
         private const val PREF_FILE = "pref_file"
         private const val PREF_HOME = "pref_home"
         private const val PREF_JAVASCRIPT = "pref_javascript"
-        private const val PREF_PLUGINS = "pref_plugins"
-        private const val PREF_USER_AGENT = "pref_user_agent"
+        private const val PREF_TEXT_SIZE = "pref_text_size"
+        private const val PREF_USER_AGENT = "pref_user_agent_int"
+        private const val PREF_DOM_STORAGE = "pref_dom_storage"
+        private const val PREF_ACCEPT_COOKIES = "pref_accept_cookies"
+        private const val PREF_THIRD_PARTY_COOKIES = "pref_third_party_cookies"
+        private const val PREF_CLEAR_COOKIES = "pref_clear_coolies"
         private const val PREF_SHOW_URL = "pref_show_url"
     }
 
@@ -33,34 +39,43 @@ class MediaWebViewPreferences @Inject constructor(
             prefs.edit().putBoolean(PREF_JAVASCRIPT, value).apply()
         }
 
-    var plugins: PluginState
-        get() {
-            val default = when (Constants.plugins) {
-                PluginState.ON -> "Always on"
-                PluginState.ON_DEMAND -> "On demand"
-                else -> "Off"
-            }
+    var textSize: TextSize
+        get() = TextSize.parse(prefs.getInt(PREF_TEXT_SIZE, Constants.textSize.size))
+        set(value) {
+            prefs.edit().putInt(PREF_TEXT_SIZE, value.size).apply()
+        }
+
+    var userAgent: UserAgent
+        get() = UserAgent.entries[prefs.getInt(PREF_USER_AGENT, Constants.userAgent.ordinal)]
+        set(value) {
+            prefs.edit().putInt(PREF_USER_AGENT, value.ordinal).apply()
+        }
 
-            return when (prefs.getString(PREF_PLUGINS, default) ?: default) {
-                "Always on" -> PluginState.ON
-                "On demand" -> PluginState.ON_DEMAND
-                else -> PluginState.OFF
-            }
+    var domStorage: Boolean
+        get() = prefs.getBoolean(PREF_DOM_STORAGE, Constants.domStorage)
+        set(value) {
+            prefs.edit().putBoolean(PREF_DOM_STORAGE, value).apply()
         }
+
+    var acceptCookies: Boolean
+        get() = prefs.getBoolean(PREF_ACCEPT_COOKIES, Constants.acceptCookies)
+        set(value) {
+            prefs.edit().putBoolean(PREF_ACCEPT_COOKIES, value).apply()
+        }
+
+    var thirdPartyCookies: Boolean
+        get() = prefs.getBoolean(PREF_THIRD_PARTY_COOKIES, Constants.thirdPartyCookies)
         set(value) {
-            prefs.edit().putString(
-                PREF_PLUGINS, when (value) {
-                    PluginState.ON -> "Always on"
-                    PluginState.ON_DEMAND -> "On demand"
-                    else -> "Off"
-                }
-            ).apply()
+            prefs.edit().putBoolean(PREF_THIRD_PARTY_COOKIES, value).apply()
         }
 
-    var userAgent: String
-        get() = prefs.getString(PREF_USER_AGENT, Constants.userAgent) ?: Constants.userAgent
+    var clearCookies: ClearCookies
+        get() = ClearCookies.entries[prefs.getInt(
+            PREF_CLEAR_COOKIES,
+            Constants.clearCookies.ordinal
+        )]
         set(value) {
-            prefs.edit().putString(PREF_USER_AGENT, value).apply()
+            prefs.edit().putInt(PREF_CLEAR_COOKIES, value.ordinal).apply()
         }
 
     var showUrl: Boolean

+ 54 - 10
app/src/main/java/com/codeskraps/sbrowser/feature/settings/SettingsViewModel.kt

@@ -1,12 +1,16 @@
 package com.codeskraps.sbrowser.feature.settings
 
 import android.webkit.WebSettings.PluginState
+import android.webkit.WebView
 import androidx.lifecycle.viewModelScope
 import com.codeskraps.sbrowser.MediaWebViewPreferences
 import com.codeskraps.sbrowser.feature.settings.mvi.SettingsAction
 import com.codeskraps.sbrowser.feature.settings.mvi.SettingsEvent
 import com.codeskraps.sbrowser.feature.settings.mvi.SettingsState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
 import com.codeskraps.sbrowser.feature.webview.media.MediaWebView
+import com.codeskraps.sbrowser.feature.webview.media.TextSize
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 import com.codeskraps.sbrowser.util.StateReducerViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
@@ -28,8 +32,12 @@ class SettingsViewModel @Inject constructor(
                     SettingsState(
                         homeUrl = mediaWebViewPreferences.homeUrl,
                         javaScript = mediaWebViewPreferences.javaScript,
-                        plugins = mediaWebViewPreferences.plugins,
+                        textSize = mediaWebViewPreferences.textSize,
                         userAgent = mediaWebViewPreferences.userAgent,
+                        domStorage = mediaWebViewPreferences.domStorage,
+                        acceptCookies = mediaWebViewPreferences.acceptCookies,
+                        thirdPartyCookies = mediaWebViewPreferences.thirdPartyCookies,
+                        clearCookies = mediaWebViewPreferences.clearCookies,
                         showUrl = mediaWebViewPreferences.showUrl
                     )
                 )
@@ -42,8 +50,12 @@ class SettingsViewModel @Inject constructor(
             is SettingsEvent.Load -> event.state
             is SettingsEvent.Home -> onHome(currentState, event.url)
             is SettingsEvent.JavaScript -> onJavaScript(currentState, event.value)
-            is SettingsEvent.Plugins -> onPlugins(currentState, event.value)
+            is SettingsEvent.TextSize -> onTextSize(currentState, event.value)
             is SettingsEvent.UserAgent -> onUserAgent(currentState, event.value)
+            is SettingsEvent.DomStorage -> onDomStorage(currentState, event.value)
+            is SettingsEvent.AcceptCookies -> onAcceptCookies(currentState, event.value)
+            is SettingsEvent.ThirdPartyCookies -> onThirdPartyCookies(currentState, event.value)
+            is SettingsEvent.ClearCookies -> onClearCookies(currentState, event.value)
             is SettingsEvent.ShowUrl -> onShowUrl(currentState, event.value)
         }
     }
@@ -59,20 +71,52 @@ class SettingsViewModel @Inject constructor(
         return currentState.copy(javaScript = value)
     }
 
-    private fun onPlugins(currentState: SettingsState, value: PluginState): SettingsState {
-        mediaWebView.settings.pluginState = value
-        mediaWebViewPreferences.plugins = value
-        return currentState.copy(plugins = value)
+    private fun onTextSize(currentState: SettingsState, value: TextSize): SettingsState {
+        mediaWebView.settings.textZoom = value.size
+        mediaWebViewPreferences.textSize = value
+        return currentState.copy(textSize = value)
     }
 
-    private fun onUserAgent(currentState: SettingsState, value: String): SettingsState {
-        mediaWebView.settings.userAgentString?.let {
-            mediaWebView.settings.userAgentString = it.replace("Android", value)
-        }
+    private fun onUserAgent(currentState: SettingsState, value: UserAgent): SettingsState {
+        mediaWebView.settings.userAgentString = value.value
         mediaWebViewPreferences.userAgent = value
+        mediaWebView.reload()
         return currentState.copy(userAgent = value)
     }
 
+    private fun onDomStorage(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.settings.domStorageEnabled = value
+        mediaWebViewPreferences.domStorage = value
+        return currentState.copy(domStorage = value)
+    }
+
+    private fun onAcceptCookies(currentState: SettingsState, value: Boolean): SettingsState {
+        with(mediaWebView.cookieManager) {
+            setAcceptCookie(value)
+            setAcceptThirdPartyCookies(
+                mediaWebView.attachView as WebView,
+                currentState.thirdPartyCookies
+            )
+            if (!value) removeAllCookies {}
+        }
+        mediaWebViewPreferences.acceptCookies = value
+        return currentState.copy(acceptCookies = value)
+    }
+
+    private fun onThirdPartyCookies(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.cookieManager.setAcceptThirdPartyCookies(
+            mediaWebView.attachView as WebView,
+            value
+        )
+        mediaWebViewPreferences.thirdPartyCookies = value
+        return currentState.copy(thirdPartyCookies = value)
+    }
+
+    private fun onClearCookies(currentState: SettingsState, value: ClearCookies): SettingsState {
+        mediaWebViewPreferences.clearCookies = value
+        return currentState.copy(clearCookies = value)
+    }
+
     private fun onShowUrl(currentState: SettingsState, value: Boolean): SettingsState {
         mediaWebViewPreferences.showUrl = value
         return currentState.copy(showUrl = value)

+ 13 - 7
app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/CheckPreference.kt

@@ -1,6 +1,5 @@
 package com.codeskraps.sbrowser.feature.settings.components
 
-import android.graphics.Paint.Align
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -11,8 +10,10 @@ import androidx.compose.material3.Switch
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -23,10 +24,11 @@ import androidx.compose.ui.unit.sp
 fun CheckPreference(
     title: String,
     summary: String? = null,
-    value: Boolean,
+    isChecked: Boolean,
+    enabled: Boolean = true,
     onCheckedChange: ((Boolean) -> Unit)? = null
 ) {
-    var isChecked by remember { mutableStateOf(value) }
+
     Row(
         modifier = Modifier
             .fillMaxWidth()
@@ -36,7 +38,11 @@ fun CheckPreference(
             modifier = Modifier
                 .align(Alignment.CenterVertically)
         ) {
-            Text(text = title)
+            if (enabled) {
+                Text(text = title)
+            } else {
+                Text(text = title, color = MaterialTheme.colorScheme.secondaryContainer)
+            }
             if (!summary.isNullOrBlank()) {
                 Text(
                     color = MaterialTheme.colorScheme.onSecondaryContainer,
@@ -50,9 +56,9 @@ fun CheckPreference(
             modifier = Modifier
                 .align(Alignment.CenterVertically),
             checked = isChecked,
-            onCheckedChange = {
-                isChecked = it
-                onCheckedChange?.let { it(isChecked) }
+            enabled = enabled,
+            onCheckedChange = { newValue ->
+                onCheckedChange?.let { it(newValue) }
             }
         )
     }

+ 46 - 22
app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/ListPreference.kt

@@ -21,31 +21,52 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 
 @Composable
-fun ListPreference(
+fun <T> ListPreference(
     title: String,
-    summary: String,
-    items: Array<String>,
-    onChange: (String) -> Unit
+    summary: T,
+    items: Array<T>,
+    enabled: Boolean = true,
+    onChange: (T) -> Unit
 ) {
     var showDialog by remember { mutableStateOf(false) }
 
+    var modifier = Modifier
+        .fillMaxWidth()
+        .padding(start = 15.dp, top = 10.dp, end = 15.dp, bottom = 10.dp)
+    modifier = if (enabled) {
+        modifier.clickable { showDialog = true }
+    } else {
+        modifier
+    }
+
     Column(
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(start = 15.dp, top = 10.dp, end = 15.dp, bottom = 10.dp)
-            .clickable { showDialog = true }
+        modifier = modifier
     ) {
-        Text(text = title)
-        Text(
-            color = MaterialTheme.colorScheme.onSecondaryContainer,
-            fontSize = 14.sp,
-            text = summary
-        )
+        if (enabled) {
+            Text(text = title)
+            Text(
+                color = MaterialTheme.colorScheme.onSecondaryContainer,
+                fontSize = 14.sp,
+                text = summary.toString()
+            )
+        } else {
+            Text(text = title, color = MaterialTheme.colorScheme.secondaryContainer)
+            Text(
+                color = MaterialTheme.colorScheme.secondaryContainer,
+                fontSize = 14.sp,
+                text = summary.toString()
+            )
+        }
 
         if (showDialog) {
-            ListPreferenceEditDialog(value = summary, dialogTitle = title, items = items, onSave = {
-                onChange(it)
-            }) {
+            ListPreferenceEditDialog(
+                value = summary,
+                dialogTitle = title,
+                items = items,
+                onSave = {
+                    onChange(it)
+                }
+            ) {
                 showDialog = false
             }
         }
@@ -53,11 +74,11 @@ fun ListPreference(
 }
 
 @Composable
-private fun ListPreferenceEditDialog(
-    value: String,
+private fun <T> ListPreferenceEditDialog(
+    value: T,
     dialogTitle: String,
-    items: Array<String>,
-    onSave: (String) -> Unit,
+    items: Array<T>,
+    onSave: (T) -> Unit,
     onDismissRequest: () -> Unit
 ) {
     var preferenceValue by remember { mutableStateOf(value) }
@@ -85,7 +106,10 @@ private fun ListPreferenceEditDialog(
                         RadioButton(
                             selected = preferenceValue == it,
                             onClick = { preferenceValue = it })
-                        Text(text = it, modifier = Modifier.align(Alignment.CenterVertically))
+                        Text(
+                            text = it.toString(),
+                            modifier = Modifier.align(Alignment.CenterVertically)
+                        )
                     }
                 }
             }

+ 49 - 24
app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/SettingsScreen.kt

@@ -1,6 +1,5 @@
 package com.codeskraps.sbrowser.feature.settings.components
 
-import android.webkit.WebSettings.PluginState
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
@@ -13,6 +12,9 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import com.codeskraps.sbrowser.feature.settings.mvi.SettingsEvent
 import com.codeskraps.sbrowser.feature.settings.mvi.SettingsState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.TextSize
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -29,45 +31,68 @@ fun SettingsScreen(
         }
     ) { paddingValues ->
 
-        val plugins = when (state.plugins) {
-            PluginState.ON -> "Always on"
-            PluginState.ON_DEMAND -> "On demand"
-            else -> "Off"
-        }
-
         LazyColumn(modifier = Modifier.padding(paddingValues)) {
             item {
-                CategoryPreference(title = "Page content settings")
-                Preference(title = "Set home page", summary = state.homeUrl) { newValue ->
+                CategoryPreference(title = "Page Content Settings")
+                Preference(title = "Set Home Page", summary = state.homeUrl) { newValue ->
                     handleEvent(SettingsEvent.Home(newValue))
                 }
                 SpacerPreference()
-                CheckPreference(title = "Enable JavaScript", value = state.javaScript) { newValue ->
+                CheckPreference(
+                    title = "Enable JavaScript",
+                    isChecked = state.javaScript
+                ) { newValue ->
                     handleEvent(SettingsEvent.JavaScript(newValue))
                 }
                 SpacerPreference()
                 ListPreference(
-                    title = "Enable plug-ins",
-                    summary = plugins,
-                    items = arrayOf("Always on", "On demand", "Off")
+                    title = "Text Size",
+                    summary = state.textSize.ui,
+                    items = TextSize.displayArray()
+                ) { newValue ->
+                    handleEvent(SettingsEvent.TextSize(TextSize.parse(newValue)))
+                }
+                SpacerPreference()
+                ListPreference(
+                    title = "Set User Agent",
+                    summary = state.userAgent.ui,
+                    items = UserAgent.getDisplayArray()
+                ) { newValue ->
+                    handleEvent(SettingsEvent.UserAgent(UserAgent.parse(newValue)))
+                }
+                SpacerPreference()
+                CheckPreference(
+                    title = "Dom Storage",
+                    isChecked = state.domStorage
+                ) { newValue ->
+                    handleEvent(SettingsEvent.DomStorage(newValue))
+                }
+                CategoryPreference(title = "Cookies Manager")
+                CheckPreference(
+                    title = "Accept Cookies",
+                    isChecked = state.acceptCookies
+                ) { newValue ->
+                    handleEvent(SettingsEvent.AcceptCookies(newValue))
+                }
+                SpacerPreference()
+                CheckPreference(
+                    title = "Third Party Cookies",
+                    isChecked = state.thirdPartyCookies,
+                    enabled = state.acceptCookies
                 ) { newValue ->
-                    val userAgent: PluginState = when (newValue) {
-                        "Always on" -> PluginState.ON
-                        "On demand" -> PluginState.ON_DEMAND
-                        else -> PluginState.OFF
-                    }
-                    handleEvent(SettingsEvent.Plugins(userAgent))
+                    handleEvent(SettingsEvent.ThirdPartyCookies(newValue))
                 }
                 SpacerPreference()
                 ListPreference(
-                    title = "Set user agent",
-                    summary = state.userAgent,
-                    items = arrayOf("Default", "Firefox", "Chrome", "Ipad")
+                    title = "Clear Cookies",
+                    summary = state.clearCookies.ui,
+                    enabled = state.acceptCookies,
+                    items = ClearCookies.displayArray()
                 ) { newValue ->
-                    handleEvent(SettingsEvent.UserAgent(newValue))
+                    handleEvent(SettingsEvent.ClearCookies(ClearCookies.parse(newValue)))
                 }
                 CategoryPreference(title = "Notification")
-                CheckPreference(title = "Show Url", value = state.showUrl) { newValue ->
+                CheckPreference(title = "Show Url", isChecked = state.showUrl) { newValue ->
                     handleEvent(SettingsEvent.ShowUrl(newValue))
                 }
                 CategoryPreference(title = "Information")

+ 12 - 3
app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsEvent.kt

@@ -1,12 +1,21 @@
 package com.codeskraps.sbrowser.feature.settings.mvi
 
-import android.webkit.WebSettings.PluginState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 
 sealed interface SettingsEvent {
     data class Load(val state: SettingsState) : SettingsEvent
     data class Home(val url: String) : SettingsEvent
     data class JavaScript(val value: Boolean) : SettingsEvent
-    data class Plugins(val value: PluginState) : SettingsEvent
-    data class UserAgent(val value: String) : SettingsEvent
+    data class TextSize(val value: com.codeskraps.sbrowser.feature.webview.media.TextSize) :
+        SettingsEvent
+
+    data class UserAgent(val value: com.codeskraps.sbrowser.feature.webview.media.UserAgent) : SettingsEvent
+    data class DomStorage(val value: Boolean) : SettingsEvent
+    data class AcceptCookies(val value: Boolean) : SettingsEvent
+    data class ThirdPartyCookies(val value: Boolean) : SettingsEvent
+    data class ClearCookies(val value: com.codeskraps.sbrowser.feature.webview.media.ClearCookies) :
+        SettingsEvent
+
     data class ShowUrl(val value: Boolean) : SettingsEvent
 }

+ 14 - 4
app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsState.kt

@@ -1,21 +1,31 @@
 package com.codeskraps.sbrowser.feature.settings.mvi
 
-import android.webkit.WebSettings.PluginState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.TextSize
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 import com.codeskraps.sbrowser.util.Constants
 
 data class SettingsState(
     val homeUrl: String,
     val javaScript: Boolean,
-    val plugins: PluginState,
-    val userAgent: String,
+    val textSize: TextSize,
+    val userAgent: UserAgent,
+    val domStorage:Boolean,
+    val acceptCookies: Boolean,
+    val thirdPartyCookies: Boolean,
+    val clearCookies: ClearCookies,
     val showUrl: Boolean,
 ) {
     companion object {
         val initial = SettingsState(
             homeUrl = Constants.home,
             javaScript = Constants.javaScript,
-            plugins = Constants.plugins,
+            textSize = Constants.textSize,
             userAgent = Constants.userAgent,
+            domStorage = Constants.domStorage,
+            acceptCookies = Constants.acceptCookies,
+            thirdPartyCookies = Constants.thirdPartyCookies,
+            clearCookies = Constants.clearCookies,
             showUrl = Constants.showUrl
         )
     }

+ 1 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/video/VideoViewModel.kt

@@ -23,6 +23,7 @@ class VideoViewModel @Inject constructor(
     override fun reduceState(currentState: VideoState, event: VideoEvent): VideoState {
         return when (event) {
             is VideoEvent.Load -> onLoad(currentState, event.url)
+            is VideoEvent.Position -> currentState.copy(position = event.position)
         }
     }
 

+ 9 - 7
app/src/main/java/com/codeskraps/sbrowser/feature/video/components/VideoScreen.kt

@@ -8,6 +8,7 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.LifecycleResumeEffect
 import androidx.media3.common.MediaItem
 import androidx.media3.exoplayer.ExoPlayer
 import androidx.media3.ui.PlayerView
@@ -24,17 +25,15 @@ fun VideoScreen(
         val exoPlayer = remember {
             ExoPlayer.Builder(context).build().apply {
                 setMediaItem(MediaItem.fromUri(state.url))
+                playWhenReady = true
                 prepare()
             }
         }
 
-        val mediaSource = remember(state.url) {
-            MediaItem.fromUri(state.url)
-        }
-
-        LaunchedEffect(mediaSource) {
-            exoPlayer.setMediaItem(mediaSource)
-            exoPlayer.prepare()
+        LifecycleResumeEffect(exoPlayer) {
+            onPauseOrDispose {
+                handleEvent(VideoEvent.Position(exoPlayer.currentPosition))
+            }
         }
 
         DisposableEffect(Unit) {
@@ -49,6 +48,9 @@ fun VideoScreen(
                     player = exoPlayer
                 }
             },
+            update = { _ ->
+                exoPlayer.seekTo(state.position)
+            },
             modifier = Modifier.fillMaxSize()
         )
     }

+ 1 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/video/mvi/VideoEvent.kt

@@ -2,4 +2,5 @@ package com.codeskraps.sbrowser.feature.video.mvi
 
 sealed interface VideoEvent {
     data class Load(val url: String) : VideoEvent
+    data class Position(val position: Long) : VideoEvent
 }

+ 4 - 2
app/src/main/java/com/codeskraps/sbrowser/feature/video/mvi/VideoState.kt

@@ -1,11 +1,13 @@
 package com.codeskraps.sbrowser.feature.video.mvi
 
 data class VideoState(
-    val url: String
+    val url: String,
+    val position: Long
 ) {
     companion object {
         val initial = VideoState(
-            url = ""
+            url = "",
+            position = 0
         )
     }
 }

+ 17 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/ClearCookies.kt

@@ -0,0 +1,17 @@
+package com.codeskraps.sbrowser.feature.webview.media
+
+enum class ClearCookies(val ui: String) {
+    OFF("Off"),
+    SESSION_COOKIES("Session Cookies"),
+    ALL_COOKIES("All Cookies");
+
+    companion object {
+        fun displayArray() = arrayOf(OFF.ui, SESSION_COOKIES.ui, ALL_COOKIES.ui)
+
+        fun parse(name: String) = when (name) {
+            OFF.ui -> OFF
+            SESSION_COOKIES.ui -> SESSION_COOKIES
+            else -> ALL_COOKIES
+        }
+    }
+}

+ 34 - 11
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebView.kt

@@ -3,19 +3,18 @@ package com.codeskraps.sbrowser.feature.webview.media
 import android.annotation.SuppressLint
 import android.app.Application
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Canvas
+import android.os.Build
 import android.util.AttributeSet
 import android.view.View
 import android.view.ViewGroup
 import android.webkit.CookieManager
 import android.webkit.WebSettings
-import android.webkit.WebSettings.PluginState
 import android.webkit.WebView
 import com.codeskraps.sbrowser.MediaWebViewPreferences
 import com.codeskraps.sbrowser.feature.webview.mvi.MediaWebViewEvent
-import com.codeskraps.sbrowser.util.Constants
-import dagger.hilt.android.qualifiers.ApplicationContext
 import java.io.ByteArrayOutputStream
 import javax.inject.Inject
 
@@ -41,6 +40,8 @@ class MediaWebView @Inject constructor(
         get() = webView.title
     val settings: WebSettings
         get() = webView.settings
+    val cookieManager: CookieManager
+        get() = CookieManager.getInstance()
 
     fun iniLoad() {
         if (!initLoad) {
@@ -56,6 +57,7 @@ class MediaWebView @Inject constructor(
     fun setHandleListener(handleEvent: ((MediaWebViewEvent) -> Unit)?) {
         (webView.webChromeClient as MediaWebChromeClient).handleEvent = handleEvent
         (webView.webViewClient as MediaWebViewClient).handleEvent = handleEvent
+        webView.javascriptInterface.handleEvent = handleEvent
     }
 
     fun detachView() {
@@ -119,6 +121,8 @@ class MediaWebView @Inject constructor(
             }
         }
 
+        val javascriptInterface = WebScriptInterface()
+
         init {
             with(settings) {
                 loadsImagesAutomatically = true
@@ -127,22 +131,41 @@ class MediaWebView @Inject constructor(
                 loadWithOverviewMode = true
                 useWideViewPort = true
                 builtInZoomControls = true
-                //displayZoomControls = true
+                displayZoomControls = false
                 mediaPlaybackRequiresUserGesture = false
-                pluginState = mediaWebViewPreferences.plugins
-                allowFileAccess = true
+                allowFileAccess = false
+                textZoom = mediaWebViewPreferences.textSize.size
+
+
+
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                    isAlgorithmicDarkeningAllowed = isDarkMode(context)
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                    forceDark =
+                        if (isDarkMode(context)) WebSettings.FORCE_DARK_ON else WebSettings.FORCE_DARK_AUTO
+                }
 
                 setInitialScale(200)
                 setSupportZoom(true)
                 setNetworkAvailable(true)
 
-                if (mediaWebViewPreferences.userAgent != Constants.userAgent) {
-                    userAgentString =
-                        userAgentString.replace("Android", mediaWebViewPreferences.userAgent)
-                }
+                userAgentString = mediaWebViewPreferences.userAgent.value
             }
 
-            CookieManager.getInstance().setAcceptThirdPartyCookies(this@InternalWebView, true)
+            //webView.addJavascriptInterface(WebScriptInterface(), "App")
+
+            with(CookieManager.getInstance()) {
+                setAcceptCookie(mediaWebViewPreferences.acceptCookies)
+                setAcceptThirdPartyCookies(
+                    this@InternalWebView,
+                    mediaWebViewPreferences.thirdPartyCookies
+                )
+            }
         }
     }
+
+    companion object {
+        private fun isDarkMode(context: Context): Boolean =
+            context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+    }
 }

+ 35 - 2
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebViewClient.kt

@@ -1,6 +1,7 @@
 package com.codeskraps.sbrowser.feature.webview.media
 
 import android.graphics.Bitmap
+import android.util.Base64
 import android.webkit.MimeTypeMap
 import android.webkit.WebResourceRequest
 import android.webkit.WebView
@@ -9,6 +10,7 @@ import com.codeskraps.sbrowser.feature.webview.mvi.MediaWebViewEvent
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import java.io.InputStream
 import java.util.Locale
 
 
@@ -26,11 +28,42 @@ class MediaWebViewClient : WebViewClient() {
     override fun onPageFinished(view: WebView?, url: String?) {
         super.onPageFinished(view, url)
         handleEvent?.let { it(MediaWebViewEvent.Loading(false)) }
+
+        //loadScript(view)
+    }
+
+    private fun loadScript(view: WebView?) {
+        view?.context?.assets?.let {
+            runCatching {
+                val inputStream: InputStream = it.open(
+                    "\$(\"video\").on(\"play\", function() { App.onVideoStart() });"
+                )
+                val buffer = ByteArray(inputStream.available())
+                inputStream.read(buffer)
+                inputStream.close()
+
+                val encoded = Base64.encodeToString(buffer, Base64.NO_WRAP)
+
+                view.loadUrl(
+                    "javascript:(function() {" +
+                            "var parent = document.getElementsByTagName('head').item(0);" +
+                            "var script = document.createElement('script');" +
+                            "script.type = 'text/javascript';" +
+                            "script.innerHTML = window.atob('$encoded');" +
+                            "parent.appendChild(script)" +
+                            "})()"
+                )
+            }
+        }
     }
 
-    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
+    override fun shouldOverrideUrlLoading(
+        view: WebView?,
+        request: WebResourceRequest?
+    ): Boolean {
         val url = request?.url.toString()
-        val fileExtension = MimeTypeMap.getFileExtensionFromUrl(url).lowercase(Locale.getDefault())
+        val fileExtension =
+            MimeTypeMap.getFileExtensionFromUrl(url).lowercase(Locale.getDefault())
 
         if ("mp4" == fileExtension || "3gp" == fileExtension) {
             handleEvent?.let { it(MediaWebViewEvent.VideoPlayer(url)) }

+ 30 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/TextSize.kt

@@ -0,0 +1,30 @@
+package com.codeskraps.sbrowser.feature.webview.media
+
+enum class TextSize(val size: Int, val ui: String) {
+    Tiny(50, "Tiny"),
+    Small(75, "Small"),
+    Normal(100, "Normal"),
+    Large(150, "Large"),
+    Huge(200, "Huge");
+
+    companion object {
+
+        fun displayArray() = arrayOf(Tiny.ui, Small.ui, Normal.ui, Large.ui, Huge.ui)
+
+        fun parse(size: Int) = when (size) {
+            Tiny.size -> Tiny
+            Small.size -> Small
+            Large.size -> Large
+            Huge.size -> Huge
+            else -> Normal
+        }
+
+        fun parse(name: String) = when (name) {
+            Tiny.ui -> Tiny
+            Small.ui -> Small
+            Large.ui -> Large
+            Huge.ui -> Huge
+            else -> Normal
+        }
+    }
+}

+ 29 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/UserAgent.kt

@@ -0,0 +1,29 @@
+package com.codeskraps.sbrowser.feature.webview.media
+
+enum class UserAgent(val ui: String, val value: String) {
+    Default("Default", ""),
+    Chrome_Win10(
+        "Chrome on Windows 10",
+        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
+    ),
+    Chrome_IPad(
+        "Chrome on iPad",
+        "Mozilla/5.0 (iPad; CPU OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/121.0.6167.66 Mobile/15E148 Safari/604.1"
+    ),
+    Chrome_Android(
+        "Chrome on Android",
+        "Mozilla/5.0 (iPad; CPU OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/121.0.6167.66 Mobile/15E148 Safari/604.1"
+    );
+
+    companion object {
+        fun getDisplayArray() =
+            arrayOf(Default.ui, Chrome_Win10.ui, Chrome_IPad.ui, Chrome_Android.ui)
+
+        fun parse(name: String) = when(name){
+            Chrome_Win10.ui -> Chrome_Win10
+            Chrome_IPad.ui -> Chrome_IPad
+            Chrome_Android.ui -> Chrome_Android
+            else -> Default
+        }
+    }
+}

+ 14 - 0
app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/WebScriptInterface.kt

@@ -0,0 +1,14 @@
+package com.codeskraps.sbrowser.feature.webview.media
+
+import android.webkit.JavascriptInterface
+import com.codeskraps.sbrowser.feature.webview.mvi.MediaWebViewEvent
+
+class WebScriptInterface {
+
+    var handleEvent: ((MediaWebViewEvent) -> Unit)? = null
+
+    @JavascriptInterface
+    fun onVideoStart() {
+        handleEvent?.let { it(MediaWebViewEvent.VideoPlayer("")) }
+    }
+}

+ 9 - 3
app/src/main/java/com/codeskraps/sbrowser/util/Constants.kt

@@ -1,12 +1,18 @@
 package com.codeskraps.sbrowser.util
 
-import android.webkit.WebSettings.PluginState
+import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.TextSize
+import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 
 object Constants {
     const val home = "https://www.google.com/"
     const val javaScript = true
-    val plugins = PluginState.ON
-    const val userAgent = "Default"
+    val textSize = TextSize.Normal
+    val userAgent = UserAgent.Default
+    const val domStorage = false
+    const val acceptCookies = false
+    const val thirdPartyCookies = false
+    val clearCookies = ClearCookies.ALL_COOKIES
     const val showUrl = false
     const val inputExtra = "inputExtra"
 }