import 'dart:async'; import 'dart:math'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/entry.dart'; import 'package:aves/services/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/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'; import 'package:flutter/widgets.dart'; mixin SizeAwareMixin { Future checkFreeSpaceForMove( BuildContext context, Set selection, String destinationAlbum, MoveType moveType, ) async { // assume we have enough space if we cannot find the volume or its remaining free space final destinationVolume = androidFileUtils.getStorageVolume(destinationAlbum); if (destinationVolume == null) return true; final free = await storageService.getFreeSpace(destinationVolume); if (free == null) return true; late int needed; int sumSize(sum, entry) => sum + entry.sizeBytes ?? 0; switch (moveType) { case MoveType.copy: case MoveType.export: needed = selection.fold(0, sumSize); break; case MoveType.move: // when moving, we only need space for the entries that are not already on the destination volume final byVolume = groupBy(selection, (entry) => androidFileUtils.getStorageVolume(entry.path)).whereNotNullKey(); final otherVolumes = byVolume.keys.where((volume) => volume != destinationVolume); final fromOtherVolumes = otherVolumes.fold(0, (sum, volume) => sum + byVolume[volume]!.fold(0, sumSize)); // and we need at least as much space as the largest entry because individual entries are copied then deleted final largestSingle = selection.fold(0, (largest, entry) => max(largest, entry.sizeBytes ?? 0)); needed = max(fromOtherVolumes, largestSingle); break; } final hasEnoughSpace = needed < free; if (!hasEnoughSpace) { await _showNotEnoughSpaceDialog(context, needed, free, destinationVolume); } return hasEnoughSpace; } Future checkFreeSpace( BuildContext context, int needed, String destinationAlbum, ) async { // assume we have enough space if we cannot find the volume or its remaining free space final destinationVolume = androidFileUtils.getStorageVolume(destinationAlbum); if (destinationVolume == null) return true; final free = await storageService.getFreeSpace(destinationVolume); if (free == null) return true; final hasEnoughSpace = needed < free; if (!hasEnoughSpace) { await _showNotEnoughSpaceDialog(context, needed, free, destinationVolume); } return hasEnoughSpace; } Future _showNotEnoughSpaceDialog(BuildContext context, int needed, int free, StorageVolume destinationVolume) async { await showDialog( context: context, builder: (context) { final neededSize = formatFilesize(needed); final freeSize = formatFilesize(free); final volume = destinationVolume.getDescription(context); return AvesDialog( context: context, title: context.l10n.notEnoughSpaceDialogTitle, content: Text(context.l10n.notEnoughSpaceDialogMessage(neededSize, freeSize, volume)), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text(MaterialLocalizations.of(context).okButtonLabel), ), ], ); }, ); } }