diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Presenters/PhotoPicker/Sheet/PhotoPickerPresenterSheet.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Presenters/PhotoPicker/Sheet/PhotoPickerPresenterSheet.swift index 6b5f99a27..fcfbddea8 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Presenters/PhotoPicker/Sheet/PhotoPickerPresenterSheet.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Presenters/PhotoPicker/Sheet/PhotoPickerPresenterSheet.swift @@ -1,4 +1,4 @@ -import MobileCoreServices +import PhotosUI import RuuviCore import RuuviPresenters import UIKit @@ -9,7 +9,7 @@ class PhotoPickerPresenterSheet: NSObject, PhotoPickerPresenter { var permissionPresenter: PermissionPresenter! func showLibrary() { - checkPhotoLibraryPermission() + showPhotoLibrary() } func showCameraUI() { @@ -33,22 +33,33 @@ extension PhotoPickerPresenterSheet: UIImagePickerControllerDelegate, UINavigati } } -// MARK: - Private +// MARK: - PHPickerViewControllerDelegate -extension PhotoPickerPresenterSheet { - private func checkPhotoLibraryPermission() { - if permissionsManager.isPhotoLibraryPermissionGranted { - showPhotoLibrary() - } else { - permissionsManager.requestPhotoLibraryPermission { [weak self] granted in - if granted { - self?.showPhotoLibrary() - } else { - self?.permissionPresenter.presentNoPhotoLibraryPermission() +extension PhotoPickerPresenterSheet: PHPickerViewControllerDelegate { + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true, completion: { [weak self] in + guard let sSelf = self else { return } + guard let result = results.first else { return } + + if result.itemProvider.canLoadObject(ofClass: UIImage.self) { + result.itemProvider.loadObject(ofClass: UIImage.self) { (object, error) in + guard error == nil else { + return + } + if let image = object as? UIImage { + DispatchQueue.main.async { + sSelf.delegate?.photoPicker(presenter: sSelf, didPick: image) + } + } } } - } + }) } +} + +// MARK: - Private + +extension PhotoPickerPresenterSheet { private func checkCameraPermission() { if permissionsManager.isCameraPermissionGranted { @@ -66,10 +77,18 @@ extension PhotoPickerPresenterSheet { private func showPhotoLibrary() { guard let viewController = UIApplication.shared.topViewController() else { return } - let vc = UIImagePickerController() - vc.sourceType = .photoLibrary - vc.delegate = self - viewController.present(vc, animated: true) + presentPhotoPicker(from: viewController) + } + + private func presentPhotoPicker(from viewController: UIViewController) { + var configuration = PHPickerConfiguration() + configuration.filter = .images + configuration.selectionLimit = 1 + configuration.preferredAssetRepresentationMode = .current + + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = self + viewController.present(picker, animated: true, completion: nil) } private func showCamera() { diff --git a/Packages/RuuviLocal/Sources/RuuviLocal/RuuviLocalImages.swift b/Packages/RuuviLocal/Sources/RuuviLocal/RuuviLocalImages.swift index 1f991b0c8..b37c40693 100644 --- a/Packages/RuuviLocal/Sources/RuuviLocal/RuuviLocalImages.swift +++ b/Packages/RuuviLocal/Sources/RuuviLocal/RuuviLocalImages.swift @@ -26,7 +26,14 @@ public protocol RuuviLocalImages { func setBackground(_ id: Int, for identifier: Identifier) func setNextDefaultBackground(for identifier: Identifier) -> UIImage? func getCustomBackground(for identifier: Identifier) -> UIImage? - func setCustomBackground(image: UIImage, for identifier: Identifier) -> Future + func setCustomBackground( + image: UIImage, + compressionQuality: CGFloat, + for identifier: Identifier + ) -> Future< + URL, + RuuviLocalError + > func deleteCustomBackground(for uuid: Identifier) func backgroundUploadProgress(for identifier: Identifier) -> Double? diff --git a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/Documents/ImagePersistenceDocuments.swift b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/Documents/ImagePersistenceDocuments.swift index 685cc249c..c27bb7e13 100644 --- a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/Documents/ImagePersistenceDocuments.swift +++ b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/Documents/ImagePersistenceDocuments.swift @@ -19,11 +19,15 @@ class ImagePersistenceDocuments: ImagePersistence { try? FileManager.default.removeItem(at: url) } - func persistBg(image: UIImage, for identifier: Identifier) -> Future { + func persistBg( + image: UIImage, + compressionQuality: CGFloat, + for identifier: Identifier + ) -> Future { let uuid = identifier.value let promise = Promise() DispatchQueue.global().async { - if let data = image.jpegData(compressionQuality: 1.0) { + if let data = image.jpegData(compressionQuality: compressionQuality) { do { let url = try self.getBgDirectory().appendingPathComponent(uuid + self.ext) try data.write(to: url) diff --git a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/ImagePersistence.swift b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/ImagePersistence.swift index 883716398..1c37ab69c 100644 --- a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/ImagePersistence.swift +++ b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/Image/ImagePersistence.swift @@ -5,5 +5,9 @@ import UIKit protocol ImagePersistence { func fetchBg(for identifier: Identifier) -> UIImage? func deleteBgIfExists(for identifier: Identifier) - func persistBg(image: UIImage, for identifier: Identifier) -> Future + func persistBg( + image: UIImage, + compressionQuality: CGFloat, + for identifier: Identifier + ) -> Future } diff --git a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/RuuviLocalImagesUserDefaults.swift b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/RuuviLocalImagesUserDefaults.swift index a22e1f794..72892a471 100644 --- a/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/RuuviLocalImagesUserDefaults.swift +++ b/Packages/RuuviLocal/Sources/RuuviLocalUserDefaults/Background/RuuviLocalImagesUserDefaults.swift @@ -77,9 +77,17 @@ final class RuuviLocalImagesUserDefaults: RuuviLocalImages { } } - func setCustomBackground(image: UIImage, for identifier: Identifier) -> Future { + func setCustomBackground( + image: UIImage, + compressionQuality: CGFloat, + for identifier: Identifier + ) -> Future { let promise = Promise() - let persist = imagePersistence.persistBg(image: image, for: identifier) + let persist = imagePersistence.persistBg( + image: image, + compressionQuality: compressionQuality, + for: identifier + ) persist.on(success: { url in self.setBackground(0, for: identifier) let userInfoKey: BPDidChangeBackgroundKey diff --git a/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift b/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift index a977d9179..032797eb7 100644 --- a/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift +++ b/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift @@ -158,14 +158,23 @@ public final class RuuviServiceCloudSyncImpl: RuuviServiceCloudSync { } URLSession .shared - .dataTask(with: pictureUrl, completionHandler: { data, _, error in + .dataTask(with: pictureUrl, + completionHandler: { + data, + _, + error in if let error { promise.fail(error: .networking(error)) } else if let data { if let image = UIImage(data: data) { + // Sync the image with original quality from cloud DispatchQueue.main.async { self.ruuviLocalImages - .setCustomBackground(image: image, for: sensor.id.mac) + .setCustomBackground( + image: image, + compressionQuality: 1.0, + for: sensor.id.mac + ) .on(success: { fileUrl in self.ruuviLocalImages.setPictureIsCached(for: sensor) promise.succeed(value: fileUrl) diff --git a/Packages/RuuviService/Sources/RuuviServiceOwnership/RuuviServiceOwnershipImpl.swift b/Packages/RuuviService/Sources/RuuviServiceOwnership/RuuviServiceOwnershipImpl.swift index bb318788f..98d1a11d0 100644 --- a/Packages/RuuviService/Sources/RuuviServiceOwnership/RuuviServiceOwnershipImpl.swift +++ b/Packages/RuuviService/Sources/RuuviServiceOwnership/RuuviServiceOwnershipImpl.swift @@ -298,6 +298,8 @@ extension RuuviServiceOwnershipImpl { macId: MACIdentifier ) { if let customImage = localImages.getCustomBackground(for: macId) { + // Send the original custom image to cloud. The resize and compression + // is already handled at the time of setting it to local. if let jpegData = customImage.jpegData(compressionQuality: 1.0) { let remote = cloud.upload( imageData: jpegData, diff --git a/Packages/RuuviService/Sources/RuuviServiceSensorProperties/RuuviServiceSensorPropertiesImpl.swift b/Packages/RuuviService/Sources/RuuviServiceSensorProperties/RuuviServiceSensorPropertiesImpl.swift index 6d64a3ae9..f959612c5 100644 --- a/Packages/RuuviService/Sources/RuuviServiceSensorProperties/RuuviServiceSensorPropertiesImpl.swift +++ b/Packages/RuuviService/Sources/RuuviServiceSensorProperties/RuuviServiceSensorPropertiesImpl.swift @@ -112,18 +112,34 @@ public final class RuuviServiceSensorPropertiesImpl: RuuviServiceSensorPropertie }, for: mac ) - local = localImages.setCustomBackground(image: image, for: mac) + local = localImages.setCustomBackground( + image: croppedImage, + compressionQuality: compressionQuality, + for: mac + ) } else if let luid { - local = localImages.setCustomBackground(image: image, for: luid) + local = localImages.setCustomBackground( + image: croppedImage, + compressionQuality: compressionQuality, + for: luid + ) } else { promise.fail(error: .bothLuidAndMacAreNil) return promise.future } } else { if let mac = macId { - local = localImages.setCustomBackground(image: image, for: mac) + local = localImages.setCustomBackground( + image: croppedImage, + compressionQuality: compressionQuality, + for: mac + ) } else if let luid { - local = localImages.setCustomBackground(image: image, for: luid) + local = localImages.setCustomBackground( + image: croppedImage, + compressionQuality: compressionQuality, + for: luid + ) } else { promise.fail(error: .bothLuidAndMacAreNil) return promise.future