浏览代码

Merge pull request #408 from deltachat/avatar_picker_for_groups

Avatar picker for groups
björn petersen 5 年之前
父节点
当前提交
c5b4f27c82

+ 38 - 2
deltachat-ios/Controller/EditGroupViewController.swift

@@ -1,10 +1,11 @@
 import UIKit
 import UIKit
 
 
-class EditGroupViewController: UITableViewController {
+class EditGroupViewController: UITableViewController, MediaPickerDelegate {
 
 
     weak var coordinator: EditGroupCoordinator?
     weak var coordinator: EditGroupCoordinator?
 
 
     private let chat: DcChat
     private let chat: DcChat
+    private var groupImage: UIImage?
 
 
     private let rowAvatar = 0
     private let rowAvatar = 0
     private let rowGroupName = 1
     private let rowGroupName = 1
@@ -33,8 +34,8 @@ class EditGroupViewController: UITableViewController {
         self.chat = chat
         self.chat = chat
         self.avatarSelectionCell = AvatarSelectionCell(chat: chat)
         self.avatarSelectionCell = AvatarSelectionCell(chat: chat)
         super.init(style: .grouped)
         super.init(style: .grouped)
-        self.avatarSelectionCell.selectionStyle = .none
         self.avatarSelectionCell.hintLabel.text = String.localized("group_avatar")
         self.avatarSelectionCell.hintLabel.text = String.localized("group_avatar")
+        self.avatarSelectionCell.onAvatarTapped = onAvatarTapped
         title = String.localized("menu_edit_group")
         title = String.localized("menu_edit_group")
     }
     }
 
 
@@ -73,6 +74,9 @@ class EditGroupViewController: UITableViewController {
     
     
     @objc func saveContactButtonPressed() {
     @objc func saveContactButtonPressed() {
         let newName = groupNameCell.getText()
         let newName = groupNameCell.getText()
+        if let groupImage = groupImage, let dcContext = coordinator?.dcContext {
+            AvatarHelper.saveChatAvatar(dcContext: dcContext, image: groupImage, for: Int(chat.id))
+        }
         dc_set_chat_name(mailboxPointer, UInt32(chat.id), newName)
         dc_set_chat_name(mailboxPointer, UInt32(chat.id), newName)
         coordinator?.navigateBack()
         coordinator?.navigateBack()
     }
     }
@@ -85,4 +89,36 @@ class EditGroupViewController: UITableViewController {
         avatarSelectionCell.onInitialsChanged(text: textField.text)
         avatarSelectionCell.onInitialsChanged(text: textField.text)
         doneButton.isEnabled = true
         doneButton.isEnabled = true
     }
     }
+
+    private func onAvatarTapped() {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+            let photoAction = PhotoPickerAlertAction(title: String.localized("gallery"), style: .default, handler: galleryButtonPressed(_:))
+            let videoAction = PhotoPickerAlertAction(title: String.localized("camera"), style: .default, handler: cameraButtonPressed(_:))
+            alert.addAction(photoAction)
+            alert.addAction(videoAction)
+            alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
+        self.present(alert, animated: true, completion: nil)
+    }
+
+    private func galleryButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showPhotoPicker(delegate: self)
+    }
+
+    private func cameraButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showCamera(delegate: self)
+    }
+
+    func onImageSelected(image: UIImage) {
+        groupImage = image
+        doneButton.isEnabled = true
+
+        avatarSelectionCell = AvatarSelectionCell(context: nil, with: groupImage)
+        avatarSelectionCell.hintLabel.text = String.localized("group_avatar")
+        avatarSelectionCell.onAvatarTapped = onAvatarTapped
+
+        self.tableView.beginUpdates()
+        let indexPath = IndexPath(row: rowAvatar, section: 0)
+        self.tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
+        self.tableView.endUpdates()
+    }
 }
 }

+ 0 - 3
deltachat-ios/Controller/EditSettingsController.swift

@@ -165,11 +165,8 @@ class EditSettingsController: UITableViewController, MediaPickerDelegate {
         self.tableView.endUpdates()
         self.tableView.endUpdates()
     }
     }
 
 
