diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift new file mode 100644 index 000000000..d3b9c2c0d --- /dev/null +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift @@ -0,0 +1,97 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Test MediaPicker +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +final class MediaPickerCoordinator: NSObject, MediaPickerCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let mediaUTIs: [MXKUTI] + private let allowsMultipleSelection: Bool + + private let navigationRouter: NavigationRouterType + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: MediaPickerCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, mediaUTIs: [MXKUTI], allowsMultipleSelection: Bool) { + self.session = session + self.mediaUTIs = mediaUTIs + self.allowsMultipleSelection = allowsMultipleSelection + + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + + super.init() + } + + // MARK: - Public methods + + func start() { + let mediaTypes = self.mediaUTIs.map { (uti) -> String in + return uti.rawValue + } + + let mediaPickerViewController: MediaPickerViewController = MediaPickerViewController.instantiate() + mediaPickerViewController.mediaTypes = mediaTypes + mediaPickerViewController.allowsMultipleSelection = self.allowsMultipleSelection + self.navigationRouter.setRootModule(mediaPickerViewController) + mediaPickerViewController.delegate = self + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } +} + +// MARK: - MediaPickerViewControllerDelegate +extension MediaPickerCoordinator: MediaPickerViewControllerDelegate { + + func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectImage imageData: Data!, withMimeType mimetype: String!, isPhotoLibraryAsset: Bool) { + + let uti: MXKUTI? + if let mimetype = mimetype { + uti = MXKUTI(mimeType: mimetype) + } else { + uti = nil + } + + self.delegate?.mediaPickerCoordinator(self, didSelectImageData: imageData, withUTI: uti) + } + + func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectVideo videoURL: URL!) { + self.delegate?.mediaPickerCoordinator(self, didSelectVideoAt: videoURL) + } + + func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelect assets: [PHAsset]!) { + self.delegate?.mediaPickerCoordinator(self, didSelectAssets: assets) + } + + func mediaPickerControllerDidCancel(_ mediaPickerController: MediaPickerViewController!) { + self.delegate?.mediaPickerCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..0ba21d893 --- /dev/null +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift @@ -0,0 +1,124 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Test MediaPicker +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc protocol MediaPickerCoordinatorBridgePresenterDelegate { + func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) + func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideoAt url: URL) + func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectAssets assets: [PHAsset]) + func mediaPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter) +} + +/// MediaPickerCoordinatorBridgePresenter enables to start MediaPickerCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class MediaPickerCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let mediaUTIs: [MXKUTI] + private let allowsMultipleSelection: Bool + private var coordinator: MediaPickerCoordinator? + + // MARK: Public + + weak var delegate: MediaPickerCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession, mediaUTIs: [MXKUTI], allowsMultipleSelection: Bool) { + self.session = session + self.mediaUTIs = mediaUTIs + self.allowsMultipleSelection = allowsMultipleSelection + super.init() + } + + // MARK: - Public + + func present(from viewController: UIViewController, + sourceView: UIView?, + sourceRect: CGRect, + animated: Bool) { + let mediaPickerCoordinator = MediaPickerCoordinator(session: self.session, mediaUTIs: mediaUTIs, allowsMultipleSelection: self.allowsMultipleSelection) + mediaPickerCoordinator.delegate = self + + let mediaPickerPresentable = mediaPickerCoordinator.toPresentable() + + if let sourceView = sourceView { + + mediaPickerPresentable.modalPresentationStyle = .popover + + if let popoverPresentationController = mediaPickerPresentable.popoverPresentationController { + popoverPresentationController.sourceView = sourceView + + let finalSourceRect: CGRect + + if sourceRect != CGRect.null { + finalSourceRect = sourceRect + } else { + finalSourceRect = sourceView.bounds + } + + popoverPresentationController.sourceRect = finalSourceRect + } + } + + viewController.present(mediaPickerPresentable, animated: animated, completion: nil) + + mediaPickerCoordinator.start() + + self.coordinator = mediaPickerCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + if let completion = completion { + completion() + } + } + } +} + +// MARK: - MediaPickerCoordinatorDelegate +extension MediaPickerCoordinatorBridgePresenter: MediaPickerCoordinatorDelegate { + + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) { + self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectImageData: imageData, withUTI: uti) + } + + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL) { + self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectVideoAt: url) + } + + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset]) { + self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectAssets: assets) + } + + func mediaPickerCoordinatorDidCancel(_ coordinator: MediaPickerCoordinatorType) { + self.delegate?.mediaPickerCoordinatorBridgePresenterDidCancel(self) + } +} diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift new file mode 100644 index 000000000..53f154536 --- /dev/null +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift @@ -0,0 +1,31 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Test MediaPicker +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol MediaPickerCoordinatorDelegate: class { + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL) + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset]) + func mediaPickerCoordinatorDidCancel(_ coordinator: MediaPickerCoordinatorType) +} + +/// `MediaPickerCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol MediaPickerCoordinatorType: Coordinator, Presentable { + var delegate: MediaPickerCoordinatorDelegate? { get } +} diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index 68348718c..92e6e483c 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -13,3 +13,4 @@ #import "AvatarGenerator.h" #import "EncryptionInfoView.h" #import "EventFormatter.h" +#import "MediaPickerViewController.h"