From c2fa4af5b69146aabb9dc28a2495dc9b8303b4af Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 13 May 2024 15:06:26 -0700 Subject: [PATCH] camera:basic: fail if permissions are not granted Pre-work for https://github.com/android/ndk-samples/issues/1031. For some reason the UI for the storage permission isn't shown whenever targetSdkVersion is 33+, and it's just returning a denial. This sample is badly structured such that if either permission isn't granted, the UI init will never run. The two permissions requests really ought to be decoupled (the viewfinder should still work even if I never try to save a photo), and the UI should tell the user why it failed. For now I'm just making it fail more violently, because the lack of any error or logging is what made this a multi-hour debugging experience instead of the now obvious problem. --- camera/basic/src/main/cpp/camera_ui.cpp | 6 +-- .../sample/camera/basic/CameraActivity.java | 50 ++++++++++++++++--- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/camera/basic/src/main/cpp/camera_ui.cpp b/camera/basic/src/main/cpp/camera_ui.cpp index 08a941101..d695f5373 100644 --- a/camera/basic/src/main/cpp/camera_ui.cpp +++ b/camera/basic/src/main/cpp/camera_ui.cpp @@ -115,9 +115,9 @@ void CameraEngine::OnPhotoTaken(const char *fileName) { void CameraEngine::OnCameraPermission(jboolean granted) { cameraGranted_ = (granted != JNI_FALSE); - if (cameraGranted_) { - OnAppInitWindow(); - } + // TODO: Fail gracefully. + ASSERT(cameraGranted_, "required app permissions were not granted"); + OnAppInitWindow(); } /** diff --git a/camera/basic/src/main/java/com/sample/camera/basic/CameraActivity.java b/camera/basic/src/main/java/com/sample/camera/basic/CameraActivity.java index 154feb7c7..f24648e68 100644 --- a/camera/basic/src/main/java/com/sample/camera/basic/CameraActivity.java +++ b/camera/basic/src/main/java/com/sample/camera/basic/CameraActivity.java @@ -41,6 +41,8 @@ import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK; +import java.util.Arrays; + class CameraSeekBar { int _progress; long _min, _max, _absVal; @@ -212,19 +214,53 @@ public void RequestCamera() { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - /* - * if any permission failed, the sample could not play - */ - if (PERMISSION_REQUEST_CODE_CAMERA != requestCode) { + if (requestCode != PERMISSION_REQUEST_CODE_CAMERA) { + // The permissions request isn't ours. super.onRequestPermissionsResult(requestCode, permissions, grantResults); return; } - if(grantResults.length == 2) { - notifyCameraPermission(grantResults[0] == PackageManager.PERMISSION_GRANTED && - grantResults[1] == PackageManager.PERMISSION_GRANTED); + if (permissions.length == 0) { + // https://developer.android.com/reference/androidx/core/app/ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int,java.lang.String[],int[]) + // + // Note: It is possible that the permissions request interaction with the user is + // interrupted. In this case you will receive empty permissions and results arrays which + // should be treated as a cancellation. + // + // The docs aren't clear about *why* it might be canceled, so it's not clear what we + // should do here other than restart the request. + RequestCamera(); + return; + } + + boolean granted = Arrays.stream(grantResults) + .allMatch(element -> element == PackageManager.PERMISSION_GRANTED); + if (!granted) { + logDeniedPermissions(permissions, grantResults); + } + notifyCameraPermission(granted); + } + + private void logDeniedPermissions( + @NonNull String[] requestedPermissions, + @NonNull int[] grantResults + ) { + if (requestedPermissions.length != grantResults.length) { + throw new IllegalArgumentException( + String.format( + "requestedPermissions.length (%d) != grantResults.length (%d)", + requestedPermissions.length, + grantResults.length + ) + ); + } + + for (int i = 0; i < requestedPermissions.length; i++) { + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { + Log.i(DBG_TAG, requestedPermissions[i] + " DENIED"); + } } }