119 lines
4.8 KiB
Dart
119 lines
4.8 KiB
Dart
import 'package:aves/model/entry.dart';
|
|
import 'package:aves/services/common/services.dart';
|
|
import 'package:aves/utils/android_file_utils.dart';
|
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
mixin PermissionAwareMixin {
|
|
Future<bool> checkStoragePermission(BuildContext context, Set<AvesEntry> entries) {
|
|
return checkStoragePermissionForAlbums(context, entries.map((e) => e.directory).whereNotNull().toSet(), entries: entries);
|
|
}
|
|
|
|
Future<bool> checkStoragePermissionForAlbums(BuildContext context, Set<String> albumPaths, {Set<AvesEntry>? entries}) async {
|
|
final restrictedDirs = await storageService.getRestrictedDirectories();
|
|
while (true) {
|
|
final dirs = await storageService.getInaccessibleDirectories(albumPaths);
|
|
|
|
final restrictedInaccessibleDirs = dirs.where(restrictedDirs.contains).toSet();
|
|
if (restrictedInaccessibleDirs.isNotEmpty) {
|
|
if (entries != null && await storageService.canRequestMediaFileAccess()) {
|
|
// request media file access for items in restricted directories
|
|
final uris = <String>[], mimeTypes = <String>[];
|
|
entries.where((entry) {
|
|
final dir = entry.directory;
|
|
return dir != null && restrictedInaccessibleDirs.contains(VolumeRelativeDirectory.fromPath(dir));
|
|
}).forEach((entry) {
|
|
uris.add(entry.uri);
|
|
mimeTypes.add(entry.mimeType);
|
|
});
|
|
final granted = await storageService.requestMediaFileAccess(uris, mimeTypes);
|
|
if (!granted) return false;
|
|
} else if (entries == null && await storageService.canInsertMedia(restrictedInaccessibleDirs)) {
|
|
// insertion in restricted directories
|
|
} else {
|
|
// cannot proceed further
|
|
await showRestrictedDirectoryDialog(context, restrictedInaccessibleDirs.first);
|
|
return false;
|
|
}
|
|
// clear restricted directories
|
|
dirs.removeAll(restrictedInaccessibleDirs);
|
|
}
|
|
|
|
if (dirs.isEmpty) return true;
|
|
|
|
final dir = dirs.first;
|
|
final confirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) {
|
|
final l10n = context.l10n;
|
|
final directory = dir.relativeDir.isEmpty ? l10n.rootDirectoryDescription : l10n.otherDirectoryDescription(dir.relativeDir);
|
|
final volume = dir.getVolumeDescription(context);
|
|
return AvesDialog(
|
|
title: l10n.storageAccessDialogTitle,
|
|
content: Text(l10n.storageAccessDialogMessage(directory, volume)),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, true),
|
|
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
// abort if the user cancels in Flutter
|
|
if (confirmed == null || !confirmed) return false;
|
|
|
|
if (!await deviceService.isSystemFilePickerEnabled()) {
|
|
await showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
final l10n = context.l10n;
|
|
return AvesDialog(
|
|
title: l10n.missingSystemFilePickerDialogTitle,
|
|
content: Text(l10n.missingSystemFilePickerDialogMessage),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
return false;
|
|
}
|
|
|
|
final granted = await storageService.requestDirectoryAccess(dir.volumePath);
|
|
if (!granted) {
|
|
// abort if the user denies access from the native dialog
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<bool?> showRestrictedDirectoryDialog(BuildContext context, VolumeRelativeDirectory dir) {
|
|
return showDialog<bool>(
|
|
context: context,
|
|
builder: (context) {
|
|
final directory = dir.relativeDir.isEmpty ? context.l10n.rootDirectoryDescription : context.l10n.otherDirectoryDescription(dir.relativeDir);
|
|
final volume = dir.getVolumeDescription(context);
|
|
return AvesDialog(
|
|
title: context.l10n.restrictedAccessDialogTitle,
|
|
content: Text(context.l10n.restrictedAccessDialogMessage(directory, volume)),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|