This repository has been archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[camera]handle camera permission to fix a crash on first time using c…
…amera
- Loading branch information
1 parent
ad146f1
commit bf23c71
Showing
14 changed files
with
273 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import camera; | ||
@import camera.Test; | ||
@import AVFoundation; | ||
@import XCTest; | ||
#import <OCMock/OCMock.h> | ||
#import "CameraTestUtils.h" | ||
|
||
@interface CameraPermissionTests : XCTestCase | ||
|
||
@end | ||
|
||
@implementation CameraPermissionTests | ||
|
||
- (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized { | ||
XCTestExpectation *expectation = | ||
[self expectationWithDescription: | ||
@"Must copmlete without error if camera access was previously authorized."]; | ||
|
||
id mockDevice = OCMClassMock([AVCaptureDevice class]); | ||
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) | ||
.andReturn(AVAuthorizationStatusAuthorized); | ||
|
||
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
if (error == nil) { | ||
[expectation fulfill]; | ||
} | ||
}); | ||
[self waitForExpectationsWithTimeout:1 handler:nil]; | ||
} | ||
- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied { | ||
XCTestExpectation *expectation = | ||
[self expectationWithDescription: | ||
@"Must complete with error if camera access was previously denied."]; | ||
FlutterError *expectedError = | ||
[FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" | ||
message:@"User has previously denied the camera access request. Go to " | ||
@"Settings to enable camera access." | ||
details:nil]; | ||
|
||
id mockDevice = OCMClassMock([AVCaptureDevice class]); | ||
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) | ||
.andReturn(AVAuthorizationStatusDenied); | ||
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
if ([error isEqual:expectedError]) { | ||
[expectation fulfill]; | ||
} | ||
}); | ||
[self waitForExpectationsWithTimeout:1 handler:nil]; | ||
} | ||
|
||
- (void)testRequestCameraPermission_completeWithErrorIfRestricted { | ||
XCTestExpectation *expectation = | ||
[self expectationWithDescription:@"Must complete with error if camera access is restricted."]; | ||
FlutterError *expectedError = [FlutterError errorWithCode:@"CameraAccessRestricted" | ||
message:@"Camera access is restricted. " | ||
details:nil]; | ||
|
||
id mockDevice = OCMClassMock([AVCaptureDevice class]); | ||
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) | ||
.andReturn(AVAuthorizationStatusRestricted); | ||
|
||
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
if ([error isEqual:expectedError]) { | ||
[expectation fulfill]; | ||
} | ||
}); | ||
[self waitForExpectationsWithTimeout:1 handler:nil]; | ||
} | ||
|
||
- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess { | ||
XCTestExpectation *grantedExpectation = [self | ||
expectationWithDescription:@"Must complete without error if user choose to grant access"]; | ||
|
||
id mockDevice = OCMClassMock([AVCaptureDevice class]); | ||
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) | ||
.andReturn(AVAuthorizationStatusNotDetermined); | ||
// Mimic user choosing "allow" in permission dialog. | ||
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo | ||
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { | ||
block(YES); | ||
return YES; | ||
}]]); | ||
|
||
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
if (error == nil) { | ||
[grantedExpectation fulfill]; | ||
} | ||
}); | ||
[self waitForExpectationsWithTimeout:1 handler:nil]; | ||
} | ||
|
||
- (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { | ||
XCTestExpectation *expectation = | ||
[self expectationWithDescription:@"Must complete with error if user choose to deny access"]; | ||
FlutterError *expectedError = | ||
[FlutterError errorWithCode:@"CameraAccessDenied" | ||
message:@"User denied the camera access request." | ||
details:nil]; | ||
|
||
id mockDevice = OCMClassMock([AVCaptureDevice class]); | ||
OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) | ||
.andReturn(AVAuthorizationStatusNotDetermined); | ||
|
||
// Mimic user choosing "deny" in permission dialog. | ||
OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo | ||
completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { | ||
block(NO); | ||
return YES; | ||
}]]); | ||
FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
if ([error isEqual:expectedError]) { | ||
[expectation fulfill]; | ||
} | ||
}); | ||
|
||
[self waitForExpectationsWithTimeout:1 handler:nil]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
packages/camera/camera/ios/Classes/CameraPermissionUtils.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import Foundation; | ||
#import <Flutter/Flutter.h> | ||
|
||
typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *); | ||
|
||
/// Requests camera access permission. | ||
/// If it is the first time requesting camera access, a permission dialog will show up on the | ||
/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the | ||
/// user will have to update the choice in Settings app. | ||
/// @param handler if access permission is (or was previously) granted, completion handler will be | ||
/// called without error; Otherwise completion handler will be called with error. Handler can be | ||
/// called on an arbitrary dispatch queue. | ||
extern void FLTRequestCameraPermissionWithCompletionHandler( | ||
FLTCameraPermissionRequestCompletionHandler handler); |
39 changes: 39 additions & 0 deletions
39
packages/camera/camera/ios/Classes/CameraPermissionUtils.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
@import AVFoundation; | ||
#import "CameraPermissionUtils.h" | ||
|
||
void FLTRequestCameraPermissionWithCompletionHandler( | ||
FLTCameraPermissionRequestCompletionHandler handler) { | ||
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) { | ||
case AVAuthorizationStatusAuthorized: | ||
handler(nil); | ||
break; | ||
case AVAuthorizationStatusDenied: | ||
handler([FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" | ||
message:@"User has previously denied the camera access request. " | ||
@"Go to Settings to enable camera access." | ||
details:nil]); | ||
break; | ||
case AVAuthorizationStatusRestricted: | ||
handler([FlutterError errorWithCode:@"CameraAccessRestricted" | ||
message:@"Camera access is restricted. " | ||
details:nil]); | ||
break; | ||
case AVAuthorizationStatusNotDetermined: { | ||
[AVCaptureDevice | ||
requestAccessForMediaType:AVMediaTypeVideo | ||
completionHandler:^(BOOL granted) { | ||
// handler can be invoked on an arbitrary dispatch queue. | ||
handler(granted ? nil | ||
: [FlutterError | ||
errorWithCode:@"CameraAccessDenied" | ||
message:@"User denied the camera access request." | ||
details:nil]); | ||
}]; | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.