diff --git a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart index 27be7029de..45f053457e 100644 --- a/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart +++ b/mobile/lib/modules/asset_viewer/services/image_viewer.service.dart @@ -19,9 +19,11 @@ class ImageViewerService { ImageViewerService(this._apiService); Future downloadAssetToDevice(Asset asset) async { + File? imageFile; + File? videoFile; try { // Download LivePhotos image and motion part - if (asset.isImage && asset.livePhotoVideoId != null) { + if (asset.isImage && asset.livePhotoVideoId != null && Platform.isIOS) { var imageResponse = await _apiService.assetApi.downloadFileWithHttpInfo( asset.remoteId!, ); @@ -40,11 +42,11 @@ class ImageViewerService { return false; } - final AssetEntity? entity; + AssetEntity? entity; final tempDir = await getTemporaryDirectory(); - File videoFile = await File('${tempDir.path}/livephoto.mov').create(); - File imageFile = await File('${tempDir.path}/livephoto.heic').create(); + videoFile = await File('${tempDir.path}/livephoto.mov').create(); + imageFile = await File('${tempDir.path}/livephoto.heic').create(); videoFile.writeAsBytesSync(motionReponse.bodyBytes); imageFile.writeAsBytesSync(imageResponse.bodyBytes); @@ -54,6 +56,17 @@ class ImageViewerService { title: asset.fileName, ); + if (entity == null) { + _log.warning( + "Asset cannot be saved as a live photo. This is most likely a motion photo. Saving only the image file", + ); + + entity = await PhotoManager.editor.saveImage( + imageResponse.bodyBytes, + title: asset.fileName, + ); + } + return entity != null; } else { var res = await _apiService.assetApi @@ -75,17 +88,20 @@ class ImageViewerService { ); } else { final tempDir = await getTemporaryDirectory(); - File tempFile = - await File('${tempDir.path}/${asset.fileName}').create(); - tempFile.writeAsBytesSync(res.bodyBytes); + videoFile = await File('${tempDir.path}/${asset.fileName}').create(); + videoFile.writeAsBytesSync(res.bodyBytes); entity = await PhotoManager.editor - .saveVideo(tempFile, title: asset.fileName); + .saveVideo(videoFile, title: asset.fileName); } return entity != null; } } catch (error, stack) { _log.severe("Error saving file ${error.toString()}", error, stack); return false; + } finally { + // Clear temp files + imageFile?.delete(); + videoFile?.delete(); } } } diff --git a/mobile/lib/modules/backup/background_service/background.service.dart b/mobile/lib/modules/backup/background_service/background.service.dart index ad795a6f57..45d921a142 100644 --- a/mobile/lib/modules/backup/background_service/background.service.dart +++ b/mobile/lib/modules/backup/background_service/background.service.dart @@ -453,7 +453,7 @@ class BackgroundService { ); _cancellationToken = CancellationToken(); - final pmProgressHandler = PMProgressHandler(); + final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; final bool ok = await backupService.backupAsset( toUpload, diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 3663184890..4f9efdd8af 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; @@ -447,9 +449,9 @@ class BackupNotifier extends StateNotifier { // Perform Backup state = state.copyWith(cancelToken: CancellationToken()); - final pmProgressHandler = PMProgressHandler(); + final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; - pmProgressHandler.stream.listen((event) { + pmProgressHandler?.stream.listen((event) { final double progress = event.progress; state = state.copyWith(iCloudDownloadProgress: progress); }); diff --git a/mobile/lib/modules/backup/providers/manual_upload.provider.dart b/mobile/lib/modules/backup/providers/manual_upload.provider.dart index 449fd390d9..bd7756adf7 100644 --- a/mobile/lib/modules/backup/providers/manual_upload.provider.dart +++ b/mobile/lib/modules/backup/providers/manual_upload.provider.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cancellation_token_http/http.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; @@ -208,7 +210,7 @@ class ManualUploadNotifier extends StateNotifier { state.totalAssetsToUpload == 1; state = state.copyWith(showDetailedNotification: showDetailedNotification); - final pmProgressHandler = PMProgressHandler(); + final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; final bool ok = await ref.read(backupServiceProvider).backupAsset( allUploadAssets, diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index 32d2eaa4af..6894a8aef8 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -206,7 +206,7 @@ class BackupService { Future backupAsset( Iterable assetList, http.CancellationToken cancelToken, - PMProgressHandler pmProgressHandler, + PMProgressHandler? pmProgressHandler, Function(String, String, bool) uploadSuccessCb, Function(int, int) uploadProgressCb, Function(CurrentUploadAsset) setCurrentUploadAssetCb, diff --git a/mobile/lib/shared/ui/user_circle_avatar.dart b/mobile/lib/shared/ui/user_circle_avatar.dart index 4ad80dc31d..3dc0e65c1b 100644 --- a/mobile/lib/shared/ui/user_circle_avatar.dart +++ b/mobile/lib/shared/ui/user_circle_avatar.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/user.dart'; import 'package:immich_mobile/shared/ui/transparent_image.dart'; @@ -35,11 +34,10 @@ class UserCircleAvatar extends ConsumerWidget { color: isDarkTheme && user.avatarColor == AvatarColorEnum.primary ? Colors.black : Colors.white, - backgroundColor: user.avatarColor.toColor(), ), ); return CircleAvatar( - backgroundColor: context.primaryColor, + backgroundColor: user.avatarColor.toColor(), radius: radius, child: user.profileImagePath.isEmpty ? textIcon