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
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.content.ContentProviderClient;
23import android.content.ContentResolver;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.SharedPreferences.Editor;
27import android.content.res.Configuration;
28import android.graphics.Bitmap;
29import android.graphics.SurfaceTexture;
30import android.hardware.Camera.CameraInfo;
31import android.hardware.Camera.Face;
32import android.hardware.Camera.FaceDetectionListener;
33import android.hardware.Camera.Parameters;
34import android.hardware.Camera.PictureCallback;
35import android.hardware.Camera.Size;
36import android.location.Location;
37import android.media.CameraProfile;
38import android.net.Uri;
39import android.os.Bundle;
40import android.os.ConditionVariable;
41import android.os.Handler;
42import android.os.Looper;
43import android.os.Message;
44import android.os.MessageQueue;
45import android.os.SystemClock;
46import android.provider.MediaStore;
47import android.util.Log;
48import android.view.Gravity;
49import android.view.KeyEvent;
50import android.view.LayoutInflater;
51import android.view.MotionEvent;
52import android.view.OrientationEventListener;
53import android.view.SurfaceHolder;
54import android.view.View;
55import android.view.View.OnClickListener;
56import android.view.ViewGroup;
57import android.view.WindowManager;
58import android.widget.FrameLayout;
59import android.widget.FrameLayout.LayoutParams;
60import android.widget.ImageView;
61import android.widget.Toast;
62
63import com.android.camera.CameraManager.CameraProxy;
64import com.android.camera.ui.AbstractSettingPopup;
65import com.android.camera.ui.CountDownView;
66import com.android.camera.ui.FaceView;
67import com.android.camera.ui.PieRenderer;
68import com.android.camera.ui.PopupManager;
69import com.android.camera.ui.PreviewSurfaceView;
70import com.android.camera.ui.RenderOverlay;
71import com.android.camera.ui.Rotatable;
72import com.android.camera.ui.RotateTextToast;
73import com.android.camera.ui.TwoStateImageView;
74import com.android.camera.ui.ZoomRenderer;
75import com.android.gallery3d.common.ApiHelper;
76import com.android.gallery3d.filtershow.CropExtras;
77import com.android.gallery3d.filtershow.FilterShowActivity;
78
79import java.io.File;
80import java.io.FileNotFoundException;
81import java.io.FileOutputStream;
82import java.io.IOException;
83import java.io.OutputStream;
84import java.util.ArrayList;
85import java.util.Collections;
86import java.util.Formatter;
87import java.util.List;
88
89public 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
322        public void cancel() {
323            mCancelled = true;
324            interrupt();
325        }
326
327        public boolean isCanceled() {
328            return mCancelled;
329        }
330
331        @Override
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
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
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
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
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
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
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
605    private void initializePhotoControl() {
606        loadCameraPreferences();
607        if (mPhotoControl != null) {
608            mPhotoControl.initialize(mPreferenceGroup);
609        }
610        updateSceneModeUI();
611    }
612
613
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
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.
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
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
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.
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
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
719        public void onZoomStart() {
720            if (mPieRenderer != null) {
721                mPieRenderer.setBlockFocus(true);
722            }
723        }
724
725        @Override
726        public void onZoomEnd() {
727            if (mPieRenderer != null) {
728                mPieRenderer.setBlockFocus(false);
729            }
730        }
731    }
732
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
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
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
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
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
805    public void showGpsOnScreenIndicator(boolean hasSignal) { }
806
807    @Override
808    public void hideGpsOnScreenIndicator() { }
809
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
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
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
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
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
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
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
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
926        public JpegPictureCallback(Location loc) {
927            mLocation = loc;
928        }
929
930        @Override
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
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
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
1052        public NamedImages() {
1053            mQueue = new ArrayList<NamedEntity>();
1054        }
1055
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
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().
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
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
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
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
1156    public void setFocusParameters() {
1157        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1158    }
1159
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
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
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
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
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
1238    public void surfaceDestroyed(SurfaceHolder holder) {
1239        Log.v(TAG, "surfaceDestroyed: " + holder);
1240        mCameraSurfaceHolder = null;
1241        stopPreview();
1242    }
1243
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
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
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
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
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
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
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
1318    public void onReviewDoneClicked(View v) {
1319        doAttach();
1320    }
1321
1322    // onClick handler for R.id.btn_cancel
1323    @OnClickAttr
1324    public void onReviewCancelClicked(View v) {
1325        doCancel();
1326    }
1327
1328    // onClick handler for R.id.btn_retake
1329    @OnClickAttr
1330    public void onReviewRetakeClicked(View v) {
1331        if (mPaused)
1332            return;
1333
1334        hidePostCaptureAlert();
1335        setupPreview();
1336    }
1337
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
1417    private void doCancel() {
1418        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1419        mActivity.finish();
1420    }
1421
1422    @Override
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
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
1489    public void installIntentFilter() {
1490    }
1491
1492    @Override
1493    public boolean updateStorageHintOnResume() {
1494        return mFirstTimeInitialized;
1495    }
1496
1497    @Override
1498    public void updateCameraAppView() {
1499    }
1500
1501    @Override
1502    public void onResumeBeforeSuper() {
1503        mPaused = false;
1504    }
1505
1506    @Override
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
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
1547    public void onPauseBeforeSuper() {
1548        mPaused = true;
1549    }
1550
1551    @Override
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
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     */
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
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
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
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
1772    private boolean canTakePicture() {
1773        return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1774    }
1775
1776    @Override
1777    public void autoFocus() {
1778        mFocusStartTime = System.currentTimeMillis();
1779        mCameraDevice.autoFocus(mAutoFocusCallback);
1780        setCameraState(FOCUSING);
1781    }
1782
1783    @Override
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
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
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
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
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)
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
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.
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.
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
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")
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
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)
2023    private void setAutoExposureLockIfSupported() {
2024        if (mAeLockSupported) {
2025            mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
2026        }
2027    }
2028
2029    @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
2030    private void setAutoWhiteBalanceLockIfSupported() {
2031        if (mAwbLockSupported) {
2032            mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
2033        }
2034    }
2035
2036    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
2037    private void setFocusAreasIfSupported() {
2038        if (mFocusAreaSupported) {
2039            mParameters.setFocusAreas(mFocusManager.getFocusAreas());
2040        }
2041    }
2042
2043    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
2044    private void setMeteringAreasIfSupported() {
2045        if (mMeteringAreaSupported) {
2046            // Use the same area for focus and metering.
2047            mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
2048        }
2049    }
2050
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)
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.
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.
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
2224    private boolean isCameraIdle() {
2225        return (mCameraState == IDLE) ||
2226                (mCameraState == PREVIEW_STOPPED) ||
2227                ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2228                        && (mCameraState != SWITCHING_CAMERA));
2229    }
2230
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
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
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
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
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
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
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
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
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
2364    public void onPreviewTextureCopied() {
2365        mHandler.sendEmptyMessage(SWITCH_CAMERA);
2366    }
2367
2368    @Override
2369    public void onCaptureTextureCopied() {
2370    }
2371
2372    @Override
2373    public void onUserInteraction() {
2374        if (!mActivity.isFinishing()) keepScreenOnAwhile();
2375    }
2376
2377    private void resetScreenOn() {
2378        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
2379        mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2380    }
2381
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
2390    public void onRestorePreferencesClicked() {
2391    }
2392
2393    @Override
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
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
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
2424    public void onSizeChanged(int width, int height) {
2425        if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
2426    }
2427
2428    @Override
2429    public void onCountDownFinished() {
2430        mSnapshotOnIdle = false;
2431        mFocusManager.doSnap();
2432    }
2433
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
2441    public boolean needsSwitcher() {
2442        return !mIsImageCaptureIntent;
2443    }
2444
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
2457    public void dismissPopup(boolean topPopupOnly) {
2458        dismissPopup(topPopupOnly, true);
2459    }
2460
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
2475    public void onShowSwitcherPopup() {
2476        if (mPieRenderer != null && mPieRenderer.showsItems()) {
2477            mPieRenderer.hide();
2478        }
2479    }
2480
2481}
2482