Skip to content

Commit be21d9c

Browse files
Refactor permission handling for Samsung devices
This commit introduces a warning dialog for Samsung devices when media permissions are repeatedly denied. Instead of exiting the app, you are prompted to use the Storage Access Framework (SAF) to select a directory. Changes include: - Added a new state variable `showSamsungWarningDialog`. - Modified `requestPermissionLauncher` to trigger the Samsung warning dialog after two permission denials. - Introduced `safLauncher` to handle directory selection via SAF. If you cancel SAF selection, the Samsung warning is shown again. - Updated `setContent` to display the `SamsungWarningDialog`. This dialog launches SAF, attempting to point to `DCIM/Screenshots`. - Added the `SamsungWarningDialog` composable. - Reviewed `AndroidManifest.xml`; no changes were necessary for SAF.
1 parent 051e8ee commit be21d9c

1 file changed

Lines changed: 84 additions & 7 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/MainActivity.kt

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import android.net.Uri
1313
import android.os.Build
1414
import android.graphics.Rect
1515
import android.os.Bundle
16+
import android.provider.DocumentsContract
17+
import android.provider.MediaStore
1618
import android.provider.Settings
1719
import android.util.Log
1820
import 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
9291006
fun FirstLaunchInfoDialog(onDismiss: () -> Unit) {
9301007
Log.d("FirstLaunchInfoDialog", "Composing FirstLaunchInfoDialog")

0 commit comments

Comments
 (0)