diff --git a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift index 128b2dca1..8f38ebe71 100644 --- a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift +++ b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift @@ -546,7 +546,8 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour Log.i("Messsage not delivered") } else { if (VFSUtil.vfsEnabled(groupName: kLinphoneMsgNotificationAppGroupId) || ConfigManager.instance().lpConfigBoolForKey(key: "use_in_app_file_viewer_for_non_encrypted_files", section: "app")){ - let view: ImageViewer = VIEW(ImageViewer.compositeViewDescription()) + + var viewer: MediaViewer = VIEW(MediaViewer.compositeViewDescription()) var image = UIImage() if chatMessage.contents.first!.type == "image" { @@ -563,10 +564,11 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour } } - view.imageViewer = image - view.imageNameViewer = chatMessage.contents.first!.name.isEmpty ? "" : chatMessage.contents.first!.name - view.imagePathViewer = chatMessage.contents.first!.exportPlainFile() - PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) + viewer.imageViewer = image + viewer.imageNameViewer = chatMessage.contents.first!.name.isEmpty ? "" : chatMessage.contents.first!.name + viewer.imagePathViewer = chatMessage.contents.first!.exportPlainFile() + viewer.contentType = chatMessage.contents.first!.type + PhoneMainView.instance().changeCurrentView(viewer.compositeViewDescription()) } else { let previewController = QLPreviewController() diff --git a/Classes/Swift/Util/Viewers/MediaViewer.swift b/Classes/Swift/Util/Viewers/MediaViewer.swift new file mode 100644 index 000000000..d8054df60 --- /dev/null +++ b/Classes/Swift/Util/Viewers/MediaViewer.swift @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import Foundation +import AVFoundation +import AVKit + +class MediaViewer: BackNextNavigationView, UICompositeViewDelegate, UIScrollViewDelegate, QLPreviewControllerDelegate, QLPreviewControllerDataSource { + + static let compositeDescription = UICompositeViewDescription(MediaViewer.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil) + static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } + func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } + + var imageNameViewer = "" + var imagePathViewer = "" + var imageViewer = UIImage() + var previewItems : [QLPreviewItem?] = [] + var contentType : String? + + //Image + var newImageView = UIImageView() + let imageViewViewer = UIImageView() + let imageScrollView = UIScrollView() + + //Video + + override func viewDidLoad() { + super.viewDidLoad( + backAction: { + PhoneMainView.instance().popView(self.compositeViewDescription()) + },nextAction: { + }, + nextActionEnableCondition: MutableLiveData(false), + title:"") + super.nextButton.isHidden = true + + let shareButton = CallControlButton(buttonTheme:VoipTheme.nav_button("voip_export")) + super.topBar.addSubview(shareButton) + shareButton.alignParentRight(withMargin: side_buttons_margin).alignParentBottom(withMargin: 18).alignParentTop(withMargin: 18).done() + + shareButton.addTarget(self, action: #selector(shareMediaButton), for: .touchUpInside) + + UIDeviceBridge.displayModeSwitched.readCurrentAndObserve { _ in + self.view.backgroundColor = VoipTheme.voipBackgroundBWColor.get() + } + } + + override func viewWillAppear(_ animated: Bool) { + newImageView.removeFromSuperview() + imageViewViewer.removeFromSuperview() + imageScrollView.removeFromSuperview() + playerContainerView.removeFromSuperview() + if contentType == "image" { + setUpImageView() + } else if contentType == "video" { + setUpPlayerContainerView() + } + } + + override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) { + dismissFullscreenImageRotated() + self.viewWillAppear(true) + } + + @IBAction func imageTapped(_ sender: UITapGestureRecognizer) { + let imageView = sender.view as! UIImageView + newImageView = UIImageView(image: imageView.image) + newImageView.frame = CGRectMake(0, 0, UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height-20) + newImageView.backgroundColor = .black + newImageView.contentMode = .scaleAspectFit + newImageView.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer(target: self, action: #selector(dismissFullscreenImage)) + newImageView.addGestureRecognizer(tap) + self.view.addSubview(newImageView) + self.navigationController?.isNavigationBarHidden = true + self.tabBarController?.tabBar.isHidden = true + PhoneMainView.instance().hideStatusBar(true) + } + + func dismissFullscreenImageRotated() { + self.navigationController?.isNavigationBarHidden = false + self.tabBarController?.tabBar.isHidden = false + PhoneMainView.instance().hideStatusBar(false) + } + + @objc func dismissFullscreenImage(_ sender: UITapGestureRecognizer) { + self.navigationController?.isNavigationBarHidden = false + self.tabBarController?.tabBar.isHidden = false + PhoneMainView.instance().hideStatusBar(false) + sender.view?.removeFromSuperview() + } + + func viewForZooming(in scrollView: UIScrollView) -> UIView? { + return self.imageViewViewer + } + + @objc func shareMediaButton(_ sender: UIButton) { + let previewController = QLPreviewController() + self.previewItems = [] + + self.previewItems.append(self.getPreviewItem(filePath: imagePathViewer)) + + previewController.dataSource = self + previewController.delegate = self + PhoneMainView.instance().mainViewController.present(previewController, animated: true, completion: nil) + + } + + private func setUpImageView() { + let vWidth = self.view.bounds.size.width + let vHeight = self.view.bounds.size.height-66 + + imageScrollView.delegate = self + imageScrollView.frame = CGRectMake(0, 66, vWidth, vHeight) + imageScrollView.showsVerticalScrollIndicator = true + + imageScrollView.minimumZoomScale = 1.0 + imageScrollView.maximumZoomScale = 10.0 + + self.view.addSubview(imageScrollView) + + imageViewViewer.contentMode = .scaleAspectFit + imageScrollView.addSubview(imageViewViewer) + self.imageViewViewer.frame = CGRect(x: 0, y: 0, width: vWidth, height: vHeight) + self.view.bringSubviewToFront(topBar) + + let pictureTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped)) + imageViewViewer.addGestureRecognizer(pictureTap) + imageViewViewer.isUserInteractionEnabled = true + + imageViewViewer.image = imageViewer + titleLabel.text = imageNameViewer + } + + private func setUpPlayerContainerView() { + let vWidth = self.view.bounds.size.width + let vHeight = self.view.bounds.size.height-66 + if let urlEncoded = imagePathViewer.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed){ + if !urlEncoded.isEmpty { + if let urlVideo = URL(string: "file://" + urlEncoded){ + let player = AVPlayer(url: urlVideo) + let playerLayer = AVPlayerLayer(player: player) + playerLayer.frame = CGRectMake(0, 66, vWidth, vHeight) + self.view.layer.addSublayer(playerLayer) + player.play() + } + } + } + } + + func getPreviewItem(filePath: String) -> NSURL{ + let url = NSURL(fileURLWithPath: filePath) + return url + } + + func numberOfPreviewItems(in controller: QLPreviewController) -> Int { + return previewItems.count + } + + func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { + return (previewItems[index] as QLPreviewItem?)! + } +} diff --git a/Classes/Swift/Util/Viewers/TextViewer.swift b/Classes/Swift/Util/Viewers/TextViewer.swift index 4e4d99971..f89bbee5f 100644 --- a/Classes/Swift/Util/Viewers/TextViewer.swift +++ b/Classes/Swift/Util/Viewers/TextViewer.swift @@ -19,8 +19,8 @@ import Foundation -@objc class TextViewer: BackNextNavigationView, UICompositeViewDelegate { - +class TextViewer: BackNextNavigationView, UICompositeViewDelegate { + static let compositeDescription = UICompositeViewDescription(TextViewer.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: SideMenuView.self, fullscreen: false, isLeftFragment: false,fragmentWith: nil) static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } @@ -43,7 +43,7 @@ import Foundation super.topBar.addSubview(shareButton) shareButton.alignParentRight(withMargin: side_buttons_margin).alignParentBottom(withMargin: 18).alignParentTop(withMargin: 18).done() - shareButton.addTarget(self, action: #selector(shareTextButton), for: .touchUpInside) + shareButton.addTarget(self, action: #selector(shareMediaButton), for: .touchUpInside) textViewViewer.isScrollEnabled = true textViewViewer.isUserInteractionEnabled = true @@ -62,21 +62,20 @@ import Foundation textViewViewer.text = textViewer } - @IBAction func shareTextButton(_ sender: UIButton) { - - // text to share - let text = textViewer - - // set up activity view controller - let textToShare = [ text ] - let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil) - activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash - - // exclude some activity types from the list (optional) - activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ] - - // present the view controller - self.present(activityViewController, animated: true, completion: nil) - - } + @objc func shareMediaButton(_ sender: UIButton) { + // text to share + let text = textViewer + + // set up activity view controller + let textToShare = [ text ] + let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil) + activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash + + // exclude some activity types from the list (optional) + activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ] + + // present the view controller + self.present(activityViewController, animated: true, completion: nil) + + } } diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 2dbed6b09..2a0711bca 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -944,7 +944,6 @@ D7097B35296D684900AEF6C5 /* FileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7097B34296D684900AEF6C5 /* FileType.swift */; }; D71418E329C9B4E0002EEF75 /* DownloadMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71418E229C9B4E0002EEF75 /* DownloadMessageCell.swift */; }; D71418E529C9E2CD002EEF75 /* CircularProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71418E429C9E2CD002EEF75 /* CircularProgressBarView.swift */; }; - D73567CC2A433B76004E049F /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73567CB2A433B76004E049F /* ImageViewer.swift */; }; D7421D9E29228A5200290CAB /* ChatConversationViewSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7421D9D29228A5200290CAB /* ChatConversationViewSwift.swift */; }; D74A44912923BAF90017D063 /* BackActionsNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74A44902923BAF90017D063 /* BackActionsNavigationView.swift */; }; D768763529CDA88200570747 /* UploadMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D768763429CDA88200570747 /* UploadMessageCell.swift */; }; @@ -954,6 +953,7 @@ D779D39C29A76DE6007B8087 /* ChatConversationTableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D779D39B29A76DE6007B8087 /* ChatConversationTableViewModel.swift */; }; D779D39E29AC9E93007B8087 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D779D39D29AC9E92007B8087 /* AudioPlayer.swift */; }; D779D3A229B5E365007B8087 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D779D3A129B5E365007B8087 /* UIImageExtension.swift */; }; + D780FF7D2A459CE3001535E6 /* MediaViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D780FF7C2A459CE3001535E6 /* MediaViewer.swift */; }; D7A4C0082A3B135800EFBD1B /* new_chat_attachment_default.png in Resources */ = {isa = PBXBuildFile; fileRef = D7A4C0052A3B135800EFBD1B /* new_chat_attachment_default.png */; }; D7A4C0092A3B135800EFBD1B /* new_vr_off.png in Resources */ = {isa = PBXBuildFile; fileRef = D7A4C0062A3B135800EFBD1B /* new_vr_off.png */; }; D7A4C00A2A3B135800EFBD1B /* new_chat_send_default.png in Resources */ = {isa = PBXBuildFile; fileRef = D7A4C0072A3B135800EFBD1B /* new_chat_send_default.png */; }; @@ -2192,7 +2192,6 @@ D7097B34296D684900AEF6C5 /* FileType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileType.swift; sourceTree = ""; }; D71418E229C9B4E0002EEF75 /* DownloadMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadMessageCell.swift; sourceTree = ""; }; D71418E429C9E2CD002EEF75 /* CircularProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBarView.swift; sourceTree = ""; }; - D73567CB2A433B76004E049F /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = ""; }; D7421D9D29228A5200290CAB /* ChatConversationViewSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatConversationViewSwift.swift; sourceTree = ""; }; D74A44902923BAF90017D063 /* BackActionsNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackActionsNavigationView.swift; sourceTree = ""; }; D768763429CDA88200570747 /* UploadMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadMessageCell.swift; sourceTree = ""; }; @@ -2202,6 +2201,7 @@ D779D39B29A76DE6007B8087 /* ChatConversationTableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatConversationTableViewModel.swift; sourceTree = ""; }; D779D39D29AC9E92007B8087 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; D779D3A129B5E365007B8087 /* UIImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; + D780FF7C2A459CE3001535E6 /* MediaViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaViewer.swift; sourceTree = ""; }; D7A4C0052A3B135800EFBD1B /* new_chat_attachment_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = new_chat_attachment_default.png; sourceTree = ""; }; D7A4C0062A3B135800EFBD1B /* new_vr_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = new_vr_off.png; sourceTree = ""; }; D7A4C0072A3B135800EFBD1B /* new_chat_send_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = new_chat_send_default.png; sourceTree = ""; }; @@ -3878,7 +3878,7 @@ isa = PBXGroup; children = ( D7DA18702A02598700FABA0D /* TextViewer.swift */, - D73567CB2A433B76004E049F /* ImageViewer.swift */, + D780FF7C2A459CE3001535E6 /* MediaViewer.swift */, ); path = Viewers; sourceTree = ""; @@ -5046,7 +5046,6 @@ 22E0A822111C44E100B04932 /* AboutView.m in Sources */, 633671611BCBAAD200BFCBDE /* ChatConversationCreateView.m in Sources */, 634610061B61330300548952 /* UILabel+Boldify.m in Sources */, - D73567CC2A433B76004E049F /* ImageViewer.swift in Sources */, 2248E90E12F7E4CF00220D9C /* UIDigitButton.m in Sources */, 633756391B67BAF400E21BAD /* SideMenuTableView.m in Sources */, C63F7245285A24B10066163B /* VoipConferenceAudioOnlyView.swift in Sources */, @@ -5242,6 +5241,7 @@ C63F724C285A24B10066163B /* ActiveCallView.swift in Sources */, C63F7225285A24B10066163B /* UIImageExtensions.swift in Sources */, C64A854E2667B67200252AD2 /* EphemeralSettingsView.m in Sources */, + D780FF7D2A459CE3001535E6 /* MediaViewer.swift in Sources */, 8C92ABF31FA773E50006FB5D /* UIChatNotifiedEventCell.m in Sources */, C63F726E285A24B10066163B /* VFSUtil.swift in Sources */, 633FEF581D3CD5E00014B822 /* UIAvatarPresence.m in Sources */,