1616
1717package com .example .android .camera2basic ;
1818
19+ import android .Manifest ;
1920import android .app .Activity ;
2021import android .app .AlertDialog ;
2122import android .app .Dialog ;
2223import android .app .DialogFragment ;
2324import android .app .Fragment ;
2425import android .content .Context ;
2526import android .content .DialogInterface ;
27+ import android .content .pm .PackageManager ;
2628import android .content .res .Configuration ;
2729import android .graphics .ImageFormat ;
2830import android .graphics .Matrix ;
4345import android .os .Bundle ;
4446import android .os .Handler ;
4547import android .os .HandlerThread ;
46- import android .os .Message ;
48+ import android .support .annotation .NonNull ;
49+ import android .support .v13 .app .FragmentCompat ;
4750import android .util .Log ;
4851import android .util .Size ;
4952import android .util .SparseIntArray ;
6669import java .util .concurrent .Semaphore ;
6770import java .util .concurrent .TimeUnit ;
6871
69- public class Camera2BasicFragment extends Fragment implements View .OnClickListener {
72+ public class Camera2BasicFragment extends Fragment
73+ implements View .OnClickListener , FragmentCompat .OnRequestPermissionsResultCallback {
7074
7175 /**
7276 * Conversion from screen rotation to JPEG orientation.
7377 */
7478 private static final SparseIntArray ORIENTATIONS = new SparseIntArray ();
79+ private static final int REQUEST_CAMERA_PERMISSION = 1 ;
80+ private static final String FRAGMENT_DIALOG = "dialog" ;
7581
7682 static {
7783 ORIENTATIONS .append (Surface .ROTATION_0 , 90 );
@@ -94,14 +100,17 @@ public class Camera2BasicFragment extends Fragment implements View.OnClickListen
94100 * Camera state: Waiting for the focus to be locked.
95101 */
96102 private static final int STATE_WAITING_LOCK = 1 ;
103+
97104 /**
98105 * Camera state: Waiting for the exposure to be precapture state.
99106 */
100107 private static final int STATE_WAITING_PRECAPTURE = 2 ;
108+
101109 /**
102110 * Camera state: Waiting for the exposure state to be something other than precapture.
103111 */
104112 private static final int STATE_WAITING_NON_PRECAPTURE = 3 ;
113+
105114 /**
106115 * Camera state: Picture was taken.
107116 */
@@ -148,17 +157,16 @@ public void onSurfaceTextureUpdated(SurfaceTexture texture) {
148157 /**
149158 * A {@link CameraCaptureSession } for camera preview.
150159 */
151-
152160 private CameraCaptureSession mCaptureSession ;
161+
153162 /**
154163 * A reference to the opened {@link CameraDevice}.
155164 */
156-
157165 private CameraDevice mCameraDevice ;
166+
158167 /**
159168 * The {@link android.util.Size} of camera preview.
160169 */
161-
162170 private Size mPreviewSize ;
163171
164172 /**
@@ -167,22 +175,22 @@ public void onSurfaceTextureUpdated(SurfaceTexture texture) {
167175 private final CameraDevice .StateCallback mStateCallback = new CameraDevice .StateCallback () {
168176
169177 @ Override
170- public void onOpened (CameraDevice cameraDevice ) {
178+ public void onOpened (@ NonNull CameraDevice cameraDevice ) {
171179 // This method is called when the camera is opened. We start camera preview here.
172180 mCameraOpenCloseLock .release ();
173181 mCameraDevice = cameraDevice ;
174182 createCameraPreviewSession ();
175183 }
176184
177185 @ Override
178- public void onDisconnected (CameraDevice cameraDevice ) {
186+ public void onDisconnected (@ NonNull CameraDevice cameraDevice ) {
179187 mCameraOpenCloseLock .release ();
180188 cameraDevice .close ();
181189 mCameraDevice = null ;
182190 }
183191
184192 @ Override
185- public void onError (CameraDevice cameraDevice , int error ) {
193+ public void onError (@ NonNull CameraDevice cameraDevice , int error ) {
186194 mCameraOpenCloseLock .release ();
187195 cameraDevice .close ();
188196 mCameraDevice = null ;
@@ -303,43 +311,36 @@ private void process(CaptureResult result) {
303311 }
304312
305313 @ Override
306- public void onCaptureProgressed (CameraCaptureSession session , CaptureRequest request ,
307- CaptureResult partialResult ) {
314+ public void onCaptureProgressed (@ NonNull CameraCaptureSession session ,
315+ @ NonNull CaptureRequest request ,
316+ @ NonNull CaptureResult partialResult ) {
308317 process (partialResult );
309318 }
310319
311320 @ Override
312- public void onCaptureCompleted (CameraCaptureSession session , CaptureRequest request ,
313- TotalCaptureResult result ) {
321+ public void onCaptureCompleted (@ NonNull CameraCaptureSession session ,
322+ @ NonNull CaptureRequest request ,
323+ @ NonNull TotalCaptureResult result ) {
314324 process (result );
315325 }
316326
317327 };
318328
319- /**
320- * A {@link Handler} for showing {@link Toast}s.
321- */
322- private Handler mMessageHandler = new Handler () {
323- @ Override
324- public void handleMessage (Message msg ) {
325- Activity activity = getActivity ();
326- if (activity != null ) {
327- Toast .makeText (activity , (String ) msg .obj , Toast .LENGTH_SHORT ).show ();
328- }
329- }
330- };
331-
332329 /**
333330 * Shows a {@link Toast} on the UI thread.
334331 *
335332 * @param text The message to show
336333 */
337- private void showToast (String text ) {
338- // We show a Toast by sending request message to mMessageHandler. This makes sure that the
339- // Toast is shown on the UI thread.
340- Message message = Message .obtain ();
341- message .obj = text ;
342- mMessageHandler .sendMessage (message );
334+ private void showToast (final String text ) {
335+ final Activity activity = getActivity ();
336+ if (activity != null ) {
337+ activity .runOnUiThread (new Runnable () {
338+ @ Override
339+ public void run () {
340+ Toast .makeText (activity , text , Toast .LENGTH_SHORT ).show ();
341+ }
342+ });
343+ }
343344 }
344345
345346 /**
@@ -355,7 +356,7 @@ private void showToast(String text) {
355356 */
356357 private static Size chooseOptimalSize (Size [] choices , int width , int height , Size aspectRatio ) {
357358 // Collect the supported resolutions that are at least as big as the preview Surface
358- List <Size > bigEnough = new ArrayList <Size >();
359+ List <Size > bigEnough = new ArrayList <>();
359360 int w = aspectRatio .getWidth ();
360361 int h = aspectRatio .getHeight ();
361362 for (Size option : choices ) {
@@ -375,9 +376,7 @@ private static Size chooseOptimalSize(Size[] choices, int width, int height, Siz
375376 }
376377
377378 public static Camera2BasicFragment newInstance () {
378- Camera2BasicFragment fragment = new Camera2BasicFragment ();
379- fragment .setRetainInstance (true );
380- return fragment ;
379+ return new Camera2BasicFragment ();
381380 }
382381
383382 @ Override
@@ -422,6 +421,28 @@ public void onPause() {
422421 super .onPause ();
423422 }
424423
424+ private void requestCameraPermission () {
425+ if (FragmentCompat .shouldShowRequestPermissionRationale (this , Manifest .permission .CAMERA )) {
426+ new ConfirmationDialog ().show (getChildFragmentManager (), FRAGMENT_DIALOG );
427+ } else {
428+ FragmentCompat .requestPermissions (this , new String []{Manifest .permission .CAMERA },
429+ REQUEST_CAMERA_PERMISSION );
430+ }
431+ }
432+
433+ @ Override
434+ public void onRequestPermissionsResult (int requestCode , @ NonNull String [] permissions ,
435+ @ NonNull int [] grantResults ) {
436+ if (requestCode == REQUEST_CAMERA_PERMISSION ) {
437+ if (grantResults .length != 1 || grantResults [0 ] != PackageManager .PERMISSION_GRANTED ) {
438+ ErrorDialog .newInstance (getString (R .string .request_permission ))
439+ .show (getChildFragmentManager (), FRAGMENT_DIALOG );
440+ }
441+ } else {
442+ super .onRequestPermissionsResult (requestCode , permissions , grantResults );
443+ }
444+ }
445+
425446 /**
426447 * Sets up member variables related to camera.
427448 *
@@ -437,13 +458,16 @@ private void setUpCameraOutputs(int width, int height) {
437458 = manager .getCameraCharacteristics (cameraId );
438459
439460 // We don't use a front facing camera in this sample.
440- if ( characteristics .get (CameraCharacteristics .LENS_FACING )
441- == CameraCharacteristics .LENS_FACING_FRONT ) {
461+ Integer facing = characteristics .get (CameraCharacteristics .LENS_FACING );
462+ if ( facing != null && facing == CameraCharacteristics .LENS_FACING_FRONT ) {
442463 continue ;
443464 }
444465
445466 StreamConfigurationMap map = characteristics .get (
446467 CameraCharacteristics .SCALER_STREAM_CONFIGURATION_MAP );
468+ if (map == null ) {
469+ continue ;
470+ }
447471
448472 // For still image captures, we use the largest available size.
449473 Size largest = Collections .max (
@@ -478,14 +502,20 @@ private void setUpCameraOutputs(int width, int height) {
478502 } catch (NullPointerException e ) {
479503 // Currently an NPE is thrown when the Camera2API is used but not supported on the
480504 // device this code runs.
481- new ErrorDialog ().show (getFragmentManager (), "dialog" );
505+ ErrorDialog .newInstance (getString (R .string .camera_error ))
506+ .show (getChildFragmentManager (), FRAGMENT_DIALOG );
482507 }
483508 }
484509
485510 /**
486511 * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
487512 */
488513 private void openCamera (int width , int height ) {
514+ if (getActivity ().checkSelfPermission (Manifest .permission .CAMERA )
515+ != PackageManager .PERMISSION_GRANTED ) {
516+ requestCameraPermission ();
517+ return ;
518+ }
489519 setUpCameraOutputs (width , height );
490520 configureTransform (width , height );
491521 Activity activity = getActivity ();
@@ -574,7 +604,7 @@ private void createCameraPreviewSession() {
574604 new CameraCaptureSession .StateCallback () {
575605
576606 @ Override
577- public void onConfigured (CameraCaptureSession cameraCaptureSession ) {
607+ public void onConfigured (@ NonNull CameraCaptureSession cameraCaptureSession ) {
578608 // The camera is already closed
579609 if (null == mCameraDevice ) {
580610 return ;
@@ -600,7 +630,8 @@ public void onConfigured(CameraCaptureSession cameraCaptureSession) {
600630 }
601631
602632 @ Override
603- public void onConfigureFailed (CameraCaptureSession cameraCaptureSession ) {
633+ public void onConfigureFailed (
634+ @ NonNull CameraCaptureSession cameraCaptureSession ) {
604635 showToast ("Failed" );
605636 }
606637 }, null
@@ -668,8 +699,8 @@ private void lockFocus() {
668699 }
669700
670701 /**
671- * Run the precapture sequence for capturing a still image. This method should be called when we
672- * get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
702+ * Run the precapture sequence for capturing a still image. This method should be called when
703+ * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
673704 */
674705 private void runPrecaptureSequence () {
675706 try {
@@ -714,9 +745,11 @@ private void captureStillPicture() {
714745 = new CameraCaptureSession .CaptureCallback () {
715746
716747 @ Override
717- public void onCaptureCompleted (CameraCaptureSession session , CaptureRequest request ,
718- TotalCaptureResult result ) {
748+ public void onCaptureCompleted (@ NonNull CameraCaptureSession session ,
749+ @ NonNull CaptureRequest request ,
750+ @ NonNull TotalCaptureResult result ) {
719751 showToast ("Saved: " + mFile );
752+ Log .d (TAG , mFile .toString ());
720753 unlockFocus ();
721754 }
722755 };
@@ -729,11 +762,12 @@ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest requ
729762 }
730763
731764 /**
732- * Unlock the focus. This method should be called when still image capture sequence is finished.
765+ * Unlock the focus. This method should be called when still image capture sequence is
766+ * finished.
733767 */
734768 private void unlockFocus () {
735769 try {
736- // Reset the autofucos trigger
770+ // Reset the auto-focus trigger
737771 mPreviewRequestBuilder .set (CaptureRequest .CONTROL_AF_TRIGGER ,
738772 CameraMetadata .CONTROL_AF_TRIGGER_CANCEL );
739773 mPreviewRequestBuilder .set (CaptureRequest .CONTROL_AE_MODE ,
@@ -827,13 +861,26 @@ public int compare(Size lhs, Size rhs) {
827861
828862 }
829863
864+ /**
865+ * Shows an error message dialog.
866+ */
830867 public static class ErrorDialog extends DialogFragment {
831868
869+ private static final String ARG_MESSAGE = "message" ;
870+
871+ public static ErrorDialog newInstance (String message ) {
872+ ErrorDialog dialog = new ErrorDialog ();
873+ Bundle args = new Bundle ();
874+ args .putString (ARG_MESSAGE , message );
875+ dialog .setArguments (args );
876+ return dialog ;
877+ }
878+
832879 @ Override
833880 public Dialog onCreateDialog (Bundle savedInstanceState ) {
834881 final Activity activity = getActivity ();
835882 return new AlertDialog .Builder (activity )
836- .setMessage ("This device doesn't support Camera2 API." )
883+ .setMessage (getArguments (). getString ( ARG_MESSAGE ) )
837884 .setPositiveButton (android .R .string .ok , new DialogInterface .OnClickListener () {
838885 @ Override
839886 public void onClick (DialogInterface dialogInterface , int i ) {
@@ -845,4 +892,36 @@ public void onClick(DialogInterface dialogInterface, int i) {
845892
846893 }
847894
895+ /**
896+ * Shows OK/Cancel confirmation dialog about camera permission.
897+ */
898+ public static class ConfirmationDialog extends DialogFragment {
899+
900+ @ Override
901+ public Dialog onCreateDialog (Bundle savedInstanceState ) {
902+ final Fragment parent = getParentFragment ();
903+ return new AlertDialog .Builder (getActivity ())
904+ .setMessage (R .string .request_permission )
905+ .setPositiveButton (android .R .string .ok , new DialogInterface .OnClickListener () {
906+ @ Override
907+ public void onClick (DialogInterface dialog , int which ) {
908+ FragmentCompat .requestPermissions (parent ,
909+ new String []{Manifest .permission .CAMERA },
910+ REQUEST_CAMERA_PERMISSION );
911+ }
912+ })
913+ .setNegativeButton (android .R .string .cancel ,
914+ new DialogInterface .OnClickListener () {
915+ @ Override
916+ public void onClick (DialogInterface dialog , int which ) {
917+ Activity activity = parent .getActivity ();
918+ if (activity != null ) {
919+ activity .finish ();
920+ }
921+ }
922+ })
923+ .create ();
924+ }
925+ }
926+
848927}
0 commit comments