OSDN Git Service

Merge "Fix second level menu" into gb-ub-photos-bryce
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / PhotoUI.java
1 /*
2  * Copyright (C) 2013 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.hardware.Camera;
21 import android.hardware.Camera.Face;
22 import android.hardware.Camera.FaceDetectionListener;
23 import android.util.Log;
24 import android.view.Gravity;
25 import android.view.MotionEvent;
26 import android.view.SurfaceHolder;
27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import android.view.View.OnLayoutChangeListener;
30 import android.view.ViewGroup;
31 import android.view.ViewStub;
32 import android.widget.FrameLayout;
33 import android.widget.FrameLayout.LayoutParams;
34 import android.widget.Toast;
35
36 import com.android.camera.CameraPreference.OnPreferenceChangedListener;
37 import com.android.camera.FocusOverlayManager.FocusUI;
38 import com.android.camera.ui.AbstractSettingPopup;
39 import com.android.camera.ui.CountDownView;
40 import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
41 import com.android.camera.ui.FaceView;
42 import com.android.camera.ui.FocusIndicator;
43 import com.android.camera.ui.PieRenderer;
44 import com.android.camera.ui.PieRenderer.PieListener;
45 import com.android.camera.ui.RenderOverlay;
46 import com.android.camera.ui.ZoomRenderer;
47 import com.android.gallery3d.R;
48 import com.android.gallery3d.common.ApiHelper;
49
50 import java.util.List;
51
52 public class PhotoUI implements PieListener,
53     SurfaceHolder.Callback,
54     PreviewGestures.SingleTapListener,
55     FocusUI,
56     LocationManager.Listener,
57     FaceDetectionListener,
58     PreviewGestures.SwipeListener {
59
60     private static final String TAG = "CAM_UI";
61
62     private CameraActivity mActivity;
63     private PhotoController mController;
64     private PreviewGestures mGestures;
65
66     private View mRootView;
67     private Object mSurfaceTexture;
68     private volatile SurfaceHolder mSurfaceHolder;
69
70     private AbstractSettingPopup mPopup;
71     private ShutterButton mShutterButton;
72     private CountDownView mCountDownView;
73
74     private FaceView mFaceView;
75     private RenderOverlay mRenderOverlay;
76     private View mReviewCancelButton;
77     private View mReviewDoneButton;
78     private View mReviewRetakeButton;
79
80     private View mMenuButton;
81     private View mBlocker;
82     private PhotoMenu mMenu;
83
84     private OnScreenIndicators mOnScreenIndicators;
85
86     private PieRenderer mPieRenderer;
87     private ZoomRenderer mZoomRenderer;
88     private Toast mNotSelectableToast;
89
90     private int mZoomMax;
91     private List<Integer> mZoomRatios;
92
93     private int mPreviewWidth = 0;
94     private int mPreviewHeight = 0;
95     private View mPreviewThumb;
96
97     private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
98         @Override
99         public void onLayoutChange(View v, int left, int top, int right,
100                 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
101             int width = right - left;
102             int height = bottom - top;
103             // Full-screen screennail
104             int w = width;
105             int h = height;
106             if (Util.getDisplayRotation(mActivity) % 180 != 0) {
107                 w = height;
108                 h = width;
109             }
110             if (mPreviewWidth != w || mPreviewHeight != h) {
111                 mPreviewWidth = w;
112                 mPreviewHeight = h;
113                 mController.onScreenSizeChanged(width, height, w, h);
114             }
115         }
116     };
117
118     public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
119         mActivity = activity;
120         mController = controller;
121         mRootView = parent;
122
123         mActivity.getLayoutInflater().inflate(R.layout.photo_module,
124                 (ViewGroup) mRootView, true);
125         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
126
127         initIndicators();
128         mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
129         mCountDownView.setCountDownFinishedListener((OnCountDownFinishedListener) mController);
130
131         if (ApiHelper.HAS_FACE_DETECTION) {
132             ViewStub faceViewStub = (ViewStub) mRootView
133                     .findViewById(R.id.face_view_stub);
134             if (faceViewStub != null) {
135                 faceViewStub.inflate();
136                 mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
137             }
138         }
139
140     }
141
142     public View getRootView() {
143         return mRootView;
144     }
145
146     private void initIndicators() {
147         mOnScreenIndicators = new OnScreenIndicators(mActivity,
148                 mActivity.findViewById(R.id.on_screen_indicators));
149     }
150
151     public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs,
152             Camera.Parameters params, OnPreferenceChangedListener listener) {
153         if (mPieRenderer == null) {
154             mPieRenderer = new PieRenderer(mActivity);
155             mPieRenderer.setPieListener(this);
156             mRenderOverlay.addRenderer(mPieRenderer);
157         }
158         if (mMenu == null) {
159             mMenu = new PhotoMenu(mActivity, this, mPieRenderer);
160             mMenu.setListener(listener);
161         }
162         mMenu.initialize(prefGroup);
163
164         if (mZoomRenderer == null) {
165             mZoomRenderer = new ZoomRenderer(mActivity);
166             mRenderOverlay.addRenderer(mZoomRenderer);
167         }
168         if (mGestures == null) {
169             // this will handle gesture disambiguation and dispatching
170             mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer,
171                     this);
172         }
173         mGestures.reset();
174         mGestures.setRenderOverlay(mRenderOverlay);
175         mGestures.addTouchReceiver(mMenuButton);
176         mGestures.addUnclickableArea(mBlocker);
177         enablePreviewThumb(false);
178         // make sure to add touch targets for image capture
179         if (mController.isImageCaptureIntent()) {
180             if (mReviewCancelButton != null) {
181                 mGestures.addTouchReceiver(mReviewCancelButton);
182             }
183             if (mReviewDoneButton != null) {
184                 mGestures.addTouchReceiver(mReviewDoneButton);
185             }
186         }
187         mRenderOverlay.requestLayout();
188
189         initializeZoom(params);
190         updateOnScreenIndicators(params, prefGroup, prefs);
191     }
192
193     private void openMenu() {
194         if (mPieRenderer != null) {
195             // If autofocus is not finished, cancel autofocus so that the
196             // subsequent touch can be handled by PreviewGestures
197             if (mController.getCameraState() == PhotoController.FOCUSING) {
198                     mController.cancelAutoFocus();
199             }
200             mPieRenderer.showInCenter();
201         }
202     }
203
204     public void initializeControlByIntent() {
205         mBlocker = mActivity.findViewById(R.id.blocker);
206         mPreviewThumb = mActivity.findViewById(R.id.preview_thumb);
207         mPreviewThumb.setOnClickListener(new OnClickListener() {
208             @Override
209             public void onClick(View v) {
210                 mActivity.gotoGallery();
211             }
212         });
213         mMenuButton = mActivity.findViewById(R.id.menu);
214         mMenuButton.setOnClickListener(new OnClickListener() {
215             @Override
216             public void onClick(View v) {
217                 openMenu();
218             }
219         });
220         if (mController.isImageCaptureIntent()) {
221             mActivity.hideSwitcher();
222             ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
223             mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
224
225             mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
226             mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
227             mReviewRetakeButton = mActivity.findViewById(R.id.btn_retake);
228             mReviewCancelButton.setVisibility(View.VISIBLE);
229
230             mReviewDoneButton.setOnClickListener(new OnClickListener() {
231                 @Override
232                 public void onClick(View v) {
233                     mController.onCaptureDone();
234                 }
235             });
236             mReviewCancelButton.setOnClickListener(new OnClickListener() {
237                 @Override
238                 public void onClick(View v) {
239                     mController.onCaptureCancelled();
240                 }
241             });
242
243             mReviewRetakeButton.setOnClickListener(new OnClickListener() {
244                 @Override
245                 public void onClick(View v) {
246                     mController.onCaptureRetake();
247                 }
248             });
249         }
250     }
251
252     // called from onResume but only the first time
253     public  void initializeFirstTime() {
254         // Initialize shutter button.
255         mShutterButton = mActivity.getShutterButton();
256         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
257         mShutterButton.setOnShutterButtonListener(mController);
258         mShutterButton.setVisibility(View.VISIBLE);
259         mRootView.addOnLayoutChangeListener(mLayoutListener);
260     }
261
262     // called from onResume every other time
263     public void initializeSecondTime(Camera.Parameters params) {
264         initializeZoom(params);
265         if (mController.isImageCaptureIntent()) {
266             hidePostCaptureAlert();
267         }
268         if (mMenu != null) {
269             mMenu.reloadPreferences();
270         }
271         mRootView.addOnLayoutChangeListener(mLayoutListener);
272     }
273
274     public void initializeZoom(Camera.Parameters params) {
275         if ((params == null) || !params.isZoomSupported()
276                 || (mZoomRenderer == null)) return;
277         mZoomMax = params.getMaxZoom();
278         mZoomRatios = params.getZoomRatios();
279         // Currently we use immediate zoom for fast zooming to get better UX and
280         // there is no plan to take advantage of the smooth zoom.
281         if (mZoomRenderer != null) {
282             mZoomRenderer.setZoomMax(mZoomMax);
283             mZoomRenderer.setZoom(params.getZoom());
284             mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom()));
285             mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
286         }
287     }
288
289     public void enableGestures(boolean enable) {
290         if (mGestures != null) {
291             mGestures.setEnabled(enable);
292         }
293     }
294
295     @Override
296     public void showGpsOnScreenIndicator(boolean hasSignal) { }
297
298     @Override
299     public void hideGpsOnScreenIndicator() { }
300
301     public void overrideSettings(final String ... keyvalues) {
302         mMenu.overrideSettings(keyvalues);
303     }
304
305     public void updateOnScreenIndicators(Camera.Parameters params,
306             PreferenceGroup group, ComboPreferences prefs) {
307         if (params == null) return;
308         mOnScreenIndicators.updateSceneOnScreenIndicator(params.getSceneMode());
309         mOnScreenIndicators.updateExposureOnScreenIndicator(params,
310                 CameraSettings.readExposure(prefs));
311         mOnScreenIndicators.updateFlashOnScreenIndicator(params.getFlashMode());
312         int wbIndex = 2;
313         ListPreference pref = group.findPreference(CameraSettings.KEY_WHITE_BALANCE);
314         if (pref != null) {
315             wbIndex = pref.getCurrentIndex();
316         }
317         mOnScreenIndicators.updateWBIndicator(wbIndex);
318         boolean location = RecordLocationPreference.get(
319                 prefs, mActivity.getContentResolver());
320         mOnScreenIndicators.updateLocationIndicator(location);
321     }
322
323     public void setCameraState(int state) {
324     }
325
326     public boolean dispatchTouchEvent(MotionEvent m) {
327         if (mGestures != null && mRenderOverlay != null) {
328             return mGestures.dispatchTouch(m);
329         }
330         return false;
331     }
332
333     public boolean onBackPressed() {
334         if (mPieRenderer != null && mPieRenderer.showsItems()) {
335             mPieRenderer.hide();
336             return true;
337         }
338         // In image capture mode, back button should:
339         // 1) if there is any popup, dismiss them, 2) otherwise, get out of
340         // image capture
341         if (mController.isImageCaptureIntent()) {
342             if (!removeTopLevelPopup()) {
343                 // no popup to dismiss, cancel image capture
344                 mController.onCaptureCancelled();
345             }
346             return true;
347         } else if (!mController.isCameraIdle()) {
348             // ignore backs while we're taking a picture
349             return true;
350         } else {
351             return removeTopLevelPopup();
352         }
353     }
354
355     public void onFullScreenChanged(boolean full) {
356         if (mFaceView != null) {
357             mFaceView.setBlockDraw(!full);
358         }
359         if (mPopup != null) {
360             dismissPopup(full);
361         }
362         if (mGestures != null) {
363             mGestures.setEnabled(full);
364         }
365         if (mRenderOverlay != null) {
366             // this can not happen in capture mode
367             mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
368         }
369         if (mPieRenderer != null) {
370             mPieRenderer.setBlockFocus(!full);
371         }
372         setShowMenu(full);
373         if (mBlocker != null) {
374             mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
375         }
376         if (!full && mCountDownView != null) mCountDownView.cancelCountDown();
377     }
378
379     public void enablePreviewThumb(boolean enabled) {
380         if (enabled) {
381             mGestures.addTouchReceiver(mPreviewThumb);
382             mPreviewThumb.setVisibility(View.VISIBLE);
383         } else {
384             mGestures.removeTouchReceiver(mPreviewThumb);
385             mPreviewThumb.setVisibility(View.GONE);
386         }
387     }
388
389     public boolean removeTopLevelPopup() {
390         // Remove the top level popup or dialog box and return true if there's any
391         if (mPopup != null) {
392             dismissPopup();
393             return true;
394         }
395         return false;
396     }
397
398     public void showPopup(AbstractSettingPopup popup) {
399         mActivity.hideUI();
400         mBlocker.setVisibility(View.INVISIBLE);
401         setShowMenu(false);
402         mPopup = popup;
403         mPopup.setVisibility(View.VISIBLE);
404         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
405                 LayoutParams.WRAP_CONTENT);
406         lp.gravity = Gravity.CENTER;
407         ((FrameLayout) mRootView).addView(mPopup, lp);
408         mGestures.addTouchReceiver(mPopup);
409     }
410
411     public void dismissPopup() {
412         dismissPopup(true);
413     }
414
415     private void dismissPopup(boolean fullScreen) {
416         if (fullScreen) {
417             mActivity.showUI();
418             mBlocker.setVisibility(View.VISIBLE);
419         }
420         setShowMenu(fullScreen);
421         if (mPopup != null) {
422             mGestures.removeTouchReceiver(mPopup);
423             ((FrameLayout) mRootView).removeView(mPopup);
424             mPopup = null;
425         }
426         mMenu.popupDismissed();
427     }
428
429     public void onShowSwitcherPopup() {
430         if (mPieRenderer != null && mPieRenderer.showsItems()) {
431             mPieRenderer.hide();
432         }
433     }
434
435     private void setShowMenu(boolean show) {
436         if (mOnScreenIndicators != null) {
437             mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
438         }
439         if (mMenuButton != null) {
440             mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE);
441         }
442     }
443
444     public boolean collapseCameraControls() {
445         // Remove all the popups/dialog boxes
446         boolean ret = false;
447         if (mPopup != null) {
448             dismissPopup();
449             ret = true;
450         }
451         return ret;
452     }
453
454     protected void showPostCaptureAlert() {
455         mOnScreenIndicators.setVisibility(View.GONE);
456         mMenuButton.setVisibility(View.GONE);
457         Util.fadeIn(mReviewDoneButton);
458         mShutterButton.setVisibility(View.INVISIBLE);
459         Util.fadeIn(mReviewRetakeButton);
460     }
461
462     protected void hidePostCaptureAlert() {
463         mOnScreenIndicators.setVisibility(View.VISIBLE);
464         mMenuButton.setVisibility(View.VISIBLE);
465         Util.fadeOut(mReviewDoneButton);
466         mShutterButton.setVisibility(View.VISIBLE);
467         Util.fadeOut(mReviewRetakeButton);
468     }
469
470     public void setDisplayOrientation(int orientation) {
471         if (mFaceView != null) {
472             mFaceView.setDisplayOrientation(orientation);
473         }
474     }
475
476     // shutter button handling
477
478     public boolean isShutterPressed() {
479         return mShutterButton.isPressed();
480     }
481
482     // focus handling
483
484
485     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
486         @Override
487         public void onZoomValueChanged(int index) {
488             int newZoom = mController.onZoomChanged(index);
489             if (mZoomRenderer != null) {
490                 mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
491             }
492         }
493
494         @Override
495         public void onZoomStart() {
496             if (mPieRenderer != null) {
497                 mPieRenderer.setBlockFocus(true);
498             }
499         }
500
501         @Override
502         public void onZoomEnd() {
503             if (mPieRenderer != null) {
504                 mPieRenderer.setBlockFocus(false);
505             }
506         }
507     }
508
509     @Override
510     public void onPieOpened(int centerX, int centerY) {
511         dismissPopup();
512         mActivity.cancelActivityTouchHandling();
513         mActivity.setSwipingEnabled(false);
514         if (mFaceView != null) {
515             mFaceView.setBlockDraw(true);
516         }
517     }
518
519     @Override
520     public void onPieClosed() {
521         mActivity.setSwipingEnabled(true);
522         if (mFaceView != null) {
523             mFaceView.setBlockDraw(false);
524         }
525     }
526
527     // Surface Listener
528
529     @Override
530     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
531         Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
532                 + height);
533     }
534
535     @Override
536     public void surfaceCreated(SurfaceHolder holder) {
537         Log.v(TAG, "surfaceCreated: " + holder);
538         mSurfaceHolder = holder;
539         mController.onSurfaceCreated(holder);
540     }
541
542     @Override
543     public void surfaceDestroyed(SurfaceHolder holder) {
544         Log.v(TAG, "surfaceDestroyed: " + holder);
545         mSurfaceHolder = null;
546         mController.stopPreview();
547     }
548
549     public Object getSurfaceTexture() {
550         return mSurfaceTexture;
551     }
552
553     public void setSurfaceTexture(Object st) {
554         mSurfaceTexture = st;
555     }
556
557     public SurfaceHolder getSurfaceHolder() {
558         return mSurfaceHolder;
559     }
560
561     public boolean isCountingDown() {
562         return mCountDownView.isCountingDown();
563     }
564
565     public void cancelCountDown() {
566         mCountDownView.cancelCountDown();
567     }
568
569     public void startCountDown(int sec, boolean playSound) {
570         mCountDownView.startCountDown(sec, playSound);
571     }
572
573     public void showPreferencesToast() {
574         if (mNotSelectableToast == null) {
575             String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
576             mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
577         }
578         mNotSelectableToast.show();
579     }
580
581     public void onPause() {
582         mCountDownView.cancelCountDown();
583         // Close the camera now because other activities may need to use it.
584         mSurfaceTexture = null;
585
586         // Clear UI.
587         collapseCameraControls();
588         if (mFaceView != null) mFaceView.clear();
589
590
591         mRootView.removeOnLayoutChangeListener(mLayoutListener);
592         mPreviewWidth = 0;
593         mPreviewHeight = 0;
594     }
595
596     public void enableShutter(boolean enabled) {
597         if (mShutterButton != null) {
598             mShutterButton.setEnabled(enabled);
599         }
600     }
601
602     public void pressShutterButton() {
603         if (mShutterButton.isInTouchMode()) {
604             mShutterButton.requestFocusFromTouch();
605         } else {
606             mShutterButton.requestFocus();
607         }
608         mShutterButton.setPressed(true);
609     }
610
611     // forward from preview gestures to controller
612     @Override
613     public void onSingleTapUp(View view, int x, int y) {
614         mController.onSingleTapUp(view, x, y);
615     }
616
617     // focus UI implementation
618
619     private FocusIndicator getFocusIndicator() {
620         return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer;
621     }
622
623     @Override
624     public boolean hasFaces() {
625         return (mFaceView != null && mFaceView.faceExists());
626     }
627
628     public void clearFaces() {
629         if (mFaceView != null) mFaceView.clear();
630     }
631
632     @Override
633     public void clearFocus() {
634         FocusIndicator indicator = getFocusIndicator();
635         if (indicator != null) indicator.clear();
636     }
637
638     @Override
639     public void setFocusPosition(int x, int y) {
640         mPieRenderer.setFocus(x, y);
641     }
642
643     @Override
644     public void onFocusStarted() {
645         getFocusIndicator().showStart();
646     }
647
648     @Override
649     public void onFocusSucceeded(boolean timeout) {
650         getFocusIndicator().showSuccess(timeout);
651     }
652
653     @Override
654     public void onFocusFailed(boolean timeout) {
655         getFocusIndicator().showFail(timeout);
656     }
657
658     @Override
659     public void pauseFaceDetection() {
660         if (mFaceView != null) mFaceView.pause();
661     }
662
663     @Override
664     public void resumeFaceDetection() {
665         if (mFaceView != null) mFaceView.resume();
666     }
667
668     public void onStartFaceDetection(int orientation, boolean mirror) {
669         mFaceView.clear();
670         mFaceView.setVisibility(View.VISIBLE);
671         mFaceView.setDisplayOrientation(orientation);
672         mFaceView.setMirror(mirror);
673         mFaceView.resume();
674     }
675
676     @Override
677     public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
678         mFaceView.setFaces(faces);
679     }
680
681     @Override
682     public void onSwipe(int direction) {
683         if (direction == PreviewGestures.DIR_UP) {
684             openMenu();
685         }
686     }
687
688 }