Browse Source

Merge pull request #800 from deltachat/file_gallery

File gallery
bjoern 4 years ago
parent
commit
5ee7d20b57

+ 47 - 14
DcCore/DcCore/Helper/DcUtils.swift

@@ -99,19 +99,52 @@ public struct DcUtils {
     }
 
     public static func generateThumbnailFromVideo(url: URL?) -> UIImage? {
-           guard let url = url else {
-               return nil
-           }
-           do {
-               let asset = AVURLAsset(url: url)
-               let imageGenerator = AVAssetImageGenerator(asset: asset)
-               imageGenerator.appliesPreferredTrackTransform = true
-               let cgImage = try imageGenerator.copyCGImage(at: .zero, actualTime: nil)
-               return UIImage(cgImage: cgImage)
-           } catch {
-               print(error.localizedDescription)
-               return nil
-           }
-       }
+		guard let url = url else {
+			return nil
+		}
+		do {
+			let asset = AVURLAsset(url: url)
+			let imageGenerator = AVAssetImageGenerator(asset: asset)
+			imageGenerator.appliesPreferredTrackTransform = true
+			let cgImage = try imageGenerator.copyCGImage(at: .zero, actualTime: nil)
+			return UIImage(cgImage: cgImage)
+		} catch {
+			print(error.localizedDescription)
+			return nil
+		}
+	}
+
+	public static func thumbnailFromPdf(withUrl url:URL, pageNumber:Int = 1, width: CGFloat = 240) -> UIImage? {
+		guard let pdf = CGPDFDocument(url as CFURL),
+			let page = pdf.page(at: pageNumber)
+			else {
+				return nil
+		}
+
+		var pageRect = page.getBoxRect(.mediaBox)
+		let pdfScale = width / pageRect.size.width
+		pageRect.size = CGSize(width: pageRect.size.width*pdfScale, height: pageRect.size.height*pdfScale)
+		pageRect.origin = .zero
+
+		UIGraphicsBeginImageContext(pageRect.size)
+		let context = UIGraphicsGetCurrentContext()!
+
+		// White BG
+		context.setFillColor(UIColor.white.cgColor)
+		context.fill(pageRect)
+		context.saveGState()
+
+		// Next 3 lines makes the rotations so that the page look in the right direction
+		context.translateBy(x: 0.0, y: pageRect.size.height)
+		context.scaleBy(x: 1.0, y: -1.0)
+		context.concatenate(page.getDrawingTransform(.mediaBox, rect: pageRect, rotate: 0, preserveAspectRatio: true))
+
+		context.drawPDFPage(page)
+		context.restoreGState()
+
+		let image = UIGraphicsGetImageFromCurrentImageContext()
+		UIGraphicsEndImageContext()
+		return image
+	}
 
 }

+ 8 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -142,6 +142,7 @@
 		AE1988AB23EB3C7600B4CD5F /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = AE1988AA23EB3C7600B4CD5F /* Assets */; };
 		AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
+		AE39D323249CFC1A007346A1 /* DocumentGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE39D322249CFC1A007346A1 /* DocumentGalleryController.swift */; };
 		AE406EF0240FF8FF005F7022 /* ProfileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE406EEF240FF8FF005F7022 /* ProfileCell.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
 		AE52EA19229EB53C00C586C9 /* ContactDetailHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */; };
@@ -158,6 +159,7 @@
 		AE851AC7227C776400ED86F0 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC6227C776400ED86F0 /* Location.swift */; };
 		AE851AC9227C77CF00ED86F0 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC8227C77CF00ED86F0 /* Media.swift */; };
 		AE851AD0227DF50900ED86F0 /* GroupChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */; };
+		AE8DD451249D1DFB009A4BC1 /* FileTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8DD450249D1DFB009A4BC1 /* FileTableViewCell.swift */; };
 		AE8F503524753DFE007FEE0B /* GalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8F503424753DFE007FEE0B /* GalleryViewController.swift */; };
 		AE9DAF0D22C1215D004C9591 /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0C22C1215D004C9591 /* EditContactController.swift */; };
 		AE9DAF0F22C278C6004C9591 /* ChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */; };
