CameraAppUI.java revision e038c161389b5d1c0476e315736df31562328894
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
17package com.android.camera.app;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.Matrix;
22import android.graphics.SurfaceTexture;
23import android.util.Log;
24import android.view.GestureDetector;
25import android.view.LayoutInflater;
26import android.view.MotionEvent;
27import android.view.TextureView;
28import android.view.View;
29import android.view.ViewConfiguration;
30import android.view.ViewGroup;
31import android.widget.FrameLayout;
32import android.widget.FrameLayout.LayoutParams;
33import android.widget.ImageView;
34
35import com.android.camera.AnimationManager;
36import com.android.camera.filmstrip.FilmstripContentPanel;
37import com.android.camera.ui.BottomBar;
38import com.android.camera.ui.CaptureAnimationOverlay;
39import com.android.camera.widget.FilmstripLayout;
40import com.android.camera.ui.MainActivityLayout;
41import com.android.camera.ui.ModeListView;
42import com.android.camera.ui.ModeTransitionView;
43import com.android.camera.ui.PreviewOverlay;
44import com.android.camera.ui.PreviewStatusListener;
45import com.android.camera2.R;
46
47/**
48 * CameraAppUI centralizes control of views shared across modules. Whereas module
49 * specific views will be handled in each Module UI. For example, we can now
50 * bring the flash animation and capture animation up from each module to app
51 * level, as these animations are largely the same for all modules.
52 *
53 * This class also serves to disambiguate touch events. It recognizes all the
54 * swipe gestures that happen on the preview by attaching a touch listener to
55 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
56 * of how swipe from each direction should be handled, it can then redirect these
57 * events to appropriate recipient views.
58 */
59public class CameraAppUI implements ModeListView.ModeSwitchListener,
60        TextureView.SurfaceTextureListener {
61
62    /**
63     * The bottom controls on the filmstrip.
64     */
65    public static interface BottomControls {
66        /** Values for the view state of the button. */
67        public final int VIEW_NONE = 0;
68        public final int VIEW_PHOTO_SPHERE = 1;
69        public final int VIEW_RGBZ = 2;
70
71        /**
72         * Sets a new or replaces an existing listener for bottom control events.
73         */
74        void setListener(Listener listener);
75
76        /**
77         * Set if the bottom controls are visible.
78         * @param visible {@code true} if visible.
79         */
80        void setVisible(boolean visible);
81
82        /**
83         * @param visible Whether the button is visible.
84         */
85        void setEditButtonVisibility(boolean visible);
86
87        /**
88         * Sets the visibility of the view-photosphere button.
89         *
90         * @param state one of {@link #VIEW_NONE}, {@link #VIEW_PHOTO_SPHERE},
91         *            {@link #VIEW_RGBZ}.
92         */
93        void setViewButtonVisibility(int state);
94
95        /**
96         * @param visible Whether the button is visible.
97         */
98        void setTinyPlanetButtonVisibility(boolean visible);
99
100        /**
101         * @param visible Whether the button is visible.
102         */
103        void setDeleteButtonVisibility(boolean visible);
104
105        /**
106         * @param visible Whether the button is visible.
107         */
108        void setShareButtonVisibility(boolean visible);
109
110        /**
111         * @param visible Whether the button is visible.
112         */
113        void setGalleryButtonVisibility(boolean visible);
114
115        /**
116         * Classes implementing this interface can listen for events on the bottom
117         * controls.
118         */
119        public static interface Listener {
120            /**
121             * Called when the user pressed the "view" button to e.g. view a photo
122             * sphere or RGBZ image.
123             */
124            public void onView();
125
126            /**
127             * Called when the "edit" button is pressed.
128             */
129            public void onEdit();
130
131            /**
132             * Called when the "tiny planet" button is pressed.
133             */
134            public void onTinyPlanet();
135
136            /**
137             * Called when the "delete" button is pressed.
138             */
139            public void onDelete();
140
141            /**
142             * Called when the "share" button is pressed.
143             */
144            public void onShare();
145
146            /**
147             * Called when the "gallery" button is pressed.
148             */
149            public void onGallery();
150        }
151    }
152
153    private final static String TAG = "CameraAppUI";
154
155    private final AppController mController;
156    private final boolean mIsCaptureIntent;
157    private final boolean mIsSecureCamera;
158    private final AnimationManager mAnimationManager;
159
160    // Swipe states:
161    private final static int IDLE = 0;
162    private final static int SWIPE_UP = 1;
163    private final static int SWIPE_DOWN = 2;
164    private final static int SWIPE_LEFT = 3;
165    private final static int SWIPE_RIGHT = 4;
166
167    // Touch related measures:
168    private final int mSlop;
169    private final static int SWIPE_TIME_OUT_MS = 500;
170
171    private final static int SHIMMY_DELAY_MS = 1000;
172
173    // Mode cover states:
174    private final static int COVER_HIDDEN = 0;
175    private final static int COVER_SHOWN = 1;
176    private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
177
178    // App level views:
179    private final FrameLayout mCameraRootView;
180    private final ModeTransitionView mModeTransitionView;
181    private final MainActivityLayout mAppRootView;
182    private final ModeListView mModeListView;
183    private final FilmstripLayout mFilmstripLayout;
184    private TextureView mTextureView;
185    private View mFlashOverlay;
186    private FrameLayout mModuleUI;
187
188    private GestureDetector mGestureDetector;
189    private int mSwipeState = IDLE;
190    private ImageView mPreviewThumbView;
191    private PreviewOverlay mPreviewOverlay;
192    private CaptureAnimationOverlay mCaptureOverlay;
193    private PreviewStatusListener mPreviewStatusListener;
194    private int mModeCoverState = COVER_HIDDEN;
195    private FilmstripBottomControls mFilmstripBottomControls;
196    private FilmstripContentPanel mFilmstripPanel;
197    private Runnable mHideCoverRunnable;
198
199    // TODO this isn't used by all modules universally, should be part of a util class or something
200    /**
201     * Resizes the preview texture and given bottom bar for 100% preview size
202     */
203    public void adjustPreviewAndBottomBarSize(int width, int height,
204            BottomBar bottomBar, float aspectRatio,
205            int bottomBarMinHeight, int bottomBarOptimalHeight) {
206        Matrix matrix = mTextureView.getTransform(null);
207
208        float scaleX = 1f, scaleY = 1f;
209        float scaledTextureWidth, scaledTextureHeight;
210        if (width > height) {
211            scaledTextureWidth = Math.min(width,
212                                          (int) (height * aspectRatio));
213            scaledTextureHeight = Math.min(height,
214                                           (int) (width / aspectRatio));
215        } else {
216            scaledTextureWidth = Math.min(width,
217                                          (int) (height / aspectRatio));
218            scaledTextureHeight = Math.min(height,
219                                           (int) (width * aspectRatio));
220        }
221
222        scaleX = scaledTextureWidth / width;
223        scaleY = scaledTextureHeight / height;
224
225        // TODO: Need a better way to find out whether currently in landscape
226        boolean landscape = width > height;
227        if (landscape) {
228            matrix.setScale(scaleX, scaleY, 0f, (float) height / 2);
229        } else {
230            matrix.setScale(scaleX, scaleY, (float) width / 2, 0.0f);
231        }
232        setPreviewTransformMatrix(matrix);
233        float previewAspectRatio =
234                (float)scaledTextureWidth / (float)scaledTextureHeight;
235        if (previewAspectRatio < 1.0) {
236            previewAspectRatio = 1.0f/previewAspectRatio;
237        }
238        float screenAspectRatio = (float)width / (float)height;
239        if (screenAspectRatio < 1.0) {
240            screenAspectRatio = 1.0f/screenAspectRatio;
241        }
242
243        if(bottomBar != null) {
244            LayoutParams lp = (LayoutParams) bottomBar.getLayoutParams();
245            // TODO accoount for cases where resizes bar height would be < bottomBarMinHeight
246            if (previewAspectRatio >= screenAspectRatio) {
247                bottomBar.setAlpha(0.5f);
248                if (landscape) {
249                    lp.width = bottomBarOptimalHeight;
250                    lp.height = LayoutParams.MATCH_PARENT;
251                } else {
252                    lp.height = bottomBarOptimalHeight;
253                    lp.width = LayoutParams.MATCH_PARENT;
254                }
255            } else {
256                bottomBar.setAlpha(1.0f);
257                if (landscape) {
258                    lp.width = (int)((float) width - scaledTextureWidth);
259                    lp.height = LayoutParams.MATCH_PARENT;
260                } else {
261                    lp.height = (int)((float) height - scaledTextureHeight);
262                    lp.width = LayoutParams.MATCH_PARENT;
263                }
264            }
265            bottomBar.setLayoutParams(lp);
266        }
267    }
268
269    public interface AnimationFinishedListener {
270        public void onAnimationFinished(boolean success);
271    }
272
273    private class MyTouchListener implements View.OnTouchListener {
274        private boolean mScaleStarted = false;
275        @Override
276        public boolean onTouch(View v, MotionEvent event) {
277            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
278                mScaleStarted = false;
279            } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
280                mScaleStarted = true;
281            }
282            return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
283        }
284    }
285
286    /**
287     * This gesture listener finds out the direction of the scroll gestures and
288     * sends them to CameraAppUI to do further handling.
289     */
290    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
291        private MotionEvent mDown;
292
293        @Override
294        public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
295            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
296                    || mSwipeState != IDLE) {
297                return false;
298            }
299
300            int deltaX = (int) (ev.getX() - mDown.getX());
301            int deltaY = (int) (ev.getY() - mDown.getY());
302            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
303                if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
304                    // Calculate the direction of the swipe.
305                    if (deltaX >= Math.abs(deltaY)) {
306                        // Swipe right.
307                        setSwipeState(SWIPE_RIGHT);
308                    } else if (deltaX <= -Math.abs(deltaY)) {
309                        // Swipe left.
310                        setSwipeState(SWIPE_LEFT);
311                    } else if (deltaY >= Math.abs(deltaX)) {
312                        // Swipe down.
313                        setSwipeState(SWIPE_DOWN);
314                    } else if (deltaY <= -Math.abs(deltaX)) {
315                        // Swipe up.
316                        setSwipeState(SWIPE_UP);
317                    }
318                }
319            }
320            return true;
321        }
322
323        private void setSwipeState(int swipeState) {
324            mSwipeState = swipeState;
325            // Notify new swipe detected.
326            onSwipeDetected(swipeState);
327        }
328
329        @Override
330        public boolean onDown(MotionEvent ev) {
331            mDown = MotionEvent.obtain(ev);
332            mSwipeState = IDLE;
333            return false;
334        }
335    }
336
337    public CameraAppUI(AppController controller, MainActivityLayout appRootView,
338                       boolean isSecureCamera, boolean isCaptureIntent) {
339        mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
340        mController = controller;
341        mIsSecureCamera = isSecureCamera;
342        mIsCaptureIntent = isCaptureIntent;
343
344        mAppRootView = appRootView;
345        mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
346        mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
347        mModeTransitionView = (ModeTransitionView)
348                mAppRootView.findViewById(R.id.mode_transition_view);
349        mFilmstripBottomControls = new FilmstripBottomControls(
350                (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_controls));
351        mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
352        mGestureDetector = new GestureDetector(controller.getAndroidContext(),
353                new MyGestureListener());
354        mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
355        if (mModeListView != null) {
356            mModeListView.setModeSwitchListener(this);
357        } else {
358            Log.e(TAG, "Cannot find mode list in the view hierarchy");
359        }
360        mAnimationManager = new AnimationManager();
361    }
362
363    /**
364     * Redirects touch events to appropriate recipient views based on swipe direction.
365     * More specifically, swipe up and swipe down will be handled by the view that handles
366     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
367     * to mode list in order to bring up mode list.
368     */
369    private void onSwipeDetected(int swipeState) {
370        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
371            // Quick switch between photo/video.
372            if (mController.getCurrentModuleIndex() == ModeListView.MODE_PHOTO ||
373                    mController.getCurrentModuleIndex() == ModeListView.MODE_VIDEO) {
374                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
375
376                final int moduleToTransitionTo =
377                        mController.getCurrentModuleIndex() == ModeListView.MODE_PHOTO ?
378                        ModeListView.MODE_VIDEO : ModeListView.MODE_PHOTO;
379                int shadeColorId = ModeListView.getModeThemeColor(moduleToTransitionTo);
380                int iconRes = ModeListView.getModeIconResourceId(moduleToTransitionTo);
381
382                AnimationFinishedListener listener = new AnimationFinishedListener() {
383                    public void onAnimationFinished(boolean success) {
384                        if (success) {
385                            mHideCoverRunnable = new Runnable() {
386                                @Override
387                                public void run() {
388                                    mModeTransitionView.startPeepHoleAnimation();
389                                }
390                            };
391                            mModeCoverState = COVER_SHOWN;
392                            // Go to new module when the previous operation is successful.
393                            mController.onModeSelected(moduleToTransitionTo);
394                        }
395                    }
396                };
397                if (mSwipeState == SWIPE_UP) {
398                    mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
399                } else {
400                    mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
401                }
402            }
403        } else if (swipeState == SWIPE_LEFT) {
404            // Pass the touch sequence to filmstrip layout.
405            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
406
407        } else if (swipeState == SWIPE_RIGHT) {
408            // Pass the touch to mode switcher
409            mAppRootView.redirectTouchEventsTo(mModeListView);
410        }
411    }
412
413    /**
414     * Gets called when activity resumes in preview.
415     */
416    public void resume() {
417        if (mTextureView == null || mTextureView.getSurfaceTexture() != null) {
418            mModeListView.startAccordionAnimationWithDelay(SHIMMY_DELAY_MS);
419        } else {
420            // Show mode theme cover until preview is ready
421            showModeCoverUntilPreviewReady();
422        }
423        // Hide action bar first since we are in full screen mode first, and
424        // switch the system UI to lights-out mode.
425        mFilmstripPanel.hide();
426    }
427
428    /**
429     * A cover view showing the mode theme color and mode icon will be visible on
430     * top of preview until preview is ready (i.e. camera preview is started and
431     * the first frame has been received).
432     */
433    private void showModeCoverUntilPreviewReady() {
434        int modeId = mController.getCurrentModuleIndex();
435        int colorId = ModeListView.getModeThemeColor(modeId);
436        int iconId = ModeListView.getModeIconResourceId(modeId);
437        mModeTransitionView.setupModeCover(colorId, iconId);
438        mHideCoverRunnable = new Runnable() {
439            @Override
440            public void run() {
441                mModeTransitionView.hideModeCover(new AnimationFinishedListener() {
442                    @Override
443                    public void onAnimationFinished(boolean success) {
444                        if (success) {
445                            // Show shimmy in SHIMMY_DELAY_MS
446                            mModeListView.startAccordionAnimationWithDelay(SHIMMY_DELAY_MS);
447                        }
448                    }
449                });
450            }
451        };
452        mModeCoverState = COVER_SHOWN;
453    }
454
455    private void hideModeCover() {
456        if (mHideCoverRunnable != null) {
457            mAppRootView.post(mHideCoverRunnable);
458            mHideCoverRunnable = null;
459        }
460        mModeCoverState = COVER_HIDDEN;
461    }
462
463    /**
464     * Called when the back key is pressed.
465     *
466     * @return Whether the UI responded to the key event.
467     */
468    public boolean onBackPressed() {
469        return mFilmstripLayout.onBackPressed();
470    }
471
472    /**
473     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
474     * listens to SurfaceTexture changes. In addition, the listener will also provide
475     * a {@link android.view.GestureDetector.OnGestureListener}, which will listen to
476     * gestures that happen on camera preview.
477     *
478     * @param previewStatusListener the listener that gets notified when SurfaceTexture
479     *                              changes
480     */
481    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
482        mPreviewStatusListener = previewStatusListener;
483        if (mPreviewStatusListener != null) {
484            GestureDetector.OnGestureListener gestureListener
485                    = mPreviewStatusListener.getGestureListener();
486            if (gestureListener != null) {
487                mPreviewOverlay.setGestureListener(gestureListener);
488            }
489        }
490    }
491
492    /**
493     * This inflates generic_module layout, which contains all the shared views across
494     * modules. Then each module inflates their own views in the given view group. For
495     * now, this is called every time switching from a not-yet-refactored module to a
496     * refactored module. In the future, this should only need to be done once per app
497     * start.
498     */
499    public void prepareModuleUI() {
500        mCameraRootView.removeAllViews();
501        LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
502                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
503        inflater.inflate(R.layout.generic_module, mCameraRootView, true);
504
505        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
506        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
507        mTextureView.setSurfaceTextureListener(this);
508        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
509        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
510        mCaptureOverlay = (CaptureAnimationOverlay)
511                mCameraRootView.findViewById(R.id.capture_overlay);
512        mPreviewThumbView = (ImageView) mCameraRootView.findViewById(R.id.preview_thumb);
513
514    }
515
516    // TODO: Remove this when refactor is done.
517    // This is here to ensure refactored modules can work with not-yet-refactored ones.
518    public void clearCameraUI() {
519        mCameraRootView.removeAllViews();
520        mModuleUI = null;
521        mTextureView = null;
522        mPreviewOverlay = null;
523        mFlashOverlay = null;
524    }
525
526    /**
527     * Called indirectly from each module in their initialization to get a view group
528     * to inflate the module specific views in.
529     *
530     * @return a view group for modules to attach views to
531     */
532    public FrameLayout getModuleRootView() {
533        // TODO: Change it to mModuleUI when refactor is done
534        return mCameraRootView;
535    }
536
537    /**
538     * Remove all the module specific views.
539     */
540    public void clearModuleUI() {
541        if (mModuleUI != null) {
542            mModuleUI.removeAllViews();
543        }
544
545        // TODO: Bring TextureView up to the app level
546        mTextureView.removeOnLayoutChangeListener(null);
547
548        mPreviewStatusListener = null;
549        mPreviewOverlay.reset();
550    }
551
552    /**
553     * Gets called when preview is started.
554     */
555    public void onPreviewStarted() {
556        if (mModeCoverState == COVER_SHOWN) {
557            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
558        }
559    }
560
561    /**
562     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
563     *
564     * @param modeIndex mode index of the selected mode
565     */
566    @Override
567    public void onModeSelected(int modeIndex) {
568        mHideCoverRunnable = new Runnable() {
569            @Override
570            public void run() {
571                mModeListView.startModeSelectionAnimation();
572            }
573        };
574        mModeCoverState = COVER_SHOWN;
575
576        int lastIndex = mController.getCurrentModuleIndex();
577        mController.onModeSelected(modeIndex);
578        int currentIndex = mController.getCurrentModuleIndex();
579
580        if (mTextureView == null) {
581            // TODO: Remove this when all the modules use TextureView
582            int temporaryDelay = 600; // ms
583            mModeListView.postDelayed(new Runnable() {
584                @Override
585                public void run() {
586                    hideModeCover();
587                }
588            }, temporaryDelay);
589        } else if (lastIndex == currentIndex) {
590            hideModeCover();
591        }
592    }
593
594    /**
595     * Sets the transform matrix on the preview TextureView
596     */
597    public void setPreviewTransformMatrix(Matrix transformMatrix) {
598        if (mTextureView == null) {
599            throw new UnsupportedOperationException("Cannot set transform matrix on a null" +
600                    " TextureView");
601        }
602        mTextureView.setTransform(transformMatrix);
603    }
604
605
606    /********************** Capture animation **********************/
607    /* TODO: This session is subject to UX changes. In addition to the generic
608       flash animation and post capture animation, consider designating a parameter
609       for specifying the type of animation, as well as an animation finished listener
610       so that modules can have more knowledge of the status of the animation. */
611
612    /**
613     * Starts the pre-capture animation.
614     */
615    public void startPreCaptureAnimation() {
616        mCaptureOverlay.startFlashAnimation();
617    }
618
619    /**
620     * Cancels the pre-capture animation.
621     */
622    public void cancelPreCaptureAnimation() {
623        mAnimationManager.cancelAnimations();
624    }
625
626    /**
627     * Starts the post-capture animation with the current preview image.
628     */
629    public void startPostCaptureAnimation() {
630        if (mTextureView == null) {
631            Log.e(TAG, "Cannot get a frame from a null TextureView for animation");
632            return;
633        }
634        // TODO: Down sample bitmap
635        startPostCaptureAnimation(mTextureView.getBitmap());
636    }
637
638    /**
639     * Starts the post-capture animation with the given thumbnail.
640     *
641     * @param thumbnail The thumbnail for the animation.
642     */
643    public void startPostCaptureAnimation(Bitmap thumbnail) {
644        mPreviewThumbView.setImageBitmap(thumbnail);
645        mAnimationManager.startCaptureAnimation(mPreviewThumbView);
646    }
647
648    /**
649     * Cancels the post-capture animation.
650     */
651    public void cancelPostCaptureAnimation() {
652        mAnimationManager.cancelAnimations();
653    }
654
655    public FilmstripContentPanel getFilmstripContentPanel() {
656        return mFilmstripPanel;
657    }
658
659    /**
660     * @return The {@link com.android.camera.app.CameraAppUI.BottomControls} on the
661     * bottom of the filmstrip.
662     */
663    public BottomControls getFilmstripBottomControls() {
664        return mFilmstripBottomControls;
665    }
666
667    /**
668     * @param listener The listener for bottom controls.
669     */
670    public void setFilmstripBottomControlsListener(BottomControls.Listener listener) {
671        mFilmstripBottomControls.setListener(listener);
672    }
673
674    /***************************SurfaceTexture Listener*********************************/
675
676    @Override
677    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
678        Log.v(TAG, "SurfaceTexture is available");
679        if (mPreviewStatusListener != null) {
680            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
681        }
682    }
683
684    @Override
685    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
686        if (mPreviewStatusListener != null) {
687            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
688        }
689    }
690
691    @Override
692    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
693        Log.v(TAG, "SurfaceTexture is destroyed");
694        if (mPreviewStatusListener != null) {
695            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
696        }
697        return false;
698    }
699
700    @Override
701    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
702        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_FRAME) {
703            hideModeCover();
704            mModeCoverState = COVER_HIDDEN;
705        }
706        if (mPreviewStatusListener != null) {
707            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
708        }
709    }
710}
711