Browse Source

Enhance settings and media handling features

- Added JitPack repository to settings.gradle.kts for dependency management.
- Updated Java and Kotlin compatibility to version 19 in build.gradle.kts.
- Introduced new preferences in MediaWebViewPreferences for hardware acceleration, cache mode, and adblocker settings.
- Refactored ForegroundService and MainActivity to use updated constants and improve intent handling.
- Enhanced SettingsViewModel and SettingsScreen to support new settings and events for media playback and adblocker features.
- Updated WebView and MediaWebViewClient to integrate adblocker functionality and improve request handling.
- Improved UI components in SettingsScreen and BookmarksScreen for better navigation and user experience.
- Updated various drawable resources and theme settings for a more cohesive design.
codeskraps 4 days ago
parent
commit
dfd572b29a
43 changed files with 994 additions and 282 deletions
  1. 8 4
      app/build.gradle.kts
  2. BIN
      app/src/main/ic_launcher-playstore.png
  3. 3 3
      app/src/main/java/com/codeskraps/sbrowser/ForegroundService.kt
  4. 13 6
      app/src/main/java/com/codeskraps/sbrowser/MainActivity.kt
  5. 149 21
      app/src/main/java/com/codeskraps/sbrowser/MediaWebViewPreferences.kt
  6. 1 0
      app/src/main/java/com/codeskraps/sbrowser/di/AppModule.kt
  7. 13 1
      app/src/main/java/com/codeskraps/sbrowser/feature/bookmarks/presentation/components/BookmarksScreen.kt
  8. 183 11
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/SettingsViewModel.kt
  9. 221 79
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/components/SettingsScreen.kt
  10. 1 1
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsAction.kt
  11. 23 8
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsEvent.kt
  12. 29 21
      app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsState.kt
  13. 1 4
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewButtons.kt
  14. 2 2
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewProgressIndicator.kt
  15. 24 4
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewScreen.kt
  16. 0 17
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/ClearCookies.kt
  17. 31 21
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebView.kt
  18. 41 6
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/MediaWebViewClient.kt
  19. 0 30
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/TextSize.kt
  20. 0 29
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/UserAgent.kt
  21. 212 0
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/media/WebSettings.kt
  22. 1 1
      app/src/main/java/com/codeskraps/sbrowser/feature/webview/mvi/MediaWebViewState.kt
  23. 3 1
      app/src/main/java/com/codeskraps/sbrowser/ui/theme/Theme.kt
  24. 28 10
      app/src/main/java/com/codeskraps/sbrowser/util/Constants.kt
  25. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.webp
  26. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
  27. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  28. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.webp
  29. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
  30. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  31. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  32. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
  33. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  34. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  35. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
  36. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  37. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  38. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
  39. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  40. 1 1
      app/src/main/res/values/ic_launcher_background.xml
  41. 1 1
      app/src/main/res/values/themes.xml
  42. 4 0
      gradle/libs.versions.toml
  43. 1 0
      settings.gradle.kts

+ 8 - 4
app/build.gradle.kts

@@ -44,17 +44,18 @@ android {
         }
     }
     compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_17
-        targetCompatibility = JavaVersion.VERSION_17
+        sourceCompatibility = JavaVersion.VERSION_19
+        targetCompatibility = JavaVersion.VERSION_19
     }
     kotlinOptions {
-        jvmTarget = "17"
+        jvmTarget = JavaVersion.VERSION_19.toString()
     }
     buildFeatures {
         compose = true
+        buildConfig = true
     }
     composeOptions {
-        kotlinCompilerExtensionVersion = "1.5.7"
+        kotlinCompilerExtensionVersion = "1.5.15"
     }
     packaging {
         resources {
@@ -76,10 +77,12 @@ dependencies {
     implementation(libs.material3)
     implementation(libs.webkit)
     implementation(libs.androidx.core.splashscreen)
+    //implementation(libs.)
 
     //Dagger - Hilt
     implementation(libs.hilt.android)
     implementation(libs.hilt.navigation.compose)
+    implementation(libs.androidx.appcompat)
     ksp(libs.hilt.android.compiler)
 
     // Room
@@ -89,6 +92,7 @@ dependencies {
     ksp(libs.room.compiler)
 
     implementation(libs.jsoup)
+    implementation(libs.webviewadblock.library)
 
     implementation(libs.media3.exoplayer)
     implementation(libs.media3.ui)

BIN
app/src/main/ic_launcher-playstore.png


+ 3 - 3
app/src/main/java/com/codeskraps/sbrowser/ForegroundService.kt

@@ -28,7 +28,7 @@ class ForegroundService : Service() {
 
         fun createIntent(context: Context, url: String? = null): Intent =
             Intent(context, ForegroundService::class.java).apply {
-                url?.let { putExtra(Constants.inputExtra, it) }
+                url?.let { putExtra(Constants.INPUT_EXTRAS, it) }
             }
     }
 
@@ -63,7 +63,7 @@ class ForegroundService : Service() {
                 return START_NOT_STICKY
 
             } else if (it.getBoolean(HOME_EXTRA, false)) {
-                mediaWebView.loadUrl(Constants.home)
+                mediaWebView.loadUrl(Constants.HOME)
                 return START_NOT_STICKY
 
             } else if (it.getBoolean(REFRESH_EXTRA, false)) {
@@ -71,7 +71,7 @@ class ForegroundService : Service() {
                 return START_NOT_STICKY
 
             } else {
-                url = it.getString(Constants.inputExtra) ?: url
+                url = it.getString(Constants.INPUT_EXTRAS) ?: url
             }
         }
 

+ 13 - 6
app/src/main/java/com/codeskraps/sbrowser/MainActivity.kt

@@ -7,6 +7,7 @@ import android.view.View
 import android.view.ViewTreeObserver
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
 import androidx.activity.viewModels
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material3.MaterialTheme
@@ -52,7 +53,7 @@ class MainActivity : ComponentActivity() {
     private val splashViewModel: SplashViewModel by viewModels()
 
     override fun onCreate(savedInstanceState: Bundle?) {
-        val splashScreen = installSplashScreen()
+        installSplashScreen()
         super.onCreate(savedInstanceState)
 
         // Set up the OnPreDrawListener to keep the splashscreen on-screen
@@ -120,7 +121,10 @@ class MainActivity : ComponentActivity() {
                             BookmarksScreen(
                                 state = state,
                                 handleEvent = viewModel.state::handleEvent,
-                                action = viewModel.action
+                                action = viewModel.action,
+                                onBack = {
+                                    navController.navigateUp()
+                                }
                             ) { route ->
                                 navController.navigate(route)
                             }
@@ -133,7 +137,10 @@ class MainActivity : ComponentActivity() {
 
                             SettingsScreen(
                                 state = state,
-                                handleEvent = viewModel.state::handleEvent
+                                handleEvent = viewModel.state::handleEvent,
+                                onBack = {
+                                    navController.navigateUp()
+                                }
                             )
                         }
                         composable(
@@ -162,9 +169,9 @@ class MainActivity : ComponentActivity() {
 
     override fun onDestroy() {
         when (mediaWebViewPreferences.clearCookies) {
-            ClearCookies.OFF -> {}
-            ClearCookies.SESSION_COOKIES -> mediaWebView.cookieManager.removeSessionCookies {}
-            ClearCookies.ALL_COOKIES -> mediaWebView.cookieManager.removeAllCookies {}
+            ClearCookies.Off -> {}
+            ClearCookies.SessionCookies -> mediaWebView.cookieManager.removeSessionCookies {}
+            ClearCookies.AllCookies -> mediaWebView.cookieManager.removeAllCookies {}
         }
         super.onDestroy()
     }

+ 149 - 21
app/src/main/java/com/codeskraps/sbrowser/MediaWebViewPreferences.kt

@@ -1,7 +1,11 @@
 package com.codeskraps.sbrowser
 
 import android.content.Context
+import androidx.core.content.edit
+import com.codeskraps.sbrowser.feature.webview.media.CacheMode
 import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 import com.codeskraps.sbrowser.feature.webview.media.TextSize
 import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 import com.codeskraps.sbrowser.util.Constants
@@ -21,6 +25,25 @@ class MediaWebViewPreferences @Inject constructor(
         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"
+
+        // New preferences
+        private const val PREF_HARDWARE_ACCELERATION = "pref_hardware_acceleration"
+        private const val PREF_CACHE_MODE = "pref_cache_mode"
+        private const val PREF_SMOOTH_SCROLLING = "pref_smooth_scrolling"
+        private const val PREF_RENDER_PRIORITY = "pref_render_priority"
+        private const val PREF_MIXED_CONTENT = "pref_mixed_content"
+        private const val PREF_SAFE_BROWSING = "pref_safe_browsing"
+        private const val PREF_FORCE_DARK = "pref_force_dark"
+        private const val PREF_ALGORITHMIC_DARKENING = "pref_algorithmic_darkening"
+        private const val PREF_MEDIA_GESTURE = "pref_media_gesture"
+        private const val PREF_BLOCK_IMAGES = "pref_block_images"
+        private const val PREF_BLOCK_LOADS = "pref_block_loads"
+        private const val PREF_ALLOW_FILE_ACCESS = "pref_allow_file_access"
+
+        // Adblocker preferences
+        private const val PREF_ADBLOCKER_ENABLED = "pref_adblocker_enabled"
+        private const val PREF_ADBLOCKER_STRICT = "pref_adblocker_strict"
+        private const val PREF_ADBLOCKER_WHITELIST = "pref_adblocker_whitelist"
     }
 
     private val prefs by lazy {
@@ -28,59 +51,164 @@ class MediaWebViewPreferences @Inject constructor(
     }
 
     var homeUrl: String
-        get() = prefs.getString(PREF_HOME, Constants.home) ?: Constants.home
+        get() = prefs.getString(PREF_HOME, Constants.HOME) ?: Constants.HOME
         set(value) {
-            prefs.edit().putString(PREF_HOME, value).apply()
+            prefs.edit { putString(PREF_HOME, value) }
         }
 
     var javaScript: Boolean
-        get() = prefs.getBoolean(PREF_JAVASCRIPT, Constants.javaScript)
+        get() = prefs.getBoolean(PREF_JAVASCRIPT, Constants.JAVA_SCRIPT)
         set(value) {
-            prefs.edit().putBoolean(PREF_JAVASCRIPT, value).apply()
+            prefs.edit { putBoolean(PREF_JAVASCRIPT, value) }
         }
 
     var textSize: TextSize
-        get() = TextSize.parse(prefs.getInt(PREF_TEXT_SIZE, Constants.textSize.size))
+        get() = TextSize.fromValue(prefs.getInt(PREF_TEXT_SIZE, Constants.TEXT_SIZE.value))
         set(value) {
-            prefs.edit().putInt(PREF_TEXT_SIZE, value.size).apply()
+            prefs.edit { putInt(PREF_TEXT_SIZE, value.value) }
         }
 
     var userAgent: UserAgent
-        get() = UserAgent.entries[prefs.getInt(PREF_USER_AGENT, Constants.userAgent.ordinal)]
+        get() = UserAgent.fromValue(prefs.getInt(PREF_USER_AGENT, Constants.USER_AGENT.value))
         set(value) {
-            prefs.edit().putInt(PREF_USER_AGENT, value.ordinal).apply()
+            prefs.edit { putInt(PREF_USER_AGENT, value.value) }
         }
 
     var domStorage: Boolean
-        get() = prefs.getBoolean(PREF_DOM_STORAGE, Constants.domStorage)
+        get() = prefs.getBoolean(PREF_DOM_STORAGE, Constants.DOM_STORAGE)
         set(value) {
-            prefs.edit().putBoolean(PREF_DOM_STORAGE, value).apply()
+            prefs.edit { putBoolean(PREF_DOM_STORAGE, value) }
         }
 
     var acceptCookies: Boolean
-        get() = prefs.getBoolean(PREF_ACCEPT_COOKIES, Constants.acceptCookies)
+        get() = prefs.getBoolean(PREF_ACCEPT_COOKIES, Constants.ACCEPT_COOKIES)
         set(value) {
-            prefs.edit().putBoolean(PREF_ACCEPT_COOKIES, value).apply()
+            prefs.edit { putBoolean(PREF_ACCEPT_COOKIES, value) }
         }
 
     var thirdPartyCookies: Boolean
-        get() = prefs.getBoolean(PREF_THIRD_PARTY_COOKIES, Constants.thirdPartyCookies)
+        get() = prefs.getBoolean(PREF_THIRD_PARTY_COOKIES, Constants.THIRD_PARTY_COOKIES)
         set(value) {
-            prefs.edit().putBoolean(PREF_THIRD_PARTY_COOKIES, value).apply()
+            prefs.edit { putBoolean(PREF_THIRD_PARTY_COOKIES, value) }
         }
 
     var clearCookies: ClearCookies
-        get() = ClearCookies.entries[prefs.getInt(
-            PREF_CLEAR_COOKIES,
-            Constants.clearCookies.ordinal
-        )]
+        get() = ClearCookies.fromValue(
+            prefs.getInt(
+                PREF_CLEAR_COOKIES,
+                Constants.CLEAR_COOKIES.value
+            )
+        )
         set(value) {
-            prefs.edit().putInt(PREF_CLEAR_COOKIES, value.ordinal).apply()
+            prefs.edit { putInt(PREF_CLEAR_COOKIES, value.value) }
         }
 
     var showUrl: Boolean
-        get() = prefs.getBoolean(PREF_SHOW_URL, Constants.showUrl)
+        get() = prefs.getBoolean(PREF_SHOW_URL, Constants.SHOW_URL)
+        set(value) {
+            prefs.edit { putBoolean(PREF_SHOW_URL, value) }
+        }
+
+    // New preferences
+    var hardwareAcceleration: Boolean
+        get() = prefs.getBoolean(PREF_HARDWARE_ACCELERATION, Constants.HARDWARE_ACCELERATION)
+        set(value) {
+            prefs.edit { putBoolean(PREF_HARDWARE_ACCELERATION, value) }
+        }
+
+    var cacheMode: CacheMode
+        get() = CacheMode.fromValue(prefs.getInt(PREF_CACHE_MODE, Constants.CACHE_MODE.value))
+        set(value) {
+            prefs.edit { putInt(PREF_CACHE_MODE, value.value) }
+        }
+
+    var smoothScrolling: Boolean
+        get() = prefs.getBoolean(PREF_SMOOTH_SCROLLING, Constants.SMOOTH_SCROLLING)
+        set(value) {
+            prefs.edit { putBoolean(PREF_SMOOTH_SCROLLING, value) }
+        }
+
+    var renderPriority: RenderPriority
+        get() = RenderPriority.fromValue(
+            prefs.getInt(
+                PREF_RENDER_PRIORITY,
+                Constants.RENDER_PRIORITY.value
+            )
+        )
+        set(value) {
+            prefs.edit { putInt(PREF_RENDER_PRIORITY, value.value) }
+        }
+
+    var mixedContentMode: MixedContentMode
+        get() = MixedContentMode.fromValue(
+            prefs.getInt(
+                PREF_MIXED_CONTENT,
+                Constants.MIXED_CONTENT.value
+            )
+        )
+        set(value) {
+            prefs.edit { putInt(PREF_MIXED_CONTENT, value.value) }
+        }
+
+    var safeBrowsing: Boolean
+        get() = prefs.getBoolean(PREF_SAFE_BROWSING, Constants.SAFE_BROWSING)
+        set(value) {
+            prefs.edit { putBoolean(PREF_SAFE_BROWSING, value) }
+        }
+
+    var forceDark: Boolean
+        get() = prefs.getBoolean(PREF_FORCE_DARK, Constants.FORCE_DARK)
+        set(value) {
+            prefs.edit { putBoolean(PREF_FORCE_DARK, value) }
+        }
+
+    var algorithmicDarkening: Boolean
+        get() = prefs.getBoolean(PREF_ALGORITHMIC_DARKENING, Constants.ALGORITHMIC_DARKENING)
+        set(value) {
+            prefs.edit { putBoolean(PREF_ALGORITHMIC_DARKENING, value) }
+        }
+
+    var mediaPlaybackRequiresGesture: Boolean
+        get() = prefs.getBoolean(PREF_MEDIA_GESTURE, Constants.MEDIA_PLAYBACK_REQUIRES_GESTURE)
+        set(value) {
+            prefs.edit { putBoolean(PREF_MEDIA_GESTURE, value) }
+        }
+
+    var blockNetworkImages: Boolean
+        get() = prefs.getBoolean(PREF_BLOCK_IMAGES, Constants.BLOCK_NETWORK_IMAGES)
+        set(value) {
+            prefs.edit { putBoolean(PREF_BLOCK_IMAGES, value) }
+        }
+
+    var blockNetworkLoads: Boolean
+        get() = prefs.getBoolean(PREF_BLOCK_LOADS, Constants.BLOCK_NETWORK_LOADS)
+        set(value) {
+            prefs.edit { putBoolean(PREF_BLOCK_LOADS, value) }
+        }
+
+    var allowFileAccess: Boolean
+        get() = prefs.getBoolean(PREF_ALLOW_FILE_ACCESS, Constants.ALLOW_FILE_ACCESS)
+        set(value) {
+            prefs.edit { putBoolean(PREF_ALLOW_FILE_ACCESS, value) }
+        }
+
+    // Adblocker preferences
+    var adblockerEnabled: Boolean
+        get() = prefs.getBoolean(PREF_ADBLOCKER_ENABLED, Constants.AD_BLOCKER_ENABLED)
+        set(value) {
+            prefs.edit { putBoolean(PREF_ADBLOCKER_ENABLED, value) }
+        }
+
+    var adblockerStrict: Boolean
+        get() = prefs.getBoolean(PREF_ADBLOCKER_STRICT, Constants.AD_BLOCKER_STRICT)
+        set(value) {
+            prefs.edit { putBoolean(PREF_ADBLOCKER_STRICT, value) }
+        }
+
+    var adblockerWhitelist: String
+        get() = prefs.getString(PREF_ADBLOCKER_WHITELIST, Constants.AD_BLOCKER_WHITELIST)
+            ?: Constants.AD_BLOCKER_WHITELIST
         set(value) {
-            prefs.edit().putBoolean(PREF_SHOW_URL, value).apply()
+            prefs.edit { putString(PREF_ADBLOCKER_WHITELIST, value) }
         }
 }

+ 1 - 0
app/src/main/java/com/codeskraps/sbrowser/di/AppModule.kt

@@ -4,6 +4,7 @@ import android.app.Application
 import com.codeskraps.sbrowser.MediaWebViewPreferences
 import com.codeskraps.sbrowser.feature.webview.media.MediaWebView
 import com.codeskraps.sbrowser.util.BackgroundStatus
+import com.monstertechno.adblocker.AdBlockerWebView
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 13 - 1
app/src/main/java/com/codeskraps/sbrowser/feature/bookmarks/presentation/components/BookmarksScreen.kt

@@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 import androidx.compose.foundation.lazy.grid.items
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -38,6 +39,7 @@ fun BookmarksScreen(
     state: BookmarkState,
     handleEvent: (BookmarkEvent) -> Unit,
     action: Flow<BookmarkAction>,
+    onBack: () -> Unit,
     navRoute: (String) -> Unit
 ) {
     val context = LocalContext.current
@@ -52,7 +54,17 @@ fun BookmarksScreen(
         modifier = Modifier.fillMaxSize(),
         containerColor = MaterialTheme.colorScheme.secondary,
         topBar = {
-            TopAppBar(title = { Text(text = "Bookmarks") })
+            TopAppBar(
+                title = { Text(text = "Bookmarks") },
+                navigationIcon = {
+                    IconButton(onClick = onBack) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Back",
+                        )
+                    }
+                }
+            )
         }
     ) { paddingValues ->
         if (state.isLoading) {

+ 183 - 11
app/src/main/java/com/codeskraps/sbrowser/feature/settings/SettingsViewModel.kt

@@ -6,8 +6,11 @@ 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.CacheMode
 import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
 import com.codeskraps.sbrowser.feature.webview.media.MediaWebView
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 import com.codeskraps.sbrowser.feature.webview.media.TextSize
 import com.codeskraps.sbrowser.feature.webview.media.UserAgent
 import com.codeskraps.sbrowser.umami.domain.AnalyticsRepository
@@ -37,11 +40,23 @@ class SettingsViewModel @Inject constructor(
                         acceptCookies = mediaWebViewPreferences.acceptCookies,
                         thirdPartyCookies = mediaWebViewPreferences.thirdPartyCookies,
                         clearCookies = mediaWebViewPreferences.clearCookies,
-                        showUrl = mediaWebViewPreferences.showUrl
+                        showUrl = mediaWebViewPreferences.showUrl,
+                        hardwareAcceleration = mediaWebViewPreferences.hardwareAcceleration,
+                        cacheMode = mediaWebViewPreferences.cacheMode,
+                        smoothScrolling = mediaWebViewPreferences.smoothScrolling,
+                        renderPriority = mediaWebViewPreferences.renderPriority,
+                        mixedContentMode = mediaWebViewPreferences.mixedContentMode,
+                        safeBrowsing = mediaWebViewPreferences.safeBrowsing,
+                        forceDark = mediaWebViewPreferences.forceDark,
+                        algorithmicDarkening = mediaWebViewPreferences.algorithmicDarkening,
+                        mediaPlaybackRequiresGesture = mediaWebViewPreferences.mediaPlaybackRequiresGesture,
+                        blockNetworkImages = mediaWebViewPreferences.blockNetworkImages,
+                        blockNetworkLoads = mediaWebViewPreferences.blockNetworkLoads,
+                        allowFileAccess = mediaWebViewPreferences.allowFileAccess
                     )
                 )
             )
-            
+
             analyticsRepository.trackEvent(
                 eventName = "settings_loaded",
                 eventData = mapOf(
@@ -52,7 +67,19 @@ class SettingsViewModel @Inject constructor(
                     "accept_cookies" to mediaWebViewPreferences.acceptCookies.toString(),
                     "third_party_cookies" to mediaWebViewPreferences.thirdPartyCookies.toString(),
                     "clear_cookies" to mediaWebViewPreferences.clearCookies.toString(),
-                    "show_url" to mediaWebViewPreferences.showUrl.toString()
+                    "show_url" to mediaWebViewPreferences.showUrl.toString(),
+                    "hardware_acceleration" to mediaWebViewPreferences.hardwareAcceleration.toString(),
+                    "cache_mode" to mediaWebViewPreferences.cacheMode.toString(),
+                    "smooth_scrolling" to mediaWebViewPreferences.smoothScrolling.toString(),
+                    "render_priority" to mediaWebViewPreferences.renderPriority.toString(),
+                    "mixed_content_mode" to mediaWebViewPreferences.mixedContentMode.toString(),
+                    "safe_browsing" to mediaWebViewPreferences.safeBrowsing.toString(),
+                    "force_dark" to mediaWebViewPreferences.forceDark.toString(),
+                    "algorithmic_darkening" to mediaWebViewPreferences.algorithmicDarkening.toString(),
+                    "media_gesture" to mediaWebViewPreferences.mediaPlaybackRequiresGesture.toString(),
+                    "block_images" to mediaWebViewPreferences.blockNetworkImages.toString(),
+                    "block_loads" to mediaWebViewPreferences.blockNetworkLoads.toString(),
+                    "allow_file_access" to mediaWebViewPreferences.allowFileAccess.toString()
                 )
             )
         }
@@ -70,6 +97,33 @@ class SettingsViewModel @Inject constructor(
             is SettingsEvent.ThirdPartyCookies -> onThirdPartyCookies(currentState, event.value)
             is SettingsEvent.ClearCookies -> onClearCookies(currentState, event.value)
             is SettingsEvent.ShowUrl -> onShowUrl(currentState, event.value)
+            is SettingsEvent.HardwareAcceleration -> onHardwareAcceleration(
+                currentState,
+                event.value
+            )
+
+            is SettingsEvent.CacheMode -> onCacheMode(currentState, event.value)
+            is SettingsEvent.SmoothScrolling -> onSmoothScrolling(currentState, event.value)
+            is SettingsEvent.RenderPriority -> onRenderPriority(currentState, event.value)
+            is SettingsEvent.MixedContentMode -> onMixedContentMode(currentState, event.value)
+            is SettingsEvent.SafeBrowsing -> onSafeBrowsing(currentState, event.value)
+            is SettingsEvent.ForceDark -> onForceDark(currentState, event.value)
+            is SettingsEvent.AlgorithmicDarkening -> onAlgorithmicDarkening(
+                currentState,
+                event.value
+            )
+
+            is SettingsEvent.MediaPlaybackRequiresGesture -> onMediaPlaybackRequiresGesture(
+                currentState,
+                event.value
+            )
+
+            is SettingsEvent.BlockNetworkImages -> onBlockNetworkImages(currentState, event.value)
+            is SettingsEvent.BlockNetworkLoads -> onBlockNetworkLoads(currentState, event.value)
+            is SettingsEvent.AllowFileAccess -> onAllowFileAccess(currentState, event.value)
+            is SettingsEvent.AdblockerEnabled -> onAdblockerEnabled(currentState, event.value)
+            is SettingsEvent.AdblockerStrict -> onAdblockerStrict(currentState, event.value)
+            is SettingsEvent.AdblockerWhitelist -> onAdblockerWhitelist(currentState, event.value)
         }
     }
 
@@ -85,7 +139,7 @@ class SettingsViewModel @Inject constructor(
     }
 
     private fun onJavaScript(currentState: SettingsState, value: Boolean): SettingsState {
-        mediaWebView.settings?.javaScriptEnabled = value
+        mediaWebView.settings.javaScriptEnabled = value
         mediaWebViewPreferences.javaScript = value
         viewModelScope.launch(Dispatchers.IO) {
             analyticsRepository.trackEvent(
@@ -97,14 +151,13 @@ class SettingsViewModel @Inject constructor(
     }
 
     private fun onTextSize(currentState: SettingsState, value: TextSize): SettingsState {
-        mediaWebView.settings?.textZoom = value.size
+        mediaWebView.settings.textZoom = value.value
         mediaWebViewPreferences.textSize = value
         viewModelScope.launch(Dispatchers.IO) {
             analyticsRepository.trackEvent(
                 eventName = "settings_text_size_changed",
                 eventData = mapOf(
-                    "size" to value.toString(),
-                    "zoom_value" to value.size.toString()
+                    "size" to value.toString()
                 )
             )
         }
@@ -112,15 +165,14 @@ class SettingsViewModel @Inject constructor(
     }
 
     private fun onUserAgent(currentState: SettingsState, value: UserAgent): SettingsState {
-        mediaWebView.settings?.userAgentString = value.value
+        mediaWebView.settings.userAgentString = value.toWebSetting()
         mediaWebViewPreferences.userAgent = value
         mediaWebView.reload()
         viewModelScope.launch(Dispatchers.IO) {
             analyticsRepository.trackEvent(
                 eventName = "settings_user_agent_changed",
                 eventData = mapOf(
-                    "agent" to value.toString(),
-                    "value" to value.value
+                    "agent" to value.toString()
                 )
             )
         }
@@ -128,7 +180,7 @@ class SettingsViewModel @Inject constructor(
     }
 
     private fun onDomStorage(currentState: SettingsState, value: Boolean): SettingsState {
-        mediaWebView.settings?.domStorageEnabled = value
+        mediaWebView.settings.domStorageEnabled = value
         mediaWebViewPreferences.domStorage = value
         viewModelScope.launch(Dispatchers.IO) {
             analyticsRepository.trackEvent(
@@ -197,4 +249,124 @@ class SettingsViewModel @Inject constructor(
         }
         return currentState.copy(showUrl = value)
     }
+
+    private fun onHardwareAcceleration(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.attachView.setLayerType(
+            if (value) WebView.LAYER_TYPE_HARDWARE else WebView.LAYER_TYPE_SOFTWARE,
+            null
+        )
+        mediaWebViewPreferences.hardwareAcceleration = value
+        trackEvent("settings_hardware_acceleration_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(hardwareAcceleration = value)
+    }
+
+    private fun onCacheMode(currentState: SettingsState, value: CacheMode): SettingsState {
+        mediaWebView.settings.cacheMode = value.value
+        mediaWebViewPreferences.cacheMode = value
+        trackEvent("settings_cache_mode_changed", mapOf("mode" to value.toString()))
+        return currentState.copy(cacheMode = value)
+    }
+
+    private fun onSmoothScrolling(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.settings.setEnableSmoothTransition(value)
+        mediaWebViewPreferences.smoothScrolling = value
+        trackEvent("settings_smooth_scrolling_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(smoothScrolling = value)
+    }
+
+    private fun onRenderPriority(
+        currentState: SettingsState,
+        value: RenderPriority
+    ): SettingsState {
+        mediaWebView.settings.setRenderPriority(value.toWebSetting())
+        mediaWebViewPreferences.renderPriority = value
+        trackEvent("settings_render_priority_changed", mapOf("priority" to value.toString()))
+        return currentState.copy(renderPriority = value)
+    }
+
+    private fun onMixedContentMode(
+        currentState: SettingsState,
+        value: MixedContentMode
+    ): SettingsState {
+        mediaWebView.settings.mixedContentMode = value.value
+        mediaWebViewPreferences.mixedContentMode = value
+        trackEvent("settings_mixed_content_mode_changed", mapOf("mode" to value.toString()))
+        return currentState.copy(mixedContentMode = value)
+    }
+
+    private fun onSafeBrowsing(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebViewPreferences.safeBrowsing = value
+        trackEvent("settings_safe_browsing_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(safeBrowsing = value)
+    }
+
+    private fun onForceDark(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebViewPreferences.forceDark = value
+        trackEvent("settings_force_dark_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(forceDark = value)
+    }
+
+    private fun onAlgorithmicDarkening(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebViewPreferences.algorithmicDarkening = value
+        trackEvent("settings_algorithmic_darkening_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(algorithmicDarkening = value)
+    }
+
+    private fun onMediaPlaybackRequiresGesture(
+        currentState: SettingsState,
+        value: Boolean
+    ): SettingsState {
+        mediaWebView.settings.mediaPlaybackRequiresUserGesture = value
+        mediaWebViewPreferences.mediaPlaybackRequiresGesture = value
+        trackEvent("settings_media_gesture_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(mediaPlaybackRequiresGesture = value)
+    }
+
+    private fun onBlockNetworkImages(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.settings.blockNetworkImage = value
+        mediaWebViewPreferences.blockNetworkImages = value
+        trackEvent("settings_block_images_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(blockNetworkImages = value)
+    }
+
+    private fun onBlockNetworkLoads(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.settings.blockNetworkLoads = value
+        mediaWebViewPreferences.blockNetworkLoads = value
+        trackEvent("settings_block_loads_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(blockNetworkLoads = value)
+    }
+
+    private fun onAllowFileAccess(currentState: SettingsState, value: Boolean): SettingsState {
+        mediaWebView.settings.allowFileAccess = value
+        mediaWebViewPreferences.allowFileAccess = value
+        trackEvent("settings_allow_file_access_changed", mapOf("enabled" to value.toString()))
+        return currentState.copy(allowFileAccess = value)
+    }
+
+    private fun onAdblockerEnabled(currentState: SettingsState, enabled: Boolean): SettingsState {
+        mediaWebViewPreferences.adblockerEnabled = enabled
+        trackEvent("settings_adblocker_enabled_changed", mapOf("enabled" to enabled.toString()))
+        return currentState.copy(adblockerEnabled = enabled)
+    }
+
+    private fun onAdblockerStrict(currentState: SettingsState, strict: Boolean): SettingsState {
+        mediaWebViewPreferences.adblockerStrict = strict
+        trackEvent("settings_adblocker_strict_changed", mapOf("strict" to strict.toString()))
+        return currentState.copy(adblockerStrict = strict)
+    }
+
+    private fun onAdblockerWhitelist(
+        currentState: SettingsState,
+        whitelist: String
+    ): SettingsState {
+        mediaWebViewPreferences.adblockerWhitelist = whitelist
+        trackEvent("settings_adblocker_whitelist_changed", mapOf("whitelist" to whitelist))
+        return currentState.copy(adblockerWhitelist = whitelist)
+    }
+
+    private fun trackEvent(eventName: String, eventData: Map<String, String>) {
+        viewModelScope.launch(Dispatchers.IO) {
+            analyticsRepository.trackEvent(eventName, eventData)
+        }
+    }
 }

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

@@ -1,10 +1,18 @@
 package com.codeskraps.sbrowser.feature.settings.components
 
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBar
@@ -12,95 +20,229 @@ 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.CacheMode
 import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 import com.codeskraps.sbrowser.feature.webview.media.TextSize
 import com.codeskraps.sbrowser.feature.webview.media.UserAgent
+import com.codeskraps.sbrowser.BuildConfig
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun SettingsScreen(
     state: SettingsState,
-    handleEvent: (SettingsEvent) -> Unit
+    handleEvent: (SettingsEvent) -> Unit,
+    onBack: () -> Unit
 ) {
     Scaffold(
-        modifier = Modifier.fillMaxSize(),
-        containerColor = MaterialTheme.colorScheme.secondary,
-        contentColor = MaterialTheme.colorScheme.onSecondary,
         topBar = {
-            TopAppBar(title = { Text(text = "Settings") })
-        }
-    ) { paddingValues ->
+            TopAppBar(
+                title = { Text("Settings") },
+                navigationIcon = {
+                    IconButton(onClick = onBack) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Back",
+                        )
+                    }
+                },
+                windowInsets = WindowInsets(0, 0, 0, 0)
+            )
+        },
+        contentWindowInsets = WindowInsets(0, 0, 0, 0)
+    ) { padding ->
+        Column(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(padding)
+                .windowInsetsPadding(WindowInsets.safeDrawing)
+                .verticalScroll(rememberScrollState())
+        ) {
+            // General Settings
+            CategoryPreference(title = "General")
+            Preference(
+                title = "Home Page",
+                summary = state.homeUrl,
+                onChange = { handleEvent(SettingsEvent.Home(it)) }
+            )
+
+            // Performance & Rendering
+            CategoryPreference(title = "Performance & Rendering")
+            CheckPreference(
+                title = "Hardware Acceleration",
+                summary = "Use hardware acceleration\nfor better performance",
+                isChecked = state.hardwareAcceleration,
+                onCheckedChange = { handleEvent(SettingsEvent.HardwareAcceleration(it)) }
+            )
+            SpacerPreference()
+            ListPreference(
+                title = "Cache Mode",
+                summary = state.cacheMode,
+                items = CacheMode.entries,
+                onChange = { handleEvent(SettingsEvent.CacheMode(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Smooth Scrolling",
+                summary = "Enable smooth scrolling animation",
+                isChecked = state.smoothScrolling,
+                onCheckedChange = { handleEvent(SettingsEvent.SmoothScrolling(it)) }
+            )
+            SpacerPreference()
+            ListPreference(
+                title = "Render Priority",
+                summary = state.renderPriority,
+                items = RenderPriority.entries,
+                onChange = { handleEvent(SettingsEvent.RenderPriority(it)) }
+            )
+
+            // Security & Privacy
+            CategoryPreference(title = "Security & Privacy")
+            CheckPreference(
+                title = "JavaScript",
+                summary = "Enable JavaScript execution",
+                isChecked = state.javaScript,
+                onCheckedChange = { handleEvent(SettingsEvent.JavaScript(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "DOM Storage",
+                summary = "Enable DOM storage",
+                isChecked = state.domStorage,
+                onCheckedChange = { handleEvent(SettingsEvent.DomStorage(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Accept Cookies",
+                summary = "Allow websites to store cookies",
+                isChecked = state.acceptCookies,
+                onCheckedChange = { handleEvent(SettingsEvent.AcceptCookies(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Third Party Cookies",
+                summary = "Allow third-party cookies",
+                isChecked = state.thirdPartyCookies,
+                onCheckedChange = { handleEvent(SettingsEvent.ThirdPartyCookies(it)) }
+            )
+            SpacerPreference()
+            ListPreference(
+                title = "Clear Cookies",
+                summary = state.clearCookies,
+                items = ClearCookies.entries,
+                onChange = { handleEvent(SettingsEvent.ClearCookies(it)) }
+            )
+            SpacerPreference()
+            ListPreference(
+                title = "Mixed Content Mode",
+                summary = state.mixedContentMode,
+                items = MixedContentMode.entries,
+                onChange = { handleEvent(SettingsEvent.MixedContentMode(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Safe Browsing",
+                summary = "Enable Google Safe Browsing",
+                isChecked = state.safeBrowsing,
+                onCheckedChange = { handleEvent(SettingsEvent.SafeBrowsing(it)) }
+            )
+
+            // Adblocker Settings
+            CategoryPreference(title = "Adblocker")
+            CheckPreference(
+                title = "Enable Adblocker",
+                summary = "Block ads and trackers",
+                isChecked = state.adblockerEnabled,
+                onCheckedChange = { handleEvent(SettingsEvent.AdblockerEnabled(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Strict Mode",
+                summary = "Use stricter ad blocking rules",
+                isChecked = state.adblockerStrict,
+                onCheckedChange = { handleEvent(SettingsEvent.AdblockerStrict(it)) }
+            )
+            SpacerPreference()
+            Preference(
+                title = "Whitelist",
+                summary = state.adblockerWhitelist.ifEmpty { "No whitelisted sites" },
+                onChange = { handleEvent(SettingsEvent.AdblockerWhitelist(it)) }
+            )
+
+            // Appearance
+            CategoryPreference(title = "Appearance")
+            ListPreference(
+                title = "Text Size",
+                summary = state.textSize,
+                items = TextSize.entries,
+                onChange = { handleEvent(SettingsEvent.TextSize(it)) }
+            )
+            SpacerPreference()
+            ListPreference(
+                title = "User Agent",
+                summary = state.userAgent,
+                items = UserAgent.entries,
+                onChange = { handleEvent(SettingsEvent.UserAgent(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Force Dark Mode",
+                summary = "Force dark mode on websites",
+                isChecked = state.forceDark,
+                onCheckedChange = { handleEvent(SettingsEvent.ForceDark(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Algorithmic Darkening",
+                summary = "Use algorithmic darkening\nfor unsupported sites",
+                isChecked = state.algorithmicDarkening,
+                onCheckedChange = { handleEvent(SettingsEvent.AlgorithmicDarkening(it)) }
+            )
+
+            // Content & Media
+            CategoryPreference(title = "Content & Media")
+            CheckPreference(
+                title = "Media Playback Requires Gesture",
+                summary = "Require user interaction\nfor media playback",
+                isChecked = state.mediaPlaybackRequiresGesture,
+                onCheckedChange = { handleEvent(SettingsEvent.MediaPlaybackRequiresGesture(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Block Network Images",
+                summary = "Block loading of network images",
+                isChecked = state.blockNetworkImages,
+                onCheckedChange = { handleEvent(SettingsEvent.BlockNetworkImages(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Block Network Loads",
+                summary = "Block all network loads",
+                isChecked = state.blockNetworkLoads,
+                onCheckedChange = { handleEvent(SettingsEvent.BlockNetworkLoads(it)) }
+            )
+            SpacerPreference()
+            CheckPreference(
+                title = "Allow File Access",
+                summary = "Allow access to local files",
+                isChecked = state.allowFileAccess,
+                onCheckedChange = { handleEvent(SettingsEvent.AllowFileAccess(it)) }
+            )
+
+            CategoryPreference(title = "Notification")
+            CheckPreference(
+                title = "Show URL",
+                summary = "Display the URL in the address bar",
+                isChecked = state.showUrl,
+                onCheckedChange = { handleEvent(SettingsEvent.ShowUrl(it)) }
+            )
 
-        LazyColumn(modifier = Modifier.padding(paddingValues)) {
-            item {
-                CategoryPreference(title = "Page Content Settings")
-                Preference(title = "Set Home Page", summary = state.homeUrl) { newValue ->
-                    handleEvent(SettingsEvent.Home(newValue))
-                }
-                SpacerPreference()
-                CheckPreference(
-                    title = "Enable JavaScript",
-                    isChecked = state.javaScript
-                ) { newValue ->
-                    handleEvent(SettingsEvent.JavaScript(newValue))
-                }
-                SpacerPreference()
-                ListPreference(
-                    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 ->
-                    handleEvent(SettingsEvent.ThirdPartyCookies(newValue))
-                }
-                SpacerPreference()
-                ListPreference(
-                    title = "Clear Cookies",
-                    summary = state.clearCookies.ui,
-                    enabled = state.acceptCookies,
-                    items = ClearCookies.displayArray()
-                ) { newValue ->
-                    handleEvent(SettingsEvent.ClearCookies(ClearCookies.parse(newValue)))
-                }
-                CategoryPreference(title = "Notification")
-                CheckPreference(title = "Show Url", isChecked = state.showUrl) { newValue ->
-                    handleEvent(SettingsEvent.ShowUrl(newValue))
-                }
-                CategoryPreference(title = "Information")
-                Preference(
-                    title = "sBrowser v3.0",
-                    summary = "License GNU GPL v3 - 2024 - Codeskraps"
-                )
-            }
+            CategoryPreference(title = "Information")
+            Preference(
+                title = "sBrowser v${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE})",
+                summary = "MIT License - 2025 - Codeskraps"
+            )
         }
     }
 }

+ 1 - 1
app/src/main/java/com/codeskraps/sbrowser/feature/settings/mvi/SettingsAction.kt

@@ -1,3 +1,3 @@
 package com.codeskraps.sbrowser.feature.settings.mvi
 
-sealed interface SettingsAction
+sealed class SettingsAction

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

@@ -1,21 +1,36 @@
 package com.codeskraps.sbrowser.feature.settings.mvi
 
-import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
-import com.codeskraps.sbrowser.feature.webview.media.UserAgent
+import android.webkit.WebSettings
+import com.codeskraps.sbrowser.feature.webview.media.CacheMode
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 
 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 TextSize(val value: com.codeskraps.sbrowser.feature.webview.media.TextSize) :
-        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 ClearCookies(val value: com.codeskraps.sbrowser.feature.webview.media.ClearCookies) : SettingsEvent
     data class ShowUrl(val value: Boolean) : SettingsEvent
+    // New events
+    data class HardwareAcceleration(val value: Boolean) : SettingsEvent
+    data class CacheMode(val value: com.codeskraps.sbrowser.feature.webview.media.CacheMode) : SettingsEvent
+    data class SmoothScrolling(val value: Boolean) : SettingsEvent
+    data class RenderPriority(val value: com.codeskraps.sbrowser.feature.webview.media.RenderPriority) : SettingsEvent
+    data class MixedContentMode(val value: com.codeskraps.sbrowser.feature.webview.media.MixedContentMode) : SettingsEvent
+    data class SafeBrowsing(val value: Boolean) : SettingsEvent
+    data class ForceDark(val value: Boolean) : SettingsEvent
+    data class AlgorithmicDarkening(val value: Boolean) : SettingsEvent
+    data class MediaPlaybackRequiresGesture(val value: Boolean) : SettingsEvent
+    data class BlockNetworkImages(val value: Boolean) : SettingsEvent
+    data class BlockNetworkLoads(val value: Boolean) : SettingsEvent
+    data class AllowFileAccess(val value: Boolean) : SettingsEvent
+    // Adblocker events
+    data class AdblockerEnabled(val value: Boolean) : SettingsEvent
+    data class AdblockerStrict(val value: Boolean) : SettingsEvent
+    data class AdblockerWhitelist(val value: String) : SettingsEvent
 }

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

@@ -1,32 +1,40 @@
 package com.codeskraps.sbrowser.feature.settings.mvi
 
+import com.codeskraps.sbrowser.feature.webview.media.CacheMode
 import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 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 textSize: TextSize,
-    val userAgent: UserAgent,
-    val domStorage:Boolean,
-    val acceptCookies: Boolean,
-    val thirdPartyCookies: Boolean,
-    val clearCookies: ClearCookies,
-    val showUrl: Boolean,
+    val homeUrl: String = "",
+    val javaScript: Boolean = false,
+    val textSize: TextSize = TextSize.Normal,
+    val userAgent: UserAgent = UserAgent.Default,
+    val domStorage: Boolean = false,
+    val acceptCookies: Boolean = false,
+    val thirdPartyCookies: Boolean = false,
+    val clearCookies: ClearCookies = ClearCookies.Off,
+    val showUrl: Boolean = false,
+    val hardwareAcceleration: Boolean = true,
+    val cacheMode: CacheMode = CacheMode.Default,
+    val smoothScrolling: Boolean = true,
+    val renderPriority: RenderPriority = RenderPriority.High,
+    val mixedContentMode: MixedContentMode = MixedContentMode.AlwaysAllow,
+    val safeBrowsing: Boolean = true,
+    val forceDark: Boolean = false,
+    val algorithmicDarkening: Boolean = false,
+    val mediaPlaybackRequiresGesture: Boolean = false,
+    val blockNetworkImages: Boolean = false,
+    val blockNetworkLoads: Boolean = false,
+    val allowFileAccess: Boolean = false,
+    // Adblocker settings
+    val adblockerEnabled: Boolean = true,
+    val adblockerStrict: Boolean = false,
+    val adblockerWhitelist: String = ""
 ) {
     companion object {
-        val initial = SettingsState(
-            homeUrl = Constants.home,
-            javaScript = Constants.javaScript,
-            textSize = Constants.textSize,
-            userAgent = Constants.userAgent,
-            domStorage = Constants.domStorage,
-            acceptCookies = Constants.acceptCookies,
-            thirdPartyCookies = Constants.thirdPartyCookies,
-            clearCookies = Constants.clearCookies,
-            showUrl = Constants.showUrl
-        )
+        val initial = SettingsState()
     }
 }

+ 1 - 4
app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewButtons.kt

@@ -11,13 +11,10 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.automirrored.filled.ArrowForward
-import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.icons.filled.ArrowForward
 import androidx.compose.material.icons.filled.Close
 import androidx.compose.material.icons.filled.Delete
 import androidx.compose.material.icons.filled.Home
 import androidx.compose.material.icons.filled.MoreVert
-import androidx.compose.material.icons.filled.Person
 import androidx.compose.material.icons.filled.PlayArrow
 import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Search
@@ -251,7 +248,7 @@ fun BackgroundButton(
 fun SearchButton(
     handleEvent: (MediaWebViewEvent) -> Unit
 ) {
-    IconButton(onClick = { handleEvent(MediaWebViewEvent.Load(Constants.home)) }) {
+    IconButton(onClick = { handleEvent(MediaWebViewEvent.Load(Constants.HOME)) }) {
         Icon(
             imageVector = Icons.Default.Search,
             contentDescription = "Home",

+ 2 - 2
app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewProgressIndicator.kt

@@ -13,9 +13,9 @@ fun WebViewProgressIndicator(
 ) {
     if (state.loading) {
         LinearProgressIndicator(
-            progress = state.progress,
+            progress = { state.progress },
             modifier = Modifier.fillMaxWidth(),
-            color = MaterialTheme.colorScheme.tertiary
+            color = MaterialTheme.colorScheme.tertiary,
         )
     }
 }

+ 24 - 4
app/src/main/java/com/codeskraps/sbrowser/feature/webview/components/WebViewScreen.kt

@@ -16,20 +16,31 @@ import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.SnackbarDuration
 import androidx.compose.material3.SnackbarHost
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.SnackbarResult
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.net.toUri
 import com.codeskraps.sbrowser.feature.webview.media.MediaWebView
@@ -41,6 +52,7 @@ import com.codeskraps.sbrowser.util.components.ObserveAsEvents
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.launch
 
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun WebViewScreen(
     mediaWebView: MediaWebView,
@@ -54,6 +66,8 @@ fun WebViewScreen(
     val snackbarHostState = remember { SnackbarHostState() }
     val scope = rememberCoroutineScope()
     val backPressDispatcher = LocalOnBackPressedDispatcherOwner.current
+    val topAppBarState = rememberTopAppBarState()
+    val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(topAppBarState)
 
     ObserveAsEvents(flow = action) { onAction ->
         when (onAction) {
@@ -121,17 +135,20 @@ fun WebViewScreen(
 
     Scaffold(
         snackbarHost = { SnackbarHost(snackbarHostState) },
+        topBar = {},
         bottomBar = {
             if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
                 Box(modifier = Modifier.fillMaxWidth()) {
                     WebViewProgressIndicator(state = state)
-                    Row(modifier = Modifier.fillMaxWidth()) {
+                    Row(modifier = Modifier
+                        .fillMaxWidth()
+                        .padding(bottom = 8.dp)
+                    ) {
                         AddressButton(url = mediaWebView.url ?: "", handleEvent = handleEvent)
                         GoBackButton(mediaWebView = mediaWebView)
                         GoForwardButton(mediaWebView = mediaWebView)
                         HomeButton(homeUrl = state.homeUrl, handleEvent = handleEvent)
                         RefreshStopButton(mediaWebView, state)
-                        //SearchButton(handleEvent = handleEvent)
                         BackgroundButton(state = state, handleEvent = handleEvent)
                         Spacer(modifier = Modifier.weight(1f))
                         MenuButton(navRoute = navRoute)
@@ -140,7 +157,11 @@ fun WebViewScreen(
             }
         }
     ) { paddingValues ->
-        Row(modifier = Modifier.padding(paddingValues)) {
+        Row(modifier = Modifier
+            .padding(paddingValues)
+            .windowInsetsPadding(WindowInsets.safeDrawing)
+            .nestedScroll(scrollBehavior.nestedScrollConnection)
+        ) {
             if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                 Column {
                     AddressButton(url = mediaWebView.url ?: "", handleEvent = handleEvent)
@@ -148,7 +169,6 @@ fun WebViewScreen(
                     GoForwardButton(mediaWebView = mediaWebView)
                     HomeButton(homeUrl = state.homeUrl, handleEvent = handleEvent)
                     RefreshStopButton(mediaWebView, state)
-                    //SearchButton(handleEvent = handleEvent)
                     BackgroundButton(state = state, handleEvent = handleEvent)
                     Spacer(modifier = Modifier.weight(1f))
                     MenuButton(navRoute = navRoute)

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

@@ -1,17 +0,0 @@
-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
-        }
-    }
-}

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

@@ -22,6 +22,7 @@ import javax.inject.Inject
 import androidx.core.view.ViewCompat
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.LifecycleOwner
+import com.monstertechno.adblocker.AdBlockerWebView
 
 @SuppressLint("SetJavaScriptEnabled")
 class MediaWebView @Inject constructor(
@@ -122,6 +123,10 @@ class MediaWebView @Inject constructor(
             defStyleAttr
         )
 
+        init {
+            AdBlockerWebView.init(context).initializeWebView(this)
+        }
+
         val javascriptInterface = WebScriptInterface()
 
         override fun onWindowVisibilityChanged(visibility: Int) {
@@ -131,11 +136,15 @@ class MediaWebView @Inject constructor(
         }
 
         fun setupWebView() {
-            webViewClient = MediaWebViewClient()
+            webViewClient = MediaWebViewClient(mediaWebViewPreferences)
             webChromeClient = MediaWebChromeClient()
 
             // Enable hardware acceleration and scrolling
-            setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            setLayerType(
+                if (mediaWebViewPreferences.hardwareAcceleration) View.LAYER_TYPE_HARDWARE
+                else View.LAYER_TYPE_SOFTWARE,
+                null
+            )
             ViewCompat.setNestedScrollingEnabled(this, true)
             isScrollbarFadingEnabled = true
             scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
@@ -161,35 +170,34 @@ class MediaWebView @Inject constructor(
 
         private fun WebSettings.initializeWebSettings() {
             // Basic settings
-            loadsImagesAutomatically = true
+            loadsImagesAutomatically = !mediaWebViewPreferences.blockNetworkImages
             javaScriptEnabled = mediaWebViewPreferences.javaScript
-            domStorageEnabled = true
+            domStorageEnabled = mediaWebViewPreferences.domStorage
             loadWithOverviewMode = true
             useWideViewPort = true
             builtInZoomControls = true
             displayZoomControls = false
-            mediaPlaybackRequiresUserGesture = false
-            allowFileAccess = false
-            textZoom = mediaWebViewPreferences.textSize.size
+            mediaPlaybackRequiresUserGesture = mediaWebViewPreferences.mediaPlaybackRequiresGesture
+            allowFileAccess = mediaWebViewPreferences.allowFileAccess
+            textZoom = mediaWebViewPreferences.textSize.value
 
             // Enhanced web compatibility
             javaScriptCanOpenWindowsAutomatically = true
             databaseEnabled = true
-            domStorageEnabled = true
             allowContentAccess = true
             setSupportMultipleWindows(true)
             allowFileAccessFromFileURLs = false
             allowUniversalAccessFromFileURLs = false
 
             // Performance and rendering improvements
-            setRenderPriority(WebSettings.RenderPriority.HIGH)
-            cacheMode = WebSettings.LOAD_NO_CACHE  // Try without cache first
-            setEnableSmoothTransition(true)
-            blockNetworkImage = false
-            blockNetworkLoads = false
+            setRenderPriority(mediaWebViewPreferences.renderPriority.toWebSetting())
+            cacheMode = mediaWebViewPreferences.cacheMode.value
+            setEnableSmoothTransition(mediaWebViewPreferences.smoothScrolling)
+            blockNetworkImage = mediaWebViewPreferences.blockNetworkImages
+            blockNetworkLoads = mediaWebViewPreferences.blockNetworkLoads
 
             // Additional rendering settings
-            layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
+            layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
             standardFontFamily = "sans-serif"
             defaultTextEncodingName = "UTF-8"
             defaultFontSize = 16
@@ -199,33 +207,35 @@ class MediaWebView @Inject constructor(
             setupModernWebFeatures()
 
             // Security settings with compatibility
-            mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
+            mixedContentMode = mediaWebViewPreferences.mixedContentMode.value
 
             // Display settings
             setInitialScale(0)  // Let the WebView determine the proper scale
             setSupportZoom(true)
             setNetworkAvailable(true)
 
-            // Set a modern user agent if default is not provided
-            userAgentString = mediaWebViewPreferences.userAgent.value.takeIf { it.isNotBlank() }
-                ?: "Mozilla/5.0 (Linux; Android ${android.os.Build.VERSION.RELEASE}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
+            // Set user agent
+            userAgentString = mediaWebViewPreferences.userAgent.toWebSetting()
         }
 
         private fun WebSettings.setupModernWebFeatures() {
             if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
                 WebSettingsCompat.setForceDark(
                     this,
-                    if (isDarkMode(context)) WebSettingsCompat.FORCE_DARK_ON
+                    if (mediaWebViewPreferences.forceDark) WebSettingsCompat.FORCE_DARK_ON
                     else WebSettingsCompat.FORCE_DARK_OFF
                 )
             }
 
             if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_ENABLE)) {
-                WebSettingsCompat.setSafeBrowsingEnabled(this, true)
+                WebSettingsCompat.setSafeBrowsingEnabled(this, mediaWebViewPreferences.safeBrowsing)
             }
 
             if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
-                WebSettingsCompat.setAlgorithmicDarkeningAllowed(this, isDarkMode(context))
+                WebSettingsCompat.setAlgorithmicDarkeningAllowed(
+                    this,
+                    mediaWebViewPreferences.algorithmicDarkening
+                )
             }
 
             // Enable additional modern features if available

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

@@ -6,14 +6,16 @@ import android.net.http.SslCertificate
 import android.net.http.SslError
 import android.util.Log
 import android.webkit.MimeTypeMap
-import android.webkit.RenderProcessGoneDetail
 import android.webkit.SslErrorHandler
 import android.webkit.WebResourceError
 import android.webkit.WebResourceRequest
 import android.webkit.WebResourceResponse
 import android.webkit.WebView
 import android.webkit.WebViewClient
+import com.codeskraps.sbrowser.MediaWebViewPreferences
 import com.codeskraps.sbrowser.feature.webview.mvi.MediaWebViewEvent
+import com.monstertechno.adblocker.AdBlockerWebView
+import com.monstertechno.adblocker.util.AdBlocker
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -23,8 +25,11 @@ import java.security.cert.CertificateException
 import java.security.cert.CertificateFactory
 import java.security.cert.X509Certificate
 import java.util.Locale
+import javax.inject.Inject
 
-class MediaWebViewClient : WebViewClient() {
+class MediaWebViewClient @Inject constructor(
+    private val mediaWebViewPreferences: MediaWebViewPreferences
+) : WebViewClient() {
 
     var urlListener: ((String) -> Unit)? = null
     var handleEvent: ((MediaWebViewEvent) -> Unit)? = null
@@ -96,7 +101,7 @@ class MediaWebViewClient : WebViewClient() {
                     certificate.checkValidity()
                     // Certificate is valid, we can proceed
                     handler.proceed()
-                    handleEvent?.let { 
+                    handleEvent?.let {
                         it(MediaWebViewEvent.Toast("Certificate verified - Proceeding with caution"))
                     }
                     return
@@ -108,7 +113,7 @@ class MediaWebViewClient : WebViewClient() {
 
         // If we couldn't verify the certificate or verification failed, cancel the connection
         handler.cancel()
-        handleEvent?.let { 
+        handleEvent?.let {
             it(MediaWebViewEvent.Loading(false))
             it(MediaWebViewEvent.Toast("Security Warning: $errorMessage"))
         }
@@ -129,16 +134,28 @@ class MediaWebViewClient : WebViewClient() {
                 handleEvent?.let { it(MediaWebViewEvent.ActionView) }
                 return true
             }
+
             url.startsWith("mailto:") -> {
                 handleEvent?.let { it(MediaWebViewEvent.ActionView) }
                 return true
             }
+
             url.startsWith("sms:") -> {
                 handleEvent?.let { it(MediaWebViewEvent.ActionView) }
                 return true
             }
         }
 
+        // Check if URL is in whitelist
+        if (mediaWebViewPreferences.adblockerEnabled && 
+            !mediaWebViewPreferences.adblockerWhitelist.split(",").any { 
+                url.contains(it.trim(), ignoreCase = true) 
+            } && 
+            AdBlocker.isAd(url)
+        ) {
+            return true
+        }
+
         val fileExtension = MimeTypeMap.getFileExtensionFromUrl(url).lowercase(Locale.getDefault())
 
         return when {
@@ -166,6 +183,7 @@ class MediaWebViewClient : WebViewClient() {
                 }
                 false // Let the WebView load the page while we check for video
             }
+
             else -> false // Let WebView handle the URL
         }
     }
@@ -174,7 +192,24 @@ class MediaWebViewClient : WebViewClient() {
         view: WebView?,
         request: WebResourceRequest?
     ): WebResourceResponse? {
-        // Add any content blocking or request modification logic here if needed
-        return super.shouldInterceptRequest(view, request)
+        if (!mediaWebViewPreferences.adblockerEnabled) {
+            return super.shouldInterceptRequest(view, request)
+        }
+
+        val url = request?.url.toString()
+        
+        // Check if URL is in whitelist
+        if (mediaWebViewPreferences.adblockerWhitelist.split(",").any { 
+            url.contains(it.trim(), ignoreCase = true) 
+        }) {
+            return super.shouldInterceptRequest(view, request)
+        }
+
+        // Check if URL should be blocked
+        return if (AdBlockerWebView.blockAds(view, url)) {
+            AdBlocker.createEmptyResource()
+        } else {
+            super.shouldInterceptRequest(view, request)
+        }
     }
 }

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

@@ -1,30 +0,0 @@
-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
-        }
-    }
-}

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

@@ -1,29 +0,0 @@
-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
-        }
-    }
-}

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

@@ -0,0 +1,212 @@
+package com.codeskraps.sbrowser.feature.webview.media
+
+import android.os.Build
+import android.webkit.WebSettings
+
+sealed class ClearCookies(val value: Int, private val displayName: String) {
+    data object Off : ClearCookies(0, "Off")
+    data object SessionCookies : ClearCookies(1, "Session Cookies")
+    data object AllCookies : ClearCookies(2, "All Cookies")
+
+    companion object {
+        val entries = arrayOf(Off, SessionCookies, AllCookies)
+        fun fromValue(value: Int): ClearCookies {
+            return when (value) {
+                Off.value -> Off
+                SessionCookies.value -> SessionCookies
+                AllCookies.value -> AllCookies
+                else -> AllCookies
+            }
+        }
+    }
+
+    override fun toString(): String = displayName
+}
+
+sealed class CacheMode(val value: Int, private val displayName: String) {
+    data object Default : CacheMode(WebSettings.LOAD_DEFAULT, "Default")
+    data object CacheElseNetwork :
+        CacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK, "Cache Else Network")
+
+    data object NoCache : CacheMode(WebSettings.LOAD_NO_CACHE, "No Cache")
+    data object CacheOnly : CacheMode(WebSettings.LOAD_CACHE_ONLY, "Cache Only")
+
+    companion object {
+        val entries = arrayOf(Default, CacheElseNetwork, NoCache, CacheOnly)
+        fun fromValue(value: Int): CacheMode {
+            return when (value) {
+                Default.value -> Default
+                CacheElseNetwork.value -> CacheElseNetwork
+                NoCache.value -> NoCache
+                CacheOnly.value -> CacheOnly
+                else -> Default
+            }
+        }
+    }
+
+    override fun toString(): String = displayName
+}
+
+sealed class RenderPriority(val value: Int, private val displayName: String) {
+    data object Low : RenderPriority(0, "Low")
+    data object Normal : RenderPriority(1, "Normal")
+    data object High : RenderPriority(2, "High")
+
+    companion object {
+        val entries = arrayOf(Low, Normal, High)
+        fun fromValue(value: Int): RenderPriority {
+            return when (value) {
+                Low.value -> Low
+                Normal.value -> Normal
+                High.value -> High
+                else -> High
+            }
+        }
+    }
+
+    fun toWebSetting(): WebSettings.RenderPriority {
+        return when (this) {
+            Low -> WebSettings.RenderPriority.LOW
+            Normal -> WebSettings.RenderPriority.NORMAL
+            High -> WebSettings.RenderPriority.HIGH
+        }
+    }
+
+    override fun toString(): String = displayName
+}
+
+sealed class MixedContentMode(val value: Int, private val displayName: String) {
+    data object AlwaysAllow :
+        MixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW, "Always Allow")
+
+    data object CompatibilityMode :
+        MixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE, "Compatibility Mode")
+
+    data object NeverAllow : MixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW, "Never Allow")
+
+    companion object {
+        val entries = arrayOf(AlwaysAllow, CompatibilityMode, NeverAllow)
+        fun fromValue(value: Int): MixedContentMode {
+            return when (value) {
+                AlwaysAllow.value -> AlwaysAllow
+                CompatibilityMode.value -> CompatibilityMode
+                NeverAllow.value -> NeverAllow
+                else -> CompatibilityMode
+            }
+        }
+    }
+
+    override fun toString(): String = displayName
+}
+
+sealed class TextSize(val value: Int, private val displayName: String) {
+    data object Tiny : TextSize(50, "Tiny")
+    data object Small : TextSize(75, "Small")
+    data object Normal : TextSize(100, "Normal")
+    data object Large : TextSize(150, "Large")
+    data object Huge : TextSize(200, "Huge")
+
+    companion object {
+        val entries = arrayOf(Tiny, Small, Normal, Large, Huge)
+        fun fromValue(value: Int): TextSize {
+            return when (value) {
+                Tiny.value -> Tiny
+                Small.value -> Small
+                Normal.value -> Normal
+                Large.value -> Large
+                Huge.value -> Huge
+                else -> Normal
+            }
+        }
+    }
+
+    override fun toString(): String = displayName
+}
+
+sealed class UserAgent(val value: Int, val displayName: String) {
+    data object Default : UserAgent(0, "Default")
+    data object ChromeWin10 : UserAgent(1, "Chrome on Windows 10")
+    data object ChromeMacOS : UserAgent(2, "Chrome on macOS")
+    data object ChromeIPad : UserAgent(3, "Chrome on iPad")
+    data object ChromeAndroid : UserAgent(4, "Chrome on Android")
+    data object SafariIPhone : UserAgent(5, "Safari on iPhone")
+    data object SafariMacOS : UserAgent(6, "Safari on macOS")
+    data object FirefoxWin10 : UserAgent(6, "Firefox on Windows 10")
+    data object EdgeWin10 : UserAgent(7, "Edge on Windows 10")
+
+    companion object {
+        val entries = arrayOf(
+            Default,
+            ChromeWin10,
+            ChromeMacOS,
+            ChromeIPad,
+            ChromeAndroid,
+            SafariIPhone,
+            FirefoxWin10,
+            EdgeWin10
+        )
+
+        fun fromValue(value: Int): UserAgent {
+            return when (value) {
+                Default.value -> Default
+                ChromeWin10.value -> ChromeWin10
+                ChromeMacOS.value -> ChromeMacOS
+                ChromeIPad.value -> ChromeIPad
+                ChromeAndroid.value -> ChromeAndroid
+                SafariIPhone.value -> SafariIPhone
+                SafariMacOS.value -> SafariMacOS
+                FirefoxWin10.value -> FirefoxWin10
+                EdgeWin10.value -> EdgeWin10
+                else -> Default
+            }
+        }
+
+        // User agent templates
+        private const val CHROME_MOBILE =
+            "Mozilla/5.0 (Linux; Android %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36"
+        private const val CHROME_DESKTOP =
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36"
+        private const val CHROME_MAC =
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36"
+        private const val CHROME_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"
+        private const val FIREFOX_DESKTOP =
+            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:%s) Gecko/%s Firefox/%s"
+        private const val SAFARI_MOBILE =
+            "Mozilla/5.0 (iPhone; CPU iPhone OS %s like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%s Mobile/15E148 Safari/604.1"
+        private const val SAFARI_DESKTOP =
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X %s) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%s Safari/605.1.15"
+
+        // Latest browser versions
+        private const val CHROME_VERSION = "121.0.0.0"
+        private const val FIREFOX_VERSION = "122.0"
+        private const val SAFARI_VERSION = "17.3"
+        private const val GECKO_VERSION = "20240101"
+        private const val MACOS_VERSION = "10_15_7"
+    }
+
+    fun toWebSetting(): String {
+        return when (this) {
+            Default -> ""
+            ChromeWin10 -> CHROME_DESKTOP.format(CHROME_VERSION)
+            ChromeMacOS -> CHROME_MAC.format(MACOS_VERSION, CHROME_VERSION)
+            ChromeIPad -> CHROME_IPAD
+            ChromeAndroid -> CHROME_MOBILE.format(Build.VERSION.RELEASE, CHROME_VERSION)
+            SafariIPhone -> SAFARI_MOBILE.format(
+                Build.VERSION.RELEASE.replace(".", "_"),
+                SAFARI_VERSION
+            )
+
+            SafariMacOS -> SAFARI_DESKTOP.format(MACOS_VERSION, SAFARI_VERSION)
+            FirefoxWin10 -> FIREFOX_DESKTOP.format(
+                FIREFOX_VERSION,
+                GECKO_VERSION,
+                FIREFOX_VERSION
+            )
+
+            EdgeWin10 -> "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
+        }
+    }
+
+    override fun toString(): String = displayName
+}

+ 1 - 1
app/src/main/java/com/codeskraps/sbrowser/feature/webview/mvi/MediaWebViewState.kt

@@ -10,7 +10,7 @@ data class MediaWebViewState(
 ) {
     companion object {
         val initial = MediaWebViewState(
-            homeUrl = Constants.home,
+            homeUrl = Constants.HOME,
             loading = false,
             progress = .0f,
             background = false

+ 3 - 1
app/src/main/java/com/codeskraps/sbrowser/ui/theme/Theme.kt

@@ -47,8 +47,10 @@ fun SBrowserTheme(
     if (!view.isInEditMode) {
         SideEffect {
             val window = (view.context as Activity).window
-            window.statusBarColor = colorScheme.background.toArgb()
+            window.statusBarColor = Color.Transparent.toArgb()
+            window.navigationBarColor = Color.Transparent.toArgb()
             WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
+            WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = !darkTheme
         }
     }
 

+ 28 - 10
app/src/main/java/com/codeskraps/sbrowser/util/Constants.kt

@@ -1,18 +1,36 @@
 package com.codeskraps.sbrowser.util
 
+import com.codeskraps.sbrowser.feature.webview.media.CacheMode
 import com.codeskraps.sbrowser.feature.webview.media.ClearCookies
+import com.codeskraps.sbrowser.feature.webview.media.MixedContentMode
+import com.codeskraps.sbrowser.feature.webview.media.RenderPriority
 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 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"
+    const val HOME = "https://www.google.com/"
+    const val JAVA_SCRIPT = true
+    val TEXT_SIZE = TextSize.Normal
+    val USER_AGENT = UserAgent.Default
+    const val DOM_STORAGE = false
+    const val ACCEPT_COOKIES = false
+    const val THIRD_PARTY_COOKIES = false
+    val CLEAR_COOKIES = ClearCookies.AllCookies
+    const val SHOW_URL = false
+    const val INPUT_EXTRAS = "inputExtra"
+    const val HARDWARE_ACCELERATION = true
+    val CACHE_MODE = CacheMode.Default
+    const val SMOOTH_SCROLLING = true
+    val RENDER_PRIORITY = RenderPriority.High
+    val MIXED_CONTENT = MixedContentMode.CompatibilityMode
+    const val SAFE_BROWSING = true
+    const val FORCE_DARK = false
+    const val ALGORITHMIC_DARKENING = false
+    const val MEDIA_PLAYBACK_REQUIRES_GESTURE = false
+    const val BLOCK_NETWORK_IMAGES = false
+    const val BLOCK_NETWORK_LOADS = false
+    const val ALLOW_FILE_ACCESS = false
+    const val AD_BLOCKER_ENABLED = true
+    const val AD_BLOCKER_STRICT = false
+    const val AD_BLOCKER_WHITELIST = ""
 }

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 1 - 1
app/src/main/res/values/ic_launcher_background.xml

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <color name="ic_launcher_background">#102840</color>
+    <color name="ic_launcher_background">#FFFFFF</color>
 </resources>

+ 1 - 1
app/src/main/res/values/themes.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <style name="Theme.SBrowser" parent="android:Theme.Material.Light.NoActionBar">
+    <style name="Theme.SBrowser" parent="Theme.AppCompat.NoActionBar">
         <item name="android:windowBackground">@color/window_background</item>
     </style>
 

+ 4 - 0
gradle/libs.versions.toml

@@ -16,6 +16,8 @@ room = "2.6.1"
 jsoup = "1.17.2"
 media3_version = "1.6.0"
 webkit = "1.13.0"
+webviewadblock-library = "1.1"
+appcompat = "1.7.0"
 
 [libraries]
 androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "core-splashscreen" }
@@ -49,6 +51,8 @@ jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" }
 media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3_version" }
 media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3_version" }
 webkit = { group = "androidx.webkit", name = "webkit", version.ref = "webkit" }
+webviewadblock-library = { module = "com.github.MonsterTechnoGits:WebViewAdblock-Library", version.ref = "webviewadblock-library" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
 
 [plugins]
 androidApplication = { id = "com.android.application", version.ref = "agp" }

+ 1 - 0
settings.gradle.kts

@@ -10,6 +10,7 @@ dependencyResolutionManagement {
     repositories {
         google()
         mavenCentral()
+        maven { url = uri("https://jitpack.io") }
     }
 }