@@ -432,6 +434,7 @@
 		AE1988AA23EB3C7600B4CD5F /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Assets; sourceTree = "<group>"; };
 		AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSelectionCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
+		AE39D322249CFC1A007346A1 /* DocumentGalleryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentGalleryController.swift; sourceTree = "<group>"; };
 		AE406EEF240FF8FF005F7022 /* ProfileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCell.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
 		AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailHeader.swift; sourceTree = "<group>"; };
@@ -450,6 +453,7 @@
 		AE851AC6227C776400ED86F0 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
 		AE851AC8227C77CF00ED86F0 /* Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = "<group>"; };
 		AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatDetailViewController.swift; sourceTree = "<group>"; };
+		AE8DD450249D1DFB009A4BC1 /* FileTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTableViewCell.swift; sourceTree = "<group>"; };
 		AE8F503424753DFE007FEE0B /* GalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewController.swift; sourceTree = "<group>"; };
 		AE9DAF0C22C1215D004C9591 /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
 		AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTitleView.swift; sourceTree = "<group>"; };
@@ -862,6 +866,7 @@
 			children = (
 				AE406EEF240FF8FF005F7022 /* ProfileCell.swift */,
 				AE0AA951247800E700D42A7F /* GalleryCell.swift */,
+				AE8DD450249D1DFB009A4BC1 /* FileTableViewCell.swift */,
 			);
 			path = Cell;
 			sourceTree = "<group>";
@@ -932,6 +937,7 @@
 				AED423D2249F578B00B6B2BB /* NewGroupAddMembersViewController.swift */,
 				AED423D4249F57C100B6B2BB /* AddGroupMembersViewController.swift */,
 				AED423D6249F580700B6B2BB /* BlockedContactsViewController.swift */,
+				AE39D322249CFC1A007346A1 /* DocumentGalleryController.swift */,
 			);
 			path = Controller;
 			sourceTree = "<group>";
@@ -1382,9 +1388,11 @@
 				AE6EC5282497B9B200A400E4 /* ThumbnailCache.swift in Sources */,
 				AE52EA20229EB9F000C586C9 /* EditGroupViewController.swift in Sources */,
 				AE18F294228C602A0007B1BE /* SecuritySettingsController.swift in Sources */,
+				AE39D323249CFC1A007346A1 /* DocumentGalleryController.swift in Sources */,
 				305961FD2346125100C80F33 /* TypingBubble.swift in Sources */,
 				305961D72346125100C80F33 /* MessageKit+Availability.swift in Sources */,
 				AED423D5249F57C100B6B2BB /* AddGroupMembersViewController.swift in Sources */,
+				AE8DD451249D1DFB009A4BC1 /* FileTableViewCell.swift in Sources */,
 				3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */,
 				305961FE2346125100C80F33 /* InsetLabel.swift in Sources */,
 				3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */,

+ 14 - 24
deltachat-ios/Controller/ContactDetailViewController.swift

@@ -326,37 +326,27 @@ class ContactDetailViewController: UITableViewController {
     }
 
     private func showDocuments() {
-        presentPreview(for: DC_MSG_FILE, messageType2: DC_MSG_AUDIO, messageType3: 0)
+        let messageIds = viewModel.context.getChatMedia(
+            chatId: viewModel.chatId,
+            messageType: DC_MSG_FILE,
+            messageType2: DC_MSG_AUDIO,
+            messageType3: 0
+        )
+        let fileGalleryController = DocumentGalleryController(fileMessageIds: messageIds)
+        navigationController?.pushViewController(fileGalleryController, animated: true)
     }
 
     private func showGallery() {
-        let messageIds = viewModel.context.getChatMedia(chatId: viewModel.chatId,
-                                                        messageType: DC_MSG_IMAGE,
-                                                        messageType2: DC_MSG_GIF,
-                                                        messageType3: DC_MSG_VIDEO)
+        let messageIds = viewModel.context.getChatMedia(
+            chatId: viewModel.chatId,
+            messageType: DC_MSG_IMAGE,
+            messageType2: DC_MSG_GIF,
+            messageType3: DC_MSG_VIDEO
+        )
         let galleryController = GalleryViewController(mediaMessageIds: messageIds)
             navigationController?.pushViewController(galleryController, animated: true)
     }
 
-    private func presentPreview(for messageType: Int32, messageType2: Int32, messageType3: Int32) {
-        if viewModel.chatId == 0 {
-            return
-        }
-        let messageIds = viewModel.context.getChatMedia(chatId: viewModel.chatId,
-                                                        messageType: messageType,
-                                                        messageType2: messageType2,
-                                                        messageType3: messageType3)
-        var mediaUrls: [URL] = []
-        for messageId in messageIds {
-            let message = DcMsg.init(id: messageId)
-            if let url = message.fileURL {
-                mediaUrls.insert(url, at: 0)
-            }
-        }
-        let previewController = PreviewController(currentIndex: 0, urls: mediaUrls)
-        navigationController?.pushViewController(previewController, animated: true)
-    }
-
     private func deleteChat() {
         if viewModel.chatId == 0 {
             return

+ 79 - 0
deltachat-ios/Controller/DocumentGalleryController.swift

@@ -0,0 +1,79 @@
+import UIKit
+import DcCore
+
+class DocumentGalleryController: UIViewController {
+
+    private let fileMessageIds: [Int]
+
+    private lazy var tableViews: UITableView = {
+        let table = UITableView(frame: .zero, style: .plain)
+        table.register(FileTableViewCell.self, forCellReuseIdentifier: FileTableViewCell.reuseIdentifier)
+        table.dataSource = self
+        table.delegate = self
+        table.rowHeight = 60
+        return table
+    }()
+
+    init(fileMessageIds: [Int]) {
+        self.fileMessageIds = fileMessageIds
+        super.init(nibName: nil, bundle: nil)
+        self.title = String.localized("documents")
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    // MARK: - lifecycle
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupSubviews()
+    }
+
+    // MARK: - layout
+    private func setupSubviews() {
+        view.addSubview(tableViews)
+        tableViews.translatesAutoresizingMaskIntoConstraints = false
+        tableViews.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
+        tableViews.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
+        tableViews.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
+        tableViews.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+    }
+}
+
+// MARK: - UITableViewDelegate, UITableViewDataSource
+extension DocumentGalleryController: UITableViewDelegate, UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return fileMessageIds.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: FileTableViewCell.reuseIdentifier, for: indexPath) as? FileTableViewCell else {
+            return UITableViewCell()
+        }
+        let msg = DcMsg(id: fileMessageIds[indexPath.row])
+        cell.update(msg: msg)
+        return cell
+    }
+
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let msgId = fileMessageIds[indexPath.row]
+        showPreview(msgId: msgId)
+        tableView.deselectRow(at: indexPath, animated: false)
+    }
+}
+
+// MARK: - coordinator
+extension DocumentGalleryController {
+    func showPreview(msgId: Int) {
+        guard let index = fileMessageIds.index(of: msgId) else {
+            return
+        }
+
+        let mediaUrls = fileMessageIds.compactMap {
+            return DcMsg(id: $0).fileURL
+        }
+        let previewController = PreviewController(currentIndex: index, urls: mediaUrls)
+        present(previewController, animated: true, completion: nil)
+    }
+}

+ 1 - 0
deltachat-ios/Controller/GalleryViewController.swift

@@ -128,6 +128,7 @@ extension GalleryViewController: UICollectionViewDataSource, UICollectionViewDel
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
         let msgId = mediaMessageIds[indexPath.row]
         showPreview(msgId: msgId)
+        collectionView.deselectItem(at: indexPath, animated: true)
     }
 
     func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {

+ 8 - 15
deltachat-ios/Controller/GroupChatDetailViewController.swift

@@ -225,8 +225,14 @@ class GroupChatDetailViewController: UIViewController {
     }
 
     private func showDocuments() {
-        presentPreview(for: DC_MSG_FILE, messageType2: DC_MSG_AUDIO, messageType3: 0)
-    }
+        let messageIds = dcContext.getChatMedia(
+            chatId: chatId,
+            messageType: DC_MSG_FILE,
+            messageType2: DC_MSG_AUDIO,
+            messageType3: 0
+        )
+        let fileGalleryController = DocumentGalleryController(fileMessageIds: messageIds)
+        navigationController?.pushViewController(fileGalleryController, animated: true)    }
 
     private func showGallery() {
         let messageIds = dcContext.getChatMedia(
@@ -239,19 +245,6 @@ class GroupChatDetailViewController: UIViewController {
         navigationController?.pushViewController(galleryController, animated: true)
     }
 
-    private func presentPreview(for messageType: Int32, messageType2: Int32, messageType3: Int32) {
-        let messageIds = dcContext.getChatMedia(chatId: chatId, messageType: messageType, messageType2: messageType2, messageType3: messageType3)
-        var mediaUrls: [URL] = []
-        for messageId in messageIds {
-            let message = DcMsg.init(id: messageId)
-            if let url = message.fileURL {
-                mediaUrls.insert(url, at: 0)
-            }
-        }
-        let previewController = PreviewController(currentIndex: 0, urls: mediaUrls)
-        navigationController?.pushViewController(previewController, animated: true)
-    }
-
     private func deleteChat() {
         dcContext.deleteChat(chatId: chatId)
 

+ 70 - 0
deltachat-ios/View/Cell/FileTableViewCell.swift

@@ -0,0 +1,70 @@
+import UIKit
+import DcCore
+
+class FileTableViewCell: UITableViewCell {
+
+    static let reuseIdentifier = "file_table_view_cell"
+
+    private let fileImageView: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
+        setupSubviews()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func prepareForReuse() {
+        fileImageView.image = nil
+        detailTextLabel?.text = nil
+        textLabel?.text = nil
+    }
+
+    // MARK: - layout
+    private func setupSubviews() {
+        guard let textLabel = textLabel, let detailTextLabel = detailTextLabel else { return }
+
+        contentView.addSubview(fileImageView)
+        fileImageView.translatesAutoresizingMaskIntoConstraints = false
+        fileImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0).isActive = true
+        fileImageView.heightAnchor.constraint(lessThanOrEqualTo: contentView.heightAnchor, multiplier: 0.9).isActive = true
+        fileImageView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor, constant: 0).isActive = true
+        fileImageView.widthAnchor.constraint(equalToConstant: 50).isActive = true
+        textLabel.translatesAutoresizingMaskIntoConstraints = false
+        detailTextLabel.translatesAutoresizingMaskIntoConstraints = false
+        textLabel.leadingAnchor.constraint(equalTo: fileImageView.trailingAnchor, constant: 10).isActive = true
+        textLabel.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor, constant: 0).isActive = true
+        textLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor, constant: 0).isActive = true
+        detailTextLabel.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor, constant: 0).isActive = true
+        detailTextLabel.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: 0).isActive = true
+        detailTextLabel.trailingAnchor.constraint(equalTo: textLabel.trailingAnchor, constant: 0).isActive = true
+        detailTextLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: 0).isActive = true
+    }
+
+    // MARK: - update
+    func update(msg: DcMsg) {
+        switch msg.kind {
+        case .fileText(let mediaItem):
+            if let url = mediaItem.url {
+                if let pdfThumbnail = DcUtils.thumbnailFromPdf(withUrl: url) {
+                    fileImageView.image = pdfThumbnail
+                } else {
+                    let controller = UIDocumentInteractionController(url: url)
+                    fileImageView.image = controller.icons.first ?? mediaItem.placeholderImage
+                }
+            } else {
+                fileImageView.image = mediaItem.placeholderImage
+            }
+            textLabel?.text = msg.filename
+            detailTextLabel?.attributedText = mediaItem.text?[MediaItemConstants.mediaSubtitle]
+        default:
+            break
+        }
+    }
+}

+ 0 - 1
deltachat-ios/View/Cell/GalleryCell.swift

@@ -2,7 +2,6 @@ import UIKit
 import DcCore
 import SDWebImage
 
-
 class GalleryCell: UICollectionViewCell {
     static let reuseIdentifier = "gallery_cell"