1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.camera;
18 
19 import android.annotation.TargetApi;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.content.ContentProviderClient;
23 import android.content.ContentResolver;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.SharedPreferences.Editor;
27 import android.content.res.Configuration;
28 import android.graphics.Bitmap;
29 import android.graphics.SurfaceTexture;
30 import android.hardware.Camera.CameraInfo;
31 import android.hardware.Camera.Face;
32 import android.hardware.Camera.FaceDetectionListener;
33 import android.hardware.Camera.Parameters;
34 import android.hardware.Camera.PictureCallback;
35 import android.hardware.Camera.Size;
36 import android.location.Location;
37 import android.media.CameraProfile;
38 import android.net.Uri;
39 import android.os.Bundle;
40 import android.os.ConditionVariable;
41 import android.os.Handler;
42 import android.os.Looper;
43 import android.os.Message;
44 import android.os.MessageQueue;
45 import android.os.SystemClock;
46 import android.provider.MediaStore;
47 import android.util.Log;
48 import android.view.Gravity;
49 import android.view.KeyEvent;
50 import android.view.LayoutInflater;
51 import android.view.MotionEvent;
52 import android.view.OrientationEventListener;
53 import android.view.SurfaceHolder;
54 import android.view.View;
55 import android.view.View.OnClickListener;
56 import android.view.ViewGroup;
57 import android.view.WindowManager;
58 import android.widget.FrameLayout;
59 import android.widget.FrameLayout.LayoutParams;
60 import android.widget.ImageView;
61 import android.widget.Toast;
62 
63 import com.android.camera.CameraManager.CameraProxy;
64 import com.android.camera.ui.AbstractSettingPopup;
65 import com.android.camera.ui.CountDownView;
66 import com.android.camera.ui.FaceView;
67 import com.android.camera.ui.PieRenderer;
68 import com.android.camera.ui.PopupManager;
69 import com.android.camera.ui.PreviewSurfaceView;
70 import com.android.camera.ui.RenderOverlay;
71 import com.android.camera.ui.Rotatable;
72 import com.android.camera.ui.RotateTextToast;
73 import com.android.camera.ui.TwoStateImageView;
74 import com.android.camera.ui.ZoomRenderer;
75 import com.android.gallery3d.common.ApiHelper;
76 import com.android.gallery3d.filtershow.CropExtras;
77 import com.android.gallery3d.filtershow.FilterShowActivity;
78 
79 import java.io.File;
80 import java.io.FileNotFoundException;
81 import java.io.FileOutputStream;
82 import java.io.IOException;
83 import java.io.OutputStream;
84 import java.util.ArrayList;
85 import java.util.Collections;
86 import java.util.Formatter;
87 import java.util.List;
88 
89 public class PhotoModule
90     implements CameraModule,
91     FocusOverlayManager.Listener,
92     CameraPreference.OnPreferenceChangedListener,
93     LocationManager.Listener,
94     PreviewFrameLayout.OnSizeChangedListener,
95     ShutterButton.OnShutterButtonListener,
96     SurfaceHolder.Callback,
97     PieRenderer.PieListener,
98     CountDownView.OnCountDownFinishedListener {
99 
100     private static final String TAG = "CAM_PhotoModule";
101 
102     // We number the request code from 1000 to avoid collision with Gallery.
103     private static final int REQUEST_CROP = 1000;
104 
105     private static final int SETUP_PREVIEW = 1;
106     private static final int FIRST_TIME_INIT = 2;
107     private static final int CLEAR_SCREEN_DELAY = 3;
108     private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
109     private static final int CHECK_DISPLAY_ROTATION = 5;
110     private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
111     private static final int SWITCH_CAMERA = 7;
112     private static final int SWITCH_CAMERA_START_ANIMATION = 8;
113     private static final int CAMERA_OPEN_DONE = 9;
114     private static final int START_PREVIEW_DONE = 10;
115     private static final int OPEN_CAMERA_FAIL = 11;
116     private static final int CAMERA_DISABLED = 12;
117     private static final int UPDATE_SECURE_ALBUM_ITEM = 13;
118 
119     // The subset of parameters we need to update in setCameraParameters().
120     private static final int UPDATE_PARAM_INITIALIZE = 1;
121     private static final int UPDATE_PARAM_ZOOM = 2;
122     private static final int UPDATE_PARAM_PREFERENCE = 4;
123     private static final int UPDATE_PARAM_ALL = -1;
124 
125     // This is the timeout to keep the camera in onPause for the first time
126     // after screen on if the activity is started from secure lock screen.
127     private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
128 
129     // copied from Camera hierarchy
130     private CameraActivity mActivity;
131     private View mRootView;
132     private CameraProxy mCameraDevice;
133     private int mCameraId;
134     private Parameters mParameters;
135     private boolean mPaused;
136     private AbstractSettingPopup mPopup;
137 
138     // these are only used by Camera
139 
140     // The activity is going to switch to the specified camera id. This is
141     // needed because texture copy is done in GL thread. -1 means camera is not
142     // switching.
143     protected int mPendingSwitchCameraId = -1;
144     private boolean mOpenCameraFail;
145     private boolean mCameraDisabled;
146 
147     // When setCameraParametersWhenIdle() is called, we accumulate the subsets
148     // needed to be updated in mUpdateSet.
149     private int mUpdateSet;
150 
151     private static final int SCREEN_DELAY = 2 * 60 * 1000;
152 
153     private int mZoomValue;  // The current zoom value.
154     private int mZoomMax;
155     private List<Integer> mZoomRatios;
156 
157     private Parameters mInitialParams;
158     private boolean mFocusAreaSupported;
159     private boolean mMeteringAreaSupported;
160     private boolean mAeLockSupported;
161     private boolean mAwbLockSupported;
162     private boolean mContinousFocusSupported;
163 
164     // The degrees of the device rotated clockwise from its natural orientation.
165     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
166     private ComboPreferences mPreferences;
167 
168     private static final String sTempCropFilename = "crop-temp";
169 
170     private ContentProviderClient mMediaProviderClient;
171     private ShutterButton mShutterButton;
172     private boolean mFaceDetectionStarted = false;
173 
174     private PreviewFrameLayout mPreviewFrameLayout;
175     private Object mSurfaceTexture;
176     private CountDownView mCountDownView;
177 
178     // for API level 10
179     private PreviewSurfaceView mPreviewSurfaceView;
180     private volatile SurfaceHolder mCameraSurfaceHolder;
181 
182     private FaceView mFaceView;
183     private RenderOverlay mRenderOverlay;
184     private Rotatable mReviewCancelButton;
185     private Rotatable mReviewDoneButton;
186     private View mReviewRetakeButton;
187 
188     // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
189     private String mCropValue;
190     private Uri mSaveUri;
191 
192     private View mMenu;
193     private View mBlocker;
194 
195     // Small indicators which show the camera settings in the viewfinder.
196     private ImageView mExposureIndicator;
197     private ImageView mFlashIndicator;
198     private ImageView mSceneIndicator;
199     private ImageView mHdrIndicator;
200     // A view group that contains all the small indicators.
201     private View mOnScreenIndicators;
202 
203     // We use a thread in MediaSaver to do the work of saving images. This
204     // reduces the shot-to-shot time.
205     private MediaSaver mMediaSaver;
206     // Similarly, we use a thread to generate the name of the picture and insert
207     // it into MediaStore while picture taking is still in progress.
208     private NamedImages mNamedImages;
209 
210     private Runnable mDoSnapRunnable = new Runnable() {
211         @Override
212         public void run() {
213             onShutterButtonClick();
214         }
215     };
216 
217     private final StringBuilder mBuilder = new StringBuilder();
218     private final Formatter mFormatter = new Formatter(mBuilder);
219     private final Object[] mFormatterArgs = new Object[1];
220 
221     /**
222      * An unpublished intent flag requesting to return as soon as capturing
223      * is completed.
224      *
225      * TODO: consider publishing by moving into MediaStore.
226      */
227     private static final String EXTRA_QUICK_CAPTURE =
228             "android.intent.extra.quickCapture";
229 
230     // The display rotation in degrees. This is only valid when mCameraState is
231     // not PREVIEW_STOPPED.
232     private int mDisplayRotation;
233     // The value for android.hardware.Camera.setDisplayOrientation.
234     private int mCameraDisplayOrientation;
235     // The value for UI components like indicators.
236     private int mDisplayOrientation;
237     // The value for android.hardware.Camera.Parameters.setRotation.
238     private int mJpegRotation;
239     private boolean mFirstTimeInitialized;
240     private boolean mIsImageCaptureIntent;
241 
242     private static final int PREVIEW_STOPPED = 0;
243     private static final int IDLE = 1;  // preview is active
244     // Focus is in progress. The exact focus state is in Focus.java.
245     private static final int FOCUSING = 2;
246     private static final int SNAPSHOT_IN_PROGRESS = 3;
247     // Switching between cameras.
248     private static final int SWITCHING_CAMERA = 4;
249     private int mCameraState = PREVIEW_STOPPED;
250     private boolean mSnapshotOnIdle = false;
251 
252     private ContentResolver mContentResolver;
253 
254     private LocationManager mLocationManager;
255 
256     private final ShutterCallback mShutterCallback = new ShutterCallback();
257     private final PostViewPictureCallback mPostViewPictureCallback =
258             new PostViewPictureCallback();
259     private final RawPictureCallback mRawPictureCallback =
260             new RawPictureCallback();
261     private final AutoFocusCallback mAutoFocusCallback =
262             new AutoFocusCallback();
263     private final Object mAutoFocusMoveCallback =
264             ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
265             ? new AutoFocusMoveCallback()
266             : null;
267 
268     private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
269 
270     private long mFocusStartTime;
271     private long mShutterCallbackTime;
272     private long mPostViewPictureCallbackTime;
273     private long mRawPictureCallbackTime;
274     private long mJpegPictureCallbackTime;
275     private long mOnResumeTime;
276     private byte[] mJpegImageData;
277 
278     // These latency time are for the CameraLatency test.
279     public long mAutoFocusTime;
280     public long mShutterLag;
281     public long mShutterToPictureDisplayedTime;
282     public long mPictureDisplayedToJpegCallbackTime;
283     public long mJpegCallbackFinishTime;
284     public long mCaptureStartTime;
285 
286     // This handles everything about focus.
287     private FocusOverlayManager mFocusManager;
288 
289     private PieRenderer mPieRenderer;
290     private PhotoController mPhotoControl;
291 
292     private ZoomRenderer mZoomRenderer;
293 
294     private String mSceneMode;
295     private Toast mNotSelectableToast;
296 
297     private final Handler mHandler = new MainHandler();
298     private PreferenceGroup mPreferenceGroup;
299 
300     private boolean mQuickCapture;
301 
302     CameraStartUpThread mCameraStartUpThread;
303     ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
304 
305     private PreviewGestures mGestures;
306 
307     private MediaSaver.OnMediaSavedListener mOnMediaSavedListener = new MediaSaver.OnMediaSavedListener() {
308         @Override
309 
310         public void onMediaSaved(Uri uri) {
311             if (uri != null) {
312                 mHandler.obtainMessage(UPDATE_SECURE_ALBUM_ITEM, uri).sendToTarget();
313                 Util.broadcastNewPicture(mActivity, uri);
314             }
315         }
316     };
317 
318     // The purpose is not to block the main thread in onCreate and onResume.
319     private class CameraStartUpThread extends Thread {
320         private volatile boolean mCancelled;
321 
cancel()322         public void cancel() {
323             mCancelled = true;
324             interrupt();
325         }
326 
isCanceled()327         public boolean isCanceled() {
328             return mCancelled;
329         }
330 
331         @Override
run()332         public void run() {
333             try {
334                 // We need to check whether the activity is paused before long
335                 // operations to ensure that onPause() can be done ASAP.
336                 if (mCancelled) return;
337                 mCameraDevice = Util.openCamera(mActivity, mCameraId);
338                 mParameters = mCameraDevice.getParameters();
339                 // Wait until all the initialization needed by startPreview are
340                 // done.
341                 mStartPreviewPrerequisiteReady.block();
342 
343                 initializeCapabilities();
344                 if (mFocusManager == null) initializeFocusManager();
345                 if (mCancelled) return;
346                 setCameraParameters(UPDATE_PARAM_ALL);
347                 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
348                 if (mCancelled) return;
349                 startPreview();
350                 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
351                 mOnResumeTime = SystemClock.uptimeMillis();
352                 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
353             } catch (CameraHardwareException e) {
354                 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
355             } catch (CameraDisabledException e) {
356                 mHandler.sendEmptyMessage(CAMERA_DISABLED);
357             }
358         }
359     }
360 
361     /**
362      * This Handler is used to post message back onto the main thread of the
363      * application
364      */
365     private class MainHandler extends Handler {
366         @Override
handleMessage(Message msg)367         public void handleMessage(Message msg) {
368             switch (msg.what) {
369                 case SETUP_PREVIEW: {
370                     setupPreview();
371                     break;
372                 }
373 
374                 case CLEAR_SCREEN_DELAY: {
375                     mActivity.getWindow().clearFlags(
376                             WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
377                     break;
378                 }
379 
380                 case FIRST_TIME_INIT: {
381                     initializeFirstTime();
382                     break;
383                 }
384 
385                 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
386                     setCameraParametersWhenIdle(0);
387                     break;
388                 }
389 
390                 case CHECK_DISPLAY_ROTATION: {
391                     // Set the display orientation if display rotation has changed.
392                     // Sometimes this happens when the device is held upside
393                     // down and camera app is opened. Rotation animation will
394                     // take some time and the rotation value we have got may be
395                     // wrong. Framework does not have a callback for this now.
396                     if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
397                         setDisplayOrientation();
398                     }
399                     if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
400                         mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
401                     }
402                     break;
403                 }
404 
405                 case SHOW_TAP_TO_FOCUS_TOAST: {
406                     showTapToFocusToast();
407                     break;
408                 }
409 
410                 case SWITCH_CAMERA: {
411                     switchCamera();
412                     break;
413                 }
414 
415                 case SWITCH_CAMERA_START_ANIMATION: {
416                     ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
417                     break;
418                 }
419 
420                 case CAMERA_OPEN_DONE: {
421                     initializeAfterCameraOpen();
422                     break;
423                 }
424 
425                 case START_PREVIEW_DONE: {
426                     mCameraStartUpThread = null;
427                     setCameraState(IDLE);
428                     if (!ApiHelper.HAS_SURFACE_TEXTURE) {
429                         // This may happen if surfaceCreated has arrived.
430                         mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
431                     }
432                     startFaceDetection();
433                     locationFirstRun();
434                     break;
435                 }
436 
437                 case OPEN_CAMERA_FAIL: {
438                     mCameraStartUpThread = null;
439                     mOpenCameraFail = true;
440                     Util.showErrorAndFinish(mActivity,
441                             R.string.cannot_connect_camera);
442                     break;
443                 }
444 
445                 case CAMERA_DISABLED: {
446                     mCameraStartUpThread = null;
447                     mCameraDisabled = true;
448                     Util.showErrorAndFinish(mActivity,
449                             R.string.camera_disabled);
450                     break;
451                 }
452 
453                 case UPDATE_SECURE_ALBUM_ITEM: {
454                     mActivity.addSecureAlbumItemIfNeeded(false, (Uri) msg.obj);
455                     break;
456                 }
457             }
458         }
459     }
460 
461     @Override
init(CameraActivity activity, View parent, boolean reuseNail)462     public void init(CameraActivity activity, View parent, boolean reuseNail) {
463         mActivity = activity;
464         mRootView = parent;
465         mPreferences = new ComboPreferences(mActivity);
466         CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
467         mCameraId = getPreferredCameraId(mPreferences);
468 
469         mContentResolver = mActivity.getContentResolver();
470 
471         // To reduce startup time, open the camera and start the preview in
472         // another thread.
473         mCameraStartUpThread = new CameraStartUpThread();
474         mCameraStartUpThread.start();
475 
476         mActivity.getLayoutInflater().inflate(R.layout.photo_module, (ViewGroup) mRootView);
477 
478         // Surface texture is from camera screen nail and startPreview needs it.
479         // This must be done before startPreview.
480         mIsImageCaptureIntent = isImageCaptureIntent();
481         if (reuseNail) {
482             mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
483         } else {
484             mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
485         }
486 
487         mPreferences.setLocalId(mActivity, mCameraId);
488         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
489         // we need to reset exposure for the preview
490         resetExposureCompensation();
491         // Starting the preview needs preferences, camera screen nail, and
492         // focus area indicator.
493         mStartPreviewPrerequisiteReady.open();
494 
495         initializeControlByIntent();
496         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
497         initializeMiscControls();
498         mLocationManager = new LocationManager(mActivity, this);
499         initOnScreenIndicator();
500         mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
501         mCountDownView.setCountDownFinishedListener(this);
502     }
503 
504     // Prompt the user to pick to record location for the very first run of
505     // camera only
locationFirstRun()506     private void locationFirstRun() {
507         if (RecordLocationPreference.isSet(mPreferences)) {
508             return;
509         }
510         if (mActivity.isSecureCamera()) return;
511         // Check if the back camera exists
512         int backCameraId = CameraHolder.instance().getBackCameraId();
513         if (backCameraId == -1) {
514             // If there is no back camera, do not show the prompt.
515             return;
516         }
517 
518         new AlertDialog.Builder(mActivity)
519             .setTitle(R.string.remember_location_title)
520             .setMessage(R.string.remember_location_prompt)
521             .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
522                 @Override
523                 public void onClick(DialogInterface dialog, int arg1) {
524                     setLocationPreference(RecordLocationPreference.VALUE_ON);
525                 }
526             })
527             .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
528                 @Override
529                 public void onClick(DialogInterface dialog, int arg1) {
530                     dialog.cancel();
531                 }
532             })
533             .setOnCancelListener(new DialogInterface.OnCancelListener() {
534                 @Override
535                 public void onCancel(DialogInterface dialog) {
536                     setLocationPreference(RecordLocationPreference.VALUE_OFF);
537                 }
538             })
539             .show();
540     }
541 
setLocationPreference(String value)542     private void setLocationPreference(String value) {
543         mPreferences.edit()
544             .putString(CameraSettings.KEY_RECORD_LOCATION, value)
545             .apply();
546         // TODO: Fix this to use the actual onSharedPreferencesChanged listener
547         // instead of invoking manually
548         onSharedPreferenceChanged();
549     }
550 
initializeRenderOverlay()551     private void initializeRenderOverlay() {
552         if (mPieRenderer != null) {
553             mRenderOverlay.addRenderer(mPieRenderer);
554             mFocusManager.setFocusRenderer(mPieRenderer);
555         }
556         if (mZoomRenderer != null) {
557             mRenderOverlay.addRenderer(mZoomRenderer);
558         }
559         if (mGestures != null) {
560             mGestures.clearTouchReceivers();
561             mGestures.setRenderOverlay(mRenderOverlay);
562             mGestures.addTouchReceiver(mMenu);
563             mGestures.addTouchReceiver(mBlocker);
564 
565             if (isImageCaptureIntent()) {
566                 if (mReviewCancelButton != null) {
567                     mGestures.addTouchReceiver((View) mReviewCancelButton);
568                 }
569                 if (mReviewDoneButton != null) {
570                     mGestures.addTouchReceiver((View) mReviewDoneButton);
571                 }
572             }
573         }
574         mRenderOverlay.requestLayout();
575     }
576 
initializeAfterCameraOpen()577     private void initializeAfterCameraOpen() {
578         if (mPieRenderer == null) {
579             mPieRenderer = new PieRenderer(mActivity);
580             mPhotoControl = new PhotoController(mActivity, this, mPieRenderer);
581             mPhotoControl.setListener(this);
582             mPieRenderer.setPieListener(this);
583         }
584         if (mZoomRenderer == null) {
585             mZoomRenderer = new ZoomRenderer(mActivity);
586         }
587         if (mGestures == null) {
588             // this will handle gesture disambiguation and dispatching
589             mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
590         }
591         initializeRenderOverlay();
592         initializePhotoControl();
593 
594         // These depend on camera parameters.
595         setPreviewFrameLayoutAspectRatio();
596         mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(),
597                 mPreviewFrameLayout.getHeight());
598         loadCameraPreferences();
599         initializeZoom();
600         updateOnScreenIndicators();
601         showTapToFocusToastIfNeeded();
602         onFullScreenChanged(mActivity.isInCameraApp());
603     }
604 
initializePhotoControl()605     private void initializePhotoControl() {
606         loadCameraPreferences();
607         if (mPhotoControl != null) {
608             mPhotoControl.initialize(mPreferenceGroup);
609         }
610         updateSceneModeUI();
611     }
612 
613 
resetExposureCompensation()614     private void resetExposureCompensation() {
615         String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
616                 CameraSettings.EXPOSURE_DEFAULT_VALUE);
617         if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
618             Editor editor = mPreferences.edit();
619             editor.putString(CameraSettings.KEY_EXPOSURE, "0");
620             editor.apply();
621         }
622     }
623 
keepMediaProviderInstance()624     private void keepMediaProviderInstance() {
625         // We want to keep a reference to MediaProvider in camera's lifecycle.
626         // TODO: Utilize mMediaProviderClient instance to replace
627         // ContentResolver calls.
628         if (mMediaProviderClient == null) {
629             mMediaProviderClient = mContentResolver
630                     .acquireContentProviderClient(MediaStore.AUTHORITY);
631         }
632     }
633 
634     // Snapshots can only be taken after this is called. It should be called
635     // once only. We could have done these things in onCreate() but we want to
636     // make preview screen appear as soon as possible.
initializeFirstTime()637     private void initializeFirstTime() {
638         if (mFirstTimeInitialized) return;
639 
640         // Initialize location service.
641         boolean recordLocation = RecordLocationPreference.get(
642                 mPreferences, mContentResolver);
643         mLocationManager.recordLocation(recordLocation);
644 
645         keepMediaProviderInstance();
646 
647         // Initialize shutter button.
648         mShutterButton = mActivity.getShutterButton();
649         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
650         mShutterButton.setOnShutterButtonListener(this);
651         mShutterButton.setVisibility(View.VISIBLE);
652 
653         mMediaSaver = new MediaSaver(mContentResolver);
654         mNamedImages = new NamedImages();
655 
656         mFirstTimeInitialized = true;
657         addIdleHandler();
658 
659         mActivity.updateStorageSpaceAndHint();
660     }
661 
showTapToFocusToastIfNeeded()662     private void showTapToFocusToastIfNeeded() {
663         // Show the tap to focus toast if this is the first start.
664         if (mFocusAreaSupported &&
665                 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
666             // Delay the toast for one second to wait for orientation.
667             mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
668         }
669     }
670 
addIdleHandler()671     private void addIdleHandler() {
672         MessageQueue queue = Looper.myQueue();
673         queue.addIdleHandler(new MessageQueue.IdleHandler() {
674             @Override
675             public boolean queueIdle() {
676                 Storage.ensureOSXCompatible();
677                 return false;
678             }
679         });
680     }
681 
682     // If the activity is paused and resumed, this method will be called in
683     // onResume.
initializeSecondTime()684     private void initializeSecondTime() {
685 
686         // Start location update if needed.
687         boolean recordLocation = RecordLocationPreference.get(
688                 mPreferences, mContentResolver);
689         mLocationManager.recordLocation(recordLocation);
690 
691         mMediaSaver = new MediaSaver(mContentResolver);
692         mNamedImages = new NamedImages();
693         initializeZoom();
694         keepMediaProviderInstance();
695         hidePostCaptureAlert();
696 
697         if (mPhotoControl != null) {
698             mPhotoControl.reloadPreferences();
699         }
700     }
701 
702     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
703         @Override
onZoomValueChanged(int index)704         public void onZoomValueChanged(int index) {
705             // Not useful to change zoom value when the activity is paused.
706             if (mPaused) return;
707             mZoomValue = index;
708             if (mParameters == null || mCameraDevice == null) return;
709             // Set zoom parameters asynchronously
710             mParameters.setZoom(mZoomValue);
711             mCameraDevice.setParametersAsync(mParameters);
712             if (mZoomRenderer != null) {
713                 Parameters p = mCameraDevice.getParameters();
714                 mZoomRenderer.setZoomValue(mZoomRatios.get(p.getZoom()));
715             }
716         }
717 
718         @Override
onZoomStart()719         public void onZoomStart() {
720             if (mPieRenderer != null) {
721                 mPieRenderer.setBlockFocus(true);
722             }
723         }
724 
725         @Override
onZoomEnd()726         public void onZoomEnd() {
727             if (mPieRenderer != null) {
728                 mPieRenderer.setBlockFocus(false);
729             }
730         }
731     }
732 
initializeZoom()733     private void initializeZoom() {
734         if ((mParameters == null) || !mParameters.isZoomSupported()
735                 || (mZoomRenderer == null)) return;
736         mZoomMax = mParameters.getMaxZoom();
737         mZoomRatios = mParameters.getZoomRatios();
738         // Currently we use immediate zoom for fast zooming to get better UX and
739         // there is no plan to take advantage of the smooth zoom.
740         if (mZoomRenderer != null) {
741             mZoomRenderer.setZoomMax(mZoomMax);
742             mZoomRenderer.setZoom(mParameters.getZoom());
743             mZoomRenderer.setZoomValue(mZoomRatios.get(mParameters.getZoom()));
744             mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
745         }
746     }
747 
748     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
749     @Override
startFaceDetection()750     public void startFaceDetection() {
751         if (!ApiHelper.HAS_FACE_DETECTION) return;
752         if (mFaceDetectionStarted) return;
753         if (mParameters.getMaxNumDetectedFaces() > 0) {
754             mFaceDetectionStarted = true;
755             mFaceView.clear();
756             mFaceView.setVisibility(View.VISIBLE);
757             mFaceView.setDisplayOrientation(mDisplayOrientation);
758             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
759             mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
760             mFaceView.resume();
761             mFocusManager.setFaceView(mFaceView);
762             mCameraDevice.setFaceDetectionListener(new FaceDetectionListener() {
763                 @Override
764                 public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
765                     mFaceView.setFaces(faces);
766                 }
767             });
768             mCameraDevice.startFaceDetection();
769         }
770     }
771 
772     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
773     @Override
stopFaceDetection()774     public void stopFaceDetection() {
775         if (!ApiHelper.HAS_FACE_DETECTION) return;
776         if (!mFaceDetectionStarted) return;
777         if (mParameters.getMaxNumDetectedFaces() > 0) {
778             mFaceDetectionStarted = false;
779             mCameraDevice.setFaceDetectionListener(null);
780             mCameraDevice.stopFaceDetection();
781             if (mFaceView != null) mFaceView.clear();
782         }
783     }
784 
785     @Override
dispatchTouchEvent(MotionEvent m)786     public boolean dispatchTouchEvent(MotionEvent m) {
787         if (mCameraState == SWITCHING_CAMERA) return true;
788         if (mPopup != null) {
789             return mActivity.superDispatchTouchEvent(m);
790         } else if (mGestures != null && mRenderOverlay != null) {
791             return mGestures.dispatchTouch(m);
792         }
793         return false;
794     }
795 
initOnScreenIndicator()796     private void initOnScreenIndicator() {
797         mOnScreenIndicators = mRootView.findViewById(R.id.on_screen_indicators);
798         mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator);
799         mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator);
800         mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator);
801         mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator);
802     }
803 
804     @Override
showGpsOnScreenIndicator(boolean hasSignal)805     public void showGpsOnScreenIndicator(boolean hasSignal) { }
806 
807     @Override
hideGpsOnScreenIndicator()808     public void hideGpsOnScreenIndicator() { }
809 
updateExposureOnScreenIndicator(int value)810     private void updateExposureOnScreenIndicator(int value) {
811         if (mExposureIndicator == null) {
812             return;
813         }
814         int id = 0;
815         float step = mParameters.getExposureCompensationStep();
816         value = (int) Math.round(value * step);
817         switch(value) {
818         case -3:
819             id = R.drawable.ic_indicator_ev_n3;
820             break;
821         case -2:
822             id = R.drawable.ic_indicator_ev_n2;
823             break;
824         case -1:
825             id = R.drawable.ic_indicator_ev_n1;
826             break;
827         case 0:
828             id = R.drawable.ic_indicator_ev_0;
829             break;
830         case 1:
831             id = R.drawable.ic_indicator_ev_p1;
832             break;
833         case 2:
834             id = R.drawable.ic_indicator_ev_p2;
835             break;
836         case 3:
837             id = R.drawable.ic_indicator_ev_p3;
838             break;
839         }
840         mExposureIndicator.setImageResource(id);
841 
842     }
843 
updateFlashOnScreenIndicator(String value)844     private void updateFlashOnScreenIndicator(String value) {
845         if (mFlashIndicator == null) {
846             return;
847         }
848         if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
849             mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
850         } else {
851             if (Parameters.FLASH_MODE_AUTO.equals(value)) {
852                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto);
853             } else if (Parameters.FLASH_MODE_ON.equals(value)) {
854                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on);
855             } else {
856                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
857             }
858         }
859     }
860 
updateSceneOnScreenIndicator(String value)861     private void updateSceneOnScreenIndicator(String value) {
862         if (mSceneIndicator == null) {
863             return;
864         }
865         if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value)
866                 || Parameters.SCENE_MODE_HDR.equals(value)) {
867             mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off);
868         } else {
869             mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on);
870         }
871     }
872 
updateHdrOnScreenIndicator(String value)873     private void updateHdrOnScreenIndicator(String value) {
874         if (mHdrIndicator == null) {
875             return;
876         }
877         if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) {
878             mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on);
879         } else {
880             mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off);
881         }
882     }
883 
updateOnScreenIndicators()884     private void updateOnScreenIndicators() {
885         if (mParameters == null) return;
886         updateSceneOnScreenIndicator(mParameters.getSceneMode());
887         updateExposureOnScreenIndicator(CameraSettings.readExposure(mPreferences));
888         updateFlashOnScreenIndicator(mParameters.getFlashMode());
889         updateHdrOnScreenIndicator(mParameters.getSceneMode());
890     }
891 
892     private final class ShutterCallback
893             implements android.hardware.Camera.ShutterCallback {
894         @Override
onShutter()895         public void onShutter() {
896             mShutterCallbackTime = System.currentTimeMillis();
897             mShutterLag = mShutterCallbackTime - mCaptureStartTime;
898             Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
899         }
900     }
901 
902     private final class PostViewPictureCallback implements PictureCallback {
903         @Override
onPictureTaken( byte [] data, android.hardware.Camera camera)904         public void onPictureTaken(
905                 byte [] data, android.hardware.Camera camera) {
906             mPostViewPictureCallbackTime = System.currentTimeMillis();
907             Log.v(TAG, "mShutterToPostViewCallbackTime = "
908                     + (mPostViewPictureCallbackTime - mShutterCallbackTime)
909                     + "ms");
910         }
911     }
912 
913     private final class RawPictureCallback implements PictureCallback {
914         @Override
onPictureTaken( byte [] rawData, android.hardware.Camera camera)915         public void onPictureTaken(
916                 byte [] rawData, android.hardware.Camera camera) {
917             mRawPictureCallbackTime = System.currentTimeMillis();
918             Log.v(TAG, "mShutterToRawCallbackTime = "
919                     + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
920         }
921     }
922 
923     private final class JpegPictureCallback implements PictureCallback {
924         Location mLocation;
925 
JpegPictureCallback(Location loc)926         public JpegPictureCallback(Location loc) {
927             mLocation = loc;
928         }
929 
930         @Override
onPictureTaken( final byte [] jpegData, final android.hardware.Camera camera)931         public void onPictureTaken(
932                 final byte [] jpegData, final android.hardware.Camera camera) {
933             if (mPaused) {
934                 return;
935             }
936             if (mSceneMode == Util.SCENE_MODE_HDR) {
937                 mActivity.showSwitcher();
938                 mActivity.setSwipingEnabled(true);
939             }
940 
941             mJpegPictureCallbackTime = System.currentTimeMillis();
942             // If postview callback has arrived, the captured image is displayed
943             // in postview callback. If not, the captured image is displayed in
944             // raw picture callback.
945             if (mPostViewPictureCallbackTime != 0) {
946                 mShutterToPictureDisplayedTime =
947                         mPostViewPictureCallbackTime - mShutterCallbackTime;
948                 mPictureDisplayedToJpegCallbackTime =
949                         mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
950             } else {
951                 mShutterToPictureDisplayedTime =
952                         mRawPictureCallbackTime - mShutterCallbackTime;
953                 mPictureDisplayedToJpegCallbackTime =
954                         mJpegPictureCallbackTime - mRawPictureCallbackTime;
955             }
956             Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
957                     + mPictureDisplayedToJpegCallbackTime + "ms");
958 
959             // Only animate when in full screen capture mode
960             // i.e. If monkey/a user swipes to the gallery during picture taking,
961             // don't show animation
962             if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
963                     && mActivity.mShowCameraAppView) {
964                 // Finish capture animation
965                 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
966             }
967             mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
968             if (!mIsImageCaptureIntent) {
969                 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
970                     setupPreview();
971                 } else {
972                     // Camera HAL of some devices have a bug. Starting preview
973                     // immediately after taking a picture will fail. Wait some
974                     // time before starting the preview.
975                     mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
976                 }
977             }
978 
979             if (!mIsImageCaptureIntent) {
980                 // Calculate the width and the height of the jpeg.
981                 Size s = mParameters.getPictureSize();
982                 int orientation = Exif.getOrientation(jpegData);
983                 int width, height;
984                 if ((mJpegRotation + orientation) % 180 == 0) {
985                     width = s.width;
986                     height = s.height;
987                 } else {
988                     width = s.height;
989                     height = s.width;
990                 }
991                 String title = mNamedImages.getTitle();
992                 long date = mNamedImages.getDate();
993                 if (title == null) {
994                     Log.e(TAG, "Unbalanced name/data pair");
995                 } else {
996                     if (date == -1) date = mCaptureStartTime;
997                     mMediaSaver.addImage(jpegData, title, date, mLocation, width, height,
998                             orientation, mOnMediaSavedListener);
999                 }
1000             } else {
1001                 mJpegImageData = jpegData;
1002                 if (!mQuickCapture) {
1003                     showPostCaptureAlert();
1004                 } else {
1005                     doAttach();
1006                 }
1007             }
1008 
1009             // Check this in advance of each shot so we don't add to shutter
1010             // latency. It's true that someone else could write to the SD card in
1011             // the mean time and fill it, but that could have happened between the
1012             // shutter press and saving the JPEG too.
1013             mActivity.updateStorageSpaceAndHint();
1014 
1015             long now = System.currentTimeMillis();
1016             mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1017             Log.v(TAG, "mJpegCallbackFinishTime = "
1018                     + mJpegCallbackFinishTime + "ms");
1019             mJpegPictureCallbackTime = 0;
1020         }
1021     }
1022 
1023     private final class AutoFocusCallback
1024             implements android.hardware.Camera.AutoFocusCallback {
1025         @Override
onAutoFocus( boolean focused, android.hardware.Camera camera)1026         public void onAutoFocus(
1027                 boolean focused, android.hardware.Camera camera) {
1028             if (mPaused) return;
1029 
1030             mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
1031             Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
1032             setCameraState(IDLE);
1033             mFocusManager.onAutoFocus(focused, mShutterButton.isPressed());
1034         }
1035     }
1036 
1037     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1038     private final class AutoFocusMoveCallback
1039             implements android.hardware.Camera.AutoFocusMoveCallback {
1040         @Override
onAutoFocusMoving( boolean moving, android.hardware.Camera camera)1041         public void onAutoFocusMoving(
1042             boolean moving, android.hardware.Camera camera) {
1043                 mFocusManager.onAutoFocusMoving(moving);
1044         }
1045     }
1046 
1047     private static class NamedImages {
1048         private ArrayList<NamedEntity> mQueue;
1049         private boolean mStop;
1050         private NamedEntity mNamedEntity;
1051 
NamedImages()1052         public NamedImages() {
1053             mQueue = new ArrayList<NamedEntity>();
1054         }
1055 
nameNewImage(ContentResolver resolver, long date)1056         public void nameNewImage(ContentResolver resolver, long date) {
1057             NamedEntity r = new NamedEntity();
1058             r.title = Util.createJpegName(date);
1059             r.date = date;
1060             mQueue.add(r);
1061         }
1062 
getTitle()1063         public String getTitle() {
1064             if (mQueue.isEmpty()) {
1065                 mNamedEntity = null;
1066                 return null;
1067             }
1068             mNamedEntity = mQueue.get(0);
1069             mQueue.remove(0);
1070 
1071             return mNamedEntity.title;
1072         }
1073 
1074         // Must be called after getTitle().
getDate()1075         public long getDate() {
1076             if (mNamedEntity == null) return -1;
1077             return mNamedEntity.date;
1078         }
1079 
1080         private static class NamedEntity {
1081             String title;
1082             long date;
1083         }
1084     }
1085 
setCameraState(int state)1086     private void setCameraState(int state) {
1087         mCameraState = state;
1088         switch (state) {
1089             case PREVIEW_STOPPED:
1090             case SNAPSHOT_IN_PROGRESS:
1091             case FOCUSING:
1092             case SWITCHING_CAMERA:
1093                 if (mGestures != null) mGestures.setEnabled(false);
1094                 break;
1095             case IDLE:
1096                 if (mGestures != null && mActivity.mShowCameraAppView) {
1097                     // Enable gestures only when the camera app view is visible
1098                     mGestures.setEnabled(true);
1099                 }
1100                 break;
1101         }
1102     }
1103 
animateFlash()1104     private void animateFlash() {
1105         // Only animate when in full screen capture mode
1106         // i.e. If monkey/a user swipes to the gallery during picture taking,
1107         // don't show animation
1108         if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
1109                 && mActivity.mShowCameraAppView) {
1110             // Start capture animation.
1111             ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
1112         }
1113     }
1114 
1115     @Override
capture()1116     public boolean capture() {
1117         // If we are already in the middle of taking a snapshot or the image save request
1118         // is full then ignore.
1119         if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
1120                 || mCameraState == SWITCHING_CAMERA || mMediaSaver.queueFull()) {
1121             return false;
1122         }
1123         mCaptureStartTime = System.currentTimeMillis();
1124         mPostViewPictureCallbackTime = 0;
1125         mJpegImageData = null;
1126 
1127         final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
1128 
1129         if (animateBefore) {
1130             animateFlash();
1131         }
1132 
1133         // Set rotation and gps data.
1134         mJpegRotation = Util.getJpegRotation(mCameraId, mOrientation);
1135         mParameters.setRotation(mJpegRotation);
1136         Location loc = mLocationManager.getCurrentLocation();
1137         Util.setGpsParameters(mParameters, loc);
1138         mCameraDevice.setParameters(mParameters);
1139 
1140         mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback,
1141                 mPostViewPictureCallback, new JpegPictureCallback(loc),
1142                 mCameraState, mFocusManager.getFocusState());
1143 
1144         if (!animateBefore) {
1145             animateFlash();
1146         }
1147 
1148         mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
1149 
1150         mFaceDetectionStarted = false;
1151         setCameraState(SNAPSHOT_IN_PROGRESS);
1152         return true;
1153     }
1154 
1155     @Override
setFocusParameters()1156     public void setFocusParameters() {
1157         setCameraParameters(UPDATE_PARAM_PREFERENCE);
1158     }
1159 
getPreferredCameraId(ComboPreferences preferences)1160     private int getPreferredCameraId(ComboPreferences preferences) {
1161         int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1162         if (intentCameraId != -1) {
1163             // Testing purpose. Launch a specific camera through the intent
1164             // extras.
1165             return intentCameraId;
1166         } else {
1167             return CameraSettings.readPreferredCameraId(preferences);
1168         }
1169     }
1170 
setShowMenu(boolean show)1171     private void setShowMenu(boolean show) {
1172         if (mOnScreenIndicators != null) {
1173             mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
1174         }
1175         if (mMenu != null) {
1176             mMenu.setVisibility(show ? View.VISIBLE : View.GONE);
1177         }
1178     }
1179 
1180     @Override
onFullScreenChanged(boolean full)1181     public void onFullScreenChanged(boolean full) {
1182         if (mFaceView != null) {
1183             mFaceView.setBlockDraw(!full);
1184         }
1185         if (mPopup != null) {
1186             dismissPopup(false, full);
1187         }
1188         if (mGestures != null) {
1189             mGestures.setEnabled(full);
1190         }
1191         if (mRenderOverlay != null) {
1192             // this can not happen in capture mode
1193             mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
1194         }
1195         if (mPieRenderer != null) {
1196             mPieRenderer.setBlockFocus(!full);
1197         }
1198         setShowMenu(full);
1199         if (mBlocker != null) {
1200             mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
1201         }
1202         if (!full && mCountDownView != null) mCountDownView.cancelCountDown();
1203         if (ApiHelper.HAS_SURFACE_TEXTURE) {
1204             if (mActivity.mCameraScreenNail != null) {
1205                 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
1206             }
1207             return;
1208         }
1209         if (full) {
1210             mPreviewSurfaceView.expand();
1211         } else {
1212             mPreviewSurfaceView.shrink();
1213         }
1214     }
1215 
1216     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)1217     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
1218         Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
1219                 + height);
1220     }
1221 
1222     @Override
surfaceCreated(SurfaceHolder holder)1223     public void surfaceCreated(SurfaceHolder holder) {
1224         Log.v(TAG, "surfaceCreated: " + holder);
1225         mCameraSurfaceHolder = holder;
1226         // Do not access the camera if camera start up thread is not finished.
1227         if (mCameraDevice == null || mCameraStartUpThread != null) return;
1228 
1229         mCameraDevice.setPreviewDisplayAsync(holder);
1230         // This happens when onConfigurationChanged arrives, surface has been
1231         // destroyed, and there is no onFullScreenChanged.
1232         if (mCameraState == PREVIEW_STOPPED) {
1233             setupPreview();
1234         }
1235     }
1236 
1237     @Override
surfaceDestroyed(SurfaceHolder holder)1238     public void surfaceDestroyed(SurfaceHolder holder) {
1239         Log.v(TAG, "surfaceDestroyed: " + holder);
1240         mCameraSurfaceHolder = null;
1241         stopPreview();
1242     }
1243 
updateSceneModeUI()1244     private void updateSceneModeUI() {
1245         // If scene mode is set, we cannot set flash mode, white balance, and
1246         // focus mode, instead, we read it from driver
1247         if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1248             overrideCameraSettings(mParameters.getFlashMode(),
1249                     mParameters.getWhiteBalance(), mParameters.getFocusMode());
1250         } else {
1251             overrideCameraSettings(null, null, null);
1252         }
1253     }
1254 
overrideCameraSettings(final String flashMode, final String whiteBalance, final String focusMode)1255     private void overrideCameraSettings(final String flashMode,
1256             final String whiteBalance, final String focusMode) {
1257         if (mPhotoControl != null) {
1258 //            mPieControl.enableFilter(true);
1259             mPhotoControl.overrideSettings(
1260                     CameraSettings.KEY_FLASH_MODE, flashMode,
1261                     CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1262                     CameraSettings.KEY_FOCUS_MODE, focusMode);
1263 //            mPieControl.enableFilter(false);
1264         }
1265     }
1266 
loadCameraPreferences()1267     private void loadCameraPreferences() {
1268         CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1269                 mCameraId, CameraHolder.instance().getCameraInfo());
1270         mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1271     }
1272 
1273     @Override
collapseCameraControls()1274     public boolean collapseCameraControls() {
1275         // Remove all the popups/dialog boxes
1276         boolean ret = false;
1277         if (mPopup != null) {
1278             dismissPopup(false);
1279             ret = true;
1280         }
1281         return ret;
1282     }
1283 
removeTopLevelPopup()1284     public boolean removeTopLevelPopup() {
1285         // Remove the top level popup or dialog box and return true if there's any
1286         if (mPopup != null) {
1287             dismissPopup(true);
1288             return true;
1289         }
1290         return false;
1291     }
1292 
1293     @Override
onOrientationChanged(int orientation)1294     public void onOrientationChanged(int orientation) {
1295         // We keep the last known orientation. So if the user first orient
1296         // the camera then point the camera to floor or sky, we still have
1297         // the correct orientation.
1298         if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1299         mOrientation = Util.roundOrientation(orientation, mOrientation);
1300 
1301         // Show the toast after getting the first orientation changed.
1302         if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1303             mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1304             showTapToFocusToast();
1305         }
1306     }
1307 
1308     @Override
onStop()1309     public void onStop() {
1310         if (mMediaProviderClient != null) {
1311             mMediaProviderClient.release();
1312             mMediaProviderClient = null;
1313         }
1314     }
1315 
1316     // onClick handler for R.id.btn_done
1317     @OnClickAttr
onReviewDoneClicked(View v)1318     public void onReviewDoneClicked(View v) {
1319         doAttach();
1320     }
1321 
1322     // onClick handler for R.id.btn_cancel
1323     @OnClickAttr
onReviewCancelClicked(View v)1324     public void onReviewCancelClicked(View v) {
1325         doCancel();
1326     }
1327 
1328     // onClick handler for R.id.btn_retake
1329     @OnClickAttr
onReviewRetakeClicked(View v)1330     public void onReviewRetakeClicked(View v) {
1331         if (mPaused)
1332             return;
1333 
1334         hidePostCaptureAlert();
1335         setupPreview();
1336     }
1337 
doAttach()1338     private void doAttach() {
1339         if (mPaused) {
1340             return;
1341         }
1342 
1343         byte[] data = mJpegImageData;
1344 
1345         if (mCropValue == null) {
1346             // First handle the no crop case -- just return the value.  If the
1347             // caller specifies a "save uri" then write the data to its
1348             // stream. Otherwise, pass back a scaled down version of the bitmap
1349             // directly in the extras.
1350             if (mSaveUri != null) {
1351                 OutputStream outputStream = null;
1352                 try {
1353                     outputStream = mContentResolver.openOutputStream(mSaveUri);
1354                     outputStream.write(data);
1355                     outputStream.close();
1356 
1357                     mActivity.setResultEx(Activity.RESULT_OK);
1358                     mActivity.finish();
1359                 } catch (IOException ex) {
1360                     // ignore exception
1361                 } finally {
1362                     Util.closeSilently(outputStream);
1363                 }
1364             } else {
1365                 int orientation = Exif.getOrientation(data);
1366                 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1367                 bitmap = Util.rotate(bitmap, orientation);
1368                 mActivity.setResultEx(Activity.RESULT_OK,
1369                         new Intent("inline-data").putExtra("data", bitmap));
1370                 mActivity.finish();
1371             }
1372         } else {
1373             // Save the image to a temp file and invoke the cropper
1374             Uri tempUri = null;
1375             FileOutputStream tempStream = null;
1376             try {
1377                 File path = mActivity.getFileStreamPath(sTempCropFilename);
1378                 path.delete();
1379                 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1380                 tempStream.write(data);
1381                 tempStream.close();
1382                 tempUri = Uri.fromFile(path);
1383             } catch (FileNotFoundException ex) {
1384                 mActivity.setResultEx(Activity.RESULT_CANCELED);
1385                 mActivity.finish();
1386                 return;
1387             } catch (IOException ex) {
1388                 mActivity.setResultEx(Activity.RESULT_CANCELED);
1389                 mActivity.finish();
1390                 return;
1391             } finally {
1392                 Util.closeSilently(tempStream);
1393             }
1394 
1395             Bundle newExtras = new Bundle();
1396             if (mCropValue.equals("circle")) {
1397                 newExtras.putString("circleCrop", "true");
1398             }
1399             if (mSaveUri != null) {
1400                 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1401             } else {
1402                 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1403             }
1404             if (mActivity.isSecureCamera()) {
1405                 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1406             }
1407 
1408             Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
1409 
1410             cropIntent.setData(tempUri);
1411             cropIntent.putExtras(newExtras);
1412 
1413             mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1414         }
1415     }
1416 
doCancel()1417     private void doCancel() {
1418         mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1419         mActivity.finish();
1420     }
1421 
1422     @Override
onShutterButtonFocus(boolean pressed)1423     public void onShutterButtonFocus(boolean pressed) {
1424         if (mPaused || collapseCameraControls()
1425                 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1426                 || (mCameraState == PREVIEW_STOPPED)) return;
1427 
1428         // Do not do focus if there is not enough storage.
1429         if (pressed && !canTakePicture()) return;
1430 
1431         if (pressed) {
1432             if (mSceneMode == Util.SCENE_MODE_HDR) {
1433                 mActivity.hideSwitcher();
1434                 mActivity.setSwipingEnabled(false);
1435             }
1436             mFocusManager.onShutterDown();
1437         } else {
1438             mFocusManager.onShutterUp();
1439         }
1440     }
1441 
1442     @Override
onShutterButtonClick()1443     public void onShutterButtonClick() {
1444         if (mPaused || collapseCameraControls()
1445                 || (mCameraState == SWITCHING_CAMERA)
1446                 || (mCameraState == PREVIEW_STOPPED)) return;
1447 
1448         // Do not take the picture if there is not enough storage.
1449         if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1450             Log.i(TAG, "Not enough space or storage not ready. remaining="
1451                     + mActivity.getStorageSpace());
1452             return;
1453         }
1454         Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1455 
1456         // If the user wants to do a snapshot while the previous one is still
1457         // in progress, remember the fact and do it after we finish the previous
1458         // one and re-start the preview. Snapshot in progress also includes the
1459         // state that autofocus is focusing and a picture will be taken when
1460         // focus callback arrives.
1461         if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1462                 && !mIsImageCaptureIntent) {
1463             mSnapshotOnIdle = true;
1464             return;
1465         }
1466 
1467         String timer = mPreferences.getString(
1468                 CameraSettings.KEY_TIMER,
1469                 mActivity.getString(R.string.pref_camera_timer_default));
1470         boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1471                 mActivity.getString(R.string.pref_camera_timer_sound_default))
1472                 .equals(mActivity.getString(R.string.setting_on_value));
1473 
1474         int seconds = Integer.parseInt(timer);
1475         // When shutter button is pressed, check whether the previous countdown is
1476         // finished. If not, cancel the previous countdown and start a new one.
1477         if (mCountDownView.isCountingDown()) {
1478             mCountDownView.cancelCountDown();
1479             mCountDownView.startCountDown(seconds, playSound);
1480         } else if (seconds > 0) {
1481             mCountDownView.startCountDown(seconds, playSound);
1482         } else {
1483            mSnapshotOnIdle = false;
1484            mFocusManager.doSnap();
1485         }
1486     }
1487 
1488     @Override
installIntentFilter()1489     public void installIntentFilter() {
1490     }
1491 
1492     @Override
updateStorageHintOnResume()1493     public boolean updateStorageHintOnResume() {
1494         return mFirstTimeInitialized;
1495     }
1496 
1497     @Override
updateCameraAppView()1498     public void updateCameraAppView() {
1499     }
1500 
1501     @Override
onResumeBeforeSuper()1502     public void onResumeBeforeSuper() {
1503         mPaused = false;
1504     }
1505 
1506     @Override
onResumeAfterSuper()1507     public void onResumeAfterSuper() {
1508         if (mOpenCameraFail || mCameraDisabled) return;
1509 
1510         mJpegPictureCallbackTime = 0;
1511         mZoomValue = 0;
1512 
1513         // Start the preview if it is not started.
1514         if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1515             resetExposureCompensation();
1516             mCameraStartUpThread = new CameraStartUpThread();
1517             mCameraStartUpThread.start();
1518         }
1519 
1520         // If first time initialization is not finished, put it in the
1521         // message queue.
1522         if (!mFirstTimeInitialized) {
1523             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1524         } else {
1525             initializeSecondTime();
1526         }
1527         keepScreenOnAwhile();
1528 
1529         // Dismiss open menu if exists.
1530         PopupManager.getInstance(mActivity).notifyShowPopup(null);
1531     }
1532 
waitCameraStartUpThread()1533     void waitCameraStartUpThread() {
1534         try {
1535             if (mCameraStartUpThread != null) {
1536                 mCameraStartUpThread.cancel();
1537                 mCameraStartUpThread.join();
1538                 mCameraStartUpThread = null;
1539                 setCameraState(IDLE);
1540             }
1541         } catch (InterruptedException e) {
1542             // ignore
1543         }
1544     }
1545 
1546     @Override
onPauseBeforeSuper()1547     public void onPauseBeforeSuper() {
1548         mPaused = true;
1549     }
1550 
1551     @Override
onPauseAfterSuper()1552     public void onPauseAfterSuper() {
1553         // Wait the camera start up thread to finish.
1554         waitCameraStartUpThread();
1555 
1556         // When camera is started from secure lock screen for the first time
1557         // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1558         // To reduce the latency, keep the camera for a short time so it does
1559         // not need to be opened again.
1560         if (mCameraDevice != null && mActivity.isSecureCamera()
1561                 && ActivityBase.isFirstStartAfterScreenOn()) {
1562             ActivityBase.resetFirstStartAfterScreenOn();
1563             CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1564         }
1565         // Reset the focus first. Camera CTS does not guarantee that
1566         // cancelAutoFocus is allowed after preview stops.
1567         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1568             mCameraDevice.cancelAutoFocus();
1569         }
1570         stopPreview();
1571         mCountDownView.cancelCountDown();
1572         // Close the camera now because other activities may need to use it.
1573         closeCamera();
1574         if (mSurfaceTexture != null) {
1575             ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
1576             mSurfaceTexture = null;
1577         }
1578         resetScreenOn();
1579 
1580         // Clear UI.
1581         collapseCameraControls();
1582         if (mFaceView != null) mFaceView.clear();
1583 
1584         if (mFirstTimeInitialized) {
1585             if (mMediaSaver != null) {
1586                 mMediaSaver.finish();
1587                 mMediaSaver = null;
1588                 mNamedImages = null;
1589             }
1590         }
1591 
1592         if (mLocationManager != null) mLocationManager.recordLocation(false);
1593 
1594         // If we are in an image capture intent and has taken
1595         // a picture, we just clear it in onPause.
1596         mJpegImageData = null;
1597 
1598         // Remove the messages in the event queue.
1599         mHandler.removeMessages(SETUP_PREVIEW);
1600         mHandler.removeMessages(FIRST_TIME_INIT);
1601         mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1602         mHandler.removeMessages(SWITCH_CAMERA);
1603         mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1604         mHandler.removeMessages(CAMERA_OPEN_DONE);
1605         mHandler.removeMessages(START_PREVIEW_DONE);
1606         mHandler.removeMessages(OPEN_CAMERA_FAIL);
1607         mHandler.removeMessages(CAMERA_DISABLED);
1608 
1609         mPendingSwitchCameraId = -1;
1610         if (mFocusManager != null) mFocusManager.removeMessages();
1611     }
1612 
initializeControlByIntent()1613     private void initializeControlByIntent() {
1614         mBlocker = mRootView.findViewById(R.id.blocker);
1615         mMenu = mRootView.findViewById(R.id.menu);
1616         mMenu.setOnClickListener(new OnClickListener() {
1617             @Override
1618             public void onClick(View v) {
1619                 if (mPieRenderer != null) {
1620                     // If autofocus is not finished, cancel autofocus so that the
1621                     // subsequent touch can be handled by PreviewGestures
1622                     if (mCameraState == FOCUSING) cancelAutoFocus();
1623                     mPieRenderer.showInCenter();
1624                 }
1625             }
1626         });
1627         if (mIsImageCaptureIntent) {
1628 
1629             mActivity.hideSwitcher();
1630             // Cannot use RotateImageView for "done" and "cancel" button because
1631             // the tablet layout uses RotateLayout, which cannot be cast to
1632             // RotateImageView.
1633             mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done);
1634             mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel);
1635             mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
1636             ((View) mReviewCancelButton).setVisibility(View.VISIBLE);
1637 
1638             ((View) mReviewDoneButton).setOnClickListener(new OnClickListener() {
1639                 @Override
1640                 public void onClick(View v) {
1641                     onReviewDoneClicked(v);
1642                 }
1643             });
1644             ((View) mReviewCancelButton).setOnClickListener(new OnClickListener() {
1645                 @Override
1646                 public void onClick(View v) {
1647                     onReviewCancelClicked(v);
1648                 }
1649             });
1650 
1651             mReviewRetakeButton.setOnClickListener(new OnClickListener() {
1652                 @Override
1653                 public void onClick(View v) {
1654                     onReviewRetakeClicked(v);
1655                 }
1656             });
1657 
1658             // Not grayed out upon disabled, to make the follow-up fade-out
1659             // effect look smooth. Note that the review done button in tablet
1660             // layout is not a TwoStateImageView.
1661             if (mReviewDoneButton instanceof TwoStateImageView) {
1662                 ((TwoStateImageView) mReviewDoneButton).enableFilter(false);
1663             }
1664 
1665             setupCaptureParams();
1666         }
1667     }
1668 
1669     /**
1670      * The focus manager is the first UI related element to get initialized,
1671      * and it requires the RenderOverlay, so initialize it here
1672      */
initializeFocusManager()1673     private void initializeFocusManager() {
1674         // Create FocusManager object. startPreview needs it.
1675         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
1676         // if mFocusManager not null, reuse it
1677         // otherwise create a new instance
1678         if (mFocusManager != null) {
1679             mFocusManager.removeMessages();
1680         } else {
1681             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1682             boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1683             String[] defaultFocusModes = mActivity.getResources().getStringArray(
1684                     R.array.pref_camera_focusmode_default_array);
1685             mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1686                     mInitialParams, this, mirror,
1687                     mActivity.getMainLooper());
1688         }
1689     }
1690 
initializeMiscControls()1691     private void initializeMiscControls() {
1692         // startPreview needs this.
1693         mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame);
1694         // Set touch focus listener.
1695         mActivity.setSingleTapUpListener(mPreviewFrameLayout);
1696 
1697         mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
1698         mPreviewFrameLayout.setOnSizeChangedListener(this);
1699         mPreviewFrameLayout.setOnLayoutChangeListener(mActivity);
1700         if (!ApiHelper.HAS_SURFACE_TEXTURE) {
1701             mPreviewSurfaceView =
1702                     (PreviewSurfaceView) mRootView.findViewById(R.id.preview_surface_view);
1703             mPreviewSurfaceView.setVisibility(View.VISIBLE);
1704             mPreviewSurfaceView.getHolder().addCallback(this);
1705         }
1706     }
1707 
1708     @Override
onConfigurationChanged(Configuration newConfig)1709     public void onConfigurationChanged(Configuration newConfig) {
1710         Log.v(TAG, "onConfigurationChanged");
1711         setDisplayOrientation();
1712 
1713         // Only the views in photo_module_content need to be removed and recreated
1714         // i.e. CountDownView won't be recreated
1715         ViewGroup viewGroup = (ViewGroup) mRootView.findViewById(R.id.camera_app);
1716         viewGroup.removeAllViews();
1717         LayoutInflater inflater = mActivity.getLayoutInflater();
1718         inflater.inflate(R.layout.photo_module_content, (ViewGroup) viewGroup);
1719 
1720         // from onCreate()
1721         initializeControlByIntent();
1722 
1723         initializeFocusManager();
1724         initializeMiscControls();
1725         loadCameraPreferences();
1726 
1727         // from initializeFirstTime()
1728         mShutterButton = mActivity.getShutterButton();
1729         mShutterButton.setOnShutterButtonListener(this);
1730         initializeZoom();
1731         initOnScreenIndicator();
1732         updateOnScreenIndicators();
1733         if (mFaceView != null) {
1734             mFaceView.clear();
1735             mFaceView.setVisibility(View.VISIBLE);
1736             mFaceView.setDisplayOrientation(mDisplayOrientation);
1737             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1738             mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
1739             mFaceView.resume();
1740             mFocusManager.setFaceView(mFaceView);
1741         }
1742         initializeRenderOverlay();
1743         onFullScreenChanged(mActivity.isInCameraApp());
1744         if (mJpegImageData != null) {  // Jpeg data found, picture has been taken.
1745             showPostCaptureAlert();
1746         }
1747     }
1748 
1749     @Override
onActivityResult( int requestCode, int resultCode, Intent data)1750     public void onActivityResult(
1751             int requestCode, int resultCode, Intent data) {
1752         switch (requestCode) {
1753             case REQUEST_CROP: {
1754                 Intent intent = new Intent();
1755                 if (data != null) {
1756                     Bundle extras = data.getExtras();
1757                     if (extras != null) {
1758                         intent.putExtras(extras);
1759                     }
1760                 }
1761                 mActivity.setResultEx(resultCode, intent);
1762                 mActivity.finish();
1763 
1764                 File path = mActivity.getFileStreamPath(sTempCropFilename);
1765                 path.delete();
1766 
1767                 break;
1768             }
1769         }
1770     }
1771 
canTakePicture()1772     private boolean canTakePicture() {
1773         return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1774     }
1775 
1776     @Override
autoFocus()1777     public void autoFocus() {
1778         mFocusStartTime = System.currentTimeMillis();
1779         mCameraDevice.autoFocus(mAutoFocusCallback);
1780         setCameraState(FOCUSING);
1781     }
1782 
1783     @Override
cancelAutoFocus()1784     public void cancelAutoFocus() {
1785         mCameraDevice.cancelAutoFocus();
1786         setCameraState(IDLE);
1787         setCameraParameters(UPDATE_PARAM_PREFERENCE);
1788     }
1789 
1790     // Preview area is touched. Handle touch focus.
1791     @Override
onSingleTapUp(View view, int x, int y)1792     public void onSingleTapUp(View view, int x, int y) {
1793         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1794                 || mCameraState == SNAPSHOT_IN_PROGRESS
1795                 || mCameraState == SWITCHING_CAMERA
1796                 || mCameraState == PREVIEW_STOPPED) {
1797             return;
1798         }
1799 
1800         // Do not trigger touch focus if popup window is opened.
1801         if (removeTopLevelPopup()) return;
1802 
1803         // Check if metering area or focus area is supported.
1804         if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1805         mFocusManager.onSingleTapUp(x, y);
1806     }
1807 
1808     @Override
onBackPressed()1809     public boolean onBackPressed() {
1810         if (mPieRenderer != null && mPieRenderer.showsItems()) {
1811             mPieRenderer.hide();
1812             return true;
1813         }
1814         // In image capture mode, back button should:
1815         // 1) if there is any popup, dismiss them, 2) otherwise, get out of image capture
1816         if (mIsImageCaptureIntent) {
1817             if (!removeTopLevelPopup()) {
1818                 // no popup to dismiss, cancel image capture
1819                 doCancel();
1820             }
1821             return true;
1822         } else if (!isCameraIdle()) {
1823             // ignore backs while we're taking a picture
1824             return true;
1825         } else {
1826             return removeTopLevelPopup();
1827         }
1828     }
1829 
1830     @Override
onKeyDown(int keyCode, KeyEvent event)1831     public boolean onKeyDown(int keyCode, KeyEvent event) {
1832         switch (keyCode) {
1833         case KeyEvent.KEYCODE_VOLUME_UP:
1834         case KeyEvent.KEYCODE_VOLUME_DOWN:
1835         case KeyEvent.KEYCODE_FOCUS:
1836             if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1837                 if (event.getRepeatCount() == 0) {
1838                     onShutterButtonFocus(true);
1839                 }
1840                 return true;
1841             }
1842             return false;
1843         case KeyEvent.KEYCODE_CAMERA:
1844             if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1845                 onShutterButtonClick();
1846             }
1847             return true;
1848         case KeyEvent.KEYCODE_DPAD_CENTER:
1849             // If we get a dpad center event without any focused view, move
1850             // the focus to the shutter button and press it.
1851             if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1852                 // Start auto-focus immediately to reduce shutter lag. After
1853                 // the shutter button gets the focus, onShutterButtonFocus()
1854                 // will be called again but it is fine.
1855                 if (removeTopLevelPopup()) return true;
1856                 onShutterButtonFocus(true);
1857                 if (mShutterButton.isInTouchMode()) {
1858                     mShutterButton.requestFocusFromTouch();
1859                 } else {
1860                     mShutterButton.requestFocus();
1861                 }
1862                 mShutterButton.setPressed(true);
1863             }
1864             return true;
1865         }
1866         return false;
1867     }
1868 
1869     @Override
onKeyUp(int keyCode, KeyEvent event)1870     public boolean onKeyUp(int keyCode, KeyEvent event) {
1871         switch (keyCode) {
1872         case KeyEvent.KEYCODE_VOLUME_UP:
1873         case KeyEvent.KEYCODE_VOLUME_DOWN:
1874             if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1875                 onShutterButtonClick();
1876                 return true;
1877             }
1878             return false;
1879         case KeyEvent.KEYCODE_FOCUS:
1880             if (mFirstTimeInitialized) {
1881                 onShutterButtonFocus(false);
1882             }
1883             return true;
1884         }
1885         return false;
1886     }
1887 
1888     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
closeCamera()1889     private void closeCamera() {
1890         if (mCameraDevice != null) {
1891             mCameraDevice.setZoomChangeListener(null);
1892             if(ApiHelper.HAS_FACE_DETECTION) {
1893                 mCameraDevice.setFaceDetectionListener(null);
1894             }
1895             mCameraDevice.setErrorCallback(null);
1896             CameraHolder.instance().release();
1897             mFaceDetectionStarted = false;
1898             mCameraDevice = null;
1899             setCameraState(PREVIEW_STOPPED);
1900             mFocusManager.onCameraReleased();
1901         }
1902     }
1903 
setDisplayOrientation()1904     private void setDisplayOrientation() {
1905         mDisplayRotation = Util.getDisplayRotation(mActivity);
1906         mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1907         mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
1908         if (mFaceView != null) {
1909             mFaceView.setDisplayOrientation(mDisplayOrientation);
1910         }
1911         if (mFocusManager != null) {
1912             mFocusManager.setDisplayOrientation(mDisplayOrientation);
1913         }
1914         // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
1915         mActivity.getGLRoot().requestLayoutContentPane();
1916     }
1917 
1918     // Only called by UI thread.
setupPreview()1919     private void setupPreview() {
1920         mFocusManager.resetTouchFocus();
1921         startPreview();
1922         setCameraState(IDLE);
1923         startFaceDetection();
1924     }
1925 
1926     // This can be called by UI Thread or CameraStartUpThread. So this should
1927     // not modify the views.
startPreview()1928     private void startPreview() {
1929         mCameraDevice.setErrorCallback(mErrorCallback);
1930 
1931         // ICS camera frameworks has a bug. Face detection state is not cleared
1932         // after taking a picture. Stop the preview to work around it. The bug
1933         // was fixed in JB.
1934         if (mCameraState != PREVIEW_STOPPED) stopPreview();
1935 
1936         setDisplayOrientation();
1937 
1938         if (!mSnapshotOnIdle) {
1939             // If the focus mode is continuous autofocus, call cancelAutoFocus to
1940             // resume it because it may have been paused by autoFocus call.
1941             if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1942                 mCameraDevice.cancelAutoFocus();
1943             }
1944             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1945         }
1946         setCameraParameters(UPDATE_PARAM_ALL);
1947 
1948         if (ApiHelper.HAS_SURFACE_TEXTURE) {
1949             CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
1950             if (mSurfaceTexture == null) {
1951                 Size size = mParameters.getPreviewSize();
1952                 if (mCameraDisplayOrientation % 180 == 0) {
1953                     screenNail.setSize(size.width, size.height);
1954                 } else {
1955                     screenNail.setSize(size.height, size.width);
1956                 }
1957                 screenNail.enableAspectRatioClamping();
1958                 mActivity.notifyScreenNailChanged();
1959                 screenNail.acquireSurfaceTexture();
1960                 CameraStartUpThread t = mCameraStartUpThread;
1961                 if (t != null && t.isCanceled()) {
1962                     return; // Exiting, so no need to get the surface texture.
1963                 }
1964                 mSurfaceTexture = screenNail.getSurfaceTexture();
1965             }
1966             mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1967             if (mSurfaceTexture != null) {
1968                 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) mSurfaceTexture);
1969             }
1970         } else {
1971             mCameraDevice.setDisplayOrientation(mDisplayOrientation);
1972             mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
1973         }
1974 
1975         Log.v(TAG, "startPreview");
1976         mCameraDevice.startPreviewAsync();
1977 
1978         mFocusManager.onPreviewStarted();
1979 
1980         if (mSnapshotOnIdle) {
1981             mHandler.post(mDoSnapRunnable);
1982         }
1983     }
1984 
stopPreview()1985     private void stopPreview() {
1986         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1987             Log.v(TAG, "stopPreview");
1988             mCameraDevice.stopPreview();
1989             mFaceDetectionStarted = false;
1990         }
1991         setCameraState(PREVIEW_STOPPED);
1992         if (mFocusManager != null) mFocusManager.onPreviewStopped();
1993     }
1994 
1995     @SuppressWarnings("deprecation")
updateCameraParametersInitialize()1996     private void updateCameraParametersInitialize() {
1997         // Reset preview frame rate to the maximum because it may be lowered by
1998         // video camera application.
1999         List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
2000         if (frameRates != null) {
2001             Integer max = Collections.max(frameRates);
2002             mParameters.setPreviewFrameRate(max);
2003         }
2004 
2005         mParameters.set(Util.RECORDING_HINT, Util.FALSE);
2006 
2007         // Disable video stabilization. Convenience methods not available in API
2008         // level <= 14
2009         String vstabSupported = mParameters.get("video-stabilization-supported");
2010         if ("true".equals(vstabSupported)) {
2011             mParameters.set("video-stabilization", "false");
2012         }
2013     }
2014 
updateCameraParametersZoom()2015     private void updateCameraParametersZoom() {
2016         // Set zoom.
2017         if (mParameters.isZoomSupported()) {
2018             mParameters.setZoom(mZoomValue);
2019         }
2020     }
2021 
2022     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
setAutoExposureLockIfSupported()2023     private void setAutoExposureLockIfSupported() {
2024         if (mAeLockSupported) {
2025             mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
2026         }
2027     }
2028 
2029     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
setAutoWhiteBalanceLockIfSupported()2030     private void setAutoWhiteBalanceLockIfSupported() {
2031         if (mAwbLockSupported) {
2032             mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
2033         }
2034     }
2035 
2036     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
setFocusAreasIfSupported()2037     private void setFocusAreasIfSupported() {
2038         if (mFocusAreaSupported) {
2039             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
2040         }
2041     }
2042 
2043     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
setMeteringAreasIfSupported()2044     private void setMeteringAreasIfSupported() {
2045         if (mMeteringAreaSupported) {
2046             // Use the same area for focus and metering.
2047             mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
2048         }
2049     }
2050 
updateCameraParametersPreference()2051     private void updateCameraParametersPreference() {
2052         setAutoExposureLockIfSupported();
2053         setAutoWhiteBalanceLockIfSupported();
2054         setFocusAreasIfSupported();
2055         setMeteringAreasIfSupported();
2056 
2057         // Set picture size.
2058         String pictureSize = mPreferences.getString(
2059                 CameraSettings.KEY_PICTURE_SIZE, null);
2060         if (pictureSize == null) {
2061             CameraSettings.initialCameraPictureSize(mActivity, mParameters);
2062         } else {
2063             List<Size> supported = mParameters.getSupportedPictureSizes();
2064             CameraSettings.setCameraPictureSize(
2065                     pictureSize, supported, mParameters);
2066         }
2067         Size size = mParameters.getPictureSize();
2068 
2069         // Set a preview size that is closest to the viewfinder height and has
2070         // the right aspect ratio.
2071         List<Size> sizes = mParameters.getSupportedPreviewSizes();
2072         Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
2073                 (double) size.width / size.height);
2074         Size original = mParameters.getPreviewSize();
2075         if (!original.equals(optimalSize)) {
2076             mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
2077 
2078             // Zoom related settings will be changed for different preview
2079             // sizes, so set and read the parameters to get latest values
2080             mCameraDevice.setParameters(mParameters);
2081             mParameters = mCameraDevice.getParameters();
2082         }
2083         Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
2084 
2085         // Since changing scene mode may change supported values, set scene mode
2086         // first. HDR is a scene mode. To promote it in UI, it is stored in a
2087         // separate preference.
2088         String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
2089                 mActivity.getString(R.string.pref_camera_hdr_default));
2090         if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
2091             mSceneMode = Util.SCENE_MODE_HDR;
2092         } else {
2093             mSceneMode = mPreferences.getString(
2094                 CameraSettings.KEY_SCENE_MODE,
2095                 mActivity.getString(R.string.pref_camera_scenemode_default));
2096         }
2097         if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
2098             if (!mParameters.getSceneMode().equals(mSceneMode)) {
2099                 mParameters.setSceneMode(mSceneMode);
2100 
2101                 // Setting scene mode will change the settings of flash mode,
2102                 // white balance, and focus mode. Here we read back the
2103                 // parameters, so we can know those settings.
2104                 mCameraDevice.setParameters(mParameters);
2105                 mParameters = mCameraDevice.getParameters();
2106             }
2107         } else {
2108             mSceneMode = mParameters.getSceneMode();
2109             if (mSceneMode == null) {
2110                 mSceneMode = Parameters.SCENE_MODE_AUTO;
2111             }
2112         }
2113 
2114         // Set JPEG quality.
2115         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2116                 CameraProfile.QUALITY_HIGH);
2117         mParameters.setJpegQuality(jpegQuality);
2118 
2119         // For the following settings, we need to check if the settings are
2120         // still supported by latest driver, if not, ignore the settings.
2121 
2122         // Set exposure compensation
2123         int value = CameraSettings.readExposure(mPreferences);
2124         int max = mParameters.getMaxExposureCompensation();
2125         int min = mParameters.getMinExposureCompensation();
2126         if (value >= min && value <= max) {
2127             mParameters.setExposureCompensation(value);
2128         } else {
2129             Log.w(TAG, "invalid exposure range: " + value);
2130         }
2131 
2132         if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2133             // Set flash mode.
2134             String flashMode = mPreferences.getString(
2135                     CameraSettings.KEY_FLASH_MODE,
2136                     mActivity.getString(R.string.pref_camera_flashmode_default));
2137             List<String> supportedFlash = mParameters.getSupportedFlashModes();
2138             if (Util.isSupported(flashMode, supportedFlash)) {
2139                 mParameters.setFlashMode(flashMode);
2140             } else {
2141                 flashMode = mParameters.getFlashMode();
2142                 if (flashMode == null) {
2143                     flashMode = mActivity.getString(
2144                             R.string.pref_camera_flashmode_no_flash);
2145                 }
2146             }
2147 
2148             // Set white balance parameter.
2149             String whiteBalance = mPreferences.getString(
2150                     CameraSettings.KEY_WHITE_BALANCE,
2151                     mActivity.getString(R.string.pref_camera_whitebalance_default));
2152             if (Util.isSupported(whiteBalance,
2153                     mParameters.getSupportedWhiteBalance())) {
2154                 mParameters.setWhiteBalance(whiteBalance);
2155             } else {
2156                 whiteBalance = mParameters.getWhiteBalance();
2157                 if (whiteBalance == null) {
2158                     whiteBalance = Parameters.WHITE_BALANCE_AUTO;
2159                 }
2160             }
2161 
2162             // Set focus mode.
2163             mFocusManager.overrideFocusMode(null);
2164             mParameters.setFocusMode(mFocusManager.getFocusMode());
2165         } else {
2166             mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2167         }
2168 
2169         if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2170             updateAutoFocusMoveCallback();
2171         }
2172     }
2173 
2174     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
updateAutoFocusMoveCallback()2175     private void updateAutoFocusMoveCallback() {
2176         if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
2177             mCameraDevice.setAutoFocusMoveCallback(
2178                 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
2179         } else {
2180             mCameraDevice.setAutoFocusMoveCallback(null);
2181         }
2182     }
2183 
2184     // We separate the parameters into several subsets, so we can update only
2185     // the subsets actually need updating. The PREFERENCE set needs extra
2186     // locking because the preference can be changed from GLThread as well.
setCameraParameters(int updateSet)2187     private void setCameraParameters(int updateSet) {
2188         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2189             updateCameraParametersInitialize();
2190         }
2191 
2192         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2193             updateCameraParametersZoom();
2194         }
2195 
2196         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2197             updateCameraParametersPreference();
2198         }
2199 
2200         mCameraDevice.setParameters(mParameters);
2201     }
2202 
2203     // If the Camera is idle, update the parameters immediately, otherwise
2204     // accumulate them in mUpdateSet and update later.
setCameraParametersWhenIdle(int additionalUpdateSet)2205     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2206         mUpdateSet |= additionalUpdateSet;
2207         if (mCameraDevice == null) {
2208             // We will update all the parameters when we open the device, so
2209             // we don't need to do anything now.
2210             mUpdateSet = 0;
2211             return;
2212         } else if (isCameraIdle()) {
2213             setCameraParameters(mUpdateSet);
2214             updateSceneModeUI();
2215             mUpdateSet = 0;
2216         } else {
2217             if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2218                 mHandler.sendEmptyMessageDelayed(
2219                         SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2220             }
2221         }
2222     }
2223 
isCameraIdle()2224     private boolean isCameraIdle() {
2225         return (mCameraState == IDLE) ||
2226                 (mCameraState == PREVIEW_STOPPED) ||
2227                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2228                         && (mCameraState != SWITCHING_CAMERA));
2229     }
2230 
isImageCaptureIntent()2231     private boolean isImageCaptureIntent() {
2232         String action = mActivity.getIntent().getAction();
2233         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
2234                 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
2235     }
2236 
setupCaptureParams()2237     private void setupCaptureParams() {
2238         Bundle myExtras = mActivity.getIntent().getExtras();
2239         if (myExtras != null) {
2240             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2241             mCropValue = myExtras.getString("crop");
2242         }
2243     }
2244 
showPostCaptureAlert()2245     private void showPostCaptureAlert() {
2246         if (mIsImageCaptureIntent) {
2247             mOnScreenIndicators.setVisibility(View.GONE);
2248             mMenu.setVisibility(View.GONE);
2249             Util.fadeIn((View) mReviewDoneButton);
2250             mShutterButton.setVisibility(View.INVISIBLE);
2251             Util.fadeIn(mReviewRetakeButton);
2252         }
2253     }
2254 
hidePostCaptureAlert()2255     private void hidePostCaptureAlert() {
2256         if (mIsImageCaptureIntent) {
2257             mOnScreenIndicators.setVisibility(View.VISIBLE);
2258             mMenu.setVisibility(View.VISIBLE);
2259             Util.fadeOut((View) mReviewDoneButton);
2260             mShutterButton.setVisibility(View.VISIBLE);
2261             Util.fadeOut(mReviewRetakeButton);
2262         }
2263     }
2264 
2265     @Override
onSharedPreferenceChanged()2266     public void onSharedPreferenceChanged() {
2267         // ignore the events after "onPause()"
2268         if (mPaused) return;
2269 
2270         boolean recordLocation = RecordLocationPreference.get(
2271                 mPreferences, mContentResolver);
2272         mLocationManager.recordLocation(recordLocation);
2273 
2274         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
2275         setPreviewFrameLayoutAspectRatio();
2276         updateOnScreenIndicators();
2277     }
2278 
2279     @Override
onCameraPickerClicked(int cameraId)2280     public void onCameraPickerClicked(int cameraId) {
2281         if (mPaused || mPendingSwitchCameraId != -1) return;
2282 
2283         mPendingSwitchCameraId = cameraId;
2284         if (ApiHelper.HAS_SURFACE_TEXTURE) {
2285             Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
2286             // We need to keep a preview frame for the animation before
2287             // releasing the camera. This will trigger onPreviewTextureCopied.
2288             ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
2289             // Disable all camera controls.
2290             setCameraState(SWITCHING_CAMERA);
2291         } else {
2292             switchCamera();
2293         }
2294     }
2295 
switchCamera()2296     private void switchCamera() {
2297         if (mPaused) return;
2298 
2299         Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
2300         mCameraId = mPendingSwitchCameraId;
2301         mPendingSwitchCameraId = -1;
2302         mPhotoControl.setCameraId(mCameraId);
2303 
2304         // from onPause
2305         closeCamera();
2306         collapseCameraControls();
2307         if (mFaceView != null) mFaceView.clear();
2308         if (mFocusManager != null) mFocusManager.removeMessages();
2309 
2310         // Restart the camera and initialize the UI. From onCreate.
2311         mPreferences.setLocalId(mActivity, mCameraId);
2312         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
2313         try {
2314             mCameraDevice = Util.openCamera(mActivity, mCameraId);
2315             mParameters = mCameraDevice.getParameters();
2316         } catch (CameraHardwareException e) {
2317             Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
2318             return;
2319         } catch (CameraDisabledException e) {
2320             Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
2321             return;
2322         }
2323         initializeCapabilities();
2324         CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
2325         boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
2326         mFocusManager.setMirror(mirror);
2327         mFocusManager.setParameters(mInitialParams);
2328         setupPreview();
2329         loadCameraPreferences();
2330         initializePhotoControl();
2331 
2332         // from initializeFirstTime
2333         initializeZoom();
2334         updateOnScreenIndicators();
2335         showTapToFocusToastIfNeeded();
2336 
2337         if (ApiHelper.HAS_SURFACE_TEXTURE) {
2338             // Start switch camera animation. Post a message because
2339             // onFrameAvailable from the old camera may already exist.
2340             mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
2341         }
2342     }
2343 
2344     @Override
onPieOpened(int centerX, int centerY)2345     public void onPieOpened(int centerX, int centerY) {
2346         mActivity.cancelActivityTouchHandling();
2347         mActivity.setSwipingEnabled(false);
2348         if (mFaceView != null) {
2349             mFaceView.setBlockDraw(true);
2350         }
2351     }
2352 
2353     @Override
onPieClosed()2354     public void onPieClosed() {
2355         mActivity.setSwipingEnabled(true);
2356         if (mFaceView != null) {
2357             mFaceView.setBlockDraw(false);
2358         }
2359     }
2360 
2361     // Preview texture has been copied. Now camera can be released and the
2362     // animation can be started.
2363     @Override
onPreviewTextureCopied()2364     public void onPreviewTextureCopied() {
2365         mHandler.sendEmptyMessage(SWITCH_CAMERA);
2366     }
2367 
2368     @Override
onCaptureTextureCopied()2369     public void onCaptureTextureCopied() {
2370     }
2371 
2372     @Override
onUserInteraction()2373     public void onUserInteraction() {
2374         if (!mActivity.isFinishing()) keepScreenOnAwhile();
2375     }
2376 
resetScreenOn()2377     private void resetScreenOn() {
2378         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
2379         mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2380     }
2381 
keepScreenOnAwhile()2382     private void keepScreenOnAwhile() {
2383         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
2384         mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2385         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
2386     }
2387 
2388     // TODO: Delete this function after old camera code is removed
2389     @Override
onRestorePreferencesClicked()2390     public void onRestorePreferencesClicked() {
2391     }
2392 
2393     @Override
onOverriddenPreferencesClicked()2394     public void onOverriddenPreferencesClicked() {
2395         if (mPaused) return;
2396         if (mNotSelectableToast == null) {
2397             String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
2398             mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
2399         }
2400         mNotSelectableToast.show();
2401     }
2402 
showTapToFocusToast()2403     private void showTapToFocusToast() {
2404         // TODO: Use a toast?
2405         new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
2406         // Clear the preference.
2407         Editor editor = mPreferences.edit();
2408         editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
2409         editor.apply();
2410     }
2411 
initializeCapabilities()2412     private void initializeCapabilities() {
2413         mInitialParams = mCameraDevice.getParameters();
2414         mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
2415         mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
2416         mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
2417         mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
2418         mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
2419                 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
2420     }
2421 
2422     // PreviewFrameLayout size has changed.
2423     @Override
onSizeChanged(int width, int height)2424     public void onSizeChanged(int width, int height) {
2425         if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
2426     }
2427 
2428     @Override
onCountDownFinished()2429     public void onCountDownFinished() {
2430         mSnapshotOnIdle = false;
2431         mFocusManager.doSnap();
2432     }
2433 
setPreviewFrameLayoutAspectRatio()2434     void setPreviewFrameLayoutAspectRatio() {
2435         // Set the preview frame aspect ratio according to the picture size.
2436         Size size = mParameters.getPictureSize();
2437         mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
2438     }
2439 
2440     @Override
needsSwitcher()2441     public boolean needsSwitcher() {
2442         return !mIsImageCaptureIntent;
2443     }
2444 
showPopup(AbstractSettingPopup popup)2445     public void showPopup(AbstractSettingPopup popup) {
2446         mActivity.hideUI();
2447         mBlocker.setVisibility(View.INVISIBLE);
2448         setShowMenu(false);
2449         mPopup = popup;
2450         mPopup.setVisibility(View.VISIBLE);
2451         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
2452                 LayoutParams.WRAP_CONTENT);
2453         lp.gravity = Gravity.CENTER;
2454         ((FrameLayout) mRootView).addView(mPopup, lp);
2455     }
2456 
dismissPopup(boolean topPopupOnly)2457     public void dismissPopup(boolean topPopupOnly) {
2458         dismissPopup(topPopupOnly, true);
2459     }
2460 
dismissPopup(boolean topOnly, boolean fullScreen)2461     private void dismissPopup(boolean topOnly, boolean fullScreen) {
2462         if (fullScreen) {
2463             mActivity.showUI();
2464             mBlocker.setVisibility(View.VISIBLE);
2465         }
2466         setShowMenu(fullScreen);
2467         if (mPopup != null) {
2468             ((FrameLayout) mRootView).removeView(mPopup);
2469             mPopup = null;
2470         }
2471         mPhotoControl.popupDismissed(topOnly);
2472     }
2473 
2474     @Override
onShowSwitcherPopup()2475     public void onShowSwitcherPopup() {
2476         if (mPieRenderer != null && mPieRenderer.showsItems()) {
2477             mPieRenderer.hide();
2478         }
2479     }
2480 
2481 }
2482