This repository was 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.7k
[camera]handle iOS camera access permission #5215
Merged
fluttergithubbot
merged 6 commits into
flutter:main
from
hellohuanlin:camera_permission_draft
May 14, 2022
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
d20b45c
[camera]handle camera permission to fix a crash on first time using c…
hellohuanlin a27fa25
[camera]add error handling in example, and update readme
hellohuanlin 11e8d24
format
hellohuanlin cbfe078
run update exceprts
hellohuanlin c366218
addresses more comment
hellohuanlin 4cef206
use Object instead of dynamic
hellohuanlin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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
20 changes: 20 additions & 0 deletions
20
packages/camera/camera/ios/Classes/CameraPermissionUtils.h
This file contains hidden or 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,20 @@ | ||
| // 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 | ||
hellohuanlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// 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 hidden or 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" | ||
hellohuanlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.