Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package com.android.camera;
     19 
     20 import android.app.AlertDialog;
     21 import android.content.DialogInterface;
     22 import android.graphics.Bitmap;
     23 import android.graphics.Color;
     24 import android.graphics.Matrix;
     25 import android.graphics.SurfaceTexture;
     26 import android.graphics.drawable.ColorDrawable;
     27 import android.hardware.Camera;
     28 import android.hardware.Camera.Face;
     29 import android.os.AsyncTask;
     30 import android.util.Log;
     31 import android.view.Gravity;
     32 import android.view.TextureView;
     33 import android.view.View;
     34 import android.view.View.OnClickListener;
     35 import android.view.View.OnLayoutChangeListener;
     36 import android.view.ViewGroup;
     37 import android.view.ViewStub;
     38 import android.widget.FrameLayout.LayoutParams;
     39 import android.widget.ImageView;
     40 import android.widget.PopupWindow;
     41 import android.widget.Toast;
     42 
     43 import com.android.camera.CameraPreference.OnPreferenceChangedListener;
     44 import com.android.camera.FocusOverlayManager.FocusUI;
     45 import com.android.camera.ui.AbstractSettingPopup;
     46 import com.android.camera.ui.CameraControls;
     47 import com.android.camera.ui.CameraRootView;
     48 import com.android.camera.ui.CountDownView;
     49 import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
     50 import com.android.camera.ui.FaceView;
     51 import com.android.camera.ui.FocusIndicator;
     52 import com.android.camera.ui.ModuleSwitcher;
     53 import com.android.camera.ui.PieRenderer;
     54 import com.android.camera.ui.PieRenderer.PieListener;
     55 import com.android.camera.ui.RenderOverlay;
     56 import com.android.camera.ui.ZoomRenderer;
     57 import com.android.camera.util.CameraUtil;
     58 import com.android.camera2.R;
     59 
     60 import java.util.List;
     61 
     62 public class PhotoUI implements PieListener,
     63     PreviewGestures.SingleTapListener,
     64     FocusUI, TextureView.SurfaceTextureListener,
     65     LocationManager.Listener, CameraRootView.MyDisplayListener,
     66     CameraManager.CameraFaceDetectionCallback {
     67 
     68     private static final String TAG = "CAM_UI";
     69     private static final int DOWN_SAMPLE_FACTOR = 4;
     70     private final AnimationManager mAnimationManager;
     71     private CameraActivity mActivity;
     72     private PhotoController mController;
     73     private PreviewGestures mGestures;
     74 
     75     private View mRootView;
     76     private SurfaceTexture mSurfaceTexture;
     77 
     78     private PopupWindow mPopup;
     79     private ShutterButton mShutterButton;
     80     private CountDownView mCountDownView;
     81 
     82     private FaceView mFaceView;
     83     private RenderOverlay mRenderOverlay;
     84     private View mReviewCancelButton;
     85     private View mReviewDoneButton;
     86     private View mReviewRetakeButton;
     87     private ImageView mReviewImage;
     88     private DecodeImageForReview mDecodeTaskForReview = null;
     89 
     90     private View mMenuButton;
     91     private PhotoMenu mMenu;
     92     private ModuleSwitcher mSwitcher;
     93     private CameraControls mCameraControls;
     94     private AlertDialog mLocationDialog;
     95 
     96     // Small indicators which show the camera settings in the viewfinder.
     97     private OnScreenIndicators mOnScreenIndicators;
     98 
     99     private PieRenderer mPieRenderer;
    100     private ZoomRenderer mZoomRenderer;
    101     private Toast mNotSelectableToast;
    102 
    103     private int mZoomMax;
    104     private List<Integer> mZoomRatios;
    105 
    106     private int mPreviewWidth = 0;
    107     private int mPreviewHeight = 0;
    108     private float mSurfaceTextureUncroppedWidth;
    109     private float mSurfaceTextureUncroppedHeight;
    110 
    111     private ImageView mPreviewThumb;
    112     private View mFlashOverlay;
    113 
    114     private SurfaceTextureSizeChangedListener mSurfaceTextureSizeListener;
    115     private TextureView mTextureView;
    116     private Matrix mMatrix = null;
    117     private float mAspectRatio = 4f / 3f;
    118 
    119     public interface SurfaceTextureSizeChangedListener {
    120         public void onSurfaceTextureSizeChanged(int uncroppedWidth, int uncroppedHeight);
    121     }
    122 
    123     private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
    124         @Override
    125         public void onLayoutChange(View v, int left, int top, int right,
    126                 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
    127             int width = right - left;
    128             int height = bottom - top;
    129             if (mPreviewWidth != width || mPreviewHeight != height) {
    130                 mPreviewWidth = width;
    131                 mPreviewHeight = height;
    132                 setTransformMatrix(width, height);
    133                 mController.onScreenSizeChanged((int) mSurfaceTextureUncroppedWidth,
    134                         (int) mSurfaceTextureUncroppedHeight);
    135             }
    136         }
    137     };
    138 
    139     private class DecodeTask extends AsyncTask<Void, Void, Bitmap> {
    140         private final byte [] mData;
    141         private int mOrientation;
    142         private boolean mMirror;
    143 
    144         public DecodeTask(byte[] data, int orientation, boolean mirror) {
    145             mData = data;
    146             mOrientation = orientation;
    147             mMirror = mirror;
    148         }
    149 
    150         @Override
    151         protected Bitmap doInBackground(Void... params) {
    152             // Decode image in background.
    153             Bitmap bitmap = CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR);
    154             if (mOrientation != 0 || mMirror) {
    155                 Matrix m = new Matrix();
    156                 m.preRotate(mOrientation);
    157                 if (mMirror) {
    158                     // Flip horizontally
    159                     m.setScale(-1f, 1f);
    160                 }
    161                 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m,
    162                         false);
    163             }
    164             return bitmap;
    165         }
    166 
    167         @Override
    168         protected void onPostExecute(Bitmap bitmap) {
    169             mPreviewThumb.setImageBitmap(bitmap);
    170             mAnimationManager.startCaptureAnimation(mPreviewThumb);
    171         }
    172     }
    173 
    174     private class DecodeImageForReview extends DecodeTask {
    175         public DecodeImageForReview(byte[] data, int orientation, boolean mirror) {
    176             super(data, orientation, mirror);
    177         }
    178 
    179         @Override
    180         protected void onPostExecute(Bitmap bitmap) {
    181             if (isCancelled()) {
    182                 return;
    183             }
    184             mReviewImage.setImageBitmap(bitmap);
    185             mReviewImage.setVisibility(View.VISIBLE);
    186             mDecodeTaskForReview = null;
    187         }
    188     }
    189 
    190     public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
    191         mActivity = activity;
    192         mController = controller;
    193         mRootView = parent;
    194 
    195         mActivity.getLayoutInflater().inflate(R.layout.photo_module,
    196                 (ViewGroup) mRootView, true);
    197         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
    198         mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
    199         // display the view
    200         mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);
    201         mTextureView.setSurfaceTextureListener(this);
    202         mTextureView.addOnLayoutChangeListener(mLayoutListener);
    203         initIndicators();
    204 
    205         mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
    206         mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher);
    207         mSwitcher.setCurrentIndex(ModuleSwitcher.PHOTO_MODULE_INDEX);
    208         mSwitcher.setSwitchListener(mActivity);
    209         mMenuButton = mRootView.findViewById(R.id.menu);
    210         ViewStub faceViewStub = (ViewStub) mRootView
    211                 .findViewById(R.id.face_view_stub);
    212         if (faceViewStub != null) {
    213             faceViewStub.inflate();
    214             mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
    215             setSurfaceTextureSizeChangedListener(mFaceView);
    216         }
    217         mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls);
    218         mAnimationManager = new AnimationManager();
    219     }
    220 
    221     public void setSurfaceTextureSizeChangedListener(SurfaceTextureSizeChangedListener listener) {
    222         mSurfaceTextureSizeListener = listener;
    223     }
    224 
    225     private void setTransformMatrix(int width, int height) {
    226         mMatrix = mTextureView.getTransform(mMatrix);
    227         float scaleX = 1f, scaleY = 1f;
    228         float scaledTextureWidth, scaledTextureHeight;
    229         if (width > height) {
    230             scaledTextureWidth = Math.max(width,
    231                     (int) (height * mAspectRatio));
    232             scaledTextureHeight = Math.max(height,
    233                     (int)(width / mAspectRatio));
    234         } else {
    235             scaledTextureWidth = Math.max(width,
    236                     (int) (height / mAspectRatio));
    237             scaledTextureHeight = Math.max(height,
    238                     (int) (width * mAspectRatio));
    239         }
    240 
    241         if (mSurfaceTextureUncroppedWidth != scaledTextureWidth ||
    242                 mSurfaceTextureUncroppedHeight != scaledTextureHeight) {
    243             mSurfaceTextureUncroppedWidth = scaledTextureWidth;
    244             mSurfaceTextureUncroppedHeight = scaledTextureHeight;
    245             if (mSurfaceTextureSizeListener != null) {
    246                 mSurfaceTextureSizeListener.onSurfaceTextureSizeChanged(
    247                         (int) mSurfaceTextureUncroppedWidth, (int) mSurfaceTextureUncroppedHeight);
    248             }
    249         }
    250         scaleX = scaledTextureWidth / width;
    251         scaleY = scaledTextureHeight / height;
    252         mMatrix.setScale(scaleX, scaleY, (float) width / 2, (float) height / 2);
    253         mTextureView.setTransform(mMatrix);
    254     }
    255 
    256     @Override
    257     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    258         Log.v(TAG, "SurfaceTexture ready.");
    259         mSurfaceTexture = surface;
    260         mController.onPreviewUIReady();
    261         // Workaround for b/11168275, see b/10981460 for more details
    262         if (mPreviewWidth != 0 && mPreviewHeight != 0) {
    263             // Re-apply transform matrix for new surface texture
    264             setTransformMatrix(mPreviewWidth, mPreviewHeight);
    265         }
    266     }
    267 
    268     @Override
    269     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    270         // Ignored, Camera does all the work for us
    271     }
    272 
    273     @Override
    274     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    275         mSurfaceTexture = null;
    276         mController.onPreviewUIDestroyed();
    277         Log.w(TAG, "SurfaceTexture destroyed");
    278         return true;
    279     }
    280 
    281     @Override
    282     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    283         // Do nothing.
    284     }
    285 
    286     public View getRootView() {
    287         return mRootView;
    288     }
    289 
    290     private void initIndicators() {
    291         mOnScreenIndicators = new OnScreenIndicators(mActivity,
    292                 mRootView.findViewById(R.id.on_screen_indicators));
    293     }
    294 
    295     public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs,
    296             Camera.Parameters params, OnPreferenceChangedListener listener) {
    297         if (mPieRenderer == null) {
    298             mPieRenderer = new PieRenderer(mActivity);
    299             mPieRenderer.setPieListener(this);
    300             mRenderOverlay.addRenderer(mPieRenderer);
    301         }
    302 
    303         if (mMenu == null) {
    304             mMenu = new PhotoMenu(mActivity, this, mPieRenderer);
    305             mMenu.setListener(listener);
    306         }
    307         mMenu.initialize(prefGroup);
    308 
    309         if (mZoomRenderer == null) {
    310             mZoomRenderer = new ZoomRenderer(mActivity);
    311             mRenderOverlay.addRenderer(mZoomRenderer);
    312         }
    313 
    314         if (mGestures == null) {
    315             // this will handle gesture disambiguation and dispatching
    316             mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
    317             mRenderOverlay.setGestures(mGestures);
    318         }
    319         mGestures.setZoomEnabled(params.isZoomSupported());
    320         mGestures.setRenderOverlay(mRenderOverlay);
    321         mRenderOverlay.requestLayout();
    322 
    323         initializeZoom(params);
    324         updateOnScreenIndicators(params, prefGroup, prefs);
    325     }
    326 
    327     public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) {
    328         // Decode jpeg byte array and then animate the jpeg
    329         DecodeTask task = new DecodeTask(jpegData, orientation, mirror);
    330         task.execute();
    331     }
    332 
    333     private void openMenu() {
    334         if (mPieRenderer != null) {
    335             // If autofocus is not finished, cancel autofocus so that the
    336             // subsequent touch can be handled by PreviewGestures
    337             if (mController.getCameraState() == PhotoController.FOCUSING) {
    338                     mController.cancelAutoFocus();
    339             }
    340             mPieRenderer.showInCenter();
    341         }
    342     }
    343 
    344     public void initializeControlByIntent() {
    345         mPreviewThumb = (ImageView) mRootView.findViewById(R.id.preview_thumb);
    346         mPreviewThumb.setOnClickListener(new OnClickListener() {
    347             @Override
    348             public void onClick(View v) {
    349                 mActivity.gotoGallery();
    350             }
    351         });
    352         mMenuButton = mRootView.findViewById(R.id.menu);
    353         mMenuButton.setOnClickListener(new OnClickListener() {
    354             @Override
    355             public void onClick(View v) {
    356                 openMenu();
    357             }
    358         });
    359         if (mController.isImageCaptureIntent()) {
    360             hideSwitcher();
    361             ViewGroup cameraControls = (ViewGroup) mRootView.findViewById(R.id.camera_controls);
    362             mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
    363 
    364             mReviewDoneButton = mRootView.findViewById(R.id.btn_done);
    365             mReviewCancelButton = mRootView.findViewById(R.id.btn_cancel);
    366             mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
    367             mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
    368             mReviewCancelButton.setVisibility(View.VISIBLE);
    369 
    370             mReviewDoneButton.setOnClickListener(new OnClickListener() {
    371                 @Override
    372                 public void onClick(View v) {
    373                     mController.onCaptureDone();
    374                 }
    375             });
    376             mReviewCancelButton.setOnClickListener(new OnClickListener() {
    377                 @Override
    378                 public void onClick(View v) {
    379                     mController.onCaptureCancelled();
    380                 }
    381             });
    382 
    383             mReviewRetakeButton.setOnClickListener(new OnClickListener() {
    384                 @Override
    385                 public void onClick(View v) {
    386                     mController.onCaptureRetake();
    387                 }
    388             });
    389         }
    390     }
    391 
    392     public void hideUI() {
    393         mCameraControls.setVisibility(View.INVISIBLE);
    394         mSwitcher.closePopup();
    395     }
    396 
    397     public void showUI() {
    398         mCameraControls.setVisibility(View.VISIBLE);
    399     }
    400 
    401     public boolean arePreviewControlsVisible() {
    402         return (mCameraControls.getVisibility() == View.VISIBLE);
    403     }
    404 
    405     public void hideSwitcher() {
    406         mSwitcher.closePopup();
    407         mSwitcher.setVisibility(View.INVISIBLE);
    408     }
    409 
    410     public void showSwitcher() {
    411         mSwitcher.setVisibility(View.VISIBLE);
    412     }
    413     // called from onResume but only the first time
    414     public  void initializeFirstTime() {
    415         // Initialize shutter button.
    416         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
    417         mShutterButton.setOnShutterButtonListener(mController);
    418         mShutterButton.setVisibility(View.VISIBLE);
    419     }
    420 
    421     // called from onResume every other time
    422     public void initializeSecondTime(Camera.Parameters params) {
    423         initializeZoom(params);
    424         if (mController.isImageCaptureIntent()) {
    425             hidePostCaptureAlert();
    426         }
    427         if (mMenu != null) {
    428             mMenu.reloadPreferences();
    429         }
    430     }
    431 
    432     public void showLocationDialog() {
    433         mLocationDialog = new AlertDialog.Builder(mActivity)
    434                 .setTitle(R.string.remember_location_title)
    435                 .setMessage(R.string.remember_location_prompt)
    436                 .setPositiveButton(R.string.remember_location_yes,
    437                         new DialogInterface.OnClickListener() {
    438                             @Override
    439                             public void onClick(DialogInterface dialog, int arg1) {
    440                                 mController.enableRecordingLocation(true);
    441                                 mLocationDialog = null;
    442                             }
    443                         })
    444                 .setNegativeButton(R.string.remember_location_no,
    445                         new DialogInterface.OnClickListener() {
    446                             @Override
    447                             public void onClick(DialogInterface dialog, int arg1) {
    448                                 dialog.cancel();
    449                             }
    450                         })
    451                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
    452                     @Override
    453                     public void onCancel(DialogInterface dialog) {
    454                         mController.enableRecordingLocation(false);
    455                         mLocationDialog = null;
    456                     }
    457                 })
    458                 .show();
    459     }
    460 
    461     public void initializeZoom(Camera.Parameters params) {
    462         if ((params == null) || !params.isZoomSupported()
    463                 || (mZoomRenderer == null)) return;
    464         mZoomMax = params.getMaxZoom();
    465         mZoomRatios = params.getZoomRatios();
    466         // Currently we use immediate zoom for fast zooming to get better UX and
    467         // there is no plan to take advantage of the smooth zoom.
    468         if (mZoomRenderer != null) {
    469             mZoomRenderer.setZoomMax(mZoomMax);
    470             mZoomRenderer.setZoom(params.getZoom());
    471             mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom()));
    472             mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
    473         }
    474     }
    475 
    476     @Override
    477     public void showGpsOnScreenIndicator(boolean hasSignal) { }
    478 
    479     @Override
    480     public void hideGpsOnScreenIndicator() { }
    481 
    482     public void overrideSettings(final String ... keyvalues) {
    483         mMenu.overrideSettings(keyvalues);
    484     }
    485 
    486     public void updateOnScreenIndicators(Camera.Parameters params,
    487             PreferenceGroup group, ComboPreferences prefs) {
    488         if (params == null) return;
    489         mOnScreenIndicators.updateSceneOnScreenIndicator(params.getSceneMode());
    490         mOnScreenIndicators.updateExposureOnScreenIndicator(params,
    491                 CameraSettings.readExposure(prefs));
    492         mOnScreenIndicators.updateFlashOnScreenIndicator(params.getFlashMode());
    493         int wbIndex = 2;
    494         ListPreference pref = group.findPreference(CameraSettings.KEY_WHITE_BALANCE);
    495         if (pref != null) {
    496             wbIndex = pref.getCurrentIndex();
    497         }
    498         mOnScreenIndicators.updateWBIndicator(wbIndex);
    499         boolean location = RecordLocationPreference.get(
    500                 prefs, mActivity.getContentResolver());
    501         mOnScreenIndicators.updateLocationIndicator(location);
    502     }
    503 
    504     public void setCameraState(int state) {
    505     }
    506 
    507     public void animateFlash() {
    508         mAnimationManager.startFlashAnimation(mFlashOverlay);
    509     }
    510 
    511     public void enableGestures(boolean enable) {
    512         if (mGestures != null) {
    513             mGestures.setEnabled(enable);
    514         }
    515     }
    516 
    517     // forward from preview gestures to controller
    518     @Override
    519     public void onSingleTapUp(View view, int x, int y) {
    520         mController.onSingleTapUp(view, x, y);
    521     }
    522 
    523     public boolean onBackPressed() {
    524         if (mPieRenderer != null && mPieRenderer.showsItems()) {
    525             mPieRenderer.hide();
    526             return true;
    527         }
    528         // In image capture mode, back button should:
    529         // 1) if there is any popup, dismiss them, 2) otherwise, get out of
    530         // image capture
    531         if (mController.isImageCaptureIntent()) {
    532             mController.onCaptureCancelled();
    533             return true;
    534         } else if (!mController.isCameraIdle()) {
    535             // ignore backs while we're taking a picture
    536             return true;
    537         } else {
    538             return false;
    539         }
    540     }
    541 
    542     public void onPreviewFocusChanged(boolean previewFocused) {
    543         if (previewFocused) {
    544             showUI();
    545         } else {
    546             hideUI();
    547         }
    548         if (mFaceView != null) {
    549             mFaceView.setBlockDraw(!previewFocused);
    550         }
    551         if (mGestures != null) {
    552             mGestures.setEnabled(previewFocused);
    553         }
    554         if (mRenderOverlay != null) {
    555             // this can not happen in capture mode
    556             mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE);
    557         }
    558         if (mPieRenderer != null) {
    559             mPieRenderer.setBlockFocus(!previewFocused);
    560         }
    561         setShowMenu(previewFocused);
    562         if (!previewFocused && mCountDownView != null) mCountDownView.cancelCountDown();
    563     }
    564 
    565     public void showPopup(AbstractSettingPopup popup) {
    566         hideUI();
    567 
    568         if (mPopup == null) {
    569             mPopup = new PopupWindow(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    570             mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
    571             mPopup.setOutsideTouchable(true);
    572             mPopup.setFocusable(true);
    573             mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
    574                 @Override
    575                 public void onDismiss() {
    576                     mPopup = null;
    577                     mMenu.popupDismissed();
    578                     showUI();
    579 
    580                     // Switch back into fullscreen/lights-out mode after popup
    581                     // is dimissed.
    582                     mActivity.setSystemBarsVisibility(false);
    583                 }
    584             });
    585         }
    586         popup.setVisibility(View.VISIBLE);
    587         mPopup.setContentView(popup);
    588         mPopup.showAtLocation(mRootView, Gravity.CENTER, 0, 0);
    589     }
    590 
    591     public void dismissPopup() {
    592         if (mPopup != null && mPopup.isShowing()) {
    593             mPopup.dismiss();
    594         }
    595     }
    596 
    597     public void onShowSwitcherPopup() {
    598         if (mPieRenderer != null && mPieRenderer.showsItems()) {
    599             mPieRenderer.hide();
    600         }
    601     }
    602 
    603     private void setShowMenu(boolean show) {
    604         if (mOnScreenIndicators != null) {
    605             mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
    606         }
    607         if (mMenuButton != null) {
    608             mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE);
    609         }
    610     }
    611 
    612     public boolean collapseCameraControls() {
    613         // TODO: Mode switcher should behave like a popup and should hide itself when there
    614         // is a touch outside of it.
    615         mSwitcher.closePopup();
    616         // Remove all the popups/dialog boxes
    617         boolean ret = false;
    618         if (mPopup != null) {
    619             dismissPopup();
    620             ret = true;
    621         }
    622         onShowSwitcherPopup();
    623         return ret;
    624     }
    625 
    626     protected void showCapturedImageForReview(byte[] jpegData, int orientation, boolean mirror) {
    627         mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror);
    628         mDecodeTaskForReview.execute();
    629         mOnScreenIndicators.setVisibility(View.GONE);
    630         mMenuButton.setVisibility(View.GONE);
    631         CameraUtil.fadeIn(mReviewDoneButton);
    632         mShutterButton.setVisibility(View.INVISIBLE);
    633         CameraUtil.fadeIn(mReviewRetakeButton);
    634         pauseFaceDetection();
    635     }
    636 
    637     protected void hidePostCaptureAlert() {
    638         if (mDecodeTaskForReview != null) {
    639             mDecodeTaskForReview.cancel(true);
    640         }
    641         mReviewImage.setVisibility(View.GONE);
    642         mOnScreenIndicators.setVisibility(View.VISIBLE);
    643         mMenuButton.setVisibility(View.VISIBLE);
    644         CameraUtil.fadeOut(mReviewDoneButton);
    645         mShutterButton.setVisibility(View.VISIBLE);
    646         CameraUtil.fadeOut(mReviewRetakeButton);
    647         resumeFaceDetection();
    648     }
    649 
    650     public void setDisplayOrientation(int orientation) {
    651         if (mFaceView != null) {
    652             mFaceView.setDisplayOrientation(orientation);
    653         }
    654     }
    655 
    656     // shutter button handling
    657 
    658     public boolean isShutterPressed() {
    659         return mShutterButton.isPressed();
    660     }
    661 
    662     /**
    663      * Enables or disables the shutter button.
    664      */
    665     public void enableShutter(boolean enabled) {
    666         if (mShutterButton != null) {
    667             mShutterButton.setEnabled(enabled);
    668         }
    669     }
    670 
    671     public void pressShutterButton() {
    672         if (mShutterButton.isInTouchMode()) {
    673             mShutterButton.requestFocusFromTouch();
    674         } else {
    675             mShutterButton.requestFocus();
    676         }
    677         mShutterButton.setPressed(true);
    678     }
    679 
    680     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
    681         @Override
    682         public void onZoomValueChanged(int index) {
    683             int newZoom = mController.onZoomChanged(index);
    684             if (mZoomRenderer != null) {
    685                 mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
    686             }
    687         }
    688 
    689         @Override
    690         public void onZoomStart() {
    691             if (mPieRenderer != null) {
    692                 mPieRenderer.setBlockFocus(true);
    693             }
    694         }
    695 
    696         @Override
    697         public void onZoomEnd() {
    698             if (mPieRenderer != null) {
    699                 mPieRenderer.setBlockFocus(false);
    700             }
    701         }
    702     }
    703 
    704     @Override
    705     public void onPieOpened(int centerX, int centerY) {
    706         setSwipingEnabled(false);
    707         if (mFaceView != null) {
    708             mFaceView.setBlockDraw(true);
    709         }
    710     }
    711 
    712     @Override
    713     public void onPieClosed() {
    714         setSwipingEnabled(true);
    715         if (mFaceView != null) {
    716             mFaceView.setBlockDraw(false);
    717         }
    718     }
    719 
    720     public void setSwipingEnabled(boolean enable) {
    721         mActivity.setSwipingEnabled(enable);
    722     }
    723 
    724     public SurfaceTexture getSurfaceTexture() {
    725         return mSurfaceTexture;
    726     }
    727 
    728     // Countdown timer
    729 
    730     private void initializeCountDown() {
    731         mActivity.getLayoutInflater().inflate(R.layout.count_down_to_capture,
    732                 (ViewGroup) mRootView, true);
    733         mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
    734         mCountDownView.setCountDownFinishedListener((OnCountDownFinishedListener) mController);
    735     }
    736 
    737     public boolean isCountingDown() {
    738         return mCountDownView != null && mCountDownView.isCountingDown();
    739     }
    740 
    741     public void cancelCountDown() {
    742         if (mCountDownView == null) return;
    743         mCountDownView.cancelCountDown();
    744     }
    745 
    746     public void startCountDown(int sec, boolean playSound) {
    747         if (mCountDownView == null) initializeCountDown();
    748         mCountDownView.startCountDown(sec, playSound);
    749     }
    750 
    751     public void showPreferencesToast() {
    752         if (mNotSelectableToast == null) {
    753             String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
    754             mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
    755         }
    756         mNotSelectableToast.show();
    757     }
    758 
    759     public void onPause() {
    760         cancelCountDown();
    761 
    762         // Clear UI.
    763         collapseCameraControls();
    764         if (mFaceView != null) mFaceView.clear();
    765 
    766         if (mLocationDialog != null && mLocationDialog.isShowing()) {
    767             mLocationDialog.dismiss();
    768         }
    769         mLocationDialog = null;
    770     }
    771 
    772     public void initDisplayChangeListener() {
    773         ((CameraRootView) mRootView).setDisplayChangeListener(this);
    774     }
    775 
    776     public void removeDisplayChangeListener() {
    777         ((CameraRootView) mRootView).removeDisplayChangeListener();
    778     }
    779 
    780     // focus UI implementation
    781 
    782     private FocusIndicator getFocusIndicator() {
    783         return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer;
    784     }
    785 
    786     @Override
    787     public boolean hasFaces() {
    788         return (mFaceView != null && mFaceView.faceExists());
    789     }
    790 
    791     public void clearFaces() {
    792         if (mFaceView != null) mFaceView.clear();
    793     }
    794 
    795     @Override
    796     public void clearFocus() {
    797         FocusIndicator indicator = getFocusIndicator();
    798         if (indicator != null) indicator.clear();
    799     }
    800 
    801     @Override
    802     public void setFocusPosition(int x, int y) {
    803         mPieRenderer.setFocus(x, y);
    804     }
    805 
    806     @Override
    807     public void onFocusStarted() {
    808         getFocusIndicator().showStart();
    809     }
    810 
    811     @Override
    812     public void onFocusSucceeded(boolean timeout) {
    813         getFocusIndicator().showSuccess(timeout);
    814     }
    815 
    816     @Override
    817     public void onFocusFailed(boolean timeout) {
    818         getFocusIndicator().showFail(timeout);
    819     }
    820 
    821     @Override
    822     public void pauseFaceDetection() {
    823         if (mFaceView != null) mFaceView.pause();
    824     }
    825 
    826     @Override
    827     public void resumeFaceDetection() {
    828         if (mFaceView != null) mFaceView.resume();
    829     }
    830 
    831     public void onStartFaceDetection(int orientation, boolean mirror) {
    832         mFaceView.clear();
    833         mFaceView.setVisibility(View.VISIBLE);
    834         mFaceView.setDisplayOrientation(orientation);
    835         mFaceView.setMirror(mirror);
    836         mFaceView.resume();
    837     }
    838 
    839     @Override
    840     public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) {
    841         mFaceView.setFaces(faces);
    842     }
    843 
    844     @Override
    845     public void onDisplayChanged() {
    846         Log.d(TAG, "Device flip detected.");
    847         mCameraControls.checkLayoutFlip();
    848         mController.updateCameraOrientation();
    849     }
    850 
    851 }
    852