diff options
author | Yuichi Araki <yaraki@google.com> | 2015-08-27 03:59:03 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-08-27 03:59:03 +0000 |
commit | 0623e0051e00dd8cab1d3698fa21b4bccab818c7 (patch) | |
tree | b7eb25895cec34c1ba3b7bb604ab2481f29dff2d /media | |
parent | 9086183043d20655d87b014c033a8e07a6c2e334 (diff) | |
parent | cfc6726372144c5b335a4aae0254cd7a432fec7c (diff) | |
download | android-0623e0051e00dd8cab1d3698fa21b4bccab818c7.tar.gz |
Merge "Camera2Basic: Runtime permission on M" into mnc-devmarshmallow-dev
Diffstat (limited to 'media')
4 files changed, 131 insertions, 49 deletions
diff --git a/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java b/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java index 020ca14d..4bf85343 100644 --- a/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java +++ b/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java @@ -16,6 +16,7 @@ package com.example.android.camera2basic; +import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -23,6 +24,7 @@ import android.app.DialogFragment; import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Matrix; @@ -43,7 +45,8 @@ import android.media.ImageReader; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; -import android.os.Message; +import android.support.annotation.NonNull; +import android.support.v13.app.FragmentCompat; import android.util.Log; import android.util.Size; import android.util.SparseIntArray; @@ -66,12 +69,15 @@ import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; -public class Camera2BasicFragment extends Fragment implements View.OnClickListener { +public class Camera2BasicFragment extends Fragment + implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback { /** * Conversion from screen rotation to JPEG orientation. */ private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private static final int REQUEST_CAMERA_PERMISSION = 1; + private static final String FRAGMENT_DIALOG = "dialog"; static { ORIENTATIONS.append(Surface.ROTATION_0, 90); @@ -94,14 +100,17 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen * Camera state: Waiting for the focus to be locked. */ private static final int STATE_WAITING_LOCK = 1; + /** * Camera state: Waiting for the exposure to be precapture state. */ private static final int STATE_WAITING_PRECAPTURE = 2; + /** * Camera state: Waiting for the exposure state to be something other than precapture. */ private static final int STATE_WAITING_NON_PRECAPTURE = 3; + /** * Camera state: Picture was taken. */ @@ -148,17 +157,16 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen /** * A {@link CameraCaptureSession } for camera preview. */ - private CameraCaptureSession mCaptureSession; + /** * A reference to the opened {@link CameraDevice}. */ - private CameraDevice mCameraDevice; + /** * The {@link android.util.Size} of camera preview. */ - private Size mPreviewSize; /** @@ -167,7 +175,7 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override - public void onOpened(CameraDevice cameraDevice) { + public void onOpened(@NonNull CameraDevice cameraDevice) { // This method is called when the camera is opened. We start camera preview here. mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; @@ -175,14 +183,14 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } @Override - public void onDisconnected(CameraDevice cameraDevice) { + public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override - public void onError(CameraDevice cameraDevice, int error) { + public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; @@ -303,43 +311,36 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } @Override - public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, - CaptureResult partialResult) { + public void onCaptureProgressed(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull CaptureResult partialResult) { process(partialResult); } @Override - public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, - TotalCaptureResult result) { + public void onCaptureCompleted(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { process(result); } }; /** - * A {@link Handler} for showing {@link Toast}s. - */ - private Handler mMessageHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - Activity activity = getActivity(); - if (activity != null) { - Toast.makeText(activity, (String) msg.obj, Toast.LENGTH_SHORT).show(); - } - } - }; - - /** * Shows a {@link Toast} on the UI thread. * * @param text The message to show */ - private void showToast(String text) { - // We show a Toast by sending request message to mMessageHandler. This makes sure that the - // Toast is shown on the UI thread. - Message message = Message.obtain(); - message.obj = text; - mMessageHandler.sendMessage(message); + private void showToast(final String text) { + final Activity activity = getActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); + } + }); + } } /** @@ -355,7 +356,7 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen */ private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) { // Collect the supported resolutions that are at least as big as the preview Surface - List<Size> bigEnough = new ArrayList<Size>(); + List<Size> bigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { @@ -375,9 +376,7 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } public static Camera2BasicFragment newInstance() { - Camera2BasicFragment fragment = new Camera2BasicFragment(); - fragment.setRetainInstance(true); - return fragment; + return new Camera2BasicFragment(); } @Override @@ -422,6 +421,28 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen super.onPause(); } + private void requestCameraPermission() { + if (FragmentCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { + new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); + } else { + FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, + REQUEST_CAMERA_PERMISSION); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == REQUEST_CAMERA_PERMISSION) { + if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + ErrorDialog.newInstance(getString(R.string.request_permission)) + .show(getChildFragmentManager(), FRAGMENT_DIALOG); + } + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + /** * Sets up member variables related to camera. * @@ -437,13 +458,16 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen = manager.getCameraCharacteristics(cameraId); // We don't use a front facing camera in this sample. - if (characteristics.get(CameraCharacteristics.LENS_FACING) - == CameraCharacteristics.LENS_FACING_FRONT) { + Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { continue; } StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + if (map == null) { + continue; + } // For still image captures, we use the largest available size. Size largest = Collections.max( @@ -478,7 +502,8 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } catch (NullPointerException e) { // Currently an NPE is thrown when the Camera2API is used but not supported on the // device this code runs. - new ErrorDialog().show(getFragmentManager(), "dialog"); + ErrorDialog.newInstance(getString(R.string.camera_error)) + .show(getChildFragmentManager(), FRAGMENT_DIALOG); } } @@ -486,6 +511,11 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}. */ private void openCamera(int width, int height) { + if (getActivity().checkSelfPermission(Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + requestCameraPermission(); + return; + } setUpCameraOutputs(width, height); configureTransform(width, height); Activity activity = getActivity(); @@ -574,7 +604,7 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen new CameraCaptureSession.StateCallback() { @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { + public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { // The camera is already closed if (null == mCameraDevice) { return; @@ -600,7 +630,8 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + public void onConfigureFailed( + @NonNull CameraCaptureSession cameraCaptureSession) { showToast("Failed"); } }, null @@ -668,8 +699,8 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } /** - * Run the precapture sequence for capturing a still image. This method should be called when we - * get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. + * Run the precapture sequence for capturing a still image. This method should be called when + * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. */ private void runPrecaptureSequence() { try { @@ -714,9 +745,11 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen = new CameraCaptureSession.CaptureCallback() { @Override - public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, - TotalCaptureResult result) { + public void onCaptureCompleted(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { showToast("Saved: " + mFile); + Log.d(TAG, mFile.toString()); unlockFocus(); } }; @@ -729,11 +762,12 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } /** - * Unlock the focus. This method should be called when still image capture sequence is finished. + * Unlock the focus. This method should be called when still image capture sequence is + * finished. */ private void unlockFocus() { try { - // Reset the autofucos trigger + // Reset the auto-focus trigger mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, @@ -827,13 +861,26 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } + /** + * Shows an error message dialog. + */ public static class ErrorDialog extends DialogFragment { + private static final String ARG_MESSAGE = "message"; + + public static ErrorDialog newInstance(String message) { + ErrorDialog dialog = new ErrorDialog(); + Bundle args = new Bundle(); + args.putString(ARG_MESSAGE, message); + dialog.setArguments(args); + return dialog; + } + @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); return new AlertDialog.Builder(activity) - .setMessage("This device doesn't support Camera2 API.") + .setMessage(getArguments().getString(ARG_MESSAGE)) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { @@ -845,4 +892,36 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen } + /** + * Shows OK/Cancel confirmation dialog about camera permission. + */ + public static class ConfirmationDialog extends DialogFragment { + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Fragment parent = getParentFragment(); + return new AlertDialog.Builder(getActivity()) + .setMessage(R.string.request_permission) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + FragmentCompat.requestPermissions(parent, + new String[]{Manifest.permission.CAMERA}, + REQUEST_CAMERA_PERMISSION); + } + }) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Activity activity = parent.getActivity(); + if (activity != null) { + activity.finish(); + } + } + }) + .create(); + } + } + } diff --git a/media/Camera2Basic/Application/src/main/res/values/strings.xml b/media/Camera2Basic/Application/src/main/res/values/strings.xml index 66f10008..7fdf6e60 100644 --- a/media/Camera2Basic/Application/src/main/res/values/strings.xml +++ b/media/Camera2Basic/Application/src/main/res/values/strings.xml @@ -16,4 +16,6 @@ <resources> <string name="picture">Picture</string> <string name="description_info">Info</string> + <string name="request_permission">This sample needs camera permission.</string> + <string name="camera_error">This device doesn\'t support Camera2 API.</string> </resources> diff --git a/media/Camera2Basic/README.md b/media/Camera2Basic/README.md index 73ad24ae..a77df09d 100644 --- a/media/Camera2Basic/README.md +++ b/media/Camera2Basic/README.md @@ -42,7 +42,7 @@ when you are done. Pre-requisites -------------- -- Android SDK v21 +- Android SDK v23 - Android Build Tools v23.0.0 - Android Support Repository diff --git a/media/Camera2Basic/template-params.xml b/media/Camera2Basic/template-params.xml index 98418305..7f7edef7 100644 --- a/media/Camera2Basic/template-params.xml +++ b/media/Camera2Basic/template-params.xml @@ -21,8 +21,9 @@ <name>Camera2Basic</name> <group>Media</group> <package>com.example.android.camera2basic</package> + + <dependency>com.android.support:appcompat-v7:23.0.0</dependency> <minSdk>21</minSdk> - <targetSdkVersion>22</targetSdkVersion> <strings> <intro> <![CDATA[ |