@@ -370,7 +370,40 @@ fun OpenAudioBookifyApp(viewModel: MainViewModel) {
370370 onAddBooksClick = { filePickerLauncher.launch(arrayOf(" */*" )) },
371371 onClearBooksClick = { viewModel.clearBooks() },
372372 onRemoveBookClick = { book -> viewModel.removeBook(book) },
373- onSetOutputFolderClick = { dirPickerLauncher.launch(null ) },
373+ onSetOutputFolderClick = {
374+ try {
375+ dirPickerLauncher.launch(null )
376+ } catch (e: android.content.ActivityNotFoundException ) {
377+ // user might have deleted the Files app to "debloat" their phone
378+ // fallback to Downloads or App Dir
379+ Log .e(TAG , " DocumentsUI missing, falling back to local folders" , e)
380+
381+ // Fallback 1: System Downloads folder
382+ val downloadsDir = android.os.Environment .getExternalStoragePublicDirectory(
383+ android.os.Environment .DIRECTORY_DOWNLOADS
384+ )
385+
386+ // Note: On Android 10, raw file writing to Downloads might fail without
387+ // requestLegacyExternalStorage="true" in the Manifest, so we check if it's writable.
388+ if (downloadsDir != null && downloadsDir.exists() && downloadsDir.canWrite()) {
389+ viewModel.setOutputDirUri(Uri .fromFile(downloadsDir))
390+ Toast .makeText(context, context.getString(R .string.using_downloads), Toast .LENGTH_LONG ).show()
391+ } else {
392+ // Fallback 2: App Data folder
393+ val appDataDir = context.getExternalFilesDir(null )
394+ if (appDataDir != null ) {
395+ viewModel.setOutputDirUri(Uri .fromFile(appDataDir))
396+ Toast .makeText(
397+ context,
398+ context.getString(R .string.using_app_private_dir, context.packageName),
399+ Toast .LENGTH_LONG
400+ ).show()
401+ } else {
402+ Toast .makeText(context, context.getString(R .string.err_no_writeable_dir), Toast .LENGTH_LONG ).show()
403+ }
404+ }
405+ }
406+ },
374407 onStartProcessingClick = {
375408 if (outputDirUri != null && selectedBooks.isNotEmpty()) {
376409 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU && ! hasNotificationPermission) {
0 commit comments