Ensure media-library permissions when accessing media library

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent 4b03482ee2
commit 3ca5ec2726

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import "AvatarViewHelper.h" #import "AvatarViewHelper.h"
@ -69,17 +69,22 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssert(self.delegate); OWSAssert(self.delegate);
UIImagePickerController *picker = [[UIImagePickerController alloc] init]; [self.delegate.fromViewController ows_askForCameraPermissions:^(BOOL granted) {
if (!granted) {
DDLogWarn(@"%@ Camera permission denied.", self.logTag);
return;
}
UIImagePickerController *picker = [UIImagePickerController new];
picker.delegate = self; picker.delegate = self;
picker.allowsEditing = NO; picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypeCamera; picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage ];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
picker.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeImage, nil];
[self.delegate.fromViewController presentViewController:picker [self.delegate.fromViewController presentViewController:picker
animated:YES animated:YES
completion:[UIUtil modalCompletionBlock]]; completion:[UIUtil modalCompletionBlock]];
} }];
} }
- (void)chooseFromLibrary - (void)chooseFromLibrary
@ -87,16 +92,21 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssert(self.delegate); OWSAssert(self.delegate);
UIImagePickerController *picker = [[UIImagePickerController alloc] init]; [self.delegate.fromViewController ows_askForMediaLibraryPermissions:^(BOOL granted) {
if (!granted) {
DDLogWarn(@"%@ Media Library permission denied.", self.logTag);
return;
}
UIImagePickerController *picker = [UIImagePickerController new];
picker.delegate = self; picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage ];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
picker.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeImage, nil];
[self.delegate.fromViewController presentViewController:picker [self.delegate.fromViewController presentViewController:picker
animated:YES animated:YES
completion:[UIUtil modalCompletionBlock]]; completion:[UIUtil modalCompletionBlock]];
} }];
} }
/* /*

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
// Originally based on EPContacts // Originally based on EPContacts
@ -161,7 +161,7 @@ open class ContactsPicker: OWSViewController, UITableViewDelegate, UITableViewDa
}) })
alert.addAction(cancelAction) alert.addAction(cancelAction)
let settingsText = NSLocalizedString("OPEN_SETTINGS_BUTTON", comment:"Button text which opens the settings app") let settingsText = CommonStrings.openSettingsButton
let openSettingsAction = UIAlertAction(title: settingsText, style: .default, handler: { (_) in let openSettingsAction = UIAlertAction(title: settingsText, style: .default, handler: { (_) in
UIApplication.shared.openSystemSettings() UIApplication.shared.openSystemSettings()
}) })

@ -2466,18 +2466,18 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
{ {
[self ows_askForCameraPermissions:^(BOOL granted) { [self ows_askForCameraPermissions:^(BOOL granted) {
if (!granted) { if (!granted) {
DDLogWarn(@"%@ camera permission denied.", self.logTag);
return; return;
} }
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
UIImagePickerController *picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypeCamera; picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie ]; picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie ];
picker.allowsEditing = NO; picker.allowsEditing = NO;
picker.delegate = self; picker.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissKeyBoard]; [self dismissKeyBoard];
[self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]]; [self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]];
});
}]; }];
} }
@ -2485,18 +2485,20 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { [self ows_askForMediaLibraryPermissions:^(BOOL granted) {
DDLogError(@"PhotoLibrary ImagePicker source not available"); if (!granted) {
DDLogWarn(@"%@ Media Library permission denied.", self.logTag);
return; return;
} }
UIImagePickerController *picker = [[UIImagePickerController alloc] init]; UIImagePickerController *picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self; picker.delegate = self;
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie ]; picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie ];
[self dismissKeyBoard]; [self dismissKeyBoard];
[self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]]; [self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]];
}];
} }
/* /*

@ -314,8 +314,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
imageEdgeInsets.top = round((kBarButtonSize - image.size.height) * 0.5f); imageEdgeInsets.top = round((kBarButtonSize - image.size.height) * 0.5f);
imageEdgeInsets.bottom = round(kBarButtonSize - (image.size.height + imageEdgeInsets.top)); imageEdgeInsets.bottom = round(kBarButtonSize - (image.size.height + imageEdgeInsets.top));
button.imageEdgeInsets = imageEdgeInsets; button.imageEdgeInsets = imageEdgeInsets;
button.accessibilityLabel button.accessibilityLabel = CommonStrings.openSettingsButton;
= NSLocalizedString(@"OPEN_SETTINGS_BUTTON", "Label for button which opens the settings UI");
[button addTarget:self action:@selector(settingsButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [button addTarget:self action:@selector(settingsButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(0, button.frame = CGRectMake(0,
0, 0,

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface UIViewController (Permissions) @interface UIViewController (Permissions)
- (void)ows_askForCameraPermissions:(void (^)(BOOL granted))callback; - (void)ows_askForCameraPermissions:(void (^)(BOOL granted))callback;
- (void)ows_askForMediaLibraryPermissions:(void (^)(BOOL granted))callbackParam;
- (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callback; - (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callback;
@end @end

@ -1,10 +1,11 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import "Signal-Swift.h" #import "Signal-Swift.h"
#import "UIViewController+Permissions.h" #import "UIViewController+Permissions.h"
#import <AVFoundation/AVFoundation.h> #import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>
#import <SignalMessaging/UIUtil.h> #import <SignalMessaging/UIUtil.h>
#import <SignalServiceKit/Threading.h> #import <SignalServiceKit/Threading.h>
@ -42,10 +43,8 @@ NS_ASSUME_NONNULL_BEGIN
message:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_MESSAGE", @"Alert body") message:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_MESSAGE", @"Alert body")
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
NSString *settingsTitle
= NSLocalizedString(@"OPEN_SETTINGS_BUTTON", @"Button text which opens the settings app");
UIAlertAction *openSettingsAction = UIAlertAction *openSettingsAction =
[UIAlertAction actionWithTitle:settingsTitle [UIAlertAction actionWithTitle:CommonStrings.openSettingsButton
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) { handler:^(UIAlertAction *_Nonnull action) {
[[UIApplication sharedApplication] openSystemSettings]; [[UIApplication sharedApplication] openSystemSettings];
@ -72,6 +71,84 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
- (void)ows_askForMediaLibraryPermissions:(void (^)(BOOL granted))callbackParam
{
DDLogVerbose(@"[%@] ows_askForMediaLibraryPermissions", NSStringFromClass(self.class));
// Ensure callback is invoked on main thread.
void (^completionCallback)(BOOL) = ^(BOOL granted) {
DispatchMainThreadSafe(^{
callbackParam(granted);
});
};
void (^presentSettingsDialog)(void) = ^(void) {
UIAlertController *alert = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"MISSING_MEDIA_LIBRARY_PERMISSION_TITLE",
@"Alert title when user has previously denied media library access")
message:NSLocalizedString(@"MISSING_MEDIA_LIBRARY_PERMISSION_MESSAGE",
@"Alert body when user has previously denied media library access")
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *openSettingsAction =
[UIAlertAction actionWithTitle:CommonStrings.openSettingsButton
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[[UIApplication sharedApplication] openSystemSettings];
completionCallback(NO);
}];
[alert addAction:openSettingsAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.dismissButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
completionCallback(NO);
}];
[alert addAction:dismissAction];
[self presentViewController:alert animated:YES completion:nil];
};
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
DDLogError(@"Skipping media library permissions request when app is in background.");
completionCallback(NO);
return;
}
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
DDLogError(@"%@ PhotoLibrary ImagePicker source not available", self.logTag);
completionCallback(NO);
}
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
switch (status) {
case PHAuthorizationStatusAuthorized: {
completionCallback(YES);
return;
}
case PHAuthorizationStatusDenied: {
presentSettingsDialog();
return;
}
case PHAuthorizationStatusNotDetermined: {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus newStatus) {
if (newStatus == PHAuthorizationStatusAuthorized) {
completionCallback(YES);
} else {
presentSettingsDialog();
}
}];
return;
}
case PHAuthorizationStatusRestricted: {
// when does this happen?
OWSFail(@"PHAuthorizationStatusRestricted");
return;
}
}
}
- (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callbackParam - (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callbackParam
{ {
DDLogVerbose(@"[%@] ows_askForMicrophonePermissions", NSStringFromClass(self.class)); DDLogVerbose(@"[%@] ows_askForMicrophonePermissions", NSStringFromClass(self.class));

@ -1041,6 +1041,12 @@
Alert title when camera is not authorized */ Alert title when camera is not authorized */
"MISSING_CAMERA_PERMISSION_TITLE" = "Signal needs to access your camera."; "MISSING_CAMERA_PERMISSION_TITLE" = "Signal needs to access your camera.";
/* Alert body when user has previously denied media library access */
"MISSING_MEDIA_LIBRARY_PERMISSION_MESSAGE" = "You can grant this permission in the Settings app.";
/* Alert title when user has previously denied media library access */
"MISSING_MEDIA_LIBRARY_PERMISSION_TITLE" = "Signal Requires Access to your Media Library to do this.";
/* notification title. Embeds {{caller's name or phone number}} */ /* notification title. Embeds {{caller's name or phone number}} */
"MSGVIEW_MISSED_CALL_WITH_NAME" = "Missed call from %@."; "MSGVIEW_MISSED_CALL_WITH_NAME" = "Missed call from %@.";
@ -1146,8 +1152,7 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"OK" = "OK"; "OK" = "OK";
/* Button text which opens the settings app /* Button text which opens the settings app */
Label for button which opens the settings UI */
"OPEN_SETTINGS_BUTTON" = "Settings"; "OPEN_SETTINGS_BUTTON" = "Settings";
/* Info Message when {{other user}} disables or doesn't support disappearing messages */ /* Info Message when {{other user}} disables or doesn't support disappearing messages */

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
#import "ContactsViewHelper.h" #import "ContactsViewHelper.h"
@ -282,8 +282,7 @@ NS_ASSUME_NONNULL_BEGIN
style:UIAlertActionStyleCancel style:UIAlertActionStyleCancel
handler:nil]]; handler:nil]];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OPEN_SETTINGS_BUTTON", [alertController addAction:[UIAlertAction actionWithTitle:CommonStrings.openSettingsButton
@"Button text which opens the settings app")
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) { handler:^(UIAlertAction *_Nonnull action) {
[CurrentAppContext() openSystemSettings]; [CurrentAppContext() openSystemSettings];

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
import Foundation import Foundation
@ -15,6 +15,8 @@ import Foundation
static public let cancelButton = NSLocalizedString("TXT_CANCEL_TITLE", comment:"Label for the cancel button in an alert or action sheet.") static public let cancelButton = NSLocalizedString("TXT_CANCEL_TITLE", comment:"Label for the cancel button in an alert or action sheet.")
@objc @objc
static public let retryButton = NSLocalizedString("RETRY_BUTTON_TEXT", comment:"Generic text for button that retries whatever the last action was.") static public let retryButton = NSLocalizedString("RETRY_BUTTON_TEXT", comment:"Generic text for button that retries whatever the last action was.")
@objc
static public let openSettingsButton = NSLocalizedString("OPEN_SETTINGS_BUTTON", comment: "Button text which opens the settings app")
} }
@objc public class MessageStrings: NSObject { @objc public class MessageStrings: NSObject {

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
import Foundation import Foundation
@ -14,7 +14,7 @@ import Foundation
let alertMessage = NSLocalizedString("CALL_AUDIO_PERMISSION_MESSAGE", comment:"Alert message when calling and permissions for microphone are missing") let alertMessage = NSLocalizedString("CALL_AUDIO_PERMISSION_MESSAGE", comment:"Alert message when calling and permissions for microphone are missing")
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert) let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: CommonStrings.dismissButton, style: .cancel) let dismissAction = UIAlertAction(title: CommonStrings.dismissButton, style: .cancel)
let settingsString = NSLocalizedString("OPEN_SETTINGS_BUTTON", comment: "Button text which opens the settings app") let settingsString = CommonStrings.openSettingsButton
let settingsAction = UIAlertAction(title: settingsString, style: .default) { _ in let settingsAction = UIAlertAction(title: settingsString, style: .default) { _ in
CurrentAppContext().openSystemSettings() CurrentAppContext().openSystemSettings()
} }

Loading…
Cancel
Save