-    func onDismiss() { }
-
     private func createPictureAndNameCell() -> AvatarSelectionCell {
     private func createPictureAndNameCell() -> AvatarSelectionCell {
         let cell = AvatarSelectionCell(context: dcContext)
         let cell = AvatarSelectionCell(context: dcContext)
-        cell.selectionStyle = .none
         return cell
         return cell
     }
     }
 
 

+ 41 - 2
deltachat-ios/Controller/GroupNameController.swift

@@ -1,6 +1,7 @@
 import UIKit
 import UIKit
 
 
-class GroupNameController: UITableViewController {
+class GroupNameController: UITableViewController, MediaPickerDelegate {
+
     weak var coordinator: GroupNameCoordinator?
     weak var coordinator: GroupNameCoordinator?
 
 
     var groupName: String = ""
     var groupName: String = ""
@@ -8,6 +9,7 @@ class GroupNameController: UITableViewController {
     var doneButton: UIBarButtonItem!
     var doneButton: UIBarButtonItem!
     let contactIdsForGroup: Set<Int> // TODO: check if array is sufficient
     let contactIdsForGroup: Set<Int> // TODO: check if array is sufficient
     let groupContactIds: [Int]
     let groupContactIds: [Int]
+    var groupImage: UIImage?
 
 
     private let sectionGroupDetails = 0
     private let sectionGroupDetails = 0
     private let sectionGroupDetailsRowAvatar = 0
     private let sectionGroupDetailsRowAvatar = 0
@@ -22,8 +24,8 @@ class GroupNameController: UITableViewController {
 
 
     lazy var avatarSelectionCell: AvatarSelectionCell = {
     lazy var avatarSelectionCell: AvatarSelectionCell = {
         let cell = AvatarSelectionCell(context: nil)
         let cell = AvatarSelectionCell(context: nil)
-        cell.selectionStyle = .none
         cell.hintLabel.text = String.localized("group_avatar")
         cell.hintLabel.text = String.localized("group_avatar")
+        cell.onAvatarTapped = onAvatarTapped
         return cell
         return cell
     }()    
     }()    
 
 
@@ -51,6 +53,11 @@ class GroupNameController: UITableViewController {
         let groupChatId = dc_create_group_chat(mailboxPointer, 0, groupName)
         let groupChatId = dc_create_group_chat(mailboxPointer, 0, groupName)
         for contactId in contactIdsForGroup {
         for contactId in contactIdsForGroup {
             let success = dc_add_contact_to_chat(mailboxPointer, groupChatId, UInt32(contactId))
             let success = dc_add_contact_to_chat(mailboxPointer, groupChatId, UInt32(contactId))
+
+            if let groupImage = groupImage, let dcContext = coordinator?.dcContext {
+                    AvatarHelper.saveChatAvatar(dcContext: dcContext, image: groupImage, for: Int(groupChatId))
+            }
+
             if success == 1 {
             if success == 1 {
                 logger.info("successfully added \(contactId) to group \(groupName)")
                 logger.info("successfully added \(contactId) to group \(groupName)")
             } else {
             } else {
@@ -129,4 +136,36 @@ class GroupNameController: UITableViewController {
         groupName = name
         groupName = name
         doneButton.isEnabled = name.containsCharacters()
         doneButton.isEnabled = name.containsCharacters()
     }
     }
+
+    private func onAvatarTapped() {
+        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+            let photoAction = PhotoPickerAlertAction(title: String.localized("gallery"), style: .default, handler: galleryButtonPressed(_:))
+            let videoAction = PhotoPickerAlertAction(title: String.localized("camera"), style: .default, handler: cameraButtonPressed(_:))
+            alert.addAction(photoAction)
+            alert.addAction(videoAction)
+            alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
+        self.present(alert, animated: true, completion: nil)
+    }
+
+    private func galleryButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showPhotoPicker(delegate: self)
+    }
+
+    private func cameraButtonPressed(_ action: UIAlertAction) {
+        coordinator?.showCamera(delegate: self)
+    }
+
+    func onImageSelected(image: UIImage) {
+        groupImage = image
+
+        avatarSelectionCell = AvatarSelectionCell(context: nil, with: groupImage)
+        avatarSelectionCell.hintLabel.text = String.localized("group_avatar")
+        avatarSelectionCell.onAvatarTapped = onAvatarTapped
+
+        self.tableView.beginUpdates()
+        let indexPath = IndexPath(row: sectionGroupDetailsRowAvatar, section: sectionGroupDetails)
+        self.tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
+        self.tableView.endUpdates()
+    }
+
 }
 }

+ 30 - 3
deltachat-ios/Coordinator/AppCoordinator.swift

@@ -231,7 +231,7 @@ class EditSettingsCoordinator: Coordinator {
     }
     }
 
 
     func showPhotoPicker(delegate: MediaPickerDelegate) {
     func showPhotoPicker(delegate: MediaPickerDelegate) {
-        mediaPicker.showImageCropper(delegate: delegate)
+        mediaPicker.showGallery(delegate: delegate)
     }
     }
 
 
     func showCamera(delegate: MediaPickerDelegate) {
     func showCamera(delegate: MediaPickerDelegate) {
@@ -355,7 +355,7 @@ class GroupChatDetailCoordinator: Coordinator {
 
 
     func showGroupChatEdit(chat: DcChat) {
     func showGroupChatEdit(chat: DcChat) {
         let editGroupViewController = EditGroupViewController(chat: chat)
         let editGroupViewController = EditGroupViewController(chat: chat)
-        let coordinator = EditGroupCoordinator(navigationController: navigationController)
+        let coordinator = EditGroupCoordinator(dcContext: dcContext, navigationController: navigationController)
         childCoordinators.append(coordinator)
         childCoordinators.append(coordinator)
         editGroupViewController.coordinator = coordinator
         editGroupViewController.coordinator = coordinator
         navigationController.pushViewController(editGroupViewController, animated: true)
         navigationController.pushViewController(editGroupViewController, animated: true)
@@ -368,6 +368,9 @@ class GroupChatDetailCoordinator: Coordinator {
         contactDetailController.coordinator = coordinator
         contactDetailController.coordinator = coordinator
         navigationController.pushViewController(contactDetailController, animated: true)
         navigationController.pushViewController(contactDetailController, animated: true)
     }
     }
+
+
+
 }
 }
 
 
 class ChatViewCoordinator: NSObject, Coordinator {
 class ChatViewCoordinator: NSObject, Coordinator {
@@ -567,12 +570,15 @@ class AddGroupMembersCoordinator: Coordinator {
 class GroupNameCoordinator: Coordinator {
 class GroupNameCoordinator: Coordinator {
     var dcContext: DcContext
     var dcContext: DcContext
     let navigationController: UINavigationController
     let navigationController: UINavigationController
+    let mediaPicker: MediaPicker
+
 
 
     private var childCoordinators: [Coordinator] = []
     private var childCoordinators: [Coordinator] = []
 
 
     init(dcContext: DcContext, navigationController: UINavigationController) {
     init(dcContext: DcContext, navigationController: UINavigationController) {
         self.dcContext = dcContext
         self.dcContext = dcContext
         self.navigationController = navigationController
         self.navigationController = navigationController
+        self.mediaPicker = MediaPicker(navigationController: self.navigationController)
     }
     }
 
 
     func showGroupChat(chatId: Int) {
     func showGroupChat(chatId: Int) {
@@ -583,6 +589,15 @@ class GroupNameCoordinator: Coordinator {
         navigationController.popToRootViewController(animated: false)
         navigationController.popToRootViewController(animated: false)
         navigationController.pushViewController(chatViewController, animated: true)
         navigationController.pushViewController(chatViewController, animated: true)
     }
     }
+
+    func showPhotoPicker(delegate: MediaPickerDelegate) {
+          mediaPicker.showGallery(delegate: delegate)
+      }
+
+      func showCamera(delegate: MediaPickerDelegate) {
+          mediaPicker.showCamera(delegate: delegate)
+      }
+
 }
 }
 
 
 class ContactDetailCoordinator: Coordinator, ContactDetailCoordinatorProtocol {
 class ContactDetailCoordinator: Coordinator, ContactDetailCoordinatorProtocol {
@@ -616,9 +631,21 @@ class ContactDetailCoordinator: Coordinator, ContactDetailCoordinatorProtocol {
 
 
 class EditGroupCoordinator: Coordinator {
 class EditGroupCoordinator: Coordinator {
     let navigationController: UINavigationController
     let navigationController: UINavigationController
+    let dcContext: DcContext
+    let mediaPicker: MediaPicker
 
 
-    init(navigationController: UINavigationController) {
+    init(dcContext: DcContext, navigationController: UINavigationController) {
+        self.dcContext = dcContext
         self.navigationController = navigationController
         self.navigationController = navigationController
+        mediaPicker = MediaPicker(navigationController: self.navigationController)
+    }
+
+    func showPhotoPicker(delegate: MediaPickerDelegate) {
+        mediaPicker.showGallery(delegate: delegate)
+    }
+
+    func showCamera(delegate: MediaPickerDelegate) {
+        mediaPicker.showCamera(delegate: delegate)
     }
     }
 
 
     func navigateBack() {
     func navigateBack() {

+ 4 - 0
deltachat-ios/DC/Wrapper.swift

@@ -153,6 +153,10 @@ class DcContext {
        }
        }
        return nil
        return nil
     }
     }
+
+    func saveChatAvatarImage(chatId: Int, path: String) {
+        dc_set_chat_profile_image(contextPointer, UInt32(chatId), path)
+    }
 }
 }
 
 
 class DcConfig {
 class DcConfig {

+ 44 - 14
deltachat-ios/Helper/AvatarHelper.swift

@@ -2,36 +2,66 @@ import Foundation
 import UIKit
 import UIKit
 
 
 class AvatarHelper {
 class AvatarHelper {
-
-    static let selfAvatarFile = "contact_avatar_self.jpg"
+    static let tmpFile = "tempAvatar.jpg"
     private static let avatarPath = "avatars"
     private static let avatarPath = "avatars"
 
 
+    enum FileError: Error {
+        case runtimeError(String)
+    }
+
     static func saveSelfAvatarImage(image: UIImage) {
     static func saveSelfAvatarImage(image: UIImage) {
+        do {
+            let avatarFile = try saveAvatarImageToFile(image: image)
+            DcConfig.selfavatar = avatarFile.path
+            deleteAvatarFile(avatarFile)
+        } catch let error {
+            logger.error("Error saving Image: \(error.localizedDescription)")
+        }
+    }
+
+    static func saveChatAvatar(dcContext: DcContext, image: UIImage, for chatId: Int) {
+        do {
+            let groupFileName = try saveAvatarImageToFile(image: image)
+            dcContext.saveChatAvatarImage(chatId: chatId, path: groupFileName.path)
+            deleteAvatarFile(groupFileName)
+        } catch let error {
+            logger.error("Error saving Image: \(error.localizedDescription)")
+        }
+    }
+
+
+    private static func saveAvatarImageToFile(image: UIImage) throws -> URL {
         if let data = image.jpegData(compressionQuality: 1.0) {
         if let data = image.jpegData(compressionQuality: 1.0) {
             let filemanager = FileManager.default
             let filemanager = FileManager.default
             let docDir = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0]
             let docDir = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0]
             let avatarDir = docDir.appendingPathComponent(avatarPath)
             let avatarDir = docDir.appendingPathComponent(avatarPath)
-            let avatarFile = avatarDir.appendingPathComponent(selfAvatarFile)
-            do {
-                try filemanager.createDirectory(atPath: avatarDir.path,
-                                                withIntermediateDirectories: false)
-            } catch let error as NSError {
-                logger.info("directory not created: \(error.localizedDescription)")
+            let avatarFile = avatarDir.appendingPathComponent(tmpFile)
+
+            if !filemanager.fileExists(atPath: avatarDir.path) {
+                try filemanager.createDirectory(atPath: avatarDir.path, withIntermediateDirectories: false)
             }
             }
 
 
             if !filemanager.changeCurrentDirectoryPath(avatarDir.path) {
             if !filemanager.changeCurrentDirectoryPath(avatarDir.path) {
-                logger.warning("Could not change into avatar directory")
-                return
+                throw FileError.runtimeError("Could not change to Avatar directory")
             }
             }
+            
+            try data.write(to: avatarFile)
+            return avatarFile
+        } else {
+            throw FileError.runtimeError("Could not convert UIImage to jpegData")
+        }
+    }
 
 
+    private static func deleteAvatarFile(_ url: URL) {
+        let filemanager = FileManager.default
+        if filemanager.fileExists(atPath: url.path) {
             do {
             do {
-                try data.write(to: avatarFile)
+                try filemanager.removeItem(atPath: url.path)
             } catch let error {
             } catch let error {
                 logger.warning("Error: \(error.localizedDescription)")
                 logger.warning("Error: \(error.localizedDescription)")
-                return
             }
             }
-
-            DcConfig.selfavatar = avatarFile.path
         }
         }
     }
     }
+
+
 }
 }

+ 3 - 5
deltachat-ios/Helper/MediaPicker.swift

@@ -4,9 +4,7 @@ import MobileCoreServices
 import ALCameraViewController
 import ALCameraViewController
 
 
 protocol MediaPickerDelegate: class {
 protocol MediaPickerDelegate: class {
-    //func onMediaSelected(url: NSURL)
     func onImageSelected(image: UIImage)
     func onImageSelected(image: UIImage)
-    func onDismiss()
 }
 }
 
 
 class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
 class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
@@ -17,7 +15,7 @@ class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerContro
         self.navigationController = navigationController
         self.navigationController = navigationController
     }
     }
 
 
-    func showImageCropper(delegate: MediaPickerDelegate) {
+    func showGallery(delegate: MediaPickerDelegate) {
         let croppingParameters = CroppingParameters(isEnabled: true,
         let croppingParameters = CroppingParameters(isEnabled: true,
                                                     allowResizing: true,
                                                     allowResizing: true,
                                                     allowMoving: true,
                                                     allowMoving: true,
@@ -28,7 +26,7 @@ class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerContro
                                                                             if let image = image {
                                                                             if let image = image {
                                                                                 self?.delegate?.onImageSelected(image: image)
                                                                                 self?.delegate?.onImageSelected(image: image)
                                                                             }
                                                                             }
-                                                                            self?.navigationController.dismiss(animated: true, completion: delegate.onDismiss)})
+                                                                            self?.navigationController.dismiss(animated: true, completion: nil)})
         self.delegate = delegate
         self.delegate = delegate
         navigationController.present(controller, animated: true, completion: nil)
         navigationController.present(controller, animated: true, completion: nil)
     }
     }
@@ -47,7 +45,7 @@ class MediaPicker: NSObject, UINavigationControllerDelegate, UIImagePickerContro
                                                                 if let image = image {
                                                                 if let image = image {
                                                                     self?.delegate?.onImageSelected(image: image)
                                                                     self?.delegate?.onImageSelected(image: image)
                                                                 }
                                                                 }
-                                                                self?.navigationController.dismiss(animated: true, completion: self?.delegate?.onDismiss)})
+                                                                self?.navigationController.dismiss(animated: true, completion: nil)})
             self.delegate = delegate
             self.delegate = delegate
             navigationController.present(cameraViewController, animated: true, completion: nil)
             navigationController.present(cameraViewController, animated: true, completion: nil)
         } else {
         } else {

+ 5 - 2
deltachat-ios/View/AvatarSelectionCell.swift

@@ -36,9 +36,11 @@ class AvatarSelectionCell: UITableViewCell {
         setupSubviews()
         setupSubviews()
     }
     }
 
 
-    init(context: DcContext?) {
+    init(context: DcContext?, with defaultImage: UIImage? = nil) {
         super.init(style: .default, reuseIdentifier: nil)
         super.init(style: .default, reuseIdentifier: nil)
-        setAvatar(image: context?.getSelfAvatarImage(), with: self.defaultImage, downscale: downscaleDefaultImage)
+        setAvatar(image: context?.getSelfAvatarImage(),
+                  with: defaultImage ?? self.defaultImage,
+                  downscale: (defaultImage != nil) ? 1 : downscaleDefaultImage)
         setupSubviews()
         setupSubviews()
     }
     }
 
 
@@ -65,6 +67,7 @@ class AvatarSelectionCell: UITableViewCell {
         let touchListener = UILongPressGestureRecognizer(target: self, action: #selector(onBadgeTouched))
         let touchListener = UILongPressGestureRecognizer(target: self, action: #selector(onBadgeTouched))
         touchListener.minimumPressDuration = 0
         touchListener.minimumPressDuration = 0
         badge.addGestureRecognizer(touchListener)
         badge.addGestureRecognizer(touchListener)
+        selectionStyle = .none
     }
     }
 
 
     func onInitialsChanged(text: String?) {
     func onInitialsChanged(text: String?) {