Skip to content

Commit

Permalink
feat(mobile): use app without storage permission (immich-app#5014)
Browse files Browse the repository at this point in the history
* feat(mobile): use app without storage permission

* address review feedback
  • Loading branch information
fyfrey authored Nov 14, 2023
1 parent 8f3ed8b commit 5145c33
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 232 deletions.
6 changes: 2 additions & 4 deletions mobile/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
"backup_controller_page_failed": "Failed ({})",
"backup_controller_page_filename": "File name: {} [{}]",
"backup_controller_page_id": "ID: {}",
"backup_controller_page_info": "Backup Information",
"backup_controller_page_none_selected": "None selected",
"backup_controller_page_remainder": "Remainder",
"backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection",
Expand All @@ -96,7 +95,6 @@
"backup_controller_page_turn_off": "Turn off foreground backup",
"backup_controller_page_turn_on": "Turn on foreground backup",
"backup_controller_page_uploading_file_info": "Uploading file info",
"backup_err_only_album": "Cannot remove the only album",
"backup_info_card_assets": "assets",
"backup_manual_cancelled": "Cancelled",
"backup_manual_failed": "Failed",
Expand Down Expand Up @@ -253,11 +251,11 @@
"permission_onboarding_get_started": "Get started",
"permission_onboarding_go_to_settings": "Go to settings",
"permission_onboarding_grant_permission": "Grant permission",
"permission_onboarding_log_out": "Log out",
"permission_onboarding_back": "Back",
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
"permission_onboarding_request": "Immich requires permission to view your photos and videos.",
"permission_onboarding_request": "Immich requires permission to access your photos and videos.",
"profile_drawer_app_logs": "Logs",
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
"profile_drawer_documentation": "Documentation",
Expand Down
2 changes: 2 additions & 0 deletions mobile/lib/modules/album/providers/album.provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
_albumService.refreshRemoteAlbums(isShared: false),
]);

Future<void> getDeviceAlbums() => _albumService.refreshDeviceAlbums();

Future<bool> deleteAlbum(Album album) => _albumService.deleteAlbum(album);

Future<Album?> createAlbum(
Expand Down
4 changes: 4 additions & 0 deletions mobile/lib/modules/album/services/album.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class AlbumService {
final List<String> selectedIds =
await _backupService.selectedAlbumsQuery().idProperty().findAll();
if (selectedIds.isEmpty) {
final numLocal = await _db.albums.where().localIdIsNotNull().count();
if (numLocal > 0) {
_syncService.removeAllLocalAlbumsAndAssets();
}
return false;
}
final List<AssetPathEntity> onDevice =
Expand Down
45 changes: 14 additions & 31 deletions mobile/lib/modules/backup/providers/backup.provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {

state = state
.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album});
_updateBackupAssetCount();
}

void addExcludedAlbumForBackup(AvailableAlbum album) {
Expand All @@ -98,7 +97,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {
}
state = state
.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album});
_updateBackupAssetCount();
}

void removeAlbumForBackup(AvailableAlbum album) {
Expand All @@ -107,7 +105,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {
currentSelectedAlbums.removeWhere((a) => a == album);

state = state.copyWith(selectedBackupAlbums: currentSelectedAlbums);
_updateBackupAssetCount();
}

void removeExcludedAlbumForBackup(AvailableAlbum album) {
Expand All @@ -116,7 +113,20 @@ class BackupNotifier extends StateNotifier<BackUpState> {
currentExcludedAlbums.removeWhere((a) => a == album);

state = state.copyWith(excludedBackupAlbums: currentExcludedAlbums);
_updateBackupAssetCount();
}

Future<void> backupAlbumSelectionDone() {
if (state.selectedBackupAlbums.isEmpty) {
// disable any backup
cancelBackup();
setAutoBackup(false);
configureBackgroundBackup(
enabled: false,
onError: (msg) {},
onBatteryInfo: () {},
);
}
return _updateBackupAssetCount();
}

void setAutoBackup(bool enabled) {
Expand Down Expand Up @@ -249,30 +259,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {
final List<BackupAlbum> selectedBackupAlbums =
await _backupService.selectedAlbumsQuery().findAll();

// First time backup - set isAll album is the default one for backup.
if (selectedBackupAlbums.isEmpty) {
log.info("First time backup; setup 'Recent(s)' album as default");

// Get album that contains all assets
final list = await PhotoManager.getAssetPathList(
hasAll: true,
onlyAll: true,
type: RequestType.common,
);

if (list.isEmpty) {
return;
}
AssetPathEntity albumHasAllAssets = list.first;

final ba = BackupAlbum(
albumHasAllAssets.id,
DateTime.fromMillisecondsSinceEpoch(0),
BackupSelection.select,
);
await _db.writeTxn(() => _db.backupAlbums.put(ba));
}

// Generate AssetPathEntity from id to add to local state
final Set<AvailableAlbum> selectedAlbums = {};
for (final BackupAlbum ba in selectedBackupAlbums) {
Expand Down Expand Up @@ -362,7 +348,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {
allUniqueAssets: {},
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
);
return;
} else {
state = state.copyWith(
allAssetsInDatabase: allAssetsInDatabase,
Expand All @@ -373,8 +358,6 @@ class BackupNotifier extends StateNotifier<BackUpState> {

// Save to persistent storage
await _updatePersistentAlbumsSelection();

return;
}

/// Get all necessary information for calculating the available albums,
Expand Down
31 changes: 4 additions & 27 deletions mobile/lib/modules/backup/ui/album_info_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,9 @@ class AlbumInfoCard extends HookConsumerWidget {
HapticFeedback.selectionClick();

if (isSelected) {
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
ImmichToast.show(
context: context,
msg: "backup_err_only_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
return;
}

ref.watch(backupProvider.notifier).removeAlbumForBackup(albumInfo);
ref.read(backupProvider.notifier).removeAlbumForBackup(albumInfo);
} else {
ref.watch(backupProvider.notifier).addAlbumForBackup(albumInfo);
ref.read(backupProvider.notifier).addAlbumForBackup(albumInfo);
}
},
onDoubleTap: () {
Expand All @@ -103,23 +93,10 @@ class AlbumInfoCard extends HookConsumerWidget {
if (isExcluded) {
// Remove from exclude album list
ref
.watch(backupProvider.notifier)
.read(backupProvider.notifier)
.removeExcludedAlbumForBackup(albumInfo);
} else {
// Add to exclude album list
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1 &&
ref
.watch(backupProvider)
.selectedBackupAlbums
.contains(albumInfo)) {
ImmichToast.show(
context: context,
msg: "backup_err_only_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
return;
}

if (albumInfo.id == 'isAll' || albumInfo.name == 'Recents') {
ImmichToast.show(
Expand All @@ -132,7 +109,7 @@ class AlbumInfoCard extends HookConsumerWidget {
}

ref
.watch(backupProvider.notifier)
.read(backupProvider.notifier)
.addExcludedAlbumForBackup(albumInfo);
}
},
Expand Down
32 changes: 4 additions & 28 deletions mobile/lib/modules/backup/ui/album_info_list_tile.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down Expand Up @@ -74,23 +73,10 @@ class AlbumInfoListTile extends HookConsumerWidget {
if (isExcluded) {
// Remove from exclude album list
ref
.watch(backupProvider.notifier)
.read(backupProvider.notifier)
.removeExcludedAlbumForBackup(albumInfo);
} else {
// Add to exclude album list
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1 &&
ref
.watch(backupProvider)
.selectedBackupAlbums
.contains(albumInfo)) {
ImmichToast.show(
context: context,
msg: "backup_err_only_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
return;
}

if (albumInfo.id == 'isAll' || albumInfo.name == 'Recents') {
ImmichToast.show(
Expand All @@ -103,7 +89,7 @@ class AlbumInfoListTile extends HookConsumerWidget {
}

ref
.watch(backupProvider.notifier)
.read(backupProvider.notifier)
.addExcludedAlbumForBackup(albumInfo);
}
},
Expand All @@ -113,19 +99,9 @@ class AlbumInfoListTile extends HookConsumerWidget {
onTap: () {
HapticFeedback.selectionClick();
if (isSelected) {
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
ImmichToast.show(
context: context,
msg: "backup_err_only_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
return;
}

ref.watch(backupProvider.notifier).removeAlbumForBackup(albumInfo);
ref.read(backupProvider.notifier).removeAlbumForBackup(albumInfo);
} else {
ref.watch(backupProvider.notifier).addAlbumForBackup(albumInfo);
ref.read(backupProvider.notifier).addAlbumForBackup(albumInfo);
}
},
leading: ClipRRect(
Expand Down
58 changes: 2 additions & 56 deletions mobile/lib/modules/backup/views/backup_album_selection_page.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/immich_colors.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/ui/album_info_card.dart';
import 'package:immich_mobile/modules/backup/ui/album_info_list_tile.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart';

class BackupAlbumSelectionPage extends HookConsumerWidget {
const BackupAlbumSelectionPage({Key? key}) : super(key: key);
Expand Down Expand Up @@ -91,19 +89,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {

buildSelectedAlbumNameChip() {
return selectedBackupAlbums.map((album) {
void removeSelection() {
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
ImmichToast.show(
context: context,
msg: "backup_err_only_album".tr(),
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
return;
}

ref.watch(backupProvider.notifier).removeAlbumForBackup(album);
}
void removeSelection() =>
ref.read(backupProvider.notifier).removeAlbumForBackup(album);

return Padding(
padding: const EdgeInsets.only(right: 8.0),
Expand Down Expand Up @@ -252,47 +239,6 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
),
),

Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Card(
margin: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: isDarkTheme
? const Color.fromARGB(255, 0, 0, 0)
: const Color.fromARGB(255, 235, 235, 235),
width: 1,
),
),
elevation: 0,
borderOnForeground: false,
child: Column(
children: [
ListTile(
visualDensity: VisualDensity.compact,
title: const Text(
"backup_album_selection_page_total_assets",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
).tr(),
trailing: Text(
ref
.watch(backupProvider)
.allUniqueAssets
.length
.toString(),
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
),
),

ListTile(
title: Text(
"backup_album_selection_page_albums_device".tr(
Expand Down
Loading

0 comments on commit 5145c33

Please sign in to comment.