mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-20 07:04:49 +02:00
feat(android13): make All Files Access optional
- Android 13+ now only requires READ_MEDIA_AUDIO by default - MANAGE_EXTERNAL_STORAGE is optional and can be enabled in Settings - Added 'All Files Access' toggle in Download Settings (Android 13+ only) - Users who encounter write errors can enable full storage access - Respects privacy-conscious users who prefer limited permissions
This commit is contained in:
@@ -3957,6 +3957,48 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to fetch some albums'**
|
||||
String get discographyFailedToFetch;
|
||||
|
||||
/// Section header for storage access settings
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Storage Access'**
|
||||
String get sectionStorageAccess;
|
||||
|
||||
/// Toggle for MANAGE_EXTERNAL_STORAGE permission
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All Files Access'**
|
||||
String get allFilesAccess;
|
||||
|
||||
/// Subtitle when all files access is enabled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Can write to any folder'**
|
||||
String get allFilesAccessEnabledSubtitle;
|
||||
|
||||
/// Subtitle when all files access is disabled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Limited to media folders only'**
|
||||
String get allFilesAccessDisabledSubtitle;
|
||||
|
||||
/// Description explaining when to enable all files access
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.'**
|
||||
String get allFilesAccessDescription;
|
||||
|
||||
/// Message when permission is permanently denied
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Permission was denied. Please enable \'All files access\' manually in system settings.'**
|
||||
String get allFilesAccessDeniedMessage;
|
||||
|
||||
/// Snackbar message when user disables all files access
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All Files Access disabled. The app will use limited storage access.'**
|
||||
String get allFilesAccessDisabledMessage;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -2199,4 +2199,28 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,4 +2184,28 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,6 +2184,30 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
/// The translations for Spanish Castilian, as used in Spain (`es_ES`).
|
||||
|
||||
@@ -2184,4 +2184,28 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,4 +2184,28 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2197,4 +2197,28 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Gagal mengambil beberapa album';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2170,4 +2170,28 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => '一部のアルバムの取得に失敗しました';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,4 +2184,28 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,4 +2184,28 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,6 +2184,30 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
/// The translations for Portuguese, as used in Portugal (`pt_PT`).
|
||||
|
||||
@@ -2230,4 +2230,28 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get discographyFailedToFetch =>
|
||||
'Не удалось получить некоторые альбомы';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2199,4 +2199,28 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Bazı albümler alınamadı';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
@@ -2184,6 +2184,30 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get discographyFailedToFetch => 'Failed to fetch some albums';
|
||||
|
||||
@override
|
||||
String get sectionStorageAccess => 'Storage Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccess => 'All Files Access';
|
||||
|
||||
@override
|
||||
String get allFilesAccessEnabledSubtitle => 'Can write to any folder';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledSubtitle => 'Limited to media folders only';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDescription =>
|
||||
'Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDeniedMessage =>
|
||||
'Permission was denied. Please enable \'All files access\' manually in system settings.';
|
||||
|
||||
@override
|
||||
String get allFilesAccessDisabledMessage =>
|
||||
'All Files Access disabled. The app will use limited storage access.';
|
||||
}
|
||||
|
||||
/// The translations for Chinese, as used in China (`zh_CN`).
|
||||
|
||||
+16
-1
@@ -1646,5 +1646,20 @@
|
||||
"discographyNoAlbums": "No albums available",
|
||||
"@discographyNoAlbums": {"description": "Error - no albums found for artist"},
|
||||
"discographyFailedToFetch": "Failed to fetch some albums",
|
||||
"@discographyFailedToFetch": {"description": "Error - some albums failed to load"}
|
||||
"@discographyFailedToFetch": {"description": "Error - some albums failed to load"},
|
||||
|
||||
"sectionStorageAccess": "Storage Access",
|
||||
"@sectionStorageAccess": {"description": "Section header for storage access settings"},
|
||||
"allFilesAccess": "All Files Access",
|
||||
"@allFilesAccess": {"description": "Toggle for MANAGE_EXTERNAL_STORAGE permission"},
|
||||
"allFilesAccessEnabledSubtitle": "Can write to any folder",
|
||||
"@allFilesAccessEnabledSubtitle": {"description": "Subtitle when all files access is enabled"},
|
||||
"allFilesAccessDisabledSubtitle": "Limited to media folders only",
|
||||
"@allFilesAccessDisabledSubtitle": {"description": "Subtitle when all files access is disabled"},
|
||||
"allFilesAccessDescription": "Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.",
|
||||
"@allFilesAccessDescription": {"description": "Description explaining when to enable all files access"},
|
||||
"allFilesAccessDeniedMessage": "Permission was denied. Please enable 'All files access' manually in system settings.",
|
||||
"@allFilesAccessDeniedMessage": {"description": "Message when permission is permanently denied"},
|
||||
"allFilesAccessDisabledMessage": "All Files Access disabled. The app will use limited storage access.",
|
||||
"@allFilesAccessDisabledMessage": {"description": "Snackbar message when user disables all files access"}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class AppSettings {
|
||||
final String lossyFormat;
|
||||
final String lossyBitrate; // e.g., 'mp3_320', 'mp3_256', 'mp3_192', 'mp3_128', 'opus_128', 'opus_96', 'opus_64'
|
||||
final String lyricsMode;
|
||||
final bool useAllFilesAccess; // Android 13+ only: enable MANAGE_EXTERNAL_STORAGE
|
||||
|
||||
const AppSettings({
|
||||
this.defaultService = 'tidal',
|
||||
@@ -68,6 +69,7 @@ class AppSettings {
|
||||
this.lossyFormat = 'mp3',
|
||||
this.lossyBitrate = 'mp3_320',
|
||||
this.lyricsMode = 'embed',
|
||||
this.useAllFilesAccess = false,
|
||||
});
|
||||
|
||||
AppSettings copyWith({
|
||||
@@ -103,6 +105,7 @@ class AppSettings {
|
||||
String? lossyFormat,
|
||||
String? lossyBitrate,
|
||||
String? lyricsMode,
|
||||
bool? useAllFilesAccess,
|
||||
}) {
|
||||
return AppSettings(
|
||||
defaultService: defaultService ?? this.defaultService,
|
||||
@@ -136,6 +139,7 @@ class AppSettings {
|
||||
lossyFormat: lossyFormat ?? this.lossyFormat,
|
||||
lossyBitrate: lossyBitrate ?? this.lossyBitrate,
|
||||
lyricsMode: lyricsMode ?? this.lyricsMode,
|
||||
useAllFilesAccess: useAllFilesAccess ?? this.useAllFilesAccess,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
||||
lossyFormat: json['lossyFormat'] as String? ?? 'mp3',
|
||||
lossyBitrate: json['lossyBitrate'] as String? ?? 'mp3_320',
|
||||
lyricsMode: json['lyricsMode'] as String? ?? 'embed',
|
||||
useAllFilesAccess: json['useAllFilesAccess'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
|
||||
@@ -75,4 +76,5 @@ Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
|
||||
'lossyFormat': instance.lossyFormat,
|
||||
'lossyBitrate': instance.lossyBitrate,
|
||||
'lyricsMode': instance.lyricsMode,
|
||||
'useAllFilesAccess': instance.useAllFilesAccess,
|
||||
};
|
||||
|
||||
@@ -251,6 +251,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
state = state.copyWith(lossyBitrate: bitrate, lossyFormat: format);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setUseAllFilesAccess(bool enabled) {
|
||||
state = state.copyWith(useAllFilesAccess: enabled);
|
||||
_saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
final settingsProvider = NotifierProvider<SettingsNotifier, AppSettings>(
|
||||
|
||||
@@ -3,18 +3,92 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
|
||||
class DownloadSettingsPage extends ConsumerWidget {
|
||||
class DownloadSettingsPage extends ConsumerStatefulWidget {
|
||||
const DownloadSettingsPage({super.key});
|
||||
|
||||
static const _builtInServices = ['tidal', 'qobuz', 'amazon'];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<DownloadSettingsPage> createState() => _DownloadSettingsPageState();
|
||||
}
|
||||
|
||||
class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
static const _builtInServices = ['tidal', 'qobuz', 'amazon'];
|
||||
int _androidSdkVersion = 0;
|
||||
bool _hasAllFilesAccess = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initDeviceInfo();
|
||||
}
|
||||
|
||||
Future<void> _initDeviceInfo() async {
|
||||
if (Platform.isAndroid) {
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
final androidInfo = await deviceInfo.androidInfo;
|
||||
final sdkVersion = androidInfo.version.sdkInt;
|
||||
final hasAccess = await Permission.manageExternalStorage.isGranted;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_androidSdkVersion = sdkVersion;
|
||||
_hasAllFilesAccess = hasAccess;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _requestAllFilesAccess() async {
|
||||
final status = await Permission.manageExternalStorage.request();
|
||||
if (status.isGranted) {
|
||||
ref.read(settingsProvider.notifier).setUseAllFilesAccess(true);
|
||||
if (mounted) {
|
||||
setState(() => _hasAllFilesAccess = true);
|
||||
}
|
||||
} else if (status.isPermanentlyDenied) {
|
||||
if (mounted) {
|
||||
final shouldOpen = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.l10n.setupStorageAccessRequired),
|
||||
content: Text(context.l10n.allFilesAccessDeniedMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: Text(context.l10n.dialogCancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: Text(context.l10n.setupOpenSettings),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (shouldOpen == true) {
|
||||
await openAppSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _disableAllFilesAccess() async {
|
||||
ref.read(settingsProvider.notifier).setUseAllFilesAccess(false);
|
||||
// Note: We can't revoke the permission programmatically,
|
||||
// but we can stop using it in the app
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.allFilesAccessDisabledMessage)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settings = ref.watch(settingsProvider);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final topPadding = MediaQuery.of(context).padding.top;
|
||||
@@ -270,6 +344,59 @@ class DownloadSettingsPage extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
|
||||
// All Files Access section (Android 13+ only)
|
||||
if (Platform.isAndroid && _androidSdkVersion >= 33) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsSectionHeader(title: context.l10n.sectionStorageAccess),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsGroup(
|
||||
children: [
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.folder_special_outlined,
|
||||
title: context.l10n.allFilesAccess,
|
||||
subtitle: _hasAllFilesAccess
|
||||
? context.l10n.allFilesAccessEnabledSubtitle
|
||||
: context.l10n.allFilesAccessDisabledSubtitle,
|
||||
value: _hasAllFilesAccess && settings.useAllFilesAccess,
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
_requestAllFilesAccess();
|
||||
} else {
|
||||
_disableAllFilesAccess();
|
||||
}
|
||||
},
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 16,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
context.l10n.allFilesAccessDescription,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 32)),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -67,10 +67,11 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
|
||||
bool storageGranted = false;
|
||||
|
||||
if (_androidSdkVersion >= 33) {
|
||||
final manageStatus = await Permission.manageExternalStorage.status;
|
||||
// Android 13+: Only require READ_MEDIA_AUDIO by default
|
||||
// MANAGE_EXTERNAL_STORAGE is optional and can be enabled in settings
|
||||
final audioStatus = await Permission.audio.status;
|
||||
debugPrint('[Permission] Android 13+ check: MANAGE_EXTERNAL_STORAGE=$manageStatus, READ_MEDIA_AUDIO=$audioStatus');
|
||||
storageGranted = manageStatus.isGranted && audioStatus.isGranted;
|
||||
debugPrint('[Permission] Android 13+ check: READ_MEDIA_AUDIO=$audioStatus');
|
||||
storageGranted = audioStatus.isGranted;
|
||||
} else if (_androidSdkVersion >= 30) {
|
||||
final manageStatus = await Permission.manageExternalStorage.status;
|
||||
debugPrint('[Permission] Android 11-12 check: MANAGE_EXTERNAL_STORAGE=$manageStatus');
|
||||
@@ -108,44 +109,20 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
|
||||
bool allGranted = false;
|
||||
|
||||
if (_androidSdkVersion >= 33) {
|
||||
var manageStatus = await Permission.manageExternalStorage.status;
|
||||
if (!manageStatus.isGranted) {
|
||||
if (mounted) {
|
||||
final shouldOpen = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.l10n.setupStorageAccessRequired),
|
||||
content: Text(
|
||||
'${context.l10n.setupStorageAccessMessage}\n\n'
|
||||
'${context.l10n.setupAllowAccessToManageFiles}',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: Text(context.l10n.dialogCancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: Text(context.l10n.setupOpenSettings),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (shouldOpen == true) {
|
||||
await Permission.manageExternalStorage.request();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
manageStatus = await Permission.manageExternalStorage.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Android 13+: Only request READ_MEDIA_AUDIO by default
|
||||
// MANAGE_EXTERNAL_STORAGE is optional (can be enabled in Settings)
|
||||
var audioStatus = await Permission.audio.status;
|
||||
if (!audioStatus.isGranted && manageStatus.isGranted) {
|
||||
if (!audioStatus.isGranted) {
|
||||
audioStatus = await Permission.audio.request();
|
||||
}
|
||||
|
||||
allGranted = manageStatus.isGranted && audioStatus.isGranted;
|
||||
allGranted = audioStatus.isGranted;
|
||||
|
||||
if (audioStatus.isPermanentlyDenied) {
|
||||
_showPermissionDeniedDialog('Audio');
|
||||
setState(() => _isLoading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (_androidSdkVersion >= 30) {
|
||||
var manageStatus = await Permission.manageExternalStorage.status;
|
||||
|
||||
Reference in New Issue
Block a user