Browse Source

Edit Dialog, DeepLinks

Carles Sentis 9 months ago
parent
commit
2fc92aa124

+ 2 - 0
.idea/.gitignore

@@ -1,3 +1,5 @@
 # Default ignored files
 /shelf/
 /workspace.xml
+# GitHub Copilot persisted chat sessions
+/copilot/chatSessions

+ 9 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -3,30 +3,39 @@
     <option name="myName" value="Project Default" />
     <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
     <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
     </inspection_tool>
   </profile>
 </component>

+ 9 - 3
app/build.gradle.kts

@@ -14,8 +14,9 @@ android {
         applicationId = "com.codeskraps.sbrowser_new"
         minSdk = 26
         targetSdk = 34
-        versionCode = 4
-        versionName = "3.2"
+        versionCode = 6
+        versionName = "3.3"
+        setProperty("archivesBaseName", "sBrowser-v$versionName.$versionCode")
 
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
         vectorDrawables {
@@ -25,12 +26,17 @@ android {
 
     buildTypes {
         release {
-            isMinifyEnabled = false
+            isMinifyEnabled = true
             proguardFiles(
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 "proguard-rules.pro"
             )
         }
+        debug {
+            isMinifyEnabled = false
+            applicationIdSuffix = ".debug"
+            versionNameSuffix = "-DEBUG"
+        }
     }
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_17

BIN
app/release/sBrowser-v3.3.5-release.aab


BIN
app/release/sBrowser-v3.3.6-release.aab


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

@@ -26,6 +26,13 @@
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+            </intent-filter>
         </activity>
         <service
             android:name=".ForegroundService"

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

@@ -1,5 +1,7 @@
 package com.codeskraps.sbrowser
 
+import android.content.Intent
+import android.net.Uri
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
@@ -25,6 +27,7 @@ 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.feature.webview.mvi.MediaWebViewEvent
 import com.codeskraps.sbrowser.navigation.Screen
 import com.codeskraps.sbrowser.ui.theme.SBrowserTheme
 import dagger.hilt.android.AndroidEntryPoint
@@ -41,6 +44,7 @@ class MainActivity : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
         setContent {
             SBrowserTheme {
                 Surface(
@@ -68,6 +72,17 @@ class MainActivity : ComponentActivity() {
                             ) { route ->
                                 navController.navigate(route)
                             }
+
+                            val action: String? = intent?.action
+                            val data: Uri? = intent?.data
+
+                            if (Intent.ACTION_VIEW == action && data != null) {
+
+                                val url = data.toString()
+                                if (url.startsWith("http://") || url.startsWith("https://")) {
+                                    viewModel.state.handleEvent(MediaWebViewEvent.Load(url))
+                                }
+                            }
                         }
                         composable(
                             route = Screen.Bookmarks.route

+ 1 - 3
app/src/main/java/com/codeskraps/sbrowser/feature/bookmarks/presentation/BookmarkViewModel.kt

@@ -18,9 +18,7 @@ import javax.inject.Inject
 class BookmarkViewModel @Inject constructor(
     private val webView: MediaWebView,
     private val localBookmarkRepository: LocalBookmarkRepository
-) : StateReducerViewModel<BookmarkState, BookmarkEvent, BookmarkAction>() {
-
-    override fun initState(): BookmarkState = BookmarkState.initial
+) : StateReducerViewModel<BookmarkState, BookmarkEvent, BookmarkAction>(BookmarkState.initial) {
 
     init {
         viewModelScope.launch(Dispatchers.IO) {

+ 1 - 3
app/src/main/java/com/codeskraps/sbrowser/feature/settings/SettingsViewModel.kt

@@ -21,9 +21,7 @@ import javax.inject.Inject
 class SettingsViewModel @Inject constructor(
     private val mediaWebView: MediaWebView,
     private val mediaWebViewPreferences: MediaWebViewPreferences
-) : StateReducerViewModel<SettingsState, SettingsEvent, SettingsAction>() {
-
-    override fun initState(): SettingsState = SettingsState.initial
+) : StateReducerViewModel<SettingsState, SettingsEvent, SettingsAction>(SettingsState.initial) {
 
     init {
         viewModelScope.launch(Dispatchers.IO) {

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

@@ -11,8 +11,7 @@ import javax.inject.Inject
 @HiltViewModel
 class VideoViewModel @Inject constructor(
     private val savedStateHandle: SavedStateHandle
-) : StateReducerViewModel<VideoState, VideoEvent, VideoAction>() {
-    override fun initState(): VideoState = VideoState.initial
+) : StateReducerViewModel<VideoState, VideoEvent, VideoAction>(VideoState.initial) {
 
     init {
         savedStateHandle.get<String>("url")?.run {

+ 3 - 3
app/src/main/java/com/codeskraps/sbrowser/feature/webview/MediaWebViewModel.kt

@@ -25,9 +25,9 @@ class MediaWebViewModel @Inject constructor(
     private val backgroundStatus: BackgroundStatus,
     private val savedStateHandle: SavedStateHandle,
     private val mediaWebViewPreferences: MediaWebViewPreferences
-) : StateReducerViewModel<MediaWebViewState, MediaWebViewEvent, MediaWebViewAction>() {
-
-    override fun initState(): MediaWebViewState = MediaWebViewState.initial
+) : StateReducerViewModel<MediaWebViewState, MediaWebViewEvent, MediaWebViewAction>(
+    MediaWebViewState.initial
+) {
 
     init {
         mediaWebView.setHandleListener(state::handleEvent)

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

@@ -3,14 +3,21 @@ package com.codeskraps.sbrowser.feature.webview.components
 import android.Manifest
 import android.content.pm.PackageManager
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+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
@@ -28,9 +35,13 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.core.app.ActivityCompat
 import com.codeskraps.sbrowser.MainActivity
@@ -63,13 +74,25 @@ fun AddressButton(
     }
 }
 
+@Preview
+@Composable
+private fun PreviewAddressEditDialog() {
+    AddressEditDialog(
+        url = "https://www.google.com",
+        handleEvent = {},
+        onDismissRequest = {}
+    )
+}
+
 @Composable
 private fun AddressEditDialog(
     url: String,
     handleEvent: (MediaWebViewEvent) -> Unit,
     onDismissRequest: () -> Unit
 ) {
+    val clipboardManager: ClipboardManager = LocalClipboardManager.current
     var editUrl by remember { mutableStateOf(url) }
+    val textClipboard = clipboardManager.getText()?.text ?: ""
 
     AlertDialog(
         onDismissRequest = { onDismissRequest() },
@@ -88,8 +111,22 @@ private fun AddressEditDialog(
         },
         title = { Text(text = "Edit Current Url") },
         text = {
-            Column {
-                Text(text = "Url:")
+            Column(modifier = Modifier.fillMaxWidth()) {
+                Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.Bottom) {
+                    Text(text = "Url:", modifier = Modifier.padding(bottom = 10.dp))
+                    Spacer(modifier = Modifier.weight(1f))
+                    if (textClipboard.isNotEmpty()) {
+                        IconButton(onClick = { editUrl = textClipboard }) {
+                            Icon(
+                                painter = painterResource(id = R.drawable.ic_paste),
+                                contentDescription = "paste"
+                            )
+                        }
+                    }
+                    IconButton(onClick = { editUrl = "" }) {
+                        Icon(imageVector = Icons.Default.Delete, contentDescription = "delete")
+                    }
+                }
                 Spacer(modifier = Modifier.height(10.dp))
                 OutlinedTextField(value = editUrl, onValueChange = { editUrl = it })
             }
@@ -146,7 +183,7 @@ fun GoBackButton(
         }
     }) {
         Icon(
-            imageVector = Icons.Default.ArrowBack,
+            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
             contentDescription = "GoBack",
             tint = if (mediaWebView.canGoBack()) {
                 MaterialTheme.colorScheme.tertiary
@@ -167,7 +204,7 @@ fun GoForwardButton(
         }
     }) {
         Icon(
-            imageVector = Icons.Default.ArrowForward,
+            imageVector = Icons.AutoMirrored.Filled.ArrowForward,
             contentDescription = "GoForward",
             tint = if (mediaWebView.canGoForward()) {
                 MaterialTheme.colorScheme.tertiary

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

@@ -1,11 +1,13 @@
 package com.codeskraps.sbrowser.feature.webview.media
 
+import android.net.Uri
 import android.text.TextUtils
 import android.util.Log
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
 import org.jsoup.nodes.Element
 import org.jsoup.select.Elements
+import java.io.IOException
 import java.util.regex.Matcher
 import java.util.regex.Pattern
 
@@ -16,7 +18,7 @@ class HandleVideo {
     }
 
     operator fun invoke(url: String, result: (String) -> Unit) {
-        try {
+        runCatching {
             val doc: Document = Jsoup.connect(url).get()
             var metalinks: Elements = doc.select("div[id=player]")
 
@@ -87,7 +89,7 @@ class HandleVideo {
                     }
                 }
             }
-        } catch (e: Exception) {
+        }.onFailure { e ->
             Log.e(TAG, "Handled - HandleVideo:$e", e)
         }
     }

+ 2 - 4
app/src/main/java/com/codeskraps/sbrowser/util/StateReducerFlow.kt

@@ -52,17 +52,15 @@ private class StateReducerFlowImpl<STATE, EVENT>(
     }
 }
 
-abstract class StateReducerViewModel<STATE, EVENT, ACTION> : ViewModel() {
+abstract class StateReducerViewModel<STATE, EVENT, ACTION>(initialState: STATE) : ViewModel() {
 
-    private val initState by lazy { initState() }
     val state = StateReducerFlow(
-        initialState = initState,
+        initialState = initialState,
         reduceState = ::reduceState
     )
 
     protected val actionChannel = Channel<ACTION>()
     val action = actionChannel.receiveAsFlow()
 
-    protected abstract fun initState(): STATE
     protected abstract fun reduceState(currentState: STATE, event: EVENT): STATE
 }

+ 5 - 0
app/src/main/res/drawable/ic_paste.xml

@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
+    
+</vector>

+ 5 - 5
gradle/libs.versions.toml

@@ -1,5 +1,5 @@
 [versions]
-agp = "8.2.2"
+agp = "8.3.0"
 kotlin = "1.9.21"
 core-ktx = "1.12.0"
 junit = "4.13.2"
@@ -7,13 +7,13 @@ androidx-test-ext-junit = "1.1.5"
 espresso-core = "3.5.1"
 lifecycle-runtime-ktx = "2.7.0"
 activity-compose = "1.8.2"
-compose-bom = "2023.10.01"
+compose-bom = "2024.02.02"
 ksp = "1.9.21-1.0.16"
-hilt = "2.50"
-hilt-navigation = "1.1.0"
+hilt = "2.51"
+hilt-navigation = "1.2.0"
 room = "2.6.1"
 jsoup = "1.17.2"
-media3_version = "1.2.1"
+media3_version = "1.3.0"
 
 [libraries]
 core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }

+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
 #Thu Jan 18 15:41:38 CET 2024
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists