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