@@ -13,6 +13,8 @@ import android.net.Uri
1313import android.os.Build
1414import android.graphics.Rect
1515import android.os.Bundle
16+ import android.provider.DocumentsContract
17+ import android.provider.MediaStore
1618import android.provider.Settings
1719import android.util.Log
1820import android.view.View
@@ -96,6 +98,7 @@ class MainActivity : ComponentActivity() {
9698
9799 private var showPermissionRationaleDialog by mutableStateOf(false )
98100 private var permissionRequestCount by mutableStateOf(0 )
101+ private var showSamsungWarningDialog by mutableStateOf(false )
99102
100103 private lateinit var navController: NavHostController
101104
@@ -117,17 +120,30 @@ class MainActivity : ComponentActivity() {
117120 Log .i(TAG , " Permissions denied once. Showing rationale dialog again for a second attempt." )
118121 showPermissionRationaleDialog = true
119122 } else if (permissionRequestCount >= 2 ) {
120- Log .w(TAG , " Permissions denied after second formal request (request count: $permissionRequestCount ). App will exit." )
121- Toast .makeText(this , " Without this authorization, operation is not possible." , Toast .LENGTH_LONG ).show()
122- finish()
123- } else {
124- Log .e(TAG , " Permissions denied with unexpected permissionRequestCount: $permissionRequestCount . Exiting." )
125- Toast .makeText(this , " Permissions repeatedly denied. Operation not possible." , Toast .LENGTH_LONG ).show()
126- finish()
123+ Log .w(TAG , " Permissions denied after second formal request (request count: $permissionRequestCount ). Showing Samsung warning." )
124+ showSamsungWarningDialog = true
127125 }
128126 }
129127 }
130128
129+ // Storage Access Framework launcher for directory selection
130+ private val safLauncher = registerForActivityResult(
131+ ActivityResultContracts .OpenDocumentTree ()
132+ ) { uri ->
133+ Log .d(TAG , " SAF launcher callback received. URI: $uri " )
134+ if (uri != null ) {
135+ // Take persistable URI permission
136+ val takeFlags = Intent .FLAG_GRANT_READ_URI_PERMISSION
137+ contentResolver.takePersistableUriPermission(uri, takeFlags)
138+ Log .i(TAG , " SAF directory selected and permission granted: $uri " )
139+ updateStatusMessage(" Directory access granted" )
140+ } else {
141+ Log .w(TAG , " SAF directory selection cancelled by user. Showing warning again." )
142+ // Show Samsung warning again to retry SAF
143+ showSamsungWarningDialog = true
144+ }
145+ }
146+
131147 // START: Added for Accessibility Service Status
132148 private val _isAccessibilityServiceEnabled = MutableStateFlow (false )
133149 val isAccessibilityServiceEnabledFlow: StateFlow <Boolean > = _isAccessibilityServiceEnabled .asStateFlow()
@@ -381,6 +397,23 @@ class MainActivity : ComponentActivity() {
381397 requestPermissionLauncher.launch(requiredPermissions)
382398 }
383399 )
400+ } else if (showSamsungWarningDialog) {
401+ Log .d(TAG , " setContent: Rendering SamsungWarningDialog" )
402+ SamsungWarningDialog (
403+ onDismiss = {
404+ Log .i(TAG , " SamsungWarningDialog OK clicked. Launching SAF for DCIM/Screenshots" )
405+ showSamsungWarningDialog = false
406+ // Launch SAF pointing to DCIM/Screenshots if possible
407+ val intent = Intent (Intent .ACTION_OPEN_DOCUMENT_TREE ).apply {
408+ // Try to pre-select DCIM/Screenshots directory
409+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
410+ putExtra(DocumentsContract .EXTRA_INITIAL_URI ,
411+ MediaStore .Images .Media .EXTERNAL_CONTENT_URI )
412+ }
413+ }
414+ safLauncher.launch(null ) // Changed from intent to null based on SAF behavior
415+ }
416+ )
384417 } else if (showFirstLaunchInfoDialog) {
385418 Log .d(TAG , " setContent: Rendering FirstLaunchInfoDialog." )
386419 FirstLaunchInfoDialog (
@@ -925,6 +958,50 @@ class MainActivity : ComponentActivity() {
925958 }
926959}
927960
961+ @Composable
962+ fun SamsungWarningDialog (onDismiss : () -> Unit ) {
963+ Log .d(" SamsungWarningDialog" , " Composing SamsungWarningDialog" )
964+ Dialog (onDismissRequest = {
965+ Log .d(" SamsungWarningDialog" , " onDismissRequest called" )
966+ onDismiss()
967+ }) {
968+ Card (
969+ modifier = Modifier
970+ .fillMaxWidth()
971+ .padding(16 .dp),
972+ ) {
973+ Column (
974+ modifier = Modifier
975+ .padding(16 .dp)
976+ .fillMaxWidth(),
977+ verticalArrangement = Arrangement .Center ,
978+ horizontalAlignment = Alignment .CenterHorizontally
979+ ) {
980+ Text (
981+ text = " Samsung Device Issue" ,
982+ style = MaterialTheme .typography.titleLarge
983+ )
984+ Spacer (modifier = Modifier .height(16 .dp))
985+ Text (
986+ text = " On some Samsung devices there is an issue with media permissions. You will now be asked to select a directory instead." ,
987+ style = MaterialTheme .typography.bodyMedium,
988+ modifier = Modifier .align(Alignment .CenterHorizontally )
989+ )
990+ Spacer (modifier = Modifier .height(24 .dp))
991+ TextButton (
992+ onClick = {
993+ Log .d(" SamsungWarningDialog" , " OK button clicked" )
994+ onDismiss()
995+ },
996+ modifier = Modifier .fillMaxWidth()
997+ ) {
998+ Text (" OK" )
999+ }
1000+ }
1001+ }
1002+ }
1003+ }
1004+
9281005@Composable
9291006fun FirstLaunchInfoDialog (onDismiss : () -> Unit ) {
9301007 Log .d(" FirstLaunchInfoDialog" , " Composing FirstLaunchInfoDialog" )
0 commit comments