fix(ios): use security-scoped bookmarks for download directory persistence

- Switch iOS bookmark creation from .minimalBookmark to .withSecurityScope
- Add .withSecurityScope option when resolving bookmarks
- Add downloadDirectoryBookmark field to AppSettings for persisting iOS bookmarks
- Resolve bookmark and startAccessingIosBookmark before queue processing
- Guarantee stopAccessingIosBookmark cleanup via try/finally
- Create bookmark on folder pick in both setup screen and download settings
- Clear bookmark when switching to SAF mode or iOS path normalization
- Fix stale bottom sheet context usage (ctx -> context) in download settings
This commit is contained in:
zarzet
2026-05-02 01:19:39 +07:00
parent 64dbf4441c
commit 45fa33e1ec
7 changed files with 161 additions and 41 deletions
+32 -1
View File
@@ -4280,6 +4280,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final settings = ref.read(settingsProvider);
updateSettings(settings);
final isSafMode = _isSafMode(settings);
var iosDownloadBookmarkActive = false;
if (settings.downloadNetworkMode == 'wifi_only') {
final connectivityResult = await Connectivity().checkConnectivity();
final hasWifi = connectivityResult.contains(ConnectivityResult.wifi);
@@ -4391,8 +4392,38 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
return;
}
}
if (!isSafMode &&
Platform.isIOS &&
settings.downloadDirectoryBookmark.isNotEmpty) {
final resolvedPath = await PlatformBridge.startAccessingIosBookmark(
settings.downloadDirectoryBookmark,
);
if (resolvedPath != null && resolvedPath.isNotEmpty) {
iosDownloadBookmarkActive = true;
if (resolvedPath != state.outputDir) {
_log.i('Resolved iOS download bookmark path: $resolvedPath');
state = state.copyWith(outputDir: resolvedPath);
}
} else {
_log.w(
'Failed to access iOS download folder bookmark, falling back to app Documents folder',
);
final musicDir = await _ensureDefaultDocumentsOutputDir();
state = state.copyWith(outputDir: musicDir.path);
ref.read(settingsProvider.notifier).setDownloadDirectory(musicDir.path);
}
}
_log.d('Concurrent downloads: ${state.concurrentDownloads}');
await _processQueueParallel();
try {
await _processQueueParallel();
} finally {
if (iosDownloadBookmarkActive) {
await PlatformBridge.stopAccessingIosBookmark();
iosDownloadBookmarkActive = false;
}
}
final stoppedWhilePaused = state.isPaused;
final keepConnectivityMonitoring =
stoppedWhilePaused && _networkPausedByWifiOnly;
+12 -3
View File
@@ -192,7 +192,10 @@ class SettingsNotifier extends Notifier<AppSettings> {
if (normalizedDir == currentDir) return;
_log.i('Normalized iOS download directory: $currentDir -> $normalizedDir');
state = state.copyWith(downloadDirectory: normalizedDir);
state = state.copyWith(
downloadDirectory: normalizedDir,
downloadDirectoryBookmark: '',
);
await _saveSettings();
}
@@ -260,8 +263,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
_saveSettings();
}
void setDownloadDirectory(String directory) {
state = state.copyWith(downloadDirectory: directory);
void setDownloadDirectory(String directory, {String? iosBookmark}) {
state = state.copyWith(
downloadDirectory: directory,
downloadDirectoryBookmark: iosBookmark ?? '',
);
_saveSettings();
}
@@ -277,6 +283,9 @@ class SettingsNotifier extends Notifier<AppSettings> {
downloadTreeUri: uri,
storageMode: uri.isNotEmpty ? 'saf' : state.storageMode,
downloadDirectory: nextDisplay,
downloadDirectoryBookmark: uri.isNotEmpty
? ''
: state.downloadDirectoryBookmark,
);
_saveSettings();
}