1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.graphics.Bitmap;
20import android.graphics.Color;
21import android.graphics.Matrix;
22import android.graphics.SurfaceTexture;
23import android.graphics.drawable.ColorDrawable;
24import android.hardware.Camera.Parameters;
25import android.os.Handler;
26import android.os.Message;
27import android.util.Log;
28import android.view.Gravity;
29import android.view.SurfaceHolder;
30import android.view.SurfaceView;
31import android.view.TextureView;
32import android.view.TextureView.SurfaceTextureListener;
33import android.view.View;
34import android.view.View.OnClickListener;
35import android.view.View.OnLayoutChangeListener;
36import android.view.ViewGroup;
37import android.widget.FrameLayout.LayoutParams;
38import android.widget.ImageView;
39import android.widget.LinearLayout;
40import android.widget.PopupWindow;
41import android.widget.TextView;
42
43import com.android.camera.CameraPreference.OnPreferenceChangedListener;
44import com.android.camera.ui.AbstractSettingPopup;
45import com.android.camera.ui.CameraControls;
46import com.android.camera.ui.CameraRootView;
47import com.android.camera.ui.ModuleSwitcher;
48import com.android.camera.ui.PieRenderer;
49import com.android.camera.ui.RenderOverlay;
50import com.android.camera.ui.RotateLayout;
51import com.android.camera.ui.ZoomRenderer;
52import com.android.camera.util.CameraUtil;
53import com.android.camera2.R;
54
55import java.util.List;
56
57public class VideoUI implements PieRenderer.PieListener,
58        PreviewGestures.SingleTapListener,
59        CameraRootView.MyDisplayListener,
60        SurfaceTextureListener, SurfaceHolder.Callback {
61    private static final String TAG = "CAM_VideoUI";
62    private static final int UPDATE_TRANSFORM_MATRIX = 1;
63    // module fields
64    private CameraActivity mActivity;
65    private View mRootView;
66    private TextureView mTextureView;
67    // An review image having same size as preview. It is displayed when
68    // recording is stopped in capture intent.
69    private ImageView mReviewImage;
70    private View mReviewCancelButton;
71    private View mReviewDoneButton;
72    private View mReviewPlayButton;
73    private ShutterButton mShutterButton;
74    private ModuleSwitcher mSwitcher;
75    private TextView mRecordingTimeView;
76    private LinearLayout mLabelsLinearLayout;
77    private View mTimeLapseLabel;
78    private RenderOverlay mRenderOverlay;
79    private PieRenderer mPieRenderer;
80    private VideoMenu mVideoMenu;
81    private CameraControls mCameraControls;
82    private SettingsPopup mPopup;
83    private ZoomRenderer mZoomRenderer;
84    private PreviewGestures mGestures;
85    private View mMenuButton;
86    private OnScreenIndicators mOnScreenIndicators;
87    private RotateLayout mRecordingTimeRect;
88    private boolean mRecordingStarted = false;
89    private SurfaceTexture mSurfaceTexture;
90    private VideoController mController;
91    private int mZoomMax;
92    private List<Integer> mZoomRatios;
93    private View mPreviewThumb;
94    private View mFlashOverlay;
95
96    private View mPreviewCover;
97    private SurfaceView mSurfaceView = null;
98    private int mPreviewWidth = 0;
99    private int mPreviewHeight = 0;
100    private float mSurfaceTextureUncroppedWidth;
101    private float mSurfaceTextureUncroppedHeight;
102    private float mAspectRatio = 4f / 3f;
103    private Matrix mMatrix = null;
104    private final AnimationManager mAnimationManager;
105    private final Handler mHandler = new Handler() {
106        @Override
107        public void handleMessage(Message msg) {
108            switch (msg.what) {
109                case UPDATE_TRANSFORM_MATRIX:
110                    setTransformMatrix(mPreviewWidth, mPreviewHeight);
111                    break;
112                default:
113                    break;
114            }
115        }
116    };
117    private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
118        @Override
119        public void onLayoutChange(View v, int left, int top, int right,
120                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
121            int width = right - left;
122            int height = bottom - top;
123            // Full-screen screennail
124            int w = width;
125            int h = height;
126            if (CameraUtil.getDisplayRotation(mActivity) % 180 != 0) {
127                w = height;
128                h = width;
129            }
130            if (mPreviewWidth != width || mPreviewHeight != height) {
131                mPreviewWidth = width;
132                mPreviewHeight = height;
133                onScreenSizeChanged(width, height, w, h);
134            }
135        }
136    };
137
138    public void showPreviewCover() {
139        mPreviewCover.setVisibility(View.VISIBLE);
140    }
141
142    private class SettingsPopup extends PopupWindow {
143        public SettingsPopup(View popup) {
144            super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
145            setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
146            setOutsideTouchable(true);
147            setFocusable(true);
148            popup.setVisibility(View.VISIBLE);
149            setContentView(popup);
150            showAtLocation(mRootView, Gravity.CENTER, 0, 0);
151        }
152
153        public void dismiss(boolean topLevelOnly) {
154            super.dismiss();
155            popupDismissed();
156            showUI();
157            mVideoMenu.popupDismissed(topLevelOnly);
158
159            // Switch back into fullscreen/lights-out mode after popup
160            // is dimissed.
161            mActivity.setSystemBarsVisibility(false);
162        }
163
164        @Override
165        public void dismiss() {
166            // Called by Framework when touch outside the popup or hit back key
167            dismiss(true);
168        }
169    }
170
171    public VideoUI(CameraActivity activity, VideoController controller, View parent) {
172        mActivity = activity;
173        mController = controller;
174        mRootView = parent;
175        mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView, true);
176        mPreviewCover = mRootView.findViewById(R.id.preview_cover);
177        mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);
178        mTextureView.setSurfaceTextureListener(this);
179        mTextureView.addOnLayoutChangeListener(mLayoutListener);
180        mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
181        mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
182        mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher);
183        mSwitcher.setCurrentIndex(ModuleSwitcher.VIDEO_MODULE_INDEX);
184        mSwitcher.setSwitchListener(mActivity);
185        initializeMiscControls();
186        initializeControlByIntent();
187        initializeOverlay();
188        mAnimationManager = new AnimationManager();
189    }
190
191
192    public void initializeSurfaceView() {
193        mSurfaceView = new SurfaceView(mActivity);
194        ((ViewGroup) mRootView).addView(mSurfaceView, 0);
195        mSurfaceView.getHolder().addCallback(this);
196    }
197
198    private void initializeControlByIntent() {
199        mMenuButton = mRootView.findViewById(R.id.menu);
200        mMenuButton.setOnClickListener(new OnClickListener() {
201            @Override
202            public void onClick(View v) {
203                if (mPieRenderer != null) {
204                    mPieRenderer.showInCenter();
205                }
206            }
207        });
208
209        mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls);
210        mOnScreenIndicators = new OnScreenIndicators(mActivity,
211                mRootView.findViewById(R.id.on_screen_indicators));
212        mOnScreenIndicators.resetToDefault();
213        if (mController.isVideoCaptureIntent()) {
214            hideSwitcher();
215            mActivity.getLayoutInflater().inflate(R.layout.review_module_control,
216                    (ViewGroup) mCameraControls);
217            // Cannot use RotateImageView for "done" and "cancel" button because
218            // the tablet layout uses RotateLayout, which cannot be cast to
219            // RotateImageView.
220            mReviewDoneButton = mRootView.findViewById(R.id.btn_done);
221            mReviewCancelButton = mRootView.findViewById(R.id.btn_cancel);
222            mReviewPlayButton = mRootView.findViewById(R.id.btn_play);
223            mReviewCancelButton.setVisibility(View.VISIBLE);
224            mReviewDoneButton.setOnClickListener(new OnClickListener() {
225                @Override
226                public void onClick(View v) {
227                    mController.onReviewDoneClicked(v);
228                }
229            });
230            mReviewCancelButton.setOnClickListener(new OnClickListener() {
231                @Override
232                public void onClick(View v) {
233                    mController.onReviewCancelClicked(v);
234                }
235            });
236            mReviewPlayButton.setOnClickListener(new OnClickListener() {
237                @Override
238                public void onClick(View v) {
239                    mController.onReviewPlayClicked(v);
240                }
241            });
242        }
243    }
244
245    public void setPreviewSize(int width, int height) {
246        if (width == 0 || height == 0) {
247            Log.w(TAG, "Preview size should not be 0.");
248            return;
249        }
250        if (width > height) {
251            mAspectRatio = (float) width / height;
252        } else {
253            mAspectRatio = (float) height / width;
254        }
255        mHandler.sendEmptyMessage(UPDATE_TRANSFORM_MATRIX);
256    }
257
258    public int getPreviewWidth() {
259        return mPreviewWidth;
260    }
261
262    public int getPreviewHeight() {
263        return mPreviewHeight;
264    }
265
266    public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
267        setTransformMatrix(width, height);
268    }
269
270    private void setTransformMatrix(int width, int height) {
271        mMatrix = mTextureView.getTransform(mMatrix);
272        int orientation = CameraUtil.getDisplayRotation(mActivity);
273        float scaleX = 1f, scaleY = 1f;
274        float scaledTextureWidth, scaledTextureHeight;
275        if (width > height) {
276            scaledTextureWidth = Math.max(width,
277                    (int) (height * mAspectRatio));
278            scaledTextureHeight = Math.max(height,
279                    (int)(width / mAspectRatio));
280        } else {
281            scaledTextureWidth = Math.max(width,
282                    (int) (height / mAspectRatio));
283            scaledTextureHeight = Math.max(height,
284                    (int) (width * mAspectRatio));
285        }
286
287        if (mSurfaceTextureUncroppedWidth != scaledTextureWidth ||
288                mSurfaceTextureUncroppedHeight != scaledTextureHeight) {
289            mSurfaceTextureUncroppedWidth = scaledTextureWidth;
290            mSurfaceTextureUncroppedHeight = scaledTextureHeight;
291        }
292        scaleX = scaledTextureWidth / width;
293        scaleY = scaledTextureHeight / height;
294        mMatrix.setScale(scaleX, scaleY, (float) width / 2, (float) height / 2);
295        mTextureView.setTransform(mMatrix);
296
297        if (mSurfaceView != null && mSurfaceView.getVisibility() == View.VISIBLE) {
298            LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
299            lp.width = (int) mSurfaceTextureUncroppedWidth;
300            lp.height = (int) mSurfaceTextureUncroppedHeight;
301            lp.gravity = Gravity.CENTER;
302            mSurfaceView.requestLayout();
303        }
304    }
305
306    /**
307     * Starts a flash animation
308     */
309    public void animateFlash() {
310        mAnimationManager.startFlashAnimation(mFlashOverlay);
311    }
312
313    /**
314     * Starts a capture animation
315     */
316    public void animateCapture() {
317        Bitmap bitmap = null;
318        if (mTextureView != null) {
319            bitmap = mTextureView.getBitmap((int) mSurfaceTextureUncroppedWidth / 2,
320                    (int) mSurfaceTextureUncroppedHeight / 2);
321        }
322        animateCapture(bitmap);
323    }
324
325    /**
326     * Starts a capture animation
327     * @param bitmap the captured image that we shrink and slide in the animation
328     */
329    public void animateCapture(Bitmap bitmap) {
330        if (bitmap == null) {
331            Log.e(TAG, "No valid bitmap for capture animation.");
332            return;
333        }
334        ((ImageView) mPreviewThumb).setImageBitmap(bitmap);
335        mAnimationManager.startCaptureAnimation(mPreviewThumb);
336    }
337
338    /**
339     * Cancels on-going animations
340     */
341    public void cancelAnimations() {
342        mAnimationManager.cancelAnimations();
343    }
344
345    public void hideUI() {
346        mCameraControls.setVisibility(View.INVISIBLE);
347        mSwitcher.closePopup();
348    }
349
350    public void showUI() {
351        mCameraControls.setVisibility(View.VISIBLE);
352    }
353
354    public boolean arePreviewControlsVisible() {
355        return (mCameraControls.getVisibility() == View.VISIBLE);
356    }
357
358    public void hideSwitcher() {
359        mSwitcher.closePopup();
360        mSwitcher.setVisibility(View.INVISIBLE);
361    }
362
363    public void showSwitcher() {
364        mSwitcher.setVisibility(View.VISIBLE);
365    }
366
367    public boolean collapseCameraControls() {
368        boolean ret = false;
369        if (mPopup != null) {
370            dismissPopup(false);
371            ret = true;
372        }
373        return ret;
374    }
375
376    public boolean removeTopLevelPopup() {
377        if (mPopup != null) {
378            dismissPopup(true);
379            return true;
380        }
381        return false;
382    }
383
384    public void enableCameraControls(boolean enable) {
385        if (mGestures != null) {
386            mGestures.setZoomOnly(!enable);
387        }
388        if (mPieRenderer != null && mPieRenderer.showsItems()) {
389            mPieRenderer.hide();
390        }
391    }
392
393    public void initDisplayChangeListener() {
394        ((CameraRootView) mRootView).setDisplayChangeListener(this);
395    }
396
397    public void removeDisplayChangeListener() {
398        ((CameraRootView) mRootView).removeDisplayChangeListener();
399    }
400
401    public void overrideSettings(final String... keyvalues) {
402        mVideoMenu.overrideSettings(keyvalues);
403    }
404
405    public void setOrientationIndicator(int orientation, boolean animation) {
406        // We change the orientation of the linearlayout only for phone UI
407        // because when in portrait the width is not enough.
408        if (mLabelsLinearLayout != null) {
409            if (((orientation / 90) & 1) == 0) {
410                mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
411            } else {
412                mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
413            }
414        }
415        mRecordingTimeRect.setOrientation(0, animation);
416    }
417
418    public SurfaceHolder getSurfaceHolder() {
419        return mSurfaceView.getHolder();
420    }
421
422    public void hideSurfaceView() {
423        mSurfaceView.setVisibility(View.GONE);
424        mTextureView.setVisibility(View.VISIBLE);
425        setTransformMatrix(mPreviewWidth, mPreviewHeight);
426    }
427
428    public void showSurfaceView() {
429        mSurfaceView.setVisibility(View.VISIBLE);
430        mTextureView.setVisibility(View.GONE);
431        setTransformMatrix(mPreviewWidth, mPreviewHeight);
432    }
433
434    private void initializeOverlay() {
435        mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
436        if (mPieRenderer == null) {
437            mPieRenderer = new PieRenderer(mActivity);
438            mVideoMenu = new VideoMenu(mActivity, this, mPieRenderer);
439            mPieRenderer.setPieListener(this);
440        }
441        mRenderOverlay.addRenderer(mPieRenderer);
442        if (mZoomRenderer == null) {
443            mZoomRenderer = new ZoomRenderer(mActivity);
444        }
445        mRenderOverlay.addRenderer(mZoomRenderer);
446        if (mGestures == null) {
447            mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
448            mRenderOverlay.setGestures(mGestures);
449        }
450        mGestures.setRenderOverlay(mRenderOverlay);
451
452        mPreviewThumb = mRootView.findViewById(R.id.preview_thumb);
453        mPreviewThumb.setOnClickListener(new OnClickListener() {
454            @Override
455            public void onClick(View v) {
456                // Do not allow navigation to filmstrip during video recording
457                if (!mRecordingStarted) {
458                    mActivity.gotoGallery();
459                }
460            }
461        });
462    }
463
464    public void setPrefChangedListener(OnPreferenceChangedListener listener) {
465        mVideoMenu.setListener(listener);
466    }
467
468    private void initializeMiscControls() {
469        mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
470        mShutterButton.setImageResource(R.drawable.btn_new_shutter_video);
471        mShutterButton.setOnShutterButtonListener(mController);
472        mShutterButton.setVisibility(View.VISIBLE);
473        mShutterButton.requestFocus();
474        mShutterButton.enableTouch(true);
475        mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
476        mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
477        mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label);
478        // The R.id.labels can only be found in phone layout.
479        // That is, mLabelsLinearLayout should be null in tablet layout.
480        mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
481    }
482
483    public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) {
484      mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode());
485      boolean location = RecordLocationPreference.get(
486              prefs, mActivity.getContentResolver());
487      mOnScreenIndicators.updateLocationIndicator(location);
488
489    }
490
491    public void setAspectRatio(double ratio) {
492      //  mPreviewFrameLayout.setAspectRatio(ratio);
493    }
494
495    public void showTimeLapseUI(boolean enable) {
496        if (mTimeLapseLabel != null) {
497            mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
498        }
499    }
500
501    private void openMenu() {
502        if (mPieRenderer != null) {
503            mPieRenderer.showInCenter();
504        }
505    }
506
507    public void dismissPopup(boolean topLevelOnly) {
508        // In review mode, we do not want to bring up the camera UI
509        if (mController.isInReviewMode()) return;
510        if (mPopup != null) {
511            mPopup.dismiss(topLevelOnly);
512        }
513    }
514
515    private void popupDismissed() {
516        mPopup = null;
517    }
518
519    public void showPopup(AbstractSettingPopup popup) {
520        hideUI();
521
522        if (mPopup != null) {
523            mPopup.dismiss(false);
524        }
525        mPopup = new SettingsPopup(popup);
526    }
527
528    public void onShowSwitcherPopup() {
529        hidePieRenderer();
530    }
531
532    public boolean hidePieRenderer() {
533        if (mPieRenderer != null && mPieRenderer.showsItems()) {
534            mPieRenderer.hide();
535            return true;
536        }
537        return false;
538    }
539
540    // disable preview gestures after shutter is pressed
541    public void setShutterPressed(boolean pressed) {
542        if (mGestures == null) return;
543        mGestures.setEnabled(!pressed);
544    }
545
546    public void enableShutter(boolean enable) {
547        if (mShutterButton != null) {
548            mShutterButton.setEnabled(enable);
549        }
550    }
551
552    // PieListener
553    @Override
554    public void onPieOpened(int centerX, int centerY) {
555        setSwipingEnabled(false);
556        // Close module selection menu when pie menu is opened.
557        mSwitcher.closePopup();
558    }
559
560    @Override
561    public void onPieClosed() {
562        setSwipingEnabled(true);
563    }
564
565    public void setSwipingEnabled(boolean enable) {
566        mActivity.setSwipingEnabled(enable);
567    }
568
569    public void showPreviewBorder(boolean enable) {
570       // TODO: mPreviewFrameLayout.showBorder(enable);
571    }
572
573    // SingleTapListener
574    // Preview area is touched. Take a picture.
575    @Override
576    public void onSingleTapUp(View view, int x, int y) {
577        mController.onSingleTapUp(view, x, y);
578    }
579
580    public void showRecordingUI(boolean recording) {
581        mRecordingStarted = recording;
582        mMenuButton.setVisibility(recording ? View.GONE : View.VISIBLE);
583        mOnScreenIndicators.setVisibility(recording ? View.GONE : View.VISIBLE);
584        if (recording) {
585            mShutterButton.setImageResource(R.drawable.btn_shutter_video_recording);
586            hideSwitcher();
587            mRecordingTimeView.setText("");
588            mRecordingTimeView.setVisibility(View.VISIBLE);
589        } else {
590            mShutterButton.setImageResource(R.drawable.btn_new_shutter_video);
591            if (!mController.isVideoCaptureIntent()) {
592                showSwitcher();
593            }
594            mRecordingTimeView.setVisibility(View.GONE);
595        }
596    }
597
598    public void showReviewImage(Bitmap bitmap) {
599        mReviewImage.setImageBitmap(bitmap);
600        mReviewImage.setVisibility(View.VISIBLE);
601    }
602
603    public void showReviewControls() {
604        CameraUtil.fadeOut(mShutterButton);
605        CameraUtil.fadeIn(mReviewDoneButton);
606        CameraUtil.fadeIn(mReviewPlayButton);
607        mReviewImage.setVisibility(View.VISIBLE);
608        mMenuButton.setVisibility(View.GONE);
609        mOnScreenIndicators.setVisibility(View.GONE);
610    }
611
612    public void hideReviewUI() {
613        mReviewImage.setVisibility(View.GONE);
614        mShutterButton.setEnabled(true);
615        mMenuButton.setVisibility(View.VISIBLE);
616        mOnScreenIndicators.setVisibility(View.VISIBLE);
617        CameraUtil.fadeOut(mReviewDoneButton);
618        CameraUtil.fadeOut(mReviewPlayButton);
619        CameraUtil.fadeIn(mShutterButton);
620    }
621
622    private void setShowMenu(boolean show) {
623        if (mOnScreenIndicators != null) {
624            mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
625        }
626        if (mMenuButton != null) {
627            mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE);
628        }
629    }
630
631    public void onPreviewFocusChanged(boolean previewFocused) {
632        if (previewFocused) {
633            showUI();
634        } else {
635            hideUI();
636        }
637        if (mGestures != null) {
638            mGestures.setEnabled(previewFocused);
639        }
640        if (mRenderOverlay != null) {
641            // this can not happen in capture mode
642            mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE);
643        }
644        setShowMenu(previewFocused);
645    }
646
647    public void initializePopup(PreferenceGroup pref) {
648        mVideoMenu.initialize(pref);
649    }
650
651    public void initializeZoom(Parameters param) {
652        if (param == null || !param.isZoomSupported()) {
653            mGestures.setZoomEnabled(false);
654            return;
655        }
656        mGestures.setZoomEnabled(true);
657        mZoomMax = param.getMaxZoom();
658        mZoomRatios = param.getZoomRatios();
659        // Currently we use immediate zoom for fast zooming to get better UX and
660        // there is no plan to take advantage of the smooth zoom.
661        mZoomRenderer.setZoomMax(mZoomMax);
662        mZoomRenderer.setZoom(param.getZoom());
663        mZoomRenderer.setZoomValue(mZoomRatios.get(param.getZoom()));
664        mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
665    }
666
667    public void clickShutter() {
668        mShutterButton.performClick();
669    }
670
671    public void pressShutter(boolean pressed) {
672        mShutterButton.setPressed(pressed);
673    }
674
675    public View getShutterButton() {
676        return mShutterButton;
677    }
678
679    public void setRecordingTime(String text) {
680        mRecordingTimeView.setText(text);
681    }
682
683    public void setRecordingTimeTextColor(int color) {
684        mRecordingTimeView.setTextColor(color);
685    }
686
687    public boolean isVisible() {
688        return mCameraControls.getVisibility() == View.VISIBLE;
689    }
690
691    @Override
692    public void onDisplayChanged() {
693        mCameraControls.checkLayoutFlip();
694        mController.updateCameraOrientation();
695    }
696
697    private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
698        @Override
699        public void onZoomValueChanged(int index) {
700            int newZoom = mController.onZoomChanged(index);
701            if (mZoomRenderer != null) {
702                mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
703            }
704        }
705
706        @Override
707        public void onZoomStart() {
708        }
709
710        @Override
711        public void onZoomEnd() {
712        }
713    }
714
715    public SurfaceTexture getSurfaceTexture() {
716        return mSurfaceTexture;
717    }
718
719    // SurfaceTexture callbacks
720    @Override
721    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
722        mSurfaceTexture = surface;
723        mController.onPreviewUIReady();
724    }
725
726    @Override
727    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
728        mSurfaceTexture = null;
729        mController.onPreviewUIDestroyed();
730        Log.d(TAG, "surfaceTexture is destroyed");
731        return true;
732    }
733
734    @Override
735    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
736    }
737
738    @Override
739    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
740        // Make sure preview cover is hidden if preview data is available.
741        if (mPreviewCover.getVisibility() != View.GONE) {
742            mPreviewCover.setVisibility(View.GONE);
743        }
744    }
745
746    // SurfaceHolder callbacks
747    @Override
748    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
749        Log.v(TAG, "Surface changed. width=" + width + ". height=" + height);
750    }
751
752    @Override
753    public void surfaceCreated(SurfaceHolder holder) {
754        Log.v(TAG, "Surface created");
755    }
756
757    @Override
758    public void surfaceDestroyed(SurfaceHolder holder) {
759        Log.v(TAG, "Surface destroyed");
760        mController.stopPreview();
761    }
762}
763