Add option to free up space for a device folder

This commit is contained in:
Neeraj Gupta 2022-11-24 16:24:59 +05:30
parent cb8962f011
commit 2732b399d4
No known key found for this signature in database
GPG key ID: 3C5A1684DC1729E1
4 changed files with 151 additions and 4 deletions

View file

@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/models/backup_status.dart';
import 'package:photos/models/device_collection.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/file_load_result.dart';
@ -322,6 +323,31 @@ extension DeviceFiles on FilesDB {
return FileLoadResult(dedupe, files.length == limit);
}
Future<BackedUpFileIDs> getBackedUpForDeviceCollection(
String pathID,
int ownerID,
) async {
final db = await database;
const String rawQuery = '''
SELECT ${FilesDB.columnLocalID}, ${FilesDB.columnUploadedFileID}
FROM ${FilesDB.filesTable}
WHERE ${FilesDB.columnLocalID} IS NOT NULL AND
(${FilesDB.columnOwnerID} IS NULL OR ${FilesDB.columnOwnerID} = ?)
AND (${FilesDB.columnUploadedFileID} IS NOT NULL AND ${FilesDB.columnUploadedFileID} IS NOT -1)
AND
${FilesDB.columnLocalID} IN
(SELECT id FROM device_files where path_id = ?)
''';
final results = await db.rawQuery(rawQuery, [ownerID, pathID]);
final localIDs = <String>{};
final uploadedIDs = <int>{};
for (final result in results) {
localIDs.add(result[FilesDB.columnLocalID]);
uploadedIDs.add(result[FilesDB.columnUploadedFileID]);
}
return BackedUpFileIDs(localIDs.toList(), uploadedIDs.toList());
}
Future<List<DeviceCollection>> getDeviceCollections({
bool includeCoverThumbnail = false,
}) async {

View file

@ -12,6 +12,7 @@ import 'package:photos/core/constants.dart';
import 'package:photos/core/errors.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/core/network.dart';
import 'package:photos/db/device_files_db.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/permission_granted_event.dart';
import 'package:photos/events/subscription_purchased_event.dart';
@ -206,8 +207,16 @@ class SyncService {
);
}
Future<BackupStatus> getBackupStatus() async {
final ids = await FilesDB.instance.getBackedUpIDs();
Future<BackupStatus> getBackupStatus({String pathID}) async {
BackedUpFileIDs ids;
if (pathID == null) {
ids = await FilesDB.instance.getBackedUpIDs();
} else {
ids = await FilesDB.instance.getBackedUpForDeviceCollection(
pathID,
Configuration.instance.getUserID(),
);
}
final size = await _getFileSize(ids.uploadedIDs);
return BackupStatus(ids.localIDs, size);
}

View file

@ -1,6 +1,7 @@
// @dart=2.9
import 'dart:async';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
@ -9,18 +10,24 @@ import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/ente_theme_data.dart';
import 'package:photos/events/subscription_purchased_event.dart';
import 'package:photos/models/backup_status.dart';
import 'package:photos/models/collection.dart';
import 'package:photos/models/device_collection.dart';
import 'package:photos/models/gallery_type.dart';
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/services/sync_service.dart';
import 'package:photos/ui/common/dialogs.dart';
import 'package:photos/ui/common/rename_dialog.dart';
import 'package:photos/ui/sharing/share_collection_widget.dart';
import 'package:photos/ui/tools/free_space_page.dart';
import 'package:photos/utils/data_util.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/magic_util.dart';
import 'package:photos/utils/navigation_util.dart';
import 'package:photos/utils/toast_util.dart';
import 'package:url_launcher/url_launcher_string.dart';
class GalleryAppBarWidget extends StatefulWidget {
final GalleryType type;
@ -154,6 +161,93 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
}
}
// todo: In the new design, clicking on free up space will directly open
// the free up space page and show loading indicator while calculating
// the space which can be claimed up. This code duplication should be removed
// whenever we move to the new design for free up space.
Future<dynamic> _deleteDeviceAlbum(BuildContext context) async {
final dialog = createProgressDialog(context, "Calculating...");
await dialog.show();
BackupStatus status;
try {
status = await SyncService.instance
.getBackupStatus(pathID: widget.deviceCollection.id);
} catch (e) {
await dialog.hide();
showGenericErrorDialog(context);
return;
}
await dialog.hide();
if (status.localIDs.isEmpty) {
showErrorDialog(
context,
"✨ All clear",
"You've no files in this folder that can be deleted",
);
} else {
final bool result = await routeToPage(context, FreeSpacePage(status));
if (result == true) {
_showSpaceFreedDialog(status);
}
}
}
void _showSpaceFreedDialog(BackupStatus status) {
final AlertDialog alert = AlertDialog(
title: const Text("Success"),
content: Text(
"You have successfully freed up " + formatBytes(status.size) + "!",
),
actions: [
TextButton(
child: Text(
"Rate us",
style: TextStyle(
color: Theme.of(context).colorScheme.greenAlternative,
),
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop('dialog');
// TODO: Replace with https://pub.dev/packages/in_app_review
if (Platform.isAndroid) {
launchUrlString(
"https://play.google.com/store/apps/details?id=io.ente.photos",
);
} else {
launchUrlString(
"https://apps.apple.com/in/app/ente-photos/id1542026904",
);
}
},
),
TextButton(
child: const Text(
"Ok",
),
onPressed: () {
if (Platform.isIOS) {
showToast(
context,
"Also empty \"Recently Deleted\" from \"Settings\" -> \"Storage\" to claim the freed space",
);
}
Navigator.of(context, rootNavigator: true).pop('dialog');
},
),
],
);
showConfettiDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
barrierColor: Colors.black87,
confettiAlignment: Alignment.topCenter,
useRootNavigator: true,
);
}
List<Widget> _getDefaultActions(BuildContext context) {
final List<Widget> actions = <Widget>[];
if (Configuration.instance.hasConfiguredAccount() &&
@ -242,6 +336,22 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
),
);
}
if (widget.type == GalleryType.localFolder) {
items.add(
PopupMenuItem(
value: 5,
child: Row(
children: const [
Icon(Icons.logout),
Padding(
padding: EdgeInsets.all(8),
),
Text("Free up device space"),
],
),
),
);
}
if (items.isNotEmpty) {
actions.add(
PopupMenuButton(
@ -263,6 +373,8 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
await _trashCollection();
} else if (value == 4) {
await _leaveAlbum(context);
} else if (value == 5) {
await _deleteDeviceAlbum(context);
} else {
showToast(context, "Something went wrong");
}

View file

@ -338,7 +338,7 @@ Future<bool> deleteLocalFiles(
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt < android11SDKINT) {
deletedIDs
.addAll(await _deleteLocalFilesInBatches(context, localAssetIDs));
.addAll(await deleteLocalFilesInBatches(context, localAssetIDs));
} else {
deletedIDs
.addAll(await _deleteLocalFilesInOneShot(context, localAssetIDs));
@ -384,7 +384,7 @@ Future<List<String>> _deleteLocalFilesInOneShot(
return deletedIDs;
}
Future<List<String>> _deleteLocalFilesInBatches(
Future<List<String>> deleteLocalFilesInBatches(
BuildContext context,
List<String> localIDs,
) async {