CameraAppUI.java revision d2d8b711c14ee47f6f4ea16cb9fe27128462ad5b
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.accessibilityservice.AccessibilityServiceInfo;
20import android.content.Context;
21import android.content.res.Resources;
22import android.graphics.Bitmap;
23import android.graphics.Canvas;
24import android.graphics.Matrix;
25import android.graphics.RectF;
26import android.graphics.SurfaceTexture;
27import android.hardware.display.DisplayManager;
28import android.util.CameraPerformanceTracker;
29import android.view.GestureDetector;
30import android.view.Gravity;
31import android.view.LayoutInflater;
32import android.view.MotionEvent;
33import android.view.TextureView;
34import android.view.View;
35import android.view.ViewConfiguration;
36import android.view.ViewGroup;
37import android.view.accessibility.AccessibilityManager;
38import android.widget.FrameLayout;
39
40import com.android.camera.AnimationManager;
41import com.android.camera.ButtonManager;
42import com.android.camera.CaptureLayoutHelper;
43import com.android.camera.ShutterButton;
44import com.android.camera.TextureViewHelper;
45import com.android.camera.debug.Log;
46import com.android.camera.filmstrip.FilmstripContentPanel;
47import com.android.camera.hardware.HardwareSpec;
48import com.android.camera.module.ModuleController;
49import com.android.camera.settings.Keys;
50import com.android.camera.settings.SettingsManager;
51import com.android.camera.ui.AbstractTutorialOverlay;
52import com.android.camera.ui.BottomBar;
53import com.android.camera.ui.BottomBarModeOptionsWrapper;
54import com.android.camera.ui.CaptureAnimationOverlay;
55import com.android.camera.ui.GridLines;
56import com.android.camera.ui.MainActivityLayout;
57import com.android.camera.ui.ModeListView;
58import com.android.camera.ui.ModeTransitionView;
59import com.android.camera.ui.PreviewOverlay;
60import com.android.camera.ui.PreviewStatusListener;
61import com.android.camera.ui.TouchCoordinate;
62import com.android.camera.util.ApiHelper;
63import com.android.camera.util.CameraUtil;
64import com.android.camera.util.Gusterpolator;
65import com.android.camera.util.PhotoSphereHelper;
66import com.android.camera.widget.Cling;
67import com.android.camera.widget.FilmstripLayout;
68import com.android.camera.widget.IndicatorIconController;
69import com.android.camera.widget.ModeOptionsOverlay;
70import com.android.camera.widget.PeekView;
71import com.android.camera2.R;
72
73import java.util.List;
74
75/**
76 * CameraAppUI centralizes control of views shared across modules. Whereas module
77 * specific views will be handled in each Module UI. For example, we can now
78 * bring the flash animation and capture animation up from each module to app
79 * level, as these animations are largely the same for all modules.
80 *
81 * This class also serves to disambiguate touch events. It recognizes all the
82 * swipe gestures that happen on the preview by attaching a touch listener to
83 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
84 * of how swipe from each direction should be handled, it can then redirect these
85 * events to appropriate recipient views.
86 */
87public class CameraAppUI implements ModeListView.ModeSwitchListener,
88                                    TextureView.SurfaceTextureListener,
89                                    ModeListView.ModeListOpenListener,
90                                    SettingsManager.OnSettingChangedListener,
91                                    ShutterButton.OnShutterButtonListener {
92
93    /**
94     * The bottom controls on the filmstrip.
95     */
96    public static interface BottomPanel {
97        /** Values for the view state of the button. */
98        public final int VIEWER_NONE = 0;
99        public final int VIEWER_PHOTO_SPHERE = 1;
100        public final int VIEWER_REFOCUS = 2;
101        public final int VIEWER_OTHER = 3;
102
103        /**
104         * Sets a new or replaces an existing listener for bottom control events.
105         */
106        void setListener(Listener listener);
107
108        /**
109         * Sets cling for external viewer button.
110         */
111        void setClingForViewer(int viewerType, Cling cling);
112
113        /**
114         * Clears cling for external viewer button.
115         */
116        void clearClingForViewer(int viewerType);
117
118        /**
119         * Returns a cling for the specified viewer type.
120         */
121        Cling getClingForViewer(int viewerType);
122
123        /**
124         * Set if the bottom controls are visible.
125         * @param visible {@code true} if visible.
126         */
127        void setVisible(boolean visible);
128
129        /**
130         * @param visible Whether the button is visible.
131         */
132        void setEditButtonVisibility(boolean visible);
133
134        /**
135         * @param enabled Whether the button is enabled.
136         */
137        void setEditEnabled(boolean enabled);
138
139        /**
140         * Sets the visibility of the view-photosphere button.
141         *
142         * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
143         *            {@link #VIEWER_REFOCUS}.
144         */
145        void setViewerButtonVisibility(int state);
146
147        /**
148         * @param enabled Whether the button is enabled.
149         */
150        void setViewEnabled(boolean enabled);
151
152        /**
153         * @param enabled Whether the button is enabled.
154         */
155        void setTinyPlanetEnabled(boolean enabled);
156
157        /**
158         * @param visible Whether the button is visible.
159         */
160        void setDeleteButtonVisibility(boolean visible);
161
162        /**
163         * @param enabled Whether the button is enabled.
164         */
165        void setDeleteEnabled(boolean enabled);
166
167        /**
168         * @param visible Whether the button is visible.
169         */
170        void setShareButtonVisibility(boolean visible);
171
172        /**
173         * @param enabled Whether the button is enabled.
174         */
175        void setShareEnabled(boolean enabled);
176
177        /**
178         * Sets the texts for progress UI.
179         *
180         * @param text The text to show.
181         */
182        void setProgressText(CharSequence text);
183
184        /**
185         * Sets the progress.
186         *
187         * @param progress The progress value. Should be between 0 and 100.
188         */
189        void setProgress(int progress);
190
191        /**
192         * Replaces the progress UI with an error message.
193         */
194        void showProgressError(CharSequence message);
195
196        /**
197         * Hide the progress error message.
198         */
199        void hideProgressError();
200
201        /**
202         * Shows the progress.
203         */
204        void showProgress();
205
206        /**
207         * Hides the progress.
208         */
209        void hideProgress();
210
211        /**
212         * Shows the controls.
213         */
214        void showControls();
215
216        /**
217         * Hides the controls.
218         */
219        void hideControls();
220
221        /**
222         * Classes implementing this interface can listen for events on the bottom
223         * controls.
224         */
225        public static interface Listener {
226            /**
227             * Called when the user pressed the "view" button to e.g. view a photo
228             * sphere or RGBZ image.
229             */
230            public void onExternalViewer();
231
232            /**
233             * Called when the "edit" button is pressed.
234             */
235            public void onEdit();
236
237            /**
238             * Called when the "tiny planet" button is pressed.
239             */
240            public void onTinyPlanet();
241
242            /**
243             * Called when the "delete" button is pressed.
244             */
245            public void onDelete();
246
247            /**
248             * Called when the "share" button is pressed.
249             */
250            public void onShare();
251
252            /**
253             * Called when the progress error message is clicked.
254             */
255            public void onProgressErrorClicked();
256        }
257    }
258
259    /**
260     * BottomBarUISpec provides a structure for modules
261     * to specify their ideal bottom bar mode options layout.
262     *
263     * Once constructed by a module, this class should be
264     * treated as read only.
265     *
266     * The application then edits this spec according to
267     * hardware limitations and displays the final bottom
268     * bar ui.
269     */
270    public static class BottomBarUISpec {
271        /** Mode options UI */
272
273        /**
274         * Set true if the camera option should be enabled.
275         * If not set or false, and multiple cameras are supported,
276         * the camera option will be disabled.
277         *
278         * If multiple cameras are not supported, this preference
279         * is ignored and the camera option will not be visible.
280         */
281        public boolean enableCamera;
282
283        /**
284         * Set true if the camera option should not be visible, regardless
285         * of hardware limitations.
286         */
287        public boolean hideCamera;
288
289        /**
290         * Set true if the photo flash option should be enabled.
291         * If not set or false, the photo flash option will be
292         * disabled.
293         *
294         * If the hardware does not support multiple flash values,
295         * this preference is ignored and the flash option will
296         * be disabled.  It will not be made invisible in order to
297         * preserve a consistent experience across devices and between
298         * front and back cameras.
299         */
300        public boolean enableFlash;
301
302        /**
303         * Set true if the video flash option should be enabled.
304         * Same disable rules apply as the photo flash option.
305         */
306        public boolean enableTorchFlash;
307
308        /**
309         * Set true if the HDR+ flash option should be enabled.
310         * Same disable rules apply as the photo flash option.
311         */
312        public boolean enableHdrPlusFlash;
313
314        /**
315         * Set true if flash should not be visible, regardless of
316         * hardware limitations.
317         */
318        public boolean hideFlash;
319
320        /**
321         * Set true if the hdr/hdr+ option should be enabled.
322         * If not set or false, the hdr/hdr+ option will be disabled.
323         *
324         * Hdr or hdr+ will be chosen based on hardware limitations,
325         * with hdr+ prefered.
326         *
327         * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
328         * will not be visible.
329         */
330        public boolean enableHdr;
331
332        /**
333         * Set true if hdr/hdr+ should not be visible, regardless of
334         * hardware limitations.
335         */
336        public boolean hideHdr;
337
338        /**
339         * Set true if grid lines should be visible.  Not setting this
340         * causes grid lines to be disabled.  This option is agnostic to
341         * the hardware.
342         */
343        public boolean enableGridLines;
344
345        /**
346         * Set true if grid lines should not be visible.
347         */
348        public boolean hideGridLines;
349
350        /**
351         * Set true if the panorama orientation option should be visible.
352         *
353         * This option is not constrained by hardware limitations.
354         */
355        public boolean enablePanoOrientation;
356
357        public boolean enableExposureCompensation;
358
359        /** Intent UI */
360
361        /**
362         * Set true if the intent ui cancel option should be visible.
363         */
364        public boolean showCancel;
365        /**
366         * Set true if the intent ui done option should be visible.
367         */
368        public boolean showDone;
369        /**
370         * Set true if the intent ui retake option should be visible.
371         */
372        public boolean showRetake;
373        /**
374         * Set true if the intent ui review option should be visible.
375         */
376        public boolean showReview;
377
378        /** Mode options callbacks */
379
380        /**
381         * A {@link com.android.camera.ButtonManager.ButtonCallback}
382         * that will be executed when the camera option is pressed. This
383         * callback can be null.
384         */
385        public ButtonManager.ButtonCallback cameraCallback;
386
387        /**
388         * A {@link com.android.camera.ButtonManager.ButtonCallback}
389         * that will be executed when the flash option is pressed. This
390         * callback can be null.
391         */
392        public ButtonManager.ButtonCallback flashCallback;
393
394        /**
395         * A {@link com.android.camera.ButtonManager.ButtonCallback}
396         * that will be executed when the hdr/hdr+ option is pressed. This
397         * callback can be null.
398         */
399        public ButtonManager.ButtonCallback hdrCallback;
400
401        /**
402         * A {@link com.android.camera.ButtonManager.ButtonCallback}
403         * that will be executed when the grid lines option is pressed. This
404         * callback can be null.
405         */
406        public ButtonManager.ButtonCallback gridLinesCallback;
407
408        /**
409         * A {@link com.android.camera.ButtonManager.ButtonCallback}
410         * that will execute when the panorama orientation option is pressed.
411         * This callback can be null.
412         */
413        public ButtonManager.ButtonCallback panoOrientationCallback;
414
415        /** Intent UI callbacks */
416
417        /**
418         * A {@link android.view.View.OnClickListener} that will execute
419         * when the cancel option is pressed. This callback can be null.
420         */
421        public View.OnClickListener cancelCallback;
422
423        /**
424         * A {@link android.view.View.OnClickListener} that will execute
425         * when the done option is pressed. This callback can be null.
426         */
427        public View.OnClickListener doneCallback;
428
429        /**
430         * A {@link android.view.View.OnClickListener} that will execute
431         * when the retake option is pressed. This callback can be null.
432         */
433        public View.OnClickListener retakeCallback;
434
435        /**
436         * A {@link android.view.View.OnClickListener} that will execute
437         * when the review option is pressed. This callback can be null.
438         */
439        public View.OnClickListener reviewCallback;
440
441        /**
442         * A ExposureCompensationSetCallback that will execute
443         * when an expsosure button is pressed. This callback can be null.
444         */
445        public interface ExposureCompensationSetCallback {
446            public void setExposure(int value);
447        }
448        public ExposureCompensationSetCallback exposureCompensationSetCallback;
449
450        /**
451         * Exposure compensation parameters.
452         */
453        public int minExposureCompensation;
454        public int maxExposureCompensation;
455        public float exposureCompensationStep;
456
457        /**
458         * Whether self-timer is enabled.
459         */
460        public boolean enableSelfTimer = false;
461
462        /**
463         * Whether the option for self-timer should show. If true and
464         * {@link #enableSelfTimer} is false, then the option should be shown
465         * disabled.
466         */
467        public boolean showSelfTimer = false;
468    }
469
470
471    private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
472
473    private final AppController mController;
474    private final boolean mIsCaptureIntent;
475    private final AnimationManager mAnimationManager;
476
477    // Swipe states:
478    private final static int IDLE = 0;
479    private final static int SWIPE_UP = 1;
480    private final static int SWIPE_DOWN = 2;
481    private final static int SWIPE_LEFT = 3;
482    private final static int SWIPE_RIGHT = 4;
483    private boolean mSwipeEnabled = true;
484
485    // Shared Surface Texture properities.
486    private SurfaceTexture mSurface;
487    private int mSurfaceWidth;
488    private int mSurfaceHeight;
489
490    // Touch related measures:
491    private final int mSlop;
492    private final static int SWIPE_TIME_OUT_MS = 500;
493
494    // Mode cover states:
495    private final static int COVER_HIDDEN = 0;
496    private final static int COVER_SHOWN = 1;
497    private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
498    private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
499
500    /**
501     * Preview down-sample rate when taking a screenshot.
502     */
503    private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
504
505    // App level views:
506    private final FrameLayout mCameraRootView;
507    private final ModeTransitionView mModeTransitionView;
508    private final MainActivityLayout mAppRootView;
509    private final ModeListView mModeListView;
510    private final FilmstripLayout mFilmstripLayout;
511    private TextureView mTextureView;
512    private FrameLayout mModuleUI;
513    private ShutterButton mShutterButton;
514    private View mLetterBoxer1;
515    private View mLetterBoxer2;
516    private BottomBar mBottomBar;
517    private ModeOptionsOverlay mModeOptionsOverlay;
518    private IndicatorIconController mIndicatorIconController;
519    private View mFocusOverlay;
520    private FrameLayout mTutorialsPlaceHolderWrapper;
521    private BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper;
522    private TextureViewHelper mTextureViewHelper;
523    private final GestureDetector mGestureDetector;
524    private DisplayManager.DisplayListener mDisplayListener;
525    private int mLastRotation;
526    private int mSwipeState = IDLE;
527    private PreviewOverlay mPreviewOverlay;
528    private GridLines mGridLines;
529    private CaptureAnimationOverlay mCaptureOverlay;
530    private PreviewStatusListener mPreviewStatusListener;
531    private int mModeCoverState = COVER_HIDDEN;
532    private final FilmstripBottomPanel mFilmstripBottomControls;
533    private final FilmstripContentPanel mFilmstripPanel;
534    private Runnable mHideCoverRunnable;
535    private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
536            = new View.OnLayoutChangeListener() {
537        @Override
538        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
539                int oldTop, int oldRight, int oldBottom) {
540            if (mPreviewStatusListener != null) {
541                mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
542                        oldTop, oldRight, oldBottom);
543            }
544        }
545    };
546    private View mModeOptionsToggle;
547    private final PeekView mPeekView;
548    private final CaptureLayoutHelper mCaptureLayoutHelper;
549    private boolean mAccessibilityEnabled;
550    private final View mAccessibilityAffordances;
551
552    private boolean mDisableAllUserInteractions;
553    /**
554     * Provides current preview frame and the controls/overlay from the module that
555     * are shown on top of the preview.
556     */
557    public interface CameraModuleScreenShotProvider {
558        /**
559         * Returns the current preview frame down-sampled using the given down-sample
560         * factor.
561         *
562         * @param downSampleFactor the down sample factor for down sampling the
563         *                         preview frame. (e.g. a down sample factor of
564         *                         2 means to scale down the preview frame to 1/2
565         *                         the width and height.)
566         * @return down-sampled preview frame
567         */
568        public Bitmap getPreviewFrame(int downSampleFactor);
569
570        /**
571         * @return the controls and overlays that are currently showing on top of
572         *         the preview drawn into a bitmap with no scaling applied.
573         */
574        public Bitmap getPreviewOverlayAndControls();
575
576        /**
577         * Returns a bitmap containing the current screenshot.
578         *
579         * @param previewDownSampleFactor the downsample factor applied on the
580         *                                preview frame when taking the screenshot
581         */
582        public Bitmap getScreenShot(int previewDownSampleFactor);
583    }
584
585    /**
586     * This listener gets called when the size of the window (excluding the system
587     * decor such as status bar and nav bar) has changed.
588     */
589    public interface NonDecorWindowSizeChangedListener {
590        public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
591    }
592
593    private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
594            new CameraModuleScreenShotProvider() {
595                @Override
596                public Bitmap getPreviewFrame(int downSampleFactor) {
597                    if (mCameraRootView == null || mTextureView == null) {
598                        return null;
599                    }
600                    // Gets the bitmap from the preview TextureView.
601                    Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
602                    return preview;
603                }
604
605                @Override
606                public Bitmap getPreviewOverlayAndControls() {
607                    Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
608                            mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
609                    Canvas canvas = new Canvas(overlays);
610                    mCameraRootView.draw(canvas);
611                    return overlays;
612                }
613
614                @Override
615                public Bitmap getScreenShot(int previewDownSampleFactor) {
616                    Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
617                            mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
618                    Canvas canvas = new Canvas(screenshot);
619                    canvas.drawARGB(255, 0, 0, 0);
620                    Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
621                    if (preview != null) {
622                        canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
623                    }
624                    Bitmap overlay = getPreviewOverlayAndControls();
625                    if (overlay != null) {
626                        canvas.drawBitmap(overlay, 0f, 0f, null);
627                    }
628                    return screenshot;
629                }
630            };
631
632    private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
633
634    public long getCoverHiddenTime() {
635        return mCoverHiddenTime;
636    }
637
638    /**
639     * This resets the preview to have no applied transform matrix.
640     */
641    public void clearPreviewTransform() {
642        mTextureViewHelper.clearTransform();
643    }
644
645    public void updatePreviewAspectRatio(float aspectRatio) {
646        mTextureViewHelper.updateAspectRatio(aspectRatio);
647    }
648
649    /**
650     * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
651     * its TextureView.  This is necessary to get the expected behavior for the TextureView's
652     * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
653     * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
654     * size).
655     *
656     * b/17286155 - Tracking a fix for this in HardwareLayer.
657     */
658    public void setDefaultBufferSizeToViewDimens() {
659        if (mSurface == null || mTextureView == null) {
660            Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
661            return;
662        }
663        mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
664    }
665
666    /**
667     * Updates the preview matrix without altering it.
668     *
669     * @param matrix
670     * @param aspectRatio the desired aspect ratio for the preview.
671     */
672    public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
673        mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
674    }
675
676    /**
677     * @return the rect that will display the preview.
678     */
679    public RectF getFullscreenRect() {
680        return mTextureViewHelper.getFullscreenRect();
681    }
682
683    /**
684     * This is to support modules that calculate their own transform matrix because
685     * they need to use a transform matrix to rotate the preview.
686     *
687     * @param matrix transform matrix to be set on the TextureView
688     */
689    public void updatePreviewTransform(Matrix matrix) {
690        mTextureViewHelper.updateTransform(matrix);
691    }
692
693    public interface AnimationFinishedListener {
694        public void onAnimationFinished(boolean success);
695    }
696
697    private class MyTouchListener implements View.OnTouchListener {
698        private boolean mScaleStarted = false;
699        @Override
700        public boolean onTouch(View v, MotionEvent event) {
701            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
702                mScaleStarted = false;
703            } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
704                mScaleStarted = true;
705            }
706            return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
707        }
708    }
709
710    /**
711     * This gesture listener finds out the direction of the scroll gestures and
712     * sends them to CameraAppUI to do further handling.
713     */
714    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
715        private MotionEvent mDown;
716
717        @Override
718        public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
719            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
720                    || mSwipeState != IDLE
721                    || mIsCaptureIntent
722                    || !mSwipeEnabled) {
723                return false;
724            }
725
726            int deltaX = (int) (ev.getX() - mDown.getX());
727            int deltaY = (int) (ev.getY() - mDown.getY());
728            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
729                if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
730                    // Calculate the direction of the swipe.
731                    if (deltaX >= Math.abs(deltaY)) {
732                        // Swipe right.
733                        setSwipeState(SWIPE_RIGHT);
734                    } else if (deltaX <= -Math.abs(deltaY)) {
735                        // Swipe left.
736                        setSwipeState(SWIPE_LEFT);
737                    }
738                }
739            }
740            return true;
741        }
742
743        private void setSwipeState(int swipeState) {
744            mSwipeState = swipeState;
745            // Notify new swipe detected.
746            onSwipeDetected(swipeState);
747        }
748
749        @Override
750        public boolean onDown(MotionEvent ev) {
751            mDown = MotionEvent.obtain(ev);
752            mSwipeState = IDLE;
753            return false;
754        }
755    }
756
757    public CameraAppUI(AppController controller, MainActivityLayout appRootView,
758            boolean isCaptureIntent) {
759        mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
760        mController = controller;
761        mIsCaptureIntent = isCaptureIntent;
762
763        mAppRootView = appRootView;
764        mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
765        mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
766        mModeTransitionView = (ModeTransitionView)
767                mAppRootView.findViewById(R.id.mode_transition_view);
768        mFilmstripBottomControls = new FilmstripBottomPanel(controller,
769                (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
770        mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
771        mGestureDetector = new GestureDetector(controller.getAndroidContext(),
772                new MyGestureListener());
773        Resources res = controller.getAndroidContext().getResources();
774        mCaptureLayoutHelper = new CaptureLayoutHelper(
775                res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
776                res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
777                res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
778        mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
779        if (mModeListView != null) {
780            mModeListView.setModeSwitchListener(this);
781            mModeListView.setModeListOpenListener(this);
782            mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
783            mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
784            boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
785                    SettingsManager.SCOPE_GLOBAL,
786                    Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
787            mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
788        } else {
789            Log.e(TAG, "Cannot find mode list in the view hierarchy");
790        }
791        mAnimationManager = new AnimationManager();
792        mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view);
793        mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
794        initDisplayListener();
795        mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
796        View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
797        modeListToggle.setOnClickListener(new View.OnClickListener() {
798            @Override
799            public void onClick(View view) {
800                openModeList();
801            }
802        });
803        View filmstripToggle = mAppRootView.findViewById(
804                R.id.accessibility_filmstrip_toggle_button);
805        filmstripToggle.setOnClickListener(new View.OnClickListener() {
806            @Override
807            public void onClick(View view) {
808                showFilmstrip();
809            }
810        });
811    }
812
813
814    /**
815     * Freeze what is currently shown on screen until the next preview frame comes
816     * in.
817     */
818    public void freezeScreenUntilPreviewReady() {
819        Log.v(TAG, "freezeScreenUntilPreviewReady");
820        mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
821                .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
822        mHideCoverRunnable = new Runnable() {
823            @Override
824            public void run() {
825                mModeTransitionView.hideImageCover();
826            }
827        };
828        mModeCoverState = COVER_SHOWN;
829    }
830
831    /**
832     * Creates a cling for the specific viewer and links the cling to the corresponding
833     * button for layout position.
834     *
835     * @param viewerType defines which viewer the cling is for.
836     */
837    public void setupClingForViewer(int viewerType) {
838        if (viewerType == BottomPanel.VIEWER_REFOCUS) {
839            FrameLayout filmstripContent = (FrameLayout) mAppRootView
840                    .findViewById(R.id.camera_filmstrip_content_layout);
841            if (filmstripContent != null) {
842                // Creates refocus cling.
843                LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
844                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
845                Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
846                // Sets instruction text in the cling.
847                refocusCling.setText(mController.getAndroidContext().getResources()
848                        .getString(R.string.cling_text_for_refocus_editor_button));
849
850                // Adds cling into view hierarchy.
851                int clingWidth = mController.getAndroidContext()
852                        .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
853                filmstripContent.addView(refocusCling, clingWidth,
854                        ViewGroup.LayoutParams.WRAP_CONTENT);
855                mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
856            }
857        }
858    }
859
860    /**
861     * Clears the listeners for the cling and remove it from the view hierarchy.
862     *
863     * @param viewerType defines which viewer the cling is for.
864     */
865    public void clearClingForViewer(int viewerType) {
866        Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
867        if (clingToBeRemoved == null) {
868            // No cling is created for the specific viewer type.
869            return;
870        }
871        mFilmstripBottomControls.clearClingForViewer(viewerType);
872        clingToBeRemoved.setVisibility(View.GONE);
873        mAppRootView.removeView(clingToBeRemoved);
874    }
875
876    /**
877     * Enable or disable swipe gestures. We want to disable them e.g. while we
878     * record a video.
879     */
880    public void setSwipeEnabled(boolean enabled) {
881        mSwipeEnabled = enabled;
882        // TODO: This can be removed once we come up with a new design for handling swipe
883        // on shutter button and mode options. (More details: b/13751653)
884        mAppRootView.setSwipeEnabled(enabled);
885    }
886
887    public void onDestroy() {
888        ((DisplayManager) mController.getAndroidContext()
889                .getSystemService(Context.DISPLAY_SERVICE))
890                .unregisterDisplayListener(mDisplayListener);
891    }
892
893    /**
894     * Initializes the display listener to listen to display changes such as
895     * 180-degree rotation change, which will not have an onConfigurationChanged
896     * callback.
897     */
898    private void initDisplayListener() {
899        if (ApiHelper.HAS_DISPLAY_LISTENER) {
900            mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
901
902            mDisplayListener = new DisplayManager.DisplayListener() {
903                @Override
904                public void onDisplayAdded(int arg0) {
905                    // Do nothing.
906                }
907
908                @Override
909                public void onDisplayChanged(int displayId) {
910                    int rotation = CameraUtil.getDisplayRotation(
911                            mController.getAndroidContext());
912                    if ((rotation - mLastRotation + 360) % 360 == 180
913                            && mPreviewStatusListener != null) {
914                        mPreviewStatusListener.onPreviewFlipped();
915                        mIndicatorBottomBarWrapper.requestLayout();
916                        mModeListView.requestLayout();
917                        mTextureView.requestLayout();
918                    }
919                    mLastRotation = rotation;
920                }
921
922                @Override
923                public void onDisplayRemoved(int arg0) {
924                    // Do nothing.
925                }
926            };
927
928            ((DisplayManager) mController.getAndroidContext()
929                    .getSystemService(Context.DISPLAY_SERVICE))
930                    .registerDisplayListener(mDisplayListener, null);
931        }
932    }
933
934    /**
935     * Redirects touch events to appropriate recipient views based on swipe direction.
936     * More specifically, swipe up and swipe down will be handled by the view that handles
937     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
938     * to mode list in order to bring up mode list.
939     */
940    private void onSwipeDetected(int swipeState) {
941        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
942            // TODO: Polish quick switch after this release.
943            // Quick switch between modes.
944            int currentModuleIndex = mController.getCurrentModuleIndex();
945            final int moduleToTransitionTo =
946                    mController.getQuickSwitchToModuleId(currentModuleIndex);
947            if (currentModuleIndex != moduleToTransitionTo) {
948                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
949                int shadeColorId = R.color.mode_cover_default_color;
950                int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
951                        mController.getAndroidContext());
952
953                AnimationFinishedListener listener = new AnimationFinishedListener() {
954                    @Override
955                    public void onAnimationFinished(boolean success) {
956                        if (success) {
957                            mHideCoverRunnable = new Runnable() {
958                                @Override
959                                public void run() {
960                                    mModeTransitionView.startPeepHoleAnimation();
961                                }
962                            };
963                            mModeCoverState = COVER_SHOWN;
964                            // Go to new module when the previous operation is successful.
965                            mController.onModeSelected(moduleToTransitionTo);
966                        }
967                    }
968                };
969            }
970        } else if (swipeState == SWIPE_LEFT) {
971            // Pass the touch sequence to filmstrip layout.
972            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
973        } else if (swipeState == SWIPE_RIGHT) {
974            // Pass the touch to mode switcher
975            mAppRootView.redirectTouchEventsTo(mModeListView);
976        }
977    }
978
979    /**
980     * Gets called when activity resumes in preview.
981     */
982    public void resume() {
983        // Show mode theme cover until preview is ready
984        showModeCoverUntilPreviewReady();
985
986        // Hide action bar first since we are in full screen mode first, and
987        // switch the system UI to lights-out mode.
988        mFilmstripPanel.hide();
989
990        // Show UI that is meant to only be used when spoken feedback is
991        // enabled.
992        mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
993        mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
994    }
995
996    /**
997     * @return Whether any spoken feedback accessibility feature is currently
998     *         enabled.
999     */
1000    private boolean isSpokenFeedbackAccessibilityEnabled() {
1001        AccessibilityManager accessibilityManager = (AccessibilityManager) mController
1002                .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1003        List<AccessibilityServiceInfo> infos = accessibilityManager
1004                .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1005        return infos != null && !infos.isEmpty();
1006    }
1007
1008    /**
1009     * Opens the mode list (e.g. because of the menu button being pressed) and
1010     * adapts the rest of the UI.
1011     */
1012    public void openModeList() {
1013        mModeOptionsOverlay.closeModeOptions();
1014        mModeListView.onMenuPressed();
1015    }
1016
1017    /**
1018     * A cover view showing the mode theme color and mode icon will be visible on
1019     * top of preview until preview is ready (i.e. camera preview is started and
1020     * the first frame has been received).
1021     */
1022    private void showModeCoverUntilPreviewReady() {
1023        int modeId = mController.getCurrentModuleIndex();
1024        int colorId = R.color.mode_cover_default_color;;
1025        int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1026        mModeTransitionView.setupModeCover(colorId, iconId);
1027        mHideCoverRunnable = new Runnable() {
1028            @Override
1029            public void run() {
1030                mModeTransitionView.hideModeCover(null);
1031                showShimmyDelayed();
1032            }
1033        };
1034        mModeCoverState = COVER_SHOWN;
1035    }
1036
1037    private void showShimmyDelayed() {
1038        if (!mIsCaptureIntent) {
1039            // Show shimmy in SHIMMY_DELAY_MS
1040            mModeListView.showModeSwitcherHint();
1041        }
1042    }
1043
1044    private void hideModeCover() {
1045        if (mHideCoverRunnable != null) {
1046            mAppRootView.post(mHideCoverRunnable);
1047            mHideCoverRunnable = null;
1048        }
1049        mModeCoverState = COVER_HIDDEN;
1050        if (mCoverHiddenTime < 0) {
1051            mCoverHiddenTime = System.currentTimeMillis();
1052        }
1053    }
1054
1055
1056    public void onPreviewVisiblityChanged(int visibility) {
1057        if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1058            setIndicatorBottomBarWrapperVisible(false);
1059            mAccessibilityAffordances.setVisibility(View.GONE);
1060        } else {
1061            setIndicatorBottomBarWrapperVisible(true);
1062            if (mAccessibilityEnabled) {
1063                mAccessibilityAffordances.setVisibility(View.VISIBLE);
1064            } else {
1065                mAccessibilityAffordances.setVisibility(View.GONE);
1066            }
1067        }
1068    }
1069
1070    /**
1071     * Call to stop the preview from being rendered.
1072     */
1073    public void pausePreviewRendering() {
1074        mTextureView.setVisibility(View.INVISIBLE);
1075    }
1076
1077    /**
1078     * Call to begin rendering the preview again.
1079     */
1080    public void resumePreviewRendering() {
1081        mTextureView.setVisibility(View.VISIBLE);
1082    }
1083
1084    /**
1085     * Returns the transform associated with the preview view.
1086     *
1087     * @param m the Matrix in which to copy the current transform.
1088     * @return The specified matrix if not null or a new Matrix instance
1089     *         otherwise.
1090     */
1091    public Matrix getPreviewTransform(Matrix m) {
1092        return mTextureView.getTransform(m);
1093    }
1094
1095    @Override
1096    public void onOpenFullScreen() {
1097        // Do nothing.
1098    }
1099
1100    @Override
1101    public void onModeListOpenProgress(float progress) {
1102        progress = 1 - progress;
1103        float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1104        mModeOptionsToggle.setAlpha(interpolatedProgress);
1105        // Change shutter button alpha linearly based on the mode list open progress:
1106        // set the alpha to disabled alpha when list is fully open, to enabled alpha
1107        // when the list is fully closed.
1108        mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1109                + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1110    }
1111
1112    @Override
1113    public void onModeListClosed() {
1114        // Make sure the alpha on mode options ellipse is reset when mode drawer
1115        // is closed.
1116        mModeOptionsToggle.setAlpha(1f);
1117        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1118    }
1119
1120    /**
1121     * Called when the back key is pressed.
1122     *
1123     * @return Whether the UI responded to the key event.
1124     */
1125    public boolean onBackPressed() {
1126        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1127            return mFilmstripLayout.onBackPressed();
1128        } else {
1129            return mModeListView.onBackPressed();
1130        }
1131    }
1132
1133    /**
1134     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1135     * listens to SurfaceTexture changes. In addition, listeners are set on
1136     * dependent app ui elements.
1137     *
1138     * @param previewStatusListener the listener that gets notified when SurfaceTexture
1139     *                              changes
1140     */
1141    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1142        mPreviewStatusListener = previewStatusListener;
1143        if (mPreviewStatusListener != null) {
1144            onPreviewListenerChanged();
1145        }
1146    }
1147
1148    /**
1149     * When the PreviewStatusListener changes, listeners need to be
1150     * set on the following app ui elements:
1151     * {@link com.android.camera.ui.PreviewOverlay},
1152     * {@link com.android.camera.ui.BottomBar},
1153     * {@link com.android.camera.ui.IndicatorIconController}.
1154     */
1155    private void onPreviewListenerChanged() {
1156        // Set a listener for recognizing preview gestures.
1157        GestureDetector.OnGestureListener gestureListener
1158            = mPreviewStatusListener.getGestureListener();
1159        if (gestureListener != null) {
1160            mPreviewOverlay.setGestureListener(gestureListener);
1161        }
1162        View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1163        if (touchListener != null) {
1164            mPreviewOverlay.setTouchListener(touchListener);
1165        }
1166
1167        mTextureViewHelper.setAutoAdjustTransform(
1168                mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1169    }
1170
1171    /**
1172     * This method should be called in onCameraOpened.  It defines CameraAppUI
1173     * specific changes that depend on the camera or camera settings.
1174     */
1175    public void onChangeCamera() {
1176        ModuleController moduleController = mController.getCurrentModuleController();
1177        applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1178
1179        if (mIndicatorIconController != null) {
1180            // Sync the settings state with the indicator state.
1181            mIndicatorIconController.syncIndicators();
1182        }
1183    }
1184
1185    /**
1186     * Adds a listener to receive callbacks when preview area changes.
1187     */
1188    public void addPreviewAreaChangedListener(
1189            PreviewStatusListener.PreviewAreaChangedListener listener) {
1190        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1191    }
1192
1193    /**
1194     * Removes a listener that receives callbacks when preview area changes.
1195     */
1196    public void removePreviewAreaChangedListener(
1197            PreviewStatusListener.PreviewAreaChangedListener listener) {
1198        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1199    }
1200
1201    /**
1202     * This inflates generic_module layout, which contains all the shared views across
1203     * modules. Then each module inflates their own views in the given view group. For
1204     * now, this is called every time switching from a not-yet-refactored module to a
1205     * refactored module. In the future, this should only need to be done once per app
1206     * start.
1207     */
1208    public void prepareModuleUI() {
1209        mController.getSettingsManager().addListener(this);
1210        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1211        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1212        mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1213                mController.getCameraProvider());
1214        mTextureViewHelper.setSurfaceTextureListener(this);
1215        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1216
1217        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1218        int unpressedColor = mController.getAndroidContext().getResources()
1219            .getColor(R.color.bottombar_unpressed);
1220        setBottomBarColor(unpressedColor);
1221        updateModeSpecificUIColors();
1222
1223        mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1224
1225        mModeOptionsOverlay
1226            = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1227
1228        // Sets the visibility of the bottom bar and the mode options.
1229        resetBottomControls(mController.getCurrentModuleController(),
1230            mController.getCurrentModuleIndex());
1231        mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1232
1233        mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1234        addShutterListener(mController.getCurrentModuleController());
1235        addShutterListener(mModeOptionsOverlay);
1236        addShutterListener(this);
1237
1238        mLetterBoxer1 = mCameraRootView.findViewById(R.id.leftLetterBoxer1);
1239        mLetterBoxer2 = mCameraRootView.findViewById(R.id.leftLetterBoxer2);
1240
1241        mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1242        mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1243
1244        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1245        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1246        mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1247
1248        mCaptureOverlay = (CaptureAnimationOverlay)
1249                mCameraRootView.findViewById(R.id.capture_overlay);
1250        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1251        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1252
1253        if (mIndicatorIconController == null) {
1254            mIndicatorIconController =
1255                new IndicatorIconController(mController, mAppRootView);
1256        }
1257
1258        mController.getButtonManager().load(mCameraRootView);
1259        mController.getButtonManager().setListener(mIndicatorIconController);
1260        mController.getSettingsManager().addListener(mIndicatorIconController);
1261
1262        mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1263        mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay);
1264        mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1265                .findViewById(R.id.tutorials_placeholder_wrapper);
1266        mIndicatorBottomBarWrapper = (BottomBarModeOptionsWrapper) mAppRootView
1267                .findViewById(R.id.indicator_bottombar_wrapper);
1268        mIndicatorBottomBarWrapper.setCaptureLayoutHelper(mCaptureLayoutHelper);
1269        mTextureViewHelper.addPreviewAreaSizeChangedListener(
1270                new PreviewStatusListener.PreviewAreaChangedListener() {
1271                    @Override
1272                    public void onPreviewAreaChanged(RectF previewArea) {
1273                        mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight());
1274                    }
1275                });
1276
1277        mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1278        mTextureViewHelper.addAspectRatioChangedListener(
1279                new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1280                    @Override
1281                    public void onPreviewAspectRatioChanged(float aspectRatio) {
1282                        mModeOptionsOverlay.requestLayout();
1283                        mBottomBar.requestLayout();
1284                    }
1285                }
1286        );
1287    }
1288
1289    /**
1290     * Called indirectly from each module in their initialization to get a view group
1291     * to inflate the module specific views in.
1292     *
1293     * @return a view group for modules to attach views to
1294     */
1295    public FrameLayout getModuleRootView() {
1296        // TODO: Change it to mModuleUI when refactor is done
1297        return mCameraRootView;
1298    }
1299
1300    /**
1301     * Remove all the module specific views.
1302     */
1303    public void clearModuleUI() {
1304        if (mModuleUI != null) {
1305            mModuleUI.removeAllViews();
1306        }
1307        removeShutterListener(mController.getCurrentModuleController());
1308        mTutorialsPlaceHolderWrapper.removeAllViews();
1309        mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1310
1311        setShutterButtonEnabled(true);
1312        mPreviewStatusListener = null;
1313        mPreviewOverlay.reset();
1314        mFocusOverlay.setVisibility(View.INVISIBLE);
1315    }
1316
1317    /**
1318     * Gets called when preview is ready to start. It sets up one shot preview callback
1319     * in order to receive a callback when the preview frame is available, so that
1320     * the preview cover can be hidden to reveal preview.
1321     *
1322     * An alternative for getting the timing to hide preview cover is through
1323     * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1324     * which is less accurate but therefore is the fallback for modules that manage
1325     * their own preview callbacks (as setting one preview callback will override
1326     * any other installed preview callbacks), or use camera2 API.
1327     */
1328    public void onPreviewReadyToStart() {
1329        if (mModeCoverState == COVER_SHOWN) {
1330            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1331            mController.setupOneShotPreviewListener();
1332        }
1333    }
1334
1335    /**
1336     * Gets called when preview is started.
1337     */
1338    public void onPreviewStarted() {
1339        Log.v(TAG, "onPreviewStarted");
1340        if (mModeCoverState == COVER_SHOWN) {
1341            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1342        }
1343        enableModeOptions();
1344    }
1345
1346    /**
1347     * Gets notified when next preview frame comes in.
1348     */
1349    public void onNewPreviewFrame() {
1350        Log.v(TAG, "onNewPreviewFrame");
1351        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1352        hideModeCover();
1353    }
1354
1355    @Override
1356    public void onShutterButtonClick() {
1357        /*
1358         * Set the mode options toggle unclickable, generally
1359         * throughout the app, whenever the shutter button is clicked.
1360         *
1361         * This could be done in the OnShutterButtonListener of the
1362         * ModeOptionsOverlay, but since it is very important that we
1363         * can clearly see when the toggle becomes clickable again,
1364         * keep all of that logic at this level.
1365         */
1366        disableModeOptions();
1367    }
1368
1369    @Override
1370    public void onShutterCoordinate(TouchCoordinate coord) {
1371        // Do nothing.
1372    }
1373
1374    @Override
1375    public void onShutterButtonFocus(boolean pressed) {
1376        // noop
1377    }
1378
1379    /**
1380     * Set the mode options toggle clickable.
1381     */
1382    public void enableModeOptions() {
1383        /*
1384         * For modules using camera 1 api, this gets called in
1385         * onSurfaceTextureUpdated whenever the preview gets stopped and
1386         * started after each capture.  This also takes care of the
1387         * case where the mode options might be unclickable when we
1388         * switch modes
1389         *
1390         * For modules using camera 2 api, they're required to call this
1391         * method when a capture is "completed".  Unfortunately this differs
1392         * per module implementation.
1393         */
1394        if (!mDisableAllUserInteractions) {
1395            mModeOptionsOverlay.setToggleClickable(true);
1396        }
1397    }
1398
1399    /**
1400     * Set the mode options toggle not clickable.
1401     */
1402    public void disableModeOptions() {
1403        mModeOptionsOverlay.setToggleClickable(false);
1404    }
1405
1406    public void setDisableAllUserInteractions(boolean disable) {
1407        if (disable) {
1408            disableModeOptions();
1409            setShutterButtonEnabled(false);
1410            setSwipeEnabled(false);
1411        } else {
1412            enableModeOptions();
1413            setShutterButtonEnabled(true);
1414            setSwipeEnabled(true);
1415        }
1416        mDisableAllUserInteractions = disable;
1417    }
1418
1419    /**
1420     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1421     *
1422     * @param modeIndex mode index of the selected mode
1423     */
1424    @Override
1425    public void onModeSelected(int modeIndex) {
1426        mHideCoverRunnable = new Runnable() {
1427            @Override
1428            public void run() {
1429                mModeListView.startModeSelectionAnimation();
1430            }
1431        };
1432        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1433        mModeCoverState = COVER_SHOWN;
1434
1435        int lastIndex = mController.getCurrentModuleIndex();
1436        // Actual mode teardown / new mode initialization happens here
1437        mController.onModeSelected(modeIndex);
1438        int currentIndex = mController.getCurrentModuleIndex();
1439
1440        if (lastIndex == currentIndex) {
1441            hideModeCover();
1442        }
1443
1444        updateModeSpecificUIColors();
1445    }
1446
1447    private void updateModeSpecificUIColors() {
1448        setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1449    }
1450
1451    @Override
1452    public void onSettingsSelected() {
1453        mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1454                                             Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1455        mModeListView.setShouldShowSettingsCling(false);
1456        mController.onSettingsSelected();
1457    }
1458
1459    @Override
1460    public int getCurrentModeIndex() {
1461        return mController.getCurrentModuleIndex();
1462    }
1463
1464    /********************** Capture animation **********************/
1465    /* TODO: This session is subject to UX changes. In addition to the generic
1466       flash animation and post capture animation, consider designating a parameter
1467       for specifying the type of animation, as well as an animation finished listener
1468       so that modules can have more knowledge of the status of the animation. */
1469
1470    /**
1471     * Starts the filmstrip peek animation.
1472     *
1473     * @param bitmap The bitmap to show.
1474     * @param strong Whether the animation shows more portion of the bitmap or
1475     *               not.
1476     * @param accessibilityString An accessibility String to be announced
1477                     during the peek animation.
1478     */
1479    public void startPeekAnimation(Bitmap bitmap, boolean strong, String accessibilityString) {
1480        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1481            return;
1482        }
1483        mPeekView.startPeekAnimation(bitmap, strong, accessibilityString);
1484    }
1485
1486    /**
1487     * Starts the pre-capture animation.
1488     *
1489     * @param shortFlash show shortest possible flash instead of regular long version.
1490     */
1491    public void startPreCaptureAnimation(boolean shortFlash) {
1492        mCaptureOverlay.startFlashAnimation(shortFlash);
1493    }
1494
1495    /**
1496     * Cancels the pre-capture animation.
1497     */
1498    public void cancelPreCaptureAnimation() {
1499        mAnimationManager.cancelAnimations();
1500    }
1501
1502    /**
1503     * Cancels the post-capture animation.
1504     */
1505    public void cancelPostCaptureAnimation() {
1506        mAnimationManager.cancelAnimations();
1507    }
1508
1509    public FilmstripContentPanel getFilmstripContentPanel() {
1510        return mFilmstripPanel;
1511    }
1512
1513    /**
1514     * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1515     * bottom of the filmstrip.
1516     */
1517    public BottomPanel getFilmstripBottomControls() {
1518        return mFilmstripBottomControls;
1519    }
1520
1521    public void showBottomControls() {
1522        mFilmstripBottomControls.show();
1523    }
1524
1525    public void hideBottomControls() {
1526        mFilmstripBottomControls.hide();
1527    }
1528
1529    /**
1530     * @param listener The listener for bottom controls.
1531     */
1532    public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1533        mFilmstripBottomControls.setListener(listener);
1534    }
1535
1536    /***************************SurfaceTexture Api and Listener*********************************/
1537
1538    /**
1539     * Return the shared surface texture.
1540     */
1541    public SurfaceTexture getSurfaceTexture() {
1542        return mSurface;
1543    }
1544
1545    /**
1546     * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1547     */
1548    public int getSurfaceWidth() {
1549        return mSurfaceWidth;
1550    }
1551
1552    /**
1553     * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1554     */
1555    public int getSurfaceHeight() {
1556        return mSurfaceHeight;
1557    }
1558
1559    @Override
1560    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1561        mSurface = surface;
1562        mSurfaceWidth = width;
1563        mSurfaceHeight = height;
1564        Log.v(TAG, "SurfaceTexture is available");
1565        if (mPreviewStatusListener != null) {
1566            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1567        }
1568        enableModeOptions();
1569    }
1570
1571    @Override
1572    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1573        mSurface = surface;
1574        mSurfaceWidth = width;
1575        mSurfaceHeight = height;
1576        if (mPreviewStatusListener != null) {
1577            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1578        }
1579    }
1580
1581    @Override
1582    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1583        mSurface = null;
1584        Log.v(TAG, "SurfaceTexture is destroyed");
1585        if (mPreviewStatusListener != null) {
1586            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1587        }
1588        return false;
1589    }
1590
1591    @Override
1592    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1593        mSurface = surface;
1594        if (mPreviewStatusListener != null) {
1595            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1596        }
1597        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1598            Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1599            CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1600            hideModeCover();
1601        }
1602    }
1603
1604    /****************************Grid lines api ******************************/
1605
1606    /**
1607     * Show a set of evenly spaced lines over the preview.  The number
1608     * of lines horizontally and vertically is determined by
1609     * {@link com.android.camera.ui.GridLines}.
1610     */
1611    public void showGridLines() {
1612        if (mGridLines != null) {
1613            mGridLines.setVisibility(View.VISIBLE);
1614        }
1615    }
1616
1617    /**
1618     * Hide the set of evenly spaced grid lines overlaying the preview.
1619     */
1620    public void hideGridLines() {
1621        if (mGridLines != null) {
1622            mGridLines.setVisibility(View.INVISIBLE);
1623        }
1624    }
1625
1626    /**
1627     * Return a callback which shows or hide the preview grid lines
1628     * depending on whether the grid lines setting is set on.
1629     */
1630    public ButtonManager.ButtonCallback getGridLinesCallback() {
1631        return new ButtonManager.ButtonCallback() {
1632            @Override
1633            public void onStateChanged(int state) {
1634                if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1635                    showGridLines();
1636                } else {
1637                    hideGridLines();
1638                }
1639            }
1640        };
1641    }
1642
1643    /***************************Mode options api *****************************/
1644
1645    /**
1646     * Set the mode options visible.
1647     */
1648    public void showModeOptions() {
1649        /* Make mode options clickable. */
1650        enableModeOptions();
1651        mModeOptionsOverlay.setVisibility(View.VISIBLE);
1652    }
1653
1654    /**
1655     * Set the mode options invisible.  This is necessary for modes
1656     * that don't show a bottom bar for the capture UI.
1657     */
1658    public void hideModeOptions() {
1659        mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1660    }
1661
1662    /****************************Bottom bar api ******************************/
1663
1664    /**
1665     * Sets up the bottom bar and mode options with the correct
1666     * shutter button and visibility based on the current module.
1667     */
1668    public void resetBottomControls(ModuleController module, int moduleIndex) {
1669        if (areBottomControlsUsed(module)) {
1670            setBottomBarShutterIcon(moduleIndex);
1671            mCaptureLayoutHelper.setShowBottomBar(true);
1672        } else {
1673            mCaptureLayoutHelper.setShowBottomBar(false);
1674        }
1675    }
1676
1677    /**
1678     * Show or hide the mode options and bottom bar, based on
1679     * whether the current module is using the bottom bar.  Returns
1680     * whether the mode options and bottom bar are used.
1681     */
1682    private boolean areBottomControlsUsed(ModuleController module) {
1683        if (module.isUsingBottomBar()) {
1684            showBottomBar();
1685            showModeOptions();
1686            return true;
1687        } else {
1688            hideBottomBar();
1689            hideModeOptions();
1690            return false;
1691        }
1692    }
1693
1694    /**
1695     * Set the bottom bar visible.
1696     */
1697    public void showBottomBar() {
1698        mBottomBar.setVisibility(View.VISIBLE);
1699    }
1700
1701    /**
1702     * Set the bottom bar invisible.
1703     */
1704    public void hideBottomBar() {
1705        mBottomBar.setVisibility(View.INVISIBLE);
1706    }
1707
1708    /**
1709     * Sets the color of the bottom bar.
1710     */
1711    public void setBottomBarColor(int colorId) {
1712        mBottomBar.setBackgroundColor(colorId);
1713    }
1714
1715    /**
1716     * Sets the pressed color of the bottom bar for a camera mode index.
1717     */
1718    public void setBottomBarColorsForModeIndex(int index) {
1719        mBottomBar.setColorsForModeIndex(index);
1720    }
1721
1722    /**
1723     * Sets the shutter button icon on the bottom bar, based on
1724     * the mode index.
1725     */
1726    public void setBottomBarShutterIcon(int modeIndex) {
1727        int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1728            mController.getAndroidContext());
1729        mBottomBar.setShutterButtonIcon(shutterIconId);
1730    }
1731
1732    public void animateBottomBarToVideoStop(int shutterIconId) {
1733        mBottomBar.animateToVideoStop(shutterIconId);
1734    }
1735
1736    public void animateBottomBarToFullSize(int shutterIconId) {
1737        mBottomBar.animateToFullSize(shutterIconId);
1738    }
1739
1740    public void setShutterButtonEnabled(final boolean enabled) {
1741        if (!mDisableAllUserInteractions) {
1742            mBottomBar.post(new Runnable() {
1743                @Override
1744                public void run() {
1745                    mBottomBar.setShutterButtonEnabled(enabled);
1746                }
1747            });
1748        }
1749    }
1750
1751    public void setShutterButtonImportantToA11y(boolean important) {
1752        mBottomBar.setShutterButtonImportantToA11y(important);
1753    }
1754
1755    public boolean isShutterButtonEnabled() {
1756        return mBottomBar.isShutterButtonEnabled();
1757    }
1758
1759    public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1760        mIndicatorBottomBarWrapper.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1761    }
1762
1763    /**
1764     * Set the visibility of the bottom bar.
1765     */
1766    // TODO: needed for when panorama is managed by the generic module ui.
1767    public void setBottomBarVisible(boolean visible) {
1768        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1769    }
1770
1771    /**
1772     * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1773     */
1774    public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1775        mShutterButton.addOnShutterButtonListener(listener);
1776    }
1777
1778
1779    /**
1780     * This adds letterboxing around the preview, one on each side
1781     *
1782     * @param width the width in pixels of each letterboxing cover
1783     */
1784    public void addLetterboxing(int width) {
1785        FrameLayout.LayoutParams params1 = (FrameLayout.LayoutParams) mLetterBoxer1
1786                .getLayoutParams();
1787        FrameLayout.LayoutParams params2 = (FrameLayout.LayoutParams) mLetterBoxer2
1788                .getLayoutParams();
1789
1790        if (mCameraRootView.getWidth() < mCameraRootView.getHeight()) {
1791            params1.width = width;
1792            params1.height = mCameraRootView.getHeight();
1793            params1.gravity = Gravity.LEFT;
1794            mLetterBoxer1.setVisibility(View.VISIBLE);
1795
1796            params2.width = width;
1797            params2.height = mCameraRootView.getHeight();
1798            params2.gravity = Gravity.RIGHT;
1799            mLetterBoxer2.setVisibility(View.VISIBLE);
1800        } else {
1801            params1.height = width;
1802            params1.width = mCameraRootView.getWidth();
1803            params1.gravity = Gravity.TOP;
1804            mLetterBoxer1.setVisibility(View.VISIBLE);
1805
1806            params2.height = width;
1807            params2.width = mCameraRootView.getWidth();
1808            params2.gravity = Gravity.BOTTOM;
1809            mLetterBoxer2.setVisibility(View.VISIBLE);
1810        }
1811    }
1812
1813    /**
1814     * Remove the letter boxing strips if they happen to be present.
1815     */
1816    public void hideLetterboxing() {
1817        mLetterBoxer1.setVisibility(View.GONE);
1818        mLetterBoxer2.setVisibility(View.GONE);
1819    }
1820
1821    /**
1822     * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1823     */
1824    public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1825        mShutterButton.removeOnShutterButtonListener(listener);
1826    }
1827
1828    /**
1829     * Performs a transition to the capture layout of the bottom bar.
1830     */
1831    public void transitionToCapture() {
1832        ModuleController moduleController = mController.getCurrentModuleController();
1833        applyModuleSpecs(moduleController.getHardwareSpec(),
1834            moduleController.getBottomBarSpec());
1835        mBottomBar.transitionToCapture();
1836    }
1837
1838    /**
1839     * Displays the Cancel button instead of the capture button.
1840     */
1841    public void transitionToCancel() {
1842        ModuleController moduleController = mController.getCurrentModuleController();
1843        applyModuleSpecs(moduleController.getHardwareSpec(),
1844                moduleController.getBottomBarSpec());
1845        mBottomBar.transitionToCancel();
1846    }
1847
1848    /**
1849     * Performs a transition to the global intent layout.
1850     */
1851    public void transitionToIntentCaptureLayout() {
1852        ModuleController moduleController = mController.getCurrentModuleController();
1853        applyModuleSpecs(moduleController.getHardwareSpec(),
1854            moduleController.getBottomBarSpec());
1855        mBottomBar.transitionToIntentCaptureLayout();
1856    }
1857
1858    /**
1859     * Performs a transition to the global intent review layout.
1860     */
1861    public void transitionToIntentReviewLayout() {
1862        ModuleController moduleController = mController.getCurrentModuleController();
1863        applyModuleSpecs(moduleController.getHardwareSpec(),
1864            moduleController.getBottomBarSpec());
1865        mBottomBar.transitionToIntentReviewLayout();
1866    }
1867
1868    /**
1869     * @return whether UI is in intent review mode
1870     */
1871    public boolean isInIntentReview() {
1872        return mBottomBar.isInIntentReview();
1873    }
1874
1875    @Override
1876    public void onSettingChanged(SettingsManager settingsManager, String key) {
1877        // Update the mode options based on the hardware spec,
1878        // when hdr changes to prevent flash from getting out of sync.
1879        if (key.equals(Keys.KEY_CAMERA_HDR)) {
1880            ModuleController moduleController = mController.getCurrentModuleController();
1881            applyModuleSpecs(moduleController.getHardwareSpec(),
1882                             moduleController.getBottomBarSpec());
1883        }
1884    }
1885
1886    /**
1887     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1888     * to the bottom bar mode options based on limitations from a
1889     * {@link com.android.camera.hardware.HardwareSpec}.
1890     *
1891     * Options not supported by the hardware are either hidden
1892     * or disabled, depending on the option.
1893     *
1894     * Otherwise, the option is fully enabled and clickable.
1895     */
1896    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1897           final BottomBarUISpec bottomBarSpec) {
1898        if (hardwareSpec == null || bottomBarSpec == null) {
1899            return;
1900        }
1901
1902        ButtonManager buttonManager = mController.getButtonManager();
1903        SettingsManager settingsManager = mController.getSettingsManager();
1904
1905        buttonManager.setToInitialState();
1906
1907        /** Standard mode options */
1908        if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1909                hardwareSpec.isFrontCameraSupported()) {
1910            if (bottomBarSpec.enableCamera) {
1911                buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1912                        bottomBarSpec.cameraCallback);
1913            } else {
1914                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1915            }
1916        } else {
1917            // Hide camera icon if front camera not available.
1918            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1919        }
1920
1921        boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1922            SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
1923        if (bottomBarSpec.hideFlash || !flashBackCamera) {
1924            buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1925        } else {
1926            if (hardwareSpec.isFlashSupported()) {
1927                if (bottomBarSpec.enableFlash) {
1928                    buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1929                        bottomBarSpec.flashCallback);
1930                } else if (bottomBarSpec.enableTorchFlash) {
1931                    buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1932                        bottomBarSpec.flashCallback);
1933                } else if (bottomBarSpec.enableHdrPlusFlash) {
1934                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1935                        bottomBarSpec.flashCallback);
1936                } else {
1937                    buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1938                }
1939            } else {
1940                // Disable flash icon if not supported by the hardware.
1941                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1942            }
1943        }
1944
1945        if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1946            // Force hide hdr or hdr plus icon.
1947            buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1948        } else {
1949            if (hardwareSpec.isHdrPlusSupported()) {
1950                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1951                                                                       mController.getModuleScope())) {
1952                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1953                            bottomBarSpec.hdrCallback);
1954                } else {
1955                    buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1956                }
1957            } else if (hardwareSpec.isHdrSupported()) {
1958                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1959                                                                       mController.getModuleScope())) {
1960                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1961                            bottomBarSpec.hdrCallback);
1962                } else {
1963                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1964                }
1965            } else {
1966                // Hide hdr plus or hdr icon if neither are supported.
1967                buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1968            }
1969        }
1970
1971        if (bottomBarSpec.hideGridLines) {
1972            // Force hide grid lines icon.
1973            buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1974            hideGridLines();
1975        } else {
1976            if (bottomBarSpec.enableGridLines) {
1977                buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1978                        bottomBarSpec.gridLinesCallback != null ?
1979                                bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1980                );
1981            } else {
1982                buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1983                hideGridLines();
1984            }
1985        }
1986
1987        if (bottomBarSpec.enableSelfTimer) {
1988            buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1989        } else {
1990            if (bottomBarSpec.showSelfTimer) {
1991                buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1992            } else {
1993                buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1994            }
1995        }
1996
1997        if (bottomBarSpec.enablePanoOrientation
1998                && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1999            buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
2000        }
2001
2002        boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
2003            !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
2004            mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2005                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
2006        if (enableExposureCompensation) {
2007            buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
2008            buttonManager.setExposureCompensationParameters(
2009                bottomBarSpec.minExposureCompensation,
2010                bottomBarSpec.maxExposureCompensation,
2011                bottomBarSpec.exposureCompensationStep);
2012
2013            buttonManager.setExposureCompensationCallback(
2014                    bottomBarSpec.exposureCompensationSetCallback);
2015            buttonManager.updateExposureButtons();
2016        } else {
2017            buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2018            buttonManager.setExposureCompensationCallback(null);
2019        }
2020
2021        /** Intent UI */
2022        if (bottomBarSpec.showCancel) {
2023            buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2024                    bottomBarSpec.cancelCallback);
2025        }
2026        if (bottomBarSpec.showDone) {
2027            buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2028                    bottomBarSpec.doneCallback);
2029        }
2030        if (bottomBarSpec.showRetake) {
2031            buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2032                    bottomBarSpec.retakeCallback);
2033        }
2034        if (bottomBarSpec.showReview) {
2035            buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2036                    bottomBarSpec.reviewCallback,
2037                    R.drawable.ic_play);
2038        }
2039    }
2040
2041    /**
2042     * Shows the given tutorial on the screen.
2043     */
2044    public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2045        tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2046    }
2047
2048    /***************************Filmstrip api *****************************/
2049
2050    public void showFilmstrip() {
2051        mModeListView.onBackPressed();
2052        mFilmstripLayout.showFilmstrip();
2053    }
2054
2055    public void hideFilmstrip() {
2056        mFilmstripLayout.hideFilmstrip();
2057    }
2058}
2059