Skip to content

Commit f697472

Browse files
authored
Merge pull request #21398 from wordpress-mobile/task/fix-camera-permissions
Fix an issue with camera not working without "All Photos" permission
2 parents c16260c + b9cce7d commit f697472

11 files changed

+139
-141
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* [*] Remove the "New Photo Post" app quick action [#21369](https://github.com/wordpress-mobile/WordPress-iOS/pull/21369)
44
* [*] (Internal) Fix unbounded growth of number media observers in Post Editor (performance issue) [#21352](https://github.com/wordpress-mobile/WordPress-iOS/pull/21352)
55
* [**] [internal] Fix a crash when disconnecting the app from the "Connected Applications" on WordPress.com. [#21375]
6+
* [*] Fix an issue where the "Take Photo" flow was not working without the "All Photos" access [#21398](https://github.com/wordpress-mobile/WordPress-iOS/pull/21398)
67

78
23.1
89
-----
Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1+
import CoreMedia
2+
import AVFoundation
3+
14
extension Blog {
5+
/// Maximum allowed duration for video uploads on free sites, in seconds (5 mins).
6+
static let maximumVideoDurationForFreeSites: TimeInterval = 300
27

3-
/// returns true if the blog is allowed to upload the given asset, true otherwise
8+
/// Returns `true` if the blog is allowed to upload the given asset.
49
func canUploadAsset(_ asset: WPMediaAsset) -> Bool {
510
return canUploadAsset(asset.exceedsFreeSitesAllowance())
611
}
712

13+
/// Returns `true` if the blog is allowed to upload the video at the given URL.
14+
func canUploadVideo(from videoURL: URL) -> Bool {
15+
let asset = AVAsset(url: videoURL)
16+
let duration = CMTimeGetSeconds(asset.duration)
17+
let exceedsAllowance = duration > Blog.maximumVideoDurationForFreeSites
18+
return canUploadAsset(exceedsAllowance)
19+
}
20+
821
public func canUploadAsset(_ assetExceedsFreeSitesAllowance: Bool) -> Bool {
922
return hasPaidPlan || !isHostedAtWPcom || !assetExceedsFreeSitesAllowance
1023
}
1124
}
25+
26+
private extension WPMediaAsset {
27+
/// Returns true if the asset is a video and its duration is longer than the maximum allowed duration on free sites.
28+
func exceedsFreeSitesAllowance() -> Bool {
29+
assetType() == .video && duration() > Blog.maximumVideoDurationForFreeSites
30+
}
31+
}

WordPress/Classes/Utility/Media/WPMediaAsset+VideoLimits.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.

WordPress/Classes/ViewRelated/Gutenberg/GutenbergMediaInserterHelper.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ class GutenbergMediaInserterHelper: NSObject {
119119
callback(media)
120120
}
121121

122-
func insertFromDevice(url: URL, callback: @escaping MediaPickerDidPickMediaCallback) {
123-
guard let media = insert(exportableAsset: url as NSURL, source: .otherApps) else {
122+
func insertFromDevice(url: URL, callback: @escaping MediaPickerDidPickMediaCallback, source: MediaSource = .otherApps) {
123+
guard let media = insert(exportableAsset: url as NSURL, source: source) else {
124124
callback([])
125125
return
126126
}

WordPress/Classes/ViewRelated/Gutenberg/GutenbergMediaPickerHelper.swift

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,42 @@ class GutenbergMediaPickerHelper: NSObject {
105105
context.present(picker, animated: true)
106106
}
107107

108-
private lazy var cameraPicker: WPMediaPickerViewController = {
109-
let cameraPicker = WPMediaPickerViewController()
110-
cameraPicker.options = WPMediaPickerOptions.withDefaults()
111-
cameraPicker.mediaPickerDelegate = self
112-
cameraPicker.dataSource = WPPHAssetDataSource.sharedInstance()
113-
return cameraPicker
114-
}()
115-
116108
func presentCameraCaptureFullScreen(animated: Bool,
117109
filter: WPMediaType,
118110
callback: @escaping GutenbergMediaPickerHelperCallback) {
111+
didPickMediaCallback = callback
112+
MediaPickerMenu(viewController: context, filter: .init(filter))
113+
.showCamera(delegate: self)
114+
}
115+
}
119116

120-
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
121-
callback(nil)
122-
return
117+
extension GutenbergMediaPickerHelper: ImagePickerControllerDelegate {
118+
func imagePicker(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
119+
context.dismiss(animated: true) {
120+
guard let mediaType = info[.mediaType] as? String else {
121+
return
122+
}
123+
switch mediaType {
124+
case UTType.image.identifier:
125+
if let image = info[.originalImage] as? UIImage {
126+
self.didPickMediaCallback?([image])
127+
self.didPickMediaCallback = nil
128+
}
129+
130+
case UTType.movie.identifier:
131+
guard let videoURL = info[.mediaURL] as? URL else {
132+
return
133+
}
134+
guard self.post.blog.canUploadVideo(from: videoURL) else {
135+
self.presentVideoLimitExceededAfterCapture(on: self.context)
136+
return
137+
}
138+
self.didPickMediaCallback?([videoURL])
139+
self.didPickMediaCallback = nil
140+
default:
141+
break
142+
}
123143
}
124-
125-
didPickMediaCallback = callback
126-
//reset the selected assets from previous uses
127-
cameraPicker.resetState(false)
128-
cameraPicker.modalPresentationStyle = .currentContext
129-
cameraPicker.viewControllerToUseToPresent = context
130-
cameraPicker.options.filter = filter
131-
cameraPicker.options.allowMultipleSelection = false
132-
cameraPicker.showCapture()
133144
}
134145
}
135146

@@ -142,14 +153,8 @@ extension GutenbergMediaPickerHelper: VideoLimitsAlertPresenter {}
142153
extension GutenbergMediaPickerHelper: WPMediaPickerViewControllerDelegate {
143154

144155
func mediaPickerController(_ picker: WPMediaPickerViewController, didFinishPicking assets: [WPMediaAsset]) {
145-
if picker == cameraPicker,
146-
let asset = assets.first,
147-
!post.blog.canUploadAsset(asset) {
148-
presentVideoLimitExceededAfterCapture(on: self.context)
149-
} else {
150-
invokeMediaPickerCallback(asset: assets)
151-
picker.dismiss(animated: true, completion: nil)
152-
}
156+
invokeMediaPickerCallback(asset: assets)
157+
picker.dismiss(animated: true, completion: nil)
153158
}
154159

155160
open func mediaPickerController(_ picker: WPMediaPickerViewController, handleError error: Error) -> Bool {
@@ -165,8 +170,7 @@ extension GutenbergMediaPickerHelper: WPMediaPickerViewControllerDelegate {
165170
}
166171

167172
func mediaPickerControllerShouldShowCustomHeaderView(_ picker: WPMediaPickerViewController) -> Bool {
168-
guard FeatureFlag.mediaPickerPermissionsNotice.enabled,
169-
picker !== cameraPicker else {
173+
guard FeatureFlag.mediaPickerPermissionsNotice.enabled else {
170174
return false
171175
}
172176

@@ -189,13 +193,11 @@ extension GutenbergMediaPickerHelper: WPMediaPickerViewControllerDelegate {
189193
}
190194

191195
func mediaPickerController(_ picker: WPMediaPickerViewController, shouldShowOverlayViewForCellFor asset: WPMediaAsset) -> Bool {
192-
picker !== cameraPicker && !post.blog.canUploadAsset(asset)
196+
!post.blog.canUploadAsset(asset)
193197
}
194198

195199
func mediaPickerController(_ picker: WPMediaPickerViewController, shouldSelect asset: WPMediaAsset) -> Bool {
196-
if picker !== cameraPicker,
197-
!post.blog.canUploadAsset(asset) {
198-
200+
if !post.blog.canUploadAsset(asset) {
199201
presentVideoLimitExceededFromPicker(on: picker)
200202
return false
201203
}

WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -650,12 +650,12 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
650650
filter: filter,
651651
dataSourceType: .mediaLibrary,
652652
allowMultipleSelection: allowMultipleSelection,
653-
callback: {(assets) in
654-
guard let media = assets as? [Media] else {
655-
callback(nil)
656-
return
657-
}
658-
self.mediaInserterHelper.insertFromSiteMediaLibrary(media: media, callback: callback)
653+
callback: { [weak self] assets in
654+
guard let self, let media = assets as? [Media] else {
655+
callback(nil)
656+
return
657+
}
658+
self.mediaInserterHelper.insertFromSiteMediaLibrary(media: media, callback: callback)
659659
})
660660
}
661661

@@ -665,24 +665,27 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
665665
filter: filter,
666666
dataSourceType: .device,
667667
allowMultipleSelection: allowMultipleSelection,
668-
callback: { assets in
669-
guard let assets, !assets.isEmpty else {
668+
callback: { [weak self] assets in
669+
guard let self, let assets, !assets.isEmpty else {
670670
return callback(nil)
671671
}
672672
self.mediaInserterHelper.insertFromDevice(assets, callback: callback)
673673
})
674674
}
675675

676676
func gutenbergDidRequestMediaFromCameraPicker(filter: WPMediaType, with callback: @escaping MediaPickerDidPickMediaCallback) {
677-
mediaPickerHelper.presentCameraCaptureFullScreen(animated: true,
678-
filter: filter,
679-
callback: {(assets) in
680-
guard let phAsset = assets?.first as? PHAsset else {
681-
callback(nil)
682-
return
683-
}
684-
self.mediaInserterHelper.insertFromDevice(asset: phAsset, callback: callback)
685-
})
677+
mediaPickerHelper.presentCameraCaptureFullScreen(animated: true, filter: filter) { [weak self] assets in
678+
guard let self, let asset = assets?.first else {
679+
return callback(nil)
680+
}
681+
if let asset = asset as? PHAsset {
682+
self.mediaInserterHelper.insertFromDevice(asset: asset, callback: callback)
683+
} else if let image = asset as? UIImage {
684+
self.mediaInserterHelper.insertFromImage(image: image, callback: callback, source: .camera)
685+
} else if let url = asset as? URL {
686+
self.mediaInserterHelper.insertFromDevice(url: url, callback: callback, source: .camera)
687+
}
688+
}
686689
}
687690

688691
func gutenbergDidRequestMediaEditor(with mediaUrl: URL, callback: @escaping MediaPickerDidPickMediaCallback) {

WordPress/Classes/ViewRelated/Media/CameraCaptureCoordinator.swift

Lines changed: 0 additions & 60 deletions
This file was deleted.

WordPress/Classes/ViewRelated/Media/MediaLibraryMediaPickingCoordinator.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ import PhotosUI
44

55
/// Prepares the alert controller that will be presented when tapping the "+" button in Media Library
66
final class MediaLibraryMediaPickingCoordinator {
7-
typealias PickersDelegate = StockPhotosPickerDelegate & WPMediaPickerViewControllerDelegate & TenorPickerDelegate & PHPickerViewControllerDelegate
7+
typealias PickersDelegate = StockPhotosPickerDelegate & WPMediaPickerViewControllerDelegate & TenorPickerDelegate & PHPickerViewControllerDelegate & ImagePickerControllerDelegate
88
private weak var delegate: PickersDelegate?
99
private var tenor: TenorPicker?
1010

1111
private var stockPhotos: StockPhotosPicker?
12-
private let cameraCapture = CameraCaptureCoordinator()
1312
private let mediaLibrary = MediaLibraryPicker()
1413

1514
init(delegate: PickersDelegate) {
@@ -30,7 +29,7 @@ final class MediaLibraryMediaPickingCoordinator {
3029
menuAlert.title = quotaUsageDescription
3130
}
3231

33-
if WPMediaCapturePresenter.isCaptureAvailable() {
32+
if UIImagePickerController.isSourceTypeAvailable(.camera) {
3433
menuAlert.addAction(cameraAction(origin: origin, blog: blog))
3534
}
3635

@@ -88,7 +87,8 @@ final class MediaLibraryMediaPickingCoordinator {
8887
}
8988

9089
private func showCameraCapture(origin: UIViewController, blog: Blog) {
91-
cameraCapture.presentMediaCapture(origin: origin, blog: blog)
90+
MediaPickerMenu(viewController: origin)
91+
.showCamera(delegate: self)
9292
}
9393

9494
private func showStockPhotos(origin: UIViewController, blog: Blog) {
@@ -147,3 +147,9 @@ extension MediaLibraryMediaPickingCoordinator: PHPickerViewControllerDelegate {
147147
delegate?.picker(picker, didFinishPicking: results)
148148
}
149149
}
150+
151+
extension MediaLibraryMediaPickingCoordinator: ImagePickerControllerDelegate {
152+
func imagePicker(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
153+
delegate?.imagePicker(picker, didFinishPickingMediaWithInfo: info)
154+
}
155+
}

WordPress/Classes/ViewRelated/Media/MediaLibraryViewController.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ class MediaLibraryViewController: WPMediaPickerViewController {
2424

2525
fileprivate var selectedAsset: Media? = nil
2626

27-
fileprivate var capturePresenter: WPMediaCapturePresenter?
28-
2927
// After 99% progress, we'll count a media item as being uploaded, and we'll
3028
// show an indeterminate spinner as the server processes it.
3129
fileprivate static let uploadCompleteProgress: Double = 0.99
@@ -474,6 +472,8 @@ class MediaLibraryViewController: WPMediaPickerViewController {
474472
}
475473
}
476474

475+
// MARK: - PHPickerViewControllerDelegate
476+
477477
extension MediaLibraryViewController: PHPickerViewControllerDelegate {
478478
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
479479
dismiss(animated: true)
@@ -485,6 +485,42 @@ extension MediaLibraryViewController: PHPickerViewControllerDelegate {
485485
}
486486
}
487487

488+
// MARK: - ImagePickerControllerDelegate
489+
490+
extension MediaLibraryViewController: ImagePickerControllerDelegate {
491+
func imagePicker(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
492+
dismiss(animated: true)
493+
494+
func addAsset(from exportableAsset: ExportableAsset) {
495+
let info = MediaAnalyticsInfo(origin: .mediaLibrary(.camera), selectionMethod: .fullScreenPicker)
496+
MediaCoordinator.shared.addMedia(from: exportableAsset, to: blog, analyticsInfo: info)
497+
}
498+
499+
guard let mediaType = info[.mediaType] as? String else {
500+
return
501+
}
502+
switch mediaType {
503+
case UTType.image.identifier:
504+
if let image = info[.originalImage] as? UIImage {
505+
addAsset(from: image)
506+
}
507+
508+
case UTType.movie.identifier:
509+
guard let videoURL = info[.mediaURL] as? URL else {
510+
return
511+
}
512+
guard self.blog.canUploadVideo(from: videoURL) else {
513+
self.presentVideoLimitExceededAfterCapture(on: self)
514+
return
515+
}
516+
addAsset(from: videoURL as NSURL)
517+
default:
518+
break
519+
}
520+
}
521+
}
522+
523+
488524
// MARK: - UIDocumentPickerDelegate
489525

490526
extension MediaLibraryViewController: UIDocumentPickerDelegate {

0 commit comments

Comments
 (0)