CameraAppUI.java revision 0d32f29b2a82c2afa87b46e309c38f67f8b5e54c
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.LayoutInflater;
31import android.view.MotionEvent;
32import android.view.TextureView;
33import android.view.View;
34import android.view.ViewConfiguration;
35import android.view.ViewGroup;
36import android.view.accessibility.AccessibilityManager;
37import android.widget.FrameLayout;
38
39import com.android.camera.AnimationManager;
40import com.android.camera.ButtonManager;
41import com.android.camera.CaptureLayoutHelper;
42import com.android.camera.ShutterButton;
43import com.android.camera.TextureViewHelper;
44import com.android.camera.debug.Log;
45import com.android.camera.filmstrip.FilmstripContentPanel;
46import com.android.camera.hardware.HardwareSpec;
47import com.android.camera.module.ModuleController;
48import com.android.camera.settings.Keys;
49import com.android.camera.settings.SettingsManager;
50import com.android.camera.ui.AbstractTutorialOverlay;
51import com.android.camera.ui.BottomBar;
52import com.android.camera.ui.CaptureAnimationOverlay;
53import com.android.camera.ui.GridLines;
54import com.android.camera.ui.MainActivityLayout;
55import com.android.camera.ui.ModeListView;
56import com.android.camera.ui.ModeTransitionView;
57import com.android.camera.ui.PreviewOverlay;
58import com.android.camera.ui.PreviewStatusListener;
59import com.android.camera.ui.StickyBottomCaptureLayout;
60import com.android.camera.ui.TouchCoordinate;
61import com.android.camera.ui.focus.FocusRing;
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.RoundedThumbnailView;
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 BottomBar mBottomBar;
515    private ModeOptionsOverlay mModeOptionsOverlay;
516    private IndicatorIconController mIndicatorIconController;
517    private FocusRing mFocusRing;
518    private FrameLayout mTutorialsPlaceHolderWrapper;
519    private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
520    private TextureViewHelper mTextureViewHelper;
521    private final GestureDetector mGestureDetector;
522    private DisplayManager.DisplayListener mDisplayListener;
523    private int mLastRotation;
524    private int mSwipeState = IDLE;
525    private PreviewOverlay mPreviewOverlay;
526    private GridLines mGridLines;
527    private CaptureAnimationOverlay mCaptureOverlay;
528    private PreviewStatusListener mPreviewStatusListener;
529    private int mModeCoverState = COVER_HIDDEN;
530    private final FilmstripBottomPanel mFilmstripBottomControls;
531    private final FilmstripContentPanel mFilmstripPanel;
532    private Runnable mHideCoverRunnable;
533    private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
534            = new View.OnLayoutChangeListener() {
535        @Override
536        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
537                int oldTop, int oldRight, int oldBottom) {
538            if (mPreviewStatusListener != null) {
539                mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
540                        oldTop, oldRight, oldBottom);
541            }
542        }
543    };
544    private View mModeOptionsToggle;
545    private final RoundedThumbnailView mRoundedThumbnailView;
546    private final CaptureLayoutHelper mCaptureLayoutHelper;
547    private boolean mAccessibilityEnabled;
548    private final View mAccessibilityAffordances;
549
550    private boolean mDisableAllUserInteractions;
551    /**
552     * Provides current preview frame and the controls/overlay from the module that
553     * are shown on top of the preview.
554     */
555    public interface CameraModuleScreenShotProvider {
556        /**
557         * Returns the current preview frame down-sampled using the given down-sample
558         * factor.
559         *
560         * @param downSampleFactor the down sample factor for down sampling the
561         *                         preview frame. (e.g. a down sample factor of
562         *                         2 means to scale down the preview frame to 1/2
563         *                         the width and height.)
564         * @return down-sampled preview frame
565         */
566        public Bitmap getPreviewFrame(int downSampleFactor);
567
568        /**
569         * @return the controls and overlays that are currently showing on top of
570         *         the preview drawn into a bitmap with no scaling applied.
571         */
572        public Bitmap getPreviewOverlayAndControls();
573
574        /**
575         * Returns a bitmap containing the current screenshot.
576         *
577         * @param previewDownSampleFactor the downsample factor applied on the
578         *                                preview frame when taking the screenshot
579         */
580        public Bitmap getScreenShot(int previewDownSampleFactor);
581    }
582
583    /**
584     * This listener gets called when the size of the window (excluding the system
585     * decor such as status bar and nav bar) has changed.
586     */
587    public interface NonDecorWindowSizeChangedListener {
588        public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
589    }
590
591    private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
592            new CameraModuleScreenShotProvider() {
593                @Override
594                public Bitmap getPreviewFrame(int downSampleFactor) {
595                    if (mCameraRootView == null || mTextureView == null) {
596                        return null;
597                    }
598                    // Gets the bitmap from the preview TextureView.
599                    Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
600                    return preview;
601                }
602
603                @Override
604                public Bitmap getPreviewOverlayAndControls() {
605                    Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
606                            mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
607                    Canvas canvas = new Canvas(overlays);
608                    mCameraRootView.draw(canvas);
609                    return overlays;
610                }
611
612                @Override
613                public Bitmap getScreenShot(int previewDownSampleFactor) {
614                    Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
615                            mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
616                    Canvas canvas = new Canvas(screenshot);
617                    canvas.drawARGB(255, 0, 0, 0);
618                    Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
619                    if (preview != null) {
620                        canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
621                    }
622                    Bitmap overlay = getPreviewOverlayAndControls();
623                    if (overlay != null) {
624                        canvas.drawBitmap(overlay, 0f, 0f, null);
625                    }
626                    return screenshot;
627                }
628            };
629
630    private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
631
632    public long getCoverHiddenTime() {
633        return mCoverHiddenTime;
634    }
635
636    /**
637     * This resets the preview to have no applied transform matrix.
638     */
639    public void clearPreviewTransform() {
640        mTextureViewHelper.clearTransform();
641    }
642
643    public void updatePreviewAspectRatio(float aspectRatio) {
644        mTextureViewHelper.updateAspectRatio(aspectRatio);
645    }
646
647    /**
648     * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
649     * its TextureView.  This is necessary to get the expected behavior for the TextureView's
650     * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
651     * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
652     * size).
653     *
654     * b/17286155 - Tracking a fix for this in HardwareLayer.
655     */
656    public void setDefaultBufferSizeToViewDimens() {
657        if (mSurface == null || mTextureView == null) {
658            Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
659            return;
660        }
661        mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
662    }
663
664    /**
665     * Updates the preview matrix without altering it.
666     *
667     * @param matrix
668     * @param aspectRatio the desired aspect ratio for the preview.
669     */
670    public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
671        mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
672    }
673
674    /**
675     * @return the rect that will display the preview.
676     */
677    public RectF getFullscreenRect() {
678        return mTextureViewHelper.getFullscreenRect();
679    }
680
681    /**
682     * This is to support modules that calculate their own transform matrix because
683     * they need to use a transform matrix to rotate the preview.
684     *
685     * @param matrix transform matrix to be set on the TextureView
686     */
687    public void updatePreviewTransform(Matrix matrix) {
688        mTextureViewHelper.updateTransform(matrix);
689    }
690
691    public interface AnimationFinishedListener {
692        public void onAnimationFinished(boolean success);
693    }
694
695    private class MyTouchListener implements View.OnTouchListener {
696        private boolean mScaleStarted = false;
697        @Override
698        public boolean onTouch(View v, MotionEvent event) {
699            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
700                mScaleStarted = false;
701            } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
702                mScaleStarted = true;
703            }
704            return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
705        }
706    }
707
708    /**
709     * This gesture listener finds out the direction of the scroll gestures and
710     * sends them to CameraAppUI to do further handling.
711     */
712    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
713        private MotionEvent mDown;
714
715        @Override
716        public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
717            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
718                    || mSwipeState != IDLE
719                    || mIsCaptureIntent
720                    || !mSwipeEnabled) {
721                return false;
722            }
723
724            int deltaX = (int) (ev.getX() - mDown.getX());
725            int deltaY = (int) (ev.getY() - mDown.getY());
726            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
727                if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
728                    // Calculate the direction of the swipe.
729                    if (deltaX >= Math.abs(deltaY)) {
730                        // Swipe right.
731                        setSwipeState(SWIPE_RIGHT);
732                    } else if (deltaX <= -Math.abs(deltaY)) {
733                        // Swipe left.
734                        setSwipeState(SWIPE_LEFT);
735                    }
736                }
737            }
738            return true;
739        }
740
741        private void setSwipeState(int swipeState) {
742            mSwipeState = swipeState;
743            // Notify new swipe detected.
744            onSwipeDetected(swipeState);
745        }
746
747        @Override
748        public boolean onDown(MotionEvent ev) {
749            mDown = MotionEvent.obtain(ev);
750            mSwipeState = IDLE;
751            return false;
752        }
753    }
754
755    public CameraAppUI(AppController controller, MainActivityLayout appRootView,
756            boolean isCaptureIntent) {
757        mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
758        mController = controller;
759        mIsCaptureIntent = isCaptureIntent;
760
761        mAppRootView = appRootView;
762        mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
763        mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
764        mModeTransitionView = (ModeTransitionView)
765                mAppRootView.findViewById(R.id.mode_transition_view);
766        mFilmstripBottomControls = new FilmstripBottomPanel(controller,
767                (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
768        mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
769        mGestureDetector = new GestureDetector(controller.getAndroidContext(),
770                new MyGestureListener());
771        Resources res = controller.getAndroidContext().getResources();
772        mCaptureLayoutHelper = new CaptureLayoutHelper(
773                res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
774                res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
775                res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
776        mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
777        if (mModeListView != null) {
778            mModeListView.setModeSwitchListener(this);
779            mModeListView.setModeListOpenListener(this);
780            mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
781            mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
782            boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
783                    SettingsManager.SCOPE_GLOBAL,
784                    Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
785            mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
786        } else {
787            Log.e(TAG, "Cannot find mode list in the view hierarchy");
788        }
789        mAnimationManager = new AnimationManager();
790        mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
791        mRoundedThumbnailView.setOnClickListener(new View.OnClickListener() {
792            @Override
793            public void onClick(View v) {
794                mFilmstripLayout.showFilmstrip();
795            }
796        });
797
798        mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
799        initDisplayListener();
800        mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
801        View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
802        modeListToggle.setOnClickListener(new View.OnClickListener() {
803            @Override
804            public void onClick(View view) {
805                openModeList();
806            }
807        });
808        View filmstripToggle = mAppRootView.findViewById(
809                R.id.accessibility_filmstrip_toggle_button);
810        filmstripToggle.setOnClickListener(new View.OnClickListener() {
811            @Override
812            public void onClick(View view) {
813                showFilmstrip();
814            }
815        });
816    }
817
818
819    /**
820     * Freeze what is currently shown on screen until the next preview frame comes
821     * in.
822     */
823    public void freezeScreenUntilPreviewReady() {
824        Log.v(TAG, "freezeScreenUntilPreviewReady");
825        mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
826                .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
827        mHideCoverRunnable = new Runnable() {
828            @Override
829            public void run() {
830                mModeTransitionView.hideImageCover();
831            }
832        };
833        mModeCoverState = COVER_SHOWN;
834    }
835
836    /**
837     * Creates a cling for the specific viewer and links the cling to the corresponding
838     * button for layout position.
839     *
840     * @param viewerType defines which viewer the cling is for.
841     */
842    public void setupClingForViewer(int viewerType) {
843        if (viewerType == BottomPanel.VIEWER_REFOCUS) {
844            FrameLayout filmstripContent = (FrameLayout) mAppRootView
845                    .findViewById(R.id.camera_filmstrip_content_layout);
846            if (filmstripContent != null) {
847                // Creates refocus cling.
848                LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
849                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
850                Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
851                // Sets instruction text in the cling.
852                refocusCling.setText(mController.getAndroidContext().getResources()
853                        .getString(R.string.cling_text_for_refocus_editor_button));
854
855                // Adds cling into view hierarchy.
856                int clingWidth = mController.getAndroidContext()
857                        .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
858                filmstripContent.addView(refocusCling, clingWidth,
859                        ViewGroup.LayoutParams.WRAP_CONTENT);
860                mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
861            }
862        }
863    }
864
865    /**
866     * Clears the listeners for the cling and remove it from the view hierarchy.
867     *
868     * @param viewerType defines which viewer the cling is for.
869     */
870    public void clearClingForViewer(int viewerType) {
871        Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
872        if (clingToBeRemoved == null) {
873            // No cling is created for the specific viewer type.
874            return;
875        }
876        mFilmstripBottomControls.clearClingForViewer(viewerType);
877        clingToBeRemoved.setVisibility(View.GONE);
878        mAppRootView.removeView(clingToBeRemoved);
879    }
880
881    /**
882     * Enable or disable swipe gestures. We want to disable them e.g. while we
883     * record a video.
884     */
885    public void setSwipeEnabled(boolean enabled) {
886        mSwipeEnabled = enabled;
887        // TODO: This can be removed once we come up with a new design for handling swipe
888        // on shutter button and mode options. (More details: b/13751653)
889        mAppRootView.setSwipeEnabled(enabled);
890    }
891
892    public void onDestroy() {
893        ((DisplayManager) mController.getAndroidContext()
894                .getSystemService(Context.DISPLAY_SERVICE))
895                .unregisterDisplayListener(mDisplayListener);
896    }
897
898    /**
899     * Initializes the display listener to listen to display changes such as
900     * 180-degree rotation change, which will not have an onConfigurationChanged
901     * callback.
902     */
903    private void initDisplayListener() {
904        if (ApiHelper.HAS_DISPLAY_LISTENER) {
905            mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
906
907            mDisplayListener = new DisplayManager.DisplayListener() {
908                @Override
909                public void onDisplayAdded(int arg0) {
910                    // Do nothing.
911                }
912
913                @Override
914                public void onDisplayChanged(int displayId) {
915                    int rotation = CameraUtil.getDisplayRotation(
916                            mController.getAndroidContext());
917                    if ((rotation - mLastRotation + 360) % 360 == 180
918                            && mPreviewStatusListener != null) {
919                        mPreviewStatusListener.onPreviewFlipped();
920                        mStickyBottomCaptureLayout.requestLayout();
921                        mModeListView.requestLayout();
922                        mTextureView.requestLayout();
923                    }
924                    mLastRotation = rotation;
925                }
926
927                @Override
928                public void onDisplayRemoved(int arg0) {
929                    // Do nothing.
930                }
931            };
932
933            ((DisplayManager) mController.getAndroidContext()
934                    .getSystemService(Context.DISPLAY_SERVICE))
935                    .registerDisplayListener(mDisplayListener, null);
936        }
937    }
938
939    /**
940     * Redirects touch events to appropriate recipient views based on swipe direction.
941     * More specifically, swipe up and swipe down will be handled by the view that handles
942     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
943     * to mode list in order to bring up mode list.
944     */
945    private void onSwipeDetected(int swipeState) {
946        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
947            // TODO: Polish quick switch after this release.
948            // Quick switch between modes.
949            int currentModuleIndex = mController.getCurrentModuleIndex();
950            final int moduleToTransitionTo =
951                    mController.getQuickSwitchToModuleId(currentModuleIndex);
952            if (currentModuleIndex != moduleToTransitionTo) {
953                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
954                int shadeColorId = R.color.mode_cover_default_color;
955                int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
956                        mController.getAndroidContext());
957
958                AnimationFinishedListener listener = new AnimationFinishedListener() {
959                    @Override
960                    public void onAnimationFinished(boolean success) {
961                        if (success) {
962                            mHideCoverRunnable = new Runnable() {
963                                @Override
964                                public void run() {
965                                    mModeTransitionView.startPeepHoleAnimation();
966                                }
967                            };
968                            mModeCoverState = COVER_SHOWN;
969                            // Go to new module when the previous operation is successful.
970                            mController.onModeSelected(moduleToTransitionTo);
971                        }
972                    }
973                };
974            }
975        } else if (swipeState == SWIPE_LEFT) {
976            // Pass the touch sequence to filmstrip layout.
977            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
978        } else if (swipeState == SWIPE_RIGHT) {
979            // Pass the touch to mode switcher
980            mAppRootView.redirectTouchEventsTo(mModeListView);
981        }
982    }
983
984    /**
985     * Gets called when activity resumes in preview.
986     */
987    public void resume() {
988        // Show mode theme cover until preview is ready
989        showModeCoverUntilPreviewReady();
990
991        // Hide action bar first since we are in full screen mode first, and
992        // switch the system UI to lights-out mode.
993        mFilmstripPanel.hide();
994
995        // Show UI that is meant to only be used when spoken feedback is
996        // enabled.
997        mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
998        mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
999    }
1000
1001    /**
1002     * @return Whether any spoken feedback accessibility feature is currently
1003     *         enabled.
1004     */
1005    private boolean isSpokenFeedbackAccessibilityEnabled() {
1006        AccessibilityManager accessibilityManager = (AccessibilityManager) mController
1007                .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1008        List<AccessibilityServiceInfo> infos = accessibilityManager
1009                .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1010        return infos != null && !infos.isEmpty();
1011    }
1012
1013    /**
1014     * Opens the mode list (e.g. because of the menu button being pressed) and
1015     * adapts the rest of the UI.
1016     */
1017    public void openModeList() {
1018        mModeOptionsOverlay.closeModeOptions();
1019        mModeListView.onMenuPressed();
1020    }
1021
1022    /**
1023     * A cover view showing the mode theme color and mode icon will be visible on
1024     * top of preview until preview is ready (i.e. camera preview is started and
1025     * the first frame has been received).
1026     */
1027    private void showModeCoverUntilPreviewReady() {
1028        int modeId = mController.getCurrentModuleIndex();
1029        int colorId = R.color.mode_cover_default_color;;
1030        int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1031        mModeTransitionView.setupModeCover(colorId, iconId);
1032        mHideCoverRunnable = new Runnable() {
1033            @Override
1034            public void run() {
1035                mModeTransitionView.hideModeCover(null);
1036                if (!mDisableAllUserInteractions) {
1037                    showShimmyDelayed();
1038                }
1039            }
1040        };
1041        mModeCoverState = COVER_SHOWN;
1042    }
1043
1044    private void showShimmyDelayed() {
1045        if (!mIsCaptureIntent) {
1046            // Show shimmy in SHIMMY_DELAY_MS
1047            mModeListView.showModeSwitcherHint();
1048        }
1049    }
1050
1051    private void hideModeCover() {
1052        if (mHideCoverRunnable != null) {
1053            mAppRootView.post(mHideCoverRunnable);
1054            mHideCoverRunnable = null;
1055        }
1056        mModeCoverState = COVER_HIDDEN;
1057        if (mCoverHiddenTime < 0) {
1058            mCoverHiddenTime = System.currentTimeMillis();
1059        }
1060    }
1061
1062
1063    public void onPreviewVisiblityChanged(int visibility) {
1064        if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1065            setIndicatorBottomBarWrapperVisible(false);
1066            mAccessibilityAffordances.setVisibility(View.GONE);
1067        } else {
1068            setIndicatorBottomBarWrapperVisible(true);
1069            if (mAccessibilityEnabled) {
1070                mAccessibilityAffordances.setVisibility(View.VISIBLE);
1071            } else {
1072                mAccessibilityAffordances.setVisibility(View.GONE);
1073            }
1074        }
1075    }
1076
1077    /**
1078     * Call to stop the preview from being rendered.
1079     */
1080    public void pausePreviewRendering() {
1081        mTextureView.setVisibility(View.INVISIBLE);
1082    }
1083
1084    /**
1085     * Call to begin rendering the preview again.
1086     */
1087    public void resumePreviewRendering() {
1088        mTextureView.setVisibility(View.VISIBLE);
1089    }
1090
1091    /**
1092     * Returns the transform associated with the preview view.
1093     *
1094     * @param m the Matrix in which to copy the current transform.
1095     * @return The specified matrix if not null or a new Matrix instance
1096     *         otherwise.
1097     */
1098    public Matrix getPreviewTransform(Matrix m) {
1099        return mTextureView.getTransform(m);
1100    }
1101
1102    @Override
1103    public void onOpenFullScreen() {
1104        // Do nothing.
1105    }
1106
1107    @Override
1108    public void onModeListOpenProgress(float progress) {
1109        progress = 1 - progress;
1110        float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1111        mModeOptionsToggle.setAlpha(interpolatedProgress);
1112        // Change shutter button alpha linearly based on the mode list open progress:
1113        // set the alpha to disabled alpha when list is fully open, to enabled alpha
1114        // when the list is fully closed.
1115        mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1116                + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1117    }
1118
1119    @Override
1120    public void onModeListClosed() {
1121        // Make sure the alpha on mode options ellipse is reset when mode drawer
1122        // is closed.
1123        mModeOptionsToggle.setAlpha(1f);
1124        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1125    }
1126
1127    /**
1128     * Called when the back key is pressed.
1129     *
1130     * @return Whether the UI responded to the key event.
1131     */
1132    public boolean onBackPressed() {
1133        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1134            return mFilmstripLayout.onBackPressed();
1135        } else {
1136            return mModeListView.onBackPressed();
1137        }
1138    }
1139
1140    /**
1141     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1142     * listens to SurfaceTexture changes. In addition, listeners are set on
1143     * dependent app ui elements.
1144     *
1145     * @param previewStatusListener the listener that gets notified when SurfaceTexture
1146     *                              changes
1147     */
1148    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1149        mPreviewStatusListener = previewStatusListener;
1150        if (mPreviewStatusListener != null) {
1151            onPreviewListenerChanged();
1152        }
1153    }
1154
1155    /**
1156     * When the PreviewStatusListener changes, listeners need to be
1157     * set on the following app ui elements:
1158     * {@link com.android.camera.ui.PreviewOverlay},
1159     * {@link com.android.camera.ui.BottomBar},
1160     * {@link com.android.camera.ui.IndicatorIconController}.
1161     */
1162    private void onPreviewListenerChanged() {
1163        // Set a listener for recognizing preview gestures.
1164        GestureDetector.OnGestureListener gestureListener
1165            = mPreviewStatusListener.getGestureListener();
1166        if (gestureListener != null) {
1167            mPreviewOverlay.setGestureListener(gestureListener);
1168        }
1169        View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1170        if (touchListener != null) {
1171            mPreviewOverlay.setTouchListener(touchListener);
1172        }
1173
1174        mTextureViewHelper.setAutoAdjustTransform(
1175                mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1176    }
1177
1178    /**
1179     * This method should be called in onCameraOpened.  It defines CameraAppUI
1180     * specific changes that depend on the camera or camera settings.
1181     */
1182    public void onChangeCamera() {
1183        ModuleController moduleController = mController.getCurrentModuleController();
1184        applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1185
1186        if (mIndicatorIconController != null) {
1187            // Sync the settings state with the indicator state.
1188            mIndicatorIconController.syncIndicators();
1189        }
1190    }
1191
1192    /**
1193     * Adds a listener to receive callbacks when preview area changes.
1194     */
1195    public void addPreviewAreaChangedListener(
1196            PreviewStatusListener.PreviewAreaChangedListener listener) {
1197        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1198    }
1199
1200    /**
1201     * Removes a listener that receives callbacks when preview area changes.
1202     */
1203    public void removePreviewAreaChangedListener(
1204            PreviewStatusListener.PreviewAreaChangedListener listener) {
1205        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1206    }
1207
1208    /**
1209     * This inflates generic_module layout, which contains all the shared views across
1210     * modules. Then each module inflates their own views in the given view group. For
1211     * now, this is called every time switching from a not-yet-refactored module to a
1212     * refactored module. In the future, this should only need to be done once per app
1213     * start.
1214     */
1215    public void prepareModuleUI() {
1216        mController.getSettingsManager().addListener(this);
1217        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1218        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1219        mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1220                mController.getCameraProvider());
1221        mTextureViewHelper.setSurfaceTextureListener(this);
1222        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1223
1224        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1225        int unpressedColor = mController.getAndroidContext().getResources()
1226            .getColor(R.color.bottombar_unpressed);
1227        setBottomBarColor(unpressedColor);
1228        updateModeSpecificUIColors();
1229
1230        mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1231
1232        mModeOptionsOverlay
1233            = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1234
1235        // Sets the visibility of the bottom bar and the mode options.
1236        resetBottomControls(mController.getCurrentModuleController(),
1237            mController.getCurrentModuleIndex());
1238        mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1239
1240        mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1241        addShutterListener(mController.getCurrentModuleController());
1242        addShutterListener(mModeOptionsOverlay);
1243        addShutterListener(this);
1244
1245        mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1246        mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1247
1248        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1249        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1250        mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1251
1252        mCaptureOverlay = (CaptureAnimationOverlay)
1253                mCameraRootView.findViewById(R.id.capture_overlay);
1254        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1255        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1256
1257        if (mIndicatorIconController == null) {
1258            mIndicatorIconController =
1259                new IndicatorIconController(mController, mAppRootView);
1260        }
1261
1262        mController.getButtonManager().load(mCameraRootView);
1263        mController.getButtonManager().setListener(mIndicatorIconController);
1264        mController.getSettingsManager().addListener(mIndicatorIconController);
1265
1266        mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1267        mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1268        mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1269                .findViewById(R.id.tutorials_placeholder_wrapper);
1270        mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1271                .findViewById(R.id.sticky_bottom_capture_layout);
1272        mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1273
1274        mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1275        mTextureViewHelper.addAspectRatioChangedListener(
1276                new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1277                    @Override
1278                    public void onPreviewAspectRatioChanged(float aspectRatio) {
1279                        mModeOptionsOverlay.requestLayout();
1280                        mBottomBar.requestLayout();
1281                    }
1282                }
1283        );
1284    }
1285
1286    /**
1287     * Called indirectly from each module in their initialization to get a view group
1288     * to inflate the module specific views in.
1289     *
1290     * @return a view group for modules to attach views to
1291     */
1292    public FrameLayout getModuleRootView() {
1293        // TODO: Change it to mModuleUI when refactor is done
1294        return mCameraRootView;
1295    }
1296
1297    /**
1298     * Remove all the module specific views.
1299     */
1300    public void clearModuleUI() {
1301        if (mModuleUI != null) {
1302            mModuleUI.removeAllViews();
1303        }
1304        removeShutterListener(mController.getCurrentModuleController());
1305        mTutorialsPlaceHolderWrapper.removeAllViews();
1306        mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1307
1308        setShutterButtonEnabled(true);
1309        mPreviewStatusListener = null;
1310        mPreviewOverlay.reset();
1311
1312        Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1313        mFocusRing.stopFocusAnimations();
1314    }
1315
1316    /**
1317     * Gets called when preview is ready to start. It sets up one shot preview callback
1318     * in order to receive a callback when the preview frame is available, so that
1319     * the preview cover can be hidden to reveal preview.
1320     *
1321     * An alternative for getting the timing to hide preview cover is through
1322     * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1323     * which is less accurate but therefore is the fallback for modules that manage
1324     * their own preview callbacks (as setting one preview callback will override
1325     * any other installed preview callbacks), or use camera2 API.
1326     */
1327    public void onPreviewReadyToStart() {
1328        if (mModeCoverState == COVER_SHOWN) {
1329            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1330            mController.setupOneShotPreviewListener();
1331        }
1332    }
1333
1334    /**
1335     * Gets called when preview is started.
1336     */
1337    public void onPreviewStarted() {
1338        Log.v(TAG, "onPreviewStarted");
1339        if (mModeCoverState == COVER_SHOWN) {
1340            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1341        }
1342        enableModeOptions();
1343    }
1344
1345    /**
1346     * Gets notified when next preview frame comes in.
1347     */
1348    public void onNewPreviewFrame() {
1349        Log.v(TAG, "onNewPreviewFrame");
1350        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1351        hideModeCover();
1352    }
1353
1354    @Override
1355    public void onShutterButtonClick() {
1356        /*
1357         * Set the mode options toggle unclickable, generally
1358         * throughout the app, whenever the shutter button is clicked.
1359         *
1360         * This could be done in the OnShutterButtonListener of the
1361         * ModeOptionsOverlay, but since it is very important that we
1362         * can clearly see when the toggle becomes clickable again,
1363         * keep all of that logic at this level.
1364         */
1365        // disableModeOptions();
1366    }
1367
1368    @Override
1369    public void onShutterCoordinate(TouchCoordinate coord) {
1370        // Do nothing.
1371    }
1372
1373    @Override
1374    public void onShutterButtonFocus(boolean pressed) {
1375        // noop
1376    }
1377
1378    @Override
1379    public void onShutterButtonLongPressed() {
1380        // noop
1381    }
1382
1383    /**
1384     * Set the mode options toggle clickable.
1385     */
1386    public void enableModeOptions() {
1387        /*
1388         * For modules using camera 1 api, this gets called in
1389         * onSurfaceTextureUpdated whenever the preview gets stopped and
1390         * started after each capture.  This also takes care of the
1391         * case where the mode options might be unclickable when we
1392         * switch modes
1393         *
1394         * For modules using camera 2 api, they're required to call this
1395         * method when a capture is "completed".  Unfortunately this differs
1396         * per module implementation.
1397         */
1398        if (!mDisableAllUserInteractions) {
1399            mModeOptionsOverlay.setToggleClickable(true);
1400        }
1401    }
1402
1403    /**
1404     * Set the mode options toggle not clickable.
1405     */
1406    public void disableModeOptions() {
1407        mModeOptionsOverlay.setToggleClickable(false);
1408    }
1409
1410    public void setDisableAllUserInteractions(boolean disable) {
1411        if (disable) {
1412            disableModeOptions();
1413            setShutterButtonEnabled(false);
1414            setSwipeEnabled(false);
1415            mModeListView.hideAnimated();
1416        } else {
1417            enableModeOptions();
1418            setShutterButtonEnabled(true);
1419            setSwipeEnabled(true);
1420        }
1421        mDisableAllUserInteractions = disable;
1422    }
1423
1424    @Override
1425    public void onModeButtonPressed(int modeIndex) {
1426        // TODO: Make CameraActivity listen to ModeListView's events.
1427        int pressedModuleId = mController.getModuleId(modeIndex);
1428        int currentModuleId = mController.getCurrentModuleIndex();
1429        if (pressedModuleId != currentModuleId) {
1430            hideCaptureIndicator();
1431        }
1432    }
1433
1434    /**
1435     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1436     *
1437     * @param modeIndex mode index of the selected mode
1438     */
1439    @Override
1440    public void onModeSelected(int modeIndex) {
1441        mHideCoverRunnable = new Runnable() {
1442            @Override
1443            public void run() {
1444                mModeListView.startModeSelectionAnimation();
1445            }
1446        };
1447        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1448        mModeCoverState = COVER_SHOWN;
1449
1450        int lastIndex = mController.getCurrentModuleIndex();
1451        // Actual mode teardown / new mode initialization happens here
1452        mController.onModeSelected(modeIndex);
1453        int currentIndex = mController.getCurrentModuleIndex();
1454
1455        if (lastIndex == currentIndex) {
1456            hideModeCover();
1457        }
1458
1459        updateModeSpecificUIColors();
1460    }
1461
1462    private void updateModeSpecificUIColors() {
1463        setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1464    }
1465
1466    @Override
1467    public void onSettingsSelected() {
1468        mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1469                                             Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1470        mModeListView.setShouldShowSettingsCling(false);
1471        mController.onSettingsSelected();
1472    }
1473
1474    @Override
1475    public int getCurrentModeIndex() {
1476        return mController.getCurrentModuleIndex();
1477    }
1478
1479    /********************** Capture animation **********************/
1480    /* TODO: This session is subject to UX changes. In addition to the generic
1481       flash animation and post capture animation, consider designating a parameter
1482       for specifying the type of animation, as well as an animation finished listener
1483       so that modules can have more knowledge of the status of the animation. */
1484
1485    /**
1486     * Starts the capture indicator pop-out animation.
1487     *
1488     * @param accessibilityString An accessibility String to be announced during the peek animation.
1489     */
1490    public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1491        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1492            return;
1493        }
1494        mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1495    }
1496
1497    /**
1498     * Updates the thumbnail image in the capture indicator.
1499     *
1500     * @param thumbnailBitmap The thumbnail image to be shown.
1501     */
1502    public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap) {
1503        mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1504    }
1505
1506    /**
1507     * Hides the capture indicator.
1508     */
1509    public void hideCaptureIndicator() {
1510        mRoundedThumbnailView.hideThumbnail();
1511    }
1512
1513    /**
1514     * Starts the flash animation.
1515     */
1516    public void startFlashAnimation(boolean shortFlash) {
1517        mCaptureOverlay.startFlashAnimation(shortFlash);
1518    }
1519
1520    /**
1521     * Cancels the pre-capture animation.
1522     */
1523    public void cancelPreCaptureAnimation() {
1524        mAnimationManager.cancelAnimations();
1525    }
1526
1527    /**
1528     * Cancels the post-capture animation.
1529     */
1530    public void cancelPostCaptureAnimation() {
1531        mAnimationManager.cancelAnimations();
1532    }
1533
1534    public FilmstripContentPanel getFilmstripContentPanel() {
1535        return mFilmstripPanel;
1536    }
1537
1538    /**
1539     * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1540     * bottom of the filmstrip.
1541     */
1542    public BottomPanel getFilmstripBottomControls() {
1543        return mFilmstripBottomControls;
1544    }
1545
1546    public void showBottomControls() {
1547        mFilmstripBottomControls.show();
1548    }
1549
1550    public void hideBottomControls() {
1551        mFilmstripBottomControls.hide();
1552    }
1553
1554    /**
1555     * @param listener The listener for bottom controls.
1556     */
1557    public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1558        mFilmstripBottomControls.setListener(listener);
1559    }
1560
1561    /***************************SurfaceTexture Api and Listener*********************************/
1562
1563    /**
1564     * Return the shared surface texture.
1565     */
1566    public SurfaceTexture getSurfaceTexture() {
1567        return mSurface;
1568    }
1569
1570    /**
1571     * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1572     */
1573    public int getSurfaceWidth() {
1574        return mSurfaceWidth;
1575    }
1576
1577    /**
1578     * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1579     */
1580    public int getSurfaceHeight() {
1581        return mSurfaceHeight;
1582    }
1583
1584    @Override
1585    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1586        mSurface = surface;
1587        mSurfaceWidth = width;
1588        mSurfaceHeight = height;
1589        Log.v(TAG, "SurfaceTexture is available");
1590        if (mPreviewStatusListener != null) {
1591            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1592        }
1593        enableModeOptions();
1594    }
1595
1596    @Override
1597    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1598        mSurface = surface;
1599        mSurfaceWidth = width;
1600        mSurfaceHeight = height;
1601        if (mPreviewStatusListener != null) {
1602            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1603        }
1604    }
1605
1606    @Override
1607    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1608        mSurface = null;
1609        Log.v(TAG, "SurfaceTexture is destroyed");
1610        if (mPreviewStatusListener != null) {
1611            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1612        }
1613        return false;
1614    }
1615
1616    @Override
1617    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1618        mSurface = surface;
1619        if (mPreviewStatusListener != null) {
1620            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1621        }
1622        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1623            Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1624            CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1625            hideModeCover();
1626        }
1627    }
1628
1629    /****************************Grid lines api ******************************/
1630
1631    /**
1632     * Show a set of evenly spaced lines over the preview.  The number
1633     * of lines horizontally and vertically is determined by
1634     * {@link com.android.camera.ui.GridLines}.
1635     */
1636    public void showGridLines() {
1637        if (mGridLines != null) {
1638            mGridLines.setVisibility(View.VISIBLE);
1639        }
1640    }
1641
1642    /**
1643     * Hide the set of evenly spaced grid lines overlaying the preview.
1644     */
1645    public void hideGridLines() {
1646        if (mGridLines != null) {
1647            mGridLines.setVisibility(View.INVISIBLE);
1648        }
1649    }
1650
1651    /**
1652     * Return a callback which shows or hide the preview grid lines
1653     * depending on whether the grid lines setting is set on.
1654     */
1655    public ButtonManager.ButtonCallback getGridLinesCallback() {
1656        return new ButtonManager.ButtonCallback() {
1657            @Override
1658            public void onStateChanged(int state) {
1659                if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1660                    showGridLines();
1661                } else {
1662                    hideGridLines();
1663                }
1664            }
1665        };
1666    }
1667
1668    /***************************Mode options api *****************************/
1669
1670    /**
1671     * Set the mode options visible.
1672     */
1673    public void showModeOptions() {
1674        /* Make mode options clickable. */
1675        enableModeOptions();
1676        mModeOptionsOverlay.setVisibility(View.VISIBLE);
1677    }
1678
1679    /**
1680     * Set the mode options invisible.  This is necessary for modes
1681     * that don't show a bottom bar for the capture UI.
1682     */
1683    public void hideModeOptions() {
1684        mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1685    }
1686
1687    /****************************Bottom bar api ******************************/
1688
1689    /**
1690     * Sets up the bottom bar and mode options with the correct
1691     * shutter button and visibility based on the current module.
1692     */
1693    public void resetBottomControls(ModuleController module, int moduleIndex) {
1694        if (areBottomControlsUsed(module)) {
1695            setBottomBarShutterIcon(moduleIndex);
1696            mCaptureLayoutHelper.setShowBottomBar(true);
1697        } else {
1698            mCaptureLayoutHelper.setShowBottomBar(false);
1699        }
1700    }
1701
1702    /**
1703     * Show or hide the mode options and bottom bar, based on
1704     * whether the current module is using the bottom bar.  Returns
1705     * whether the mode options and bottom bar are used.
1706     */
1707    private boolean areBottomControlsUsed(ModuleController module) {
1708        if (module.isUsingBottomBar()) {
1709            showBottomBar();
1710            showModeOptions();
1711            return true;
1712        } else {
1713            hideBottomBar();
1714            hideModeOptions();
1715            return false;
1716        }
1717    }
1718
1719    /**
1720     * Set the bottom bar visible.
1721     */
1722    public void showBottomBar() {
1723        mBottomBar.setVisibility(View.VISIBLE);
1724    }
1725
1726    /**
1727     * Set the bottom bar invisible.
1728     */
1729    public void hideBottomBar() {
1730        mBottomBar.setVisibility(View.INVISIBLE);
1731    }
1732
1733    /**
1734     * Sets the color of the bottom bar.
1735     */
1736    public void setBottomBarColor(int colorId) {
1737        mBottomBar.setBackgroundColor(colorId);
1738    }
1739
1740    /**
1741     * Sets the pressed color of the bottom bar for a camera mode index.
1742     */
1743    public void setBottomBarColorsForModeIndex(int index) {
1744        mBottomBar.setColorsForModeIndex(index);
1745    }
1746
1747    /**
1748     * Sets the shutter button icon on the bottom bar, based on
1749     * the mode index.
1750     */
1751    public void setBottomBarShutterIcon(int modeIndex) {
1752        int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1753            mController.getAndroidContext());
1754        mBottomBar.setShutterButtonIcon(shutterIconId);
1755    }
1756
1757    public void animateBottomBarToVideoStop(int shutterIconId) {
1758        mBottomBar.animateToVideoStop(shutterIconId);
1759    }
1760
1761    public void animateBottomBarToFullSize(int shutterIconId) {
1762        mBottomBar.animateToFullSize(shutterIconId);
1763    }
1764
1765    public void setShutterButtonEnabled(final boolean enabled) {
1766        if (!mDisableAllUserInteractions) {
1767            mBottomBar.post(new Runnable() {
1768                @Override
1769                public void run() {
1770                    mBottomBar.setShutterButtonEnabled(enabled);
1771                }
1772            });
1773        }
1774    }
1775
1776    public void setShutterButtonImportantToA11y(boolean important) {
1777        mBottomBar.setShutterButtonImportantToA11y(important);
1778    }
1779
1780    public boolean isShutterButtonEnabled() {
1781        return mBottomBar.isShutterButtonEnabled();
1782    }
1783
1784    public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1785        mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1786    }
1787
1788    /**
1789     * Set the visibility of the bottom bar.
1790     */
1791    // TODO: needed for when panorama is managed by the generic module ui.
1792    public void setBottomBarVisible(boolean visible) {
1793        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1794    }
1795
1796    /**
1797     * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1798     */
1799    public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1800        mShutterButton.addOnShutterButtonListener(listener);
1801    }
1802
1803    /**
1804     * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1805     */
1806    public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1807        mShutterButton.removeOnShutterButtonListener(listener);
1808    }
1809
1810    /**
1811     * Performs a transition to the capture layout of the bottom bar.
1812     */
1813    public void transitionToCapture() {
1814        ModuleController moduleController = mController.getCurrentModuleController();
1815        applyModuleSpecs(moduleController.getHardwareSpec(),
1816            moduleController.getBottomBarSpec());
1817        mBottomBar.transitionToCapture();
1818    }
1819
1820    /**
1821     * Displays the Cancel button instead of the capture button.
1822     */
1823    public void transitionToCancel() {
1824        ModuleController moduleController = mController.getCurrentModuleController();
1825        applyModuleSpecs(moduleController.getHardwareSpec(),
1826                moduleController.getBottomBarSpec());
1827        mBottomBar.transitionToCancel();
1828    }
1829
1830    /**
1831     * Performs a transition to the global intent layout.
1832     */
1833    public void transitionToIntentCaptureLayout() {
1834        ModuleController moduleController = mController.getCurrentModuleController();
1835        applyModuleSpecs(moduleController.getHardwareSpec(),
1836            moduleController.getBottomBarSpec());
1837        mBottomBar.transitionToIntentCaptureLayout();
1838    }
1839
1840    /**
1841     * Performs a transition to the global intent review layout.
1842     */
1843    public void transitionToIntentReviewLayout() {
1844        ModuleController moduleController = mController.getCurrentModuleController();
1845        applyModuleSpecs(moduleController.getHardwareSpec(),
1846            moduleController.getBottomBarSpec());
1847        mBottomBar.transitionToIntentReviewLayout();
1848    }
1849
1850    /**
1851     * @return whether UI is in intent review mode
1852     */
1853    public boolean isInIntentReview() {
1854        return mBottomBar.isInIntentReview();
1855    }
1856
1857    @Override
1858    public void onSettingChanged(SettingsManager settingsManager, String key) {
1859        // Update the mode options based on the hardware spec,
1860        // when hdr changes to prevent flash from getting out of sync.
1861        if (key.equals(Keys.KEY_CAMERA_HDR)) {
1862            ModuleController moduleController = mController.getCurrentModuleController();
1863            applyModuleSpecs(moduleController.getHardwareSpec(),
1864                             moduleController.getBottomBarSpec());
1865        }
1866    }
1867
1868    /**
1869     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1870     * to the bottom bar mode options based on limitations from a
1871     * {@link com.android.camera.hardware.HardwareSpec}.
1872     *
1873     * Options not supported by the hardware are either hidden
1874     * or disabled, depending on the option.
1875     *
1876     * Otherwise, the option is fully enabled and clickable.
1877     */
1878    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1879           final BottomBarUISpec bottomBarSpec) {
1880        if (hardwareSpec == null || bottomBarSpec == null) {
1881            return;
1882        }
1883
1884        ButtonManager buttonManager = mController.getButtonManager();
1885        SettingsManager settingsManager = mController.getSettingsManager();
1886
1887        buttonManager.setToInitialState();
1888
1889        /** Standard mode options */
1890        if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1891                hardwareSpec.isFrontCameraSupported()) {
1892            if (bottomBarSpec.enableCamera) {
1893                buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1894                        bottomBarSpec.cameraCallback);
1895            } else {
1896                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1897            }
1898        } else {
1899            // Hide camera icon if front camera not available.
1900            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1901        }
1902
1903        boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1904            SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
1905        if (bottomBarSpec.hideFlash || !flashBackCamera) {
1906            // Hide both flash and torch button in flash disable logic
1907            buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1908            buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1909        } else {
1910            if (hardwareSpec.isFlashSupported()) {
1911                if (bottomBarSpec.enableFlash) {
1912                    buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1913                        bottomBarSpec.flashCallback);
1914                } else if (bottomBarSpec.enableTorchFlash) {
1915                    buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1916                        bottomBarSpec.flashCallback);
1917                } else if (bottomBarSpec.enableHdrPlusFlash) {
1918                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1919                        bottomBarSpec.flashCallback);
1920                } else {
1921                    // Hide both flash and torch button in flash disable logic
1922                    buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1923                    buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1924                }
1925            } else {
1926                // Disable both flash and torch icon if not supported
1927                // by the chosen camera hardware.
1928                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1929                buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1930            }
1931        }
1932
1933        if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1934            // Force hide hdr or hdr plus icon.
1935            buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1936        } else {
1937            if (hardwareSpec.isHdrPlusSupported()) {
1938                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1939                                                                       mController.getModuleScope())) {
1940                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1941                            bottomBarSpec.hdrCallback);
1942                } else {
1943                    buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1944                }
1945            } else if (hardwareSpec.isHdrSupported()) {
1946                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1947                                                                       mController.getModuleScope())) {
1948                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1949                            bottomBarSpec.hdrCallback);
1950                } else {
1951                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1952                }
1953            } else {
1954                // Hide hdr plus or hdr icon if neither are supported.
1955                buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1956            }
1957        }
1958
1959        if (bottomBarSpec.hideGridLines) {
1960            // Force hide grid lines icon.
1961            buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1962            hideGridLines();
1963        } else {
1964            if (bottomBarSpec.enableGridLines) {
1965                buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1966                        bottomBarSpec.gridLinesCallback != null ?
1967                                bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1968                );
1969            } else {
1970                buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1971                hideGridLines();
1972            }
1973        }
1974
1975        if (bottomBarSpec.enableSelfTimer) {
1976            buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1977        } else {
1978            if (bottomBarSpec.showSelfTimer) {
1979                buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1980            } else {
1981                buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1982            }
1983        }
1984
1985        if (bottomBarSpec.enablePanoOrientation
1986                && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1987            buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1988        }
1989
1990        boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1991            !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1992            mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1993                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1994        if (enableExposureCompensation) {
1995            buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1996            buttonManager.setExposureCompensationParameters(
1997                bottomBarSpec.minExposureCompensation,
1998                bottomBarSpec.maxExposureCompensation,
1999                bottomBarSpec.exposureCompensationStep);
2000
2001            buttonManager.setExposureCompensationCallback(
2002                    bottomBarSpec.exposureCompensationSetCallback);
2003            buttonManager.updateExposureButtons();
2004        } else {
2005            buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2006            buttonManager.setExposureCompensationCallback(null);
2007        }
2008
2009        /** Intent UI */
2010        if (bottomBarSpec.showCancel) {
2011            buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2012                    bottomBarSpec.cancelCallback);
2013        }
2014        if (bottomBarSpec.showDone) {
2015            buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2016                    bottomBarSpec.doneCallback);
2017        }
2018        if (bottomBarSpec.showRetake) {
2019            buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2020                    bottomBarSpec.retakeCallback);
2021        }
2022        if (bottomBarSpec.showReview) {
2023            buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2024                    bottomBarSpec.reviewCallback,
2025                    R.drawable.ic_play);
2026        }
2027    }
2028
2029    /**
2030     * Shows the given tutorial on the screen.
2031     */
2032    public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2033        tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2034    }
2035
2036    /***************************Filmstrip api *****************************/
2037
2038    public void showFilmstrip() {
2039        mModeListView.onBackPressed();
2040        mFilmstripLayout.showFilmstrip();
2041    }
2042
2043    public void hideFilmstrip() {
2044        mFilmstripLayout.hideFilmstrip();
2045    }
2046}
2047