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