• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.camera;
18 
19 import android.graphics.Bitmap;
20 import android.hardware.Camera.Parameters;
21 import android.util.Log;
22 import android.view.Gravity;
23 import android.view.MotionEvent;
24 import android.view.SurfaceHolder;
25 import android.view.View;
26 import android.view.View.OnClickListener;
27 import android.view.ViewGroup;
28 import android.widget.FrameLayout;
29 import android.widget.FrameLayout.LayoutParams;
30 import android.widget.ImageView;
31 import android.widget.LinearLayout;
32 import android.widget.TextView;
33 
34 import com.android.camera.CameraPreference.OnPreferenceChangedListener;
35 import com.android.camera.ui.AbstractSettingPopup;
36 import com.android.camera.ui.PieRenderer;
37 import com.android.camera.ui.PreviewSurfaceView;
38 import com.android.camera.ui.RenderOverlay;
39 import com.android.camera.ui.RotateLayout;
40 import com.android.camera.ui.ZoomRenderer;
41 import com.android.gallery3d.R;
42 import com.android.gallery3d.common.ApiHelper;
43 
44 import java.util.List;
45 
46 public class VideoUI implements SurfaceHolder.Callback, PieRenderer.PieListener,
47         PreviewGestures.SingleTapListener,
48         PreviewGestures.SwipeListener {
49     private final static String TAG = "CAM_VideoUI";
50     // module fields
51     private CameraActivity mActivity;
52     private View mRootView;
53     private PreviewFrameLayout mPreviewFrameLayout;
54     private boolean mSurfaceViewReady;
55     private PreviewSurfaceView mPreviewSurfaceView;
56     // An review image having same size as preview. It is displayed when
57     // recording is stopped in capture intent.
58     private ImageView mReviewImage;
59     private View mReviewCancelButton;
60     private View mReviewDoneButton;
61     private View mReviewPlayButton;
62     private ShutterButton mShutterButton;
63     private TextView mRecordingTimeView;
64     private LinearLayout mLabelsLinearLayout;
65     private View mTimeLapseLabel;
66     private RenderOverlay mRenderOverlay;
67     private PieRenderer mPieRenderer;
68     private VideoMenu mVideoMenu;
69     private AbstractSettingPopup mPopup;
70     private ZoomRenderer mZoomRenderer;
71     private PreviewGestures mGestures;
72     private View mMenu;
73     private View mBlocker;
74     private OnScreenIndicators mOnScreenIndicators;
75     private RotateLayout mRecordingTimeRect;
76     private VideoController mController;
77     private int mZoomMax;
78     private List<Integer> mZoomRatios;
79     private View mPreviewThumb;
80 
VideoUI(CameraActivity activity, VideoController controller, View parent)81     public VideoUI(CameraActivity activity, VideoController controller, View parent) {
82         mActivity = activity;
83         mController = controller;
84         mRootView = parent;
85         mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView, true);
86         mPreviewSurfaceView = (PreviewSurfaceView) mRootView
87                 .findViewById(R.id.preview_surface_view);
88         initializeMiscControls();
89         initializeControlByIntent();
90         initializeOverlay();
91     }
92 
initializeControlByIntent()93     private void initializeControlByIntent() {
94         mBlocker = mActivity.findViewById(R.id.blocker);
95         mMenu = mActivity.findViewById(R.id.menu);
96         mMenu.setOnClickListener(new OnClickListener() {
97             @Override
98             public void onClick(View v) {
99                 if (mPieRenderer != null) {
100                     mPieRenderer.showInCenter();
101                 }
102             }
103         });
104         mOnScreenIndicators = new OnScreenIndicators(mActivity,
105                 mActivity.findViewById(R.id.on_screen_indicators));
106         mOnScreenIndicators.resetToDefault();
107         if (mController.isVideoCaptureIntent()) {
108             mActivity.hideSwitcher();
109             ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
110             mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
111             // Cannot use RotateImageView for "done" and "cancel" button because
112             // the tablet layout uses RotateLayout, which cannot be cast to
113             // RotateImageView.
114             mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
115             mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
116             mReviewPlayButton = mActivity.findViewById(R.id.btn_play);
117             mReviewCancelButton.setVisibility(View.VISIBLE);
118             mReviewDoneButton.setOnClickListener(new OnClickListener() {
119                 @Override
120                 public void onClick(View v) {
121                     mController.onReviewDoneClicked(v);
122                 }
123             });
124             mReviewCancelButton.setOnClickListener(new OnClickListener() {
125                 @Override
126                 public void onClick(View v) {
127                     mController.onReviewCancelClicked(v);
128                 }
129             });
130             mReviewPlayButton.setOnClickListener(new OnClickListener() {
131                 @Override
132                 public void onClick(View v) {
133                     mController.onReviewPlayClicked(v);
134                 }
135             });
136         }
137     }
138 
collapseCameraControls()139     public boolean collapseCameraControls() {
140         boolean ret = false;
141         if (mPopup != null) {
142             dismissPopup(false);
143             ret = true;
144         }
145         return ret;
146     }
147 
removeTopLevelPopup()148     public boolean removeTopLevelPopup() {
149         if (mPopup != null) {
150             dismissPopup(true);
151             return true;
152         }
153         return false;
154     }
155 
enableCameraControls(boolean enable)156     public void enableCameraControls(boolean enable) {
157         if (mGestures != null) {
158             mGestures.setZoomOnly(!enable);
159         }
160         if (mPieRenderer != null && mPieRenderer.showsItems()) {
161             mPieRenderer.hide();
162         }
163     }
164 
overrideSettings(final String... keyvalues)165     public void overrideSettings(final String... keyvalues) {
166         mVideoMenu.overrideSettings(keyvalues);
167     }
168 
getPreview()169     public View getPreview() {
170         return mPreviewFrameLayout;
171     }
172 
setOrientationIndicator(int orientation, boolean animation)173     public void setOrientationIndicator(int orientation, boolean animation) {
174         if (mGestures != null) {
175             mGestures.setOrientation(orientation);
176         }
177         // We change the orientation of the linearlayout only for phone UI
178         // because when in portrait the width is not enough.
179         if (mLabelsLinearLayout != null) {
180             if (((orientation / 90) & 1) == 0) {
181                 mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
182             } else {
183                 mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
184             }
185         }
186         mRecordingTimeRect.setOrientation(0, animation);
187     }
188 
getSurfaceHolder()189     public SurfaceHolder getSurfaceHolder() {
190         return mPreviewSurfaceView.getHolder();
191     }
192 
hideSurfaceView()193     public void hideSurfaceView() {
194         mPreviewSurfaceView.setVisibility(View.GONE);
195     }
196 
showSurfaceView()197     public void showSurfaceView() {
198         mPreviewSurfaceView.setVisibility(View.VISIBLE);
199     }
200 
initializeOverlay()201     private void initializeOverlay() {
202         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
203         if (mPieRenderer == null) {
204             mPieRenderer = new PieRenderer(mActivity);
205             mVideoMenu = new VideoMenu(mActivity, this, mPieRenderer);
206             mPieRenderer.setPieListener(this);
207         }
208         mRenderOverlay.addRenderer(mPieRenderer);
209         if (mZoomRenderer == null) {
210             mZoomRenderer = new ZoomRenderer(mActivity);
211         }
212         mRenderOverlay.addRenderer(mZoomRenderer);
213         if (mGestures == null) {
214             mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer, this);
215         }
216         mGestures.setRenderOverlay(mRenderOverlay);
217         mGestures.reset();
218         mGestures.addTouchReceiver(mMenu);
219         mGestures.addUnclickableArea(mBlocker);
220         if (mController.isVideoCaptureIntent()) {
221             if (mReviewCancelButton != null) {
222                 mGestures.addTouchReceiver(mReviewCancelButton);
223             }
224             if (mReviewDoneButton != null) {
225                 mGestures.addTouchReceiver(mReviewDoneButton);
226             }
227             if (mReviewPlayButton != null) {
228                 mGestures.addTouchReceiver(mReviewPlayButton);
229             }
230         }
231 
232         mPreviewThumb = mActivity.findViewById(R.id.preview_thumb);
233         mPreviewThumb.setOnClickListener(new OnClickListener() {
234             @Override
235             public void onClick(View v) {
236                 mActivity.gotoGallery();
237             }
238         });
239     }
240 
setPrefChangedListener(OnPreferenceChangedListener listener)241     public void setPrefChangedListener(OnPreferenceChangedListener listener) {
242         mVideoMenu.setListener(listener);
243     }
244 
initializeMiscControls()245     private void initializeMiscControls() {
246         mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame);
247         mPreviewFrameLayout.setOnLayoutChangeListener(mActivity);
248         mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
249         mShutterButton = mActivity.getShutterButton();
250         mShutterButton.setImageResource(R.drawable.btn_new_shutter_video);
251         mShutterButton.setOnShutterButtonListener(mController);
252         mShutterButton.setVisibility(View.VISIBLE);
253         mShutterButton.requestFocus();
254         mShutterButton.enableTouch(true);
255         mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
256         mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
257         mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label);
258         // The R.id.labels can only be found in phone layout.
259         // That is, mLabelsLinearLayout should be null in tablet layout.
260         mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
261     }
262 
updateOnScreenIndicators(Parameters param, ComboPreferences prefs)263     public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) {
264       mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode());
265       boolean location = RecordLocationPreference.get(
266               prefs, mActivity.getContentResolver());
267       mOnScreenIndicators.updateLocationIndicator(location);
268 
269     }
270 
setAspectRatio(double ratio)271     public void setAspectRatio(double ratio) {
272         mPreviewFrameLayout.setAspectRatio(ratio);
273     }
274 
showTimeLapseUI(boolean enable)275     public void showTimeLapseUI(boolean enable) {
276         if (mTimeLapseLabel != null) {
277             mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
278         }
279     }
280 
openMenu()281     private void openMenu() {
282         if (mPieRenderer != null) {
283             mPieRenderer.showInCenter();
284         }
285     }
286 
showPopup(AbstractSettingPopup popup)287     public void showPopup(AbstractSettingPopup popup) {
288         mActivity.hideUI();
289         mBlocker.setVisibility(View.INVISIBLE);
290         setShowMenu(false);
291         mPopup = popup;
292         mPopup.setVisibility(View.VISIBLE);
293         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
294                 LayoutParams.WRAP_CONTENT);
295         lp.gravity = Gravity.CENTER;
296         ((FrameLayout) mRootView).addView(mPopup, lp);
297         mGestures.addTouchReceiver(mPopup);
298     }
299 
dismissPopup(boolean topLevelOnly)300     public void dismissPopup(boolean topLevelOnly) {
301         dismissPopup(topLevelOnly, true);
302     }
303 
dismissPopup(boolean topLevelPopupOnly, boolean fullScreen)304     public void dismissPopup(boolean topLevelPopupOnly, boolean fullScreen) {
305         // In review mode, we do not want to bring up the camera UI
306         if (mController.isInReviewMode()) return;
307 
308         if (fullScreen) {
309             mActivity.showUI();
310             mBlocker.setVisibility(View.VISIBLE);
311         }
312         setShowMenu(fullScreen);
313         if (mPopup != null) {
314             mGestures.removeTouchReceiver(mPopup);
315             ((FrameLayout) mRootView).removeView(mPopup);
316             mPopup = null;
317         }
318         mVideoMenu.popupDismissed(topLevelPopupOnly);
319     }
320 
onShowSwitcherPopup()321     public void onShowSwitcherPopup() {
322         hidePieRenderer();
323     }
324 
hidePieRenderer()325     public boolean hidePieRenderer() {
326         if (mPieRenderer != null && mPieRenderer.showsItems()) {
327             mPieRenderer.hide();
328             return true;
329         }
330         return false;
331     }
332 
333     // disable preview gestures after shutter is pressed
setShutterPressed(boolean pressed)334     public void setShutterPressed(boolean pressed) {
335         if (mGestures == null) return;
336         mGestures.setEnabled(!pressed);
337     }
338 
enableShutter(boolean enable)339     public void enableShutter(boolean enable) {
340         if (mShutterButton != null) {
341             mShutterButton.setEnabled(enable);
342         }
343     }
344 
345     // PieListener
346     @Override
onPieOpened(int centerX, int centerY)347     public void onPieOpened(int centerX, int centerY) {
348         dismissPopup(false, true);
349         mActivity.cancelActivityTouchHandling();
350         mActivity.setSwipingEnabled(false);
351     }
352 
353     @Override
onPieClosed()354     public void onPieClosed() {
355         mActivity.setSwipingEnabled(true);
356     }
357 
showPreviewBorder(boolean enable)358     public void showPreviewBorder(boolean enable) {
359         mPreviewFrameLayout.showBorder(enable);
360     }
361 
362     // SingleTapListener
363     // Preview area is touched. Take a picture.
364     @Override
onSingleTapUp(View view, int x, int y)365     public void onSingleTapUp(View view, int x, int y) {
366         mController.onSingleTapUp(view, x, y);
367     }
368 
369     // SurfaceView callback
370     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)371     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
372         Log.v(TAG, "Surface changed. width=" + width + ". height=" + height);
373     }
374 
375     @Override
surfaceCreated(SurfaceHolder holder)376     public void surfaceCreated(SurfaceHolder holder) {
377         Log.v(TAG, "Surface created");
378         mSurfaceViewReady = true;
379     }
380 
381     @Override
surfaceDestroyed(SurfaceHolder holder)382     public void surfaceDestroyed(SurfaceHolder holder) {
383         Log.v(TAG, "Surface destroyed");
384         mSurfaceViewReady = false;
385         mController.stopPreview();
386     }
387 
isSurfaceViewReady()388     public boolean isSurfaceViewReady() {
389         return mSurfaceViewReady;
390     }
391 
showRecordingUI(boolean recording, boolean zoomSupported)392     public void showRecordingUI(boolean recording, boolean zoomSupported) {
393         mMenu.setVisibility(recording ? View.GONE : View.VISIBLE);
394         mOnScreenIndicators.setVisibility(recording ? View.GONE : View.VISIBLE);
395         if (recording) {
396             mShutterButton.setImageResource(R.drawable.btn_shutter_video_recording);
397             mActivity.hideSwitcher();
398             mRecordingTimeView.setText("");
399             mRecordingTimeView.setVisibility(View.VISIBLE);
400             // The camera is not allowed to be accessed in older api levels during
401             // recording. It is therefore necessary to hide the zoom UI on older
402             // platforms.
403             // See the documentation of android.media.MediaRecorder.start() for
404             // further explanation.
405             if (!ApiHelper.HAS_ZOOM_WHEN_RECORDING && zoomSupported) {
406                 // TODO: disable zoom UI here.
407             }
408         } else {
409             mShutterButton.setImageResource(R.drawable.btn_new_shutter_video);
410             mActivity.showSwitcher();
411             mRecordingTimeView.setVisibility(View.GONE);
412             if (!ApiHelper.HAS_ZOOM_WHEN_RECORDING && zoomSupported) {
413                 // TODO: enable zoom UI here.
414             }
415         }
416     }
417 
showReviewImage(Bitmap bitmap)418     public void showReviewImage(Bitmap bitmap) {
419         mReviewImage.setImageBitmap(bitmap);
420         mReviewImage.setVisibility(View.VISIBLE);
421     }
422 
showReviewControls()423     public void showReviewControls() {
424         Util.fadeOut(mShutterButton);
425         Util.fadeIn(mReviewDoneButton);
426         Util.fadeIn(mReviewPlayButton);
427         mReviewImage.setVisibility(View.VISIBLE);
428         mMenu.setVisibility(View.GONE);
429         mOnScreenIndicators.setVisibility(View.GONE);
430     }
431 
hideReviewUI()432     public void hideReviewUI() {
433         mReviewImage.setVisibility(View.GONE);
434         mShutterButton.setEnabled(true);
435         mMenu.setVisibility(View.VISIBLE);
436         mOnScreenIndicators.setVisibility(View.VISIBLE);
437         Util.fadeOut(mReviewDoneButton);
438         Util.fadeOut(mReviewPlayButton);
439         Util.fadeIn(mShutterButton);
440     }
441 
setShowMenu(boolean show)442     private void setShowMenu(boolean show) {
443         if (mOnScreenIndicators != null) {
444             mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
445         }
446         if (mMenu != null) {
447             mMenu.setVisibility(show ? View.VISIBLE : View.GONE);
448         }
449     }
450 
onFullScreenChanged(boolean full)451     public void onFullScreenChanged(boolean full) {
452         if (mGestures != null) {
453             mGestures.setEnabled(full);
454         }
455         if (mPopup != null) {
456             dismissPopup(false, full);
457         }
458         if (mRenderOverlay != null) {
459             // this can not happen in capture mode
460             mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
461         }
462         setShowMenu(full);
463         if (mBlocker != null) {
464             // this can not happen in capture mode
465             mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
466         }
467     }
468 
initializePopup(PreferenceGroup pref)469     public void initializePopup(PreferenceGroup pref) {
470         mVideoMenu.initialize(pref);
471     }
472 
initializeZoom(Parameters param)473     public void initializeZoom(Parameters param) {
474         if (param == null || !param.isZoomSupported()) return;
475         mZoomMax = param.getMaxZoom();
476         mZoomRatios = param.getZoomRatios();
477         // Currently we use immediate zoom for fast zooming to get better UX and
478         // there is no plan to take advantage of the smooth zoom.
479         mZoomRenderer.setZoomMax(mZoomMax);
480         mZoomRenderer.setZoom(param.getZoom());
481         mZoomRenderer.setZoomValue(mZoomRatios.get(param.getZoom()));
482         mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
483     }
484 
clickShutter()485     public void clickShutter() {
486         mShutterButton.performClick();
487     }
488 
pressShutter(boolean pressed)489     public void pressShutter(boolean pressed) {
490         mShutterButton.setPressed(pressed);
491     }
492 
dispatchTouchEvent(MotionEvent m)493     public boolean dispatchTouchEvent(MotionEvent m) {
494         if (mGestures != null && mRenderOverlay != null) {
495             return mGestures.dispatchTouch(m);
496         }
497         return false;
498     }
499 
setRecordingTime(String text)500     public void setRecordingTime(String text) {
501         mRecordingTimeView.setText(text);
502     }
503 
setRecordingTimeTextColor(int color)504     public void setRecordingTimeTextColor(int color) {
505         mRecordingTimeView.setTextColor(color);
506     }
507 
508     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
509         @Override
onZoomValueChanged(int index)510         public void onZoomValueChanged(int index) {
511             int newZoom = mController.onZoomChanged(index);
512             if (mZoomRenderer != null) {
513                 mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
514             }
515         }
516 
517         @Override
onZoomStart()518         public void onZoomStart() {
519         }
520 
521         @Override
onZoomEnd()522         public void onZoomEnd() {
523         }
524     }
525 
526     @Override
onSwipe(int direction)527     public void onSwipe(int direction) {
528         if (direction == PreviewGestures.DIR_UP) {
529             openMenu();
530         }
531     }
532 
533     /**
534      * Enable or disable the preview thumbnail for click events.
535      */
enablePreviewThumb(boolean enabled)536     public void enablePreviewThumb(boolean enabled) {
537         if (enabled) {
538             mGestures.addTouchReceiver(mPreviewThumb);
539             mPreviewThumb.setVisibility(View.VISIBLE);
540         } else {
541             mGestures.removeTouchReceiver(mPreviewThumb);
542             mPreviewThumb.setVisibility(View.GONE);
543         }
544     }
545 }
546