CameraAppUI.java revision d5e51466a2848f1c937656c8f8185290034c2429
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.app;
18
19import android.content.Context;
20import android.graphics.Matrix;
21import android.graphics.SurfaceTexture;
22import android.hardware.display.DisplayManager;
23import android.util.Log;
24import android.view.GestureDetector;
25import android.view.LayoutInflater;
26import android.view.MotionEvent;
27import android.view.TextureView;
28import android.view.View;
29import android.view.ViewConfiguration;
30import android.view.ViewGroup;
31import android.widget.FrameLayout;
32
33import com.android.camera.AnimationManager;
34import com.android.camera.ButtonManager;
35import com.android.camera.ShutterButton;
36import com.android.camera.TextureViewHelper;
37import com.android.camera.filmstrip.FilmstripContentPanel;
38import com.android.camera.hardware.HardwareSpec;
39import com.android.camera.module.ModuleController;
40import com.android.camera.settings.SettingsManager;
41import com.android.camera.ui.BottomBar;
42import com.android.camera.ui.CaptureAnimationOverlay;
43import com.android.camera.ui.GridLines;
44import com.android.camera.ui.MainActivityLayout;
45import com.android.camera.ui.ModeListView;
46import com.android.camera.ui.ModeTransitionView;
47import com.android.camera.ui.PreviewOverlay;
48import com.android.camera.ui.PreviewStatusListener;
49import com.android.camera.util.ApiHelper;
50import android.util.CameraPerformanceTracker;
51import com.android.camera.util.CameraUtil;
52import com.android.camera.util.PhotoSphereHelper;
53import com.android.camera.util.UsageStatistics;
54import com.android.camera.widget.FilmstripLayout;
55import com.android.camera.widget.IndicatorIconController;
56import com.android.camera.widget.ModeOptionsOverlay;
57import com.android.camera2.R;
58import com.google.common.logging.eventprotos;
59
60/**
61 * CameraAppUI centralizes control of views shared across modules. Whereas module
62 * specific views will be handled in each Module UI. For example, we can now
63 * bring the flash animation and capture animation up from each module to app
64 * level, as these animations are largely the same for all modules.
65 *
66 * This class also serves to disambiguate touch events. It recognizes all the
67 * swipe gestures that happen on the preview by attaching a touch listener to
68 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
69 * of how swipe from each direction should be handled, it can then redirect these
70 * events to appropriate recipient views.
71 */
72public class CameraAppUI implements ModeListView.ModeSwitchListener,
73        TextureView.SurfaceTextureListener, ModeListView.ModeListOpenListener {
74
75    /**
76     * The bottom controls on the filmstrip.
77     */
78    public static interface BottomControls {
79        /** Values for the view state of the button. */
80        public final int VIEWER_NONE = 0;
81        public final int VIEWER_PHOTO_SPHERE = 1;
82        public final int VIEWER_REFOCUS = 2;
83        public final int VIEWER_OTHER = 3;
84
85        /**
86         * Sets a new or replaces an existing listener for bottom control events.
87         */
88        void setListener(Listener listener);
89
90        /**
91         * Set if the bottom controls are visible.
92         * @param visible {@code true} if visible.
93         */
94        void setVisible(boolean visible);
95
96        /**
97         * @param visible Whether the button is visible.
98         */
99        void setEditButtonVisibility(boolean visible);
100
101        /**
102         * @param enabled Whether the button is enabled.
103         */
104        void setEditEnabled(boolean enabled);
105
106        /**
107         * Sets the visibility of the view-photosphere button.
108         *
109         * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
110         *            {@link #VIEWER_REFOCUS}.
111         */
112        void setViewerButtonVisibility(int state);
113
114        /**
115         * @param enabled Whether the button is enabled.
116         */
117        void setViewEnabled(boolean enabled);
118
119        /**
120         * @param enabled Whether the button is enabled.
121         */
122        void setTinyPlanetEnabled(boolean enabled);
123
124        /**
125         * @param visible Whether the button is visible.
126         */
127        void setDeleteButtonVisibility(boolean visible);
128
129        /**
130         * @param enabled Whether the button is enabled.
131         */
132        void setDeleteEnabled(boolean enabled);
133
134        /**
135         * @param visible Whether the button is visible.
136         */
137        void setShareButtonVisibility(boolean visible);
138
139        /**
140         * @param enabled Whether the button is enabled.
141         */
142        void setShareEnabled(boolean enabled);
143
144        /**
145         * Classes implementing this interface can listen for events on the bottom
146         * controls.
147         */
148        public static interface Listener {
149            /**
150             * Called when the user pressed the "view" button to e.g. view a photo
151             * sphere or RGBZ image.
152             */
153            public void onExternalViewer();
154
155            /**
156             * Called when the "edit" button is pressed.
157             */
158            public void onEdit();
159
160            /**
161             * Called when the "tiny planet" button is pressed.
162             */
163            public void onTinyPlanet();
164
165            /**
166             * Called when the "delete" button is pressed.
167             */
168            public void onDelete();
169
170            /**
171             * Called when the "share" button is pressed.
172             */
173            public void onShare();
174        }
175    }
176
177    /**
178     * BottomBarUISpec provides a structure for modules
179     * to specify their ideal bottom bar mode options layout.
180     *
181     * Once constructed by a module, this class should be
182     * treated as read only.
183     *
184     * The application then edits this spec according to
185     * hardware limitations and displays the final bottom
186     * bar ui.
187     */
188    public static class BottomBarUISpec {
189        /** Mode options UI */
190
191        /**
192         * Set true if the camera option should be enabled.
193         * If not set or false, and multiple cameras are supported,
194         * the camera option will be disabled.
195         *
196         * If multiple cameras are not supported, this preference
197         * is ignored and the camera option will not be visible.
198         */
199        public boolean enableCamera;
200
201        /**
202         * Set true if the photo flash option should be enabled.
203         * If not set or false, the photo flash option will be
204         * disabled.
205         *
206         * If the hardware does not support multiple flash values,
207         * this preference is ignored and the flash option will
208         * be disabled.  It will not be made invisible in order to
209         * preserve a consistent experience across devices and between
210         * front and back cameras.
211         */
212        public boolean enableFlash;
213
214        /**
215         * Set true if the video flash option should be enabled.
216         * Same disable rules apply as the photo flash option.
217         */
218        public boolean enableTorchFlash;
219
220        /**
221         * Set true if flash should not be visible, regardless of
222         * hardware limitations.
223         */
224        public boolean hideFlash;
225
226        /**
227         * Set true if the hdr/hdr+ option should be enabled.
228         * If not set or false, the hdr/hdr+ option will be disabled.
229         *
230         * Hdr or hdr+ will be chosen based on hardware limitations,
231         * with hdr+ prefered.
232         *
233         * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
234         * will not be visible.
235         */
236        public boolean enableHdr;
237
238        /**
239         * Set true if hdr/hdr+ should not be visible, regardless of
240         * hardware limitations.
241         */
242        public boolean hideHdr;
243
244        /**
245         * Set true if grid lines should be visible.  Not setting this
246         * causes grid lines to be disabled.  This option is agnostic to
247         * the hardware.
248         */
249        public boolean enableGridLines;
250
251        /**
252         * Set true if grid lines should not be visible.
253         */
254        public boolean hideGridLines;
255
256        /**
257         * Set true if the panorama horizontal option should be visible.
258         *
259         * This option is not constrained by hardware limitations.
260         */
261        public boolean enablePanoHorizontal;
262
263        /**
264         * Set true if the panorama vertical option should be visible.
265         *
266         * This option is not constrained by hardware limitations.
267         */
268        public boolean enablePanoVertical;
269
270        /** Intent UI */
271
272        /**
273         * Set true if the intent ui cancel option should be visible.
274         */
275        public boolean showCancel;
276        /**
277         * Set true if the intent ui done option should be visible.
278         */
279        public boolean showDone;
280        /**
281         * Set true if the intent ui retake option should be visible.
282         */
283        public boolean showRetake;
284        /**
285         * Set true if the intent ui review option should be visible.
286         */
287        public boolean showReview;
288
289        /** Mode options callbacks */
290
291        /**
292         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
293         * that will be executed when the camera option is pressed. This
294         * callback can be null.
295         */
296        public ButtonManager.ButtonCallback cameraCallback;
297
298        /**
299         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
300         * that will be executed when the flash option is pressed. This
301         * callback can be null.
302         */
303        public ButtonManager.ButtonCallback flashCallback;
304
305        /**
306         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
307         * that will be executed when the hdr/hdr+ option is pressed. This
308         * callback can be null.
309         */
310        public ButtonManager.ButtonCallback hdrCallback;
311
312        /**
313         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
314         * that will be executed when the grid lines option is pressed. This
315         * callback can be null.
316         */
317        public ButtonManager.ButtonCallback gridLinesCallback;
318
319        /**
320         * A {@link android.view.View.OnClickListener} that will execute
321         * when the panorama horizontal option is pressed.
322         * This callback can be null.
323         */
324        public View.OnClickListener panoHorizontalCallback;
325
326        /**
327         * A {@link android.view.View.OnClickListener} that will execute
328         * when the panorama vertical option is pressed.
329         * This callback can be null.
330         */
331        public View.OnClickListener panoVerticalCallback;
332
333        /** Intent UI callbacks */
334
335        /**
336         * A {@link android.view.View.OnClickListener} that will execute
337         * when the cancel option is pressed. This callback can be null.
338         */
339        public View.OnClickListener cancelCallback;
340
341        /**
342         * A {@link android.view.View.OnClickListener} that will execute
343         * when the done option is pressed. This callback can be null.
344         */
345        public View.OnClickListener doneCallback;
346
347        /**
348         * A {@link android.view.View.OnClickListener} that will execute
349         * when the retake option is pressed. This callback can be null.
350         */
351        public View.OnClickListener retakeCallback;
352
353        /**
354         * A {@link android.view.View.OnClickListener} that will execute
355         * when the review option is pressed. This callback can be null.
356         */
357        public View.OnClickListener reviewCallback;
358    }
359
360
361    private final static String TAG = "CameraAppUI";
362
363    private final AppController mController;
364    private final boolean mIsCaptureIntent;
365    private final AnimationManager mAnimationManager;
366
367    // Swipe states:
368    private final static int IDLE = 0;
369    private final static int SWIPE_UP = 1;
370    private final static int SWIPE_DOWN = 2;
371    private final static int SWIPE_LEFT = 3;
372    private final static int SWIPE_RIGHT = 4;
373    private boolean mSwipeEnabled = true;
374
375    // Shared Surface Texture properities.
376    private SurfaceTexture mSurface;
377    private int mSurfaceWidth;
378    private int mSurfaceHeight;
379
380    // Touch related measures:
381    private final int mSlop;
382    private final static int SWIPE_TIME_OUT_MS = 500;
383
384    private final static int SHIMMY_DELAY_MS = 1000;
385
386    // Mode cover states:
387    private final static int COVER_HIDDEN = 0;
388    private final static int COVER_SHOWN = 1;
389    private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
390    private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
391
392    // App level views:
393    private final FrameLayout mCameraRootView;
394    private final ModeTransitionView mModeTransitionView;
395    private final MainActivityLayout mAppRootView;
396    private final ModeListView mModeListView;
397    private final FilmstripLayout mFilmstripLayout;
398    private TextureView mTextureView;
399    private FrameLayout mModuleUI;
400    private BottomBar mBottomBar;
401    private ModeOptionsOverlay mModeOptionsOverlay;
402    private boolean mShouldShowShimmy = false;
403    private IndicatorIconController mIndicatorIconController;
404
405    private TextureViewHelper mTextureViewHelper;
406    private final GestureDetector mGestureDetector;
407    private DisplayManager.DisplayListener mDisplayListener;
408    private int mLastRotation;
409    private int mSwipeState = IDLE;
410    private PreviewOverlay mPreviewOverlay;
411    private GridLines mGridLines;
412    private CaptureAnimationOverlay mCaptureOverlay;
413    private PreviewStatusListener mPreviewStatusListener;
414    private int mModeCoverState = COVER_HIDDEN;
415    private final FilmstripBottomControls mFilmstripBottomControls;
416    private final FilmstripContentPanel mFilmstripPanel;
417    private Runnable mHideCoverRunnable;
418    private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
419            = new View.OnLayoutChangeListener() {
420        @Override
421        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
422                int oldTop, int oldRight, int oldBottom) {
423            if (mPreviewStatusListener != null) {
424                mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
425                        oldTop, oldRight, oldBottom);
426            }
427        }
428    };
429
430    private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
431
432    public long getCoverHiddenTime() {
433        return mCoverHiddenTime;
434    }
435
436    /**
437     * This resets the preview to have no applied transform matrix.
438     */
439    public void clearPreviewTransform() {
440        mTextureViewHelper.clearTransform();
441    }
442
443    public void updatePreviewAspectRatio(float aspectRatio) {
444        mTextureViewHelper.updateAspectRatio(aspectRatio);
445    }
446
447    /**
448     * This is to support modules that calculate their own transform matrix because
449     * they need to use a transform matrix to rotate the preview.
450     *
451     * @param matrix transform matrix to be set on the TextureView
452     */
453    public void updatePreviewTransform(Matrix matrix) {
454        mTextureViewHelper.updateTransform(matrix);
455    }
456
457    public interface AnimationFinishedListener {
458        public void onAnimationFinished(boolean success);
459    }
460
461    private class MyTouchListener implements View.OnTouchListener {
462        private boolean mScaleStarted = false;
463        @Override
464        public boolean onTouch(View v, MotionEvent event) {
465            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
466                mScaleStarted = false;
467            } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
468                mScaleStarted = true;
469            }
470            return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
471        }
472    }
473
474    /**
475     * This gesture listener finds out the direction of the scroll gestures and
476     * sends them to CameraAppUI to do further handling.
477     */
478    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
479        private MotionEvent mDown;
480
481        @Override
482        public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
483            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
484                    || mSwipeState != IDLE
485                    || mIsCaptureIntent
486                    || !mSwipeEnabled) {
487                return false;
488            }
489
490            int deltaX = (int) (ev.getX() - mDown.getX());
491            int deltaY = (int) (ev.getY() - mDown.getY());
492            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
493                if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
494                    // Calculate the direction of the swipe.
495                    if (deltaX >= Math.abs(deltaY)) {
496                        // Swipe right.
497                        setSwipeState(SWIPE_RIGHT);
498                    } else if (deltaX <= -Math.abs(deltaY)) {
499                        // Swipe left.
500                        setSwipeState(SWIPE_LEFT);
501                    } else if (deltaY >= Math.abs(deltaX)) {
502                        // Swipe down.
503                        setSwipeState(SWIPE_DOWN);
504                    } else if (deltaY <= -Math.abs(deltaX)) {
505                        // Swipe up.
506                        setSwipeState(SWIPE_UP);
507                    }
508                }
509            }
510            return true;
511        }
512
513        private void setSwipeState(int swipeState) {
514            mSwipeState = swipeState;
515            // Notify new swipe detected.
516            onSwipeDetected(swipeState);
517        }
518
519        @Override
520        public boolean onDown(MotionEvent ev) {
521            mDown = MotionEvent.obtain(ev);
522            mSwipeState = IDLE;
523            return false;
524        }
525    }
526
527    public CameraAppUI(AppController controller, MainActivityLayout appRootView,
528            boolean isCaptureIntent) {
529        mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
530        mController = controller;
531        mIsCaptureIntent = isCaptureIntent;
532
533        mAppRootView = appRootView;
534        mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
535        mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
536        mModeTransitionView = (ModeTransitionView)
537                mAppRootView.findViewById(R.id.mode_transition_view);
538        mFilmstripBottomControls = new FilmstripBottomControls(controller,
539                (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_controls));
540        mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
541        mGestureDetector = new GestureDetector(controller.getAndroidContext(),
542                new MyGestureListener());
543        mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
544        if (mModeListView != null) {
545            mModeListView.setModeSwitchListener(this);
546            mModeListView.setModeListOpenListener(this);
547        } else {
548            Log.e(TAG, "Cannot find mode list in the view hierarchy");
549        }
550        mAnimationManager = new AnimationManager();
551        initDisplayListener();
552    }
553
554    /**
555     * Enable or disable swipe gestures. We want to disable them e.g. while we
556     * record a video.
557     */
558    public void setSwipeEnabled(boolean enabled) {
559        mAppRootView.setSwipeEnabled(enabled);
560        mSwipeEnabled = enabled;
561    }
562
563    public void onDestroy() {
564        ((DisplayManager) mController.getAndroidContext()
565                .getSystemService(Context.DISPLAY_SERVICE))
566                .unregisterDisplayListener(mDisplayListener);
567    }
568
569    /**
570     * Initializes the display listener to listen to display changes such as
571     * 180-degree rotation change, which will not have an onConfigurationChanged
572     * callback.
573     */
574    private void initDisplayListener() {
575        if (ApiHelper.HAS_DISPLAY_LISTENER) {
576            mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
577
578            mDisplayListener = new DisplayManager.DisplayListener() {
579                @Override
580                public void onDisplayAdded(int arg0) {
581                    // Do nothing.
582                }
583
584                @Override
585                public void onDisplayChanged(int displayId) {
586                    int rotation = CameraUtil.getDisplayRotation(
587                            mController.getAndroidContext());
588                    if ((rotation - mLastRotation + 360) % 360 == 180
589                            && mPreviewStatusListener != null) {
590                        mPreviewStatusListener.onPreviewFlipped();
591                    }
592                    mLastRotation = rotation;
593                }
594
595                @Override
596                public void onDisplayRemoved(int arg0) {
597                    // Do nothing.
598                }
599            };
600
601            ((DisplayManager) mController.getAndroidContext()
602                    .getSystemService(Context.DISPLAY_SERVICE))
603                    .registerDisplayListener(mDisplayListener, null);
604        }
605    }
606
607    /**
608     * Redirects touch events to appropriate recipient views based on swipe direction.
609     * More specifically, swipe up and swipe down will be handled by the view that handles
610     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
611     * to mode list in order to bring up mode list.
612     */
613    private void onSwipeDetected(int swipeState) {
614        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
615            // Quick switch between modes.
616            int currentModuleIndex = mController.getCurrentModuleIndex();
617            final int moduleToTransitionTo =
618                    mController.getQuickSwitchToModuleId(currentModuleIndex);
619            if (currentModuleIndex != moduleToTransitionTo) {
620                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
621
622                int shadeColorId = CameraUtil.getCameraThemeColorId(moduleToTransitionTo,
623                        mController.getAndroidContext());
624                int iconRes = CameraUtil.getCameraModeIconResId(moduleToTransitionTo,
625                        mController.getAndroidContext());
626
627                AnimationFinishedListener listener = new AnimationFinishedListener() {
628                    @Override
629                    public void onAnimationFinished(boolean success) {
630                        if (success) {
631                            mHideCoverRunnable = new Runnable() {
632                                @Override
633                                public void run() {
634                                    mModeTransitionView.startPeepHoleAnimation();
635                                }
636                            };
637                            mModeCoverState = COVER_SHOWN;
638                            // Go to new module when the previous operation is successful.
639                            mController.onModeSelected(moduleToTransitionTo);
640                        }
641                    }
642                };
643                if (mSwipeState == SWIPE_UP) {
644                    mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
645                } else {
646                    mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
647                }
648            }
649        } else if (swipeState == SWIPE_LEFT) {
650            // Pass the touch sequence to filmstrip layout.
651            UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.FILMSTRIP,
652                eventprotos.CameraEvent.InteractionCause.SWIPE_LEFT);
653            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
654        } else if (swipeState == SWIPE_RIGHT) {
655            // Pass the touch to mode switcher
656            mAppRootView.redirectTouchEventsTo(mModeListView);
657        }
658    }
659
660    /**
661     * Gets called when activity resumes in preview.
662     */
663    public void resume() {
664        if (mTextureView == null || mTextureView.getSurfaceTexture() != null) {
665            if (!mIsCaptureIntent) {
666                showShimmyDelayed();
667            }
668        } else {
669            // Show mode theme cover until preview is ready
670            showModeCoverUntilPreviewReady();
671        }
672        // Hide action bar first since we are in full screen mode first, and
673        // switch the system UI to lights-out mode.
674        mFilmstripPanel.hide();
675    }
676
677    /**
678     * A cover view showing the mode theme color and mode icon will be visible on
679     * top of preview until preview is ready (i.e. camera preview is started and
680     * the first frame has been received).
681     */
682    private void showModeCoverUntilPreviewReady() {
683        int modeId = mController.getCurrentModuleIndex();
684        int colorId = CameraUtil.getCameraThemeColorId(modeId, mController.getAndroidContext());
685        int iconId = CameraUtil.getCameraModeIconResId(modeId, mController.getAndroidContext());
686        mModeTransitionView.setupModeCover(colorId, iconId);
687        mHideCoverRunnable = new Runnable() {
688            @Override
689            public void run() {
690                mModeTransitionView.hideModeCover(new AnimationFinishedListener() {
691                    @Override
692                    public void onAnimationFinished(boolean success) {
693                        if (success) {
694                            showShimmyDelayed();
695                        }
696                    }
697                });
698            }
699        };
700        mModeCoverState = COVER_SHOWN;
701    }
702
703    private void showShimmyDelayed() {
704        if (!mIsCaptureIntent) {
705            // Show shimmy in SHIMMY_DELAY_MS
706            mShouldShowShimmy = mController.shouldShowShimmy();
707            if (mShouldShowShimmy) {
708                mModeListView.startAccordionAnimationWithDelay(SHIMMY_DELAY_MS);
709            }
710        }
711    }
712
713    private void hideModeCover() {
714        if (mHideCoverRunnable != null) {
715            mAppRootView.post(mHideCoverRunnable);
716            mHideCoverRunnable = null;
717        }
718        mModeCoverState = COVER_HIDDEN;
719        if (mCoverHiddenTime < 0) {
720            mCoverHiddenTime = System.currentTimeMillis();
721        }
722    }
723
724    @Override
725    public void onOpenFullScreen() {
726        if (mShouldShowShimmy) {
727            mController.decrementShimmyPlayTimes();
728            // Sets should show shimmy flag to false for this session (i.e. until
729            // next onResume)
730            mShouldShowShimmy = false;
731        }
732    }
733
734    /**
735     * Called when the back key is pressed.
736     *
737     * @return Whether the UI responded to the key event.
738     */
739    public boolean onBackPressed() {
740        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
741            return mFilmstripLayout.onBackPressed();
742        } else {
743            return mModeListView.onBackPressed();
744        }
745    }
746
747    /**
748     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
749     * listens to SurfaceTexture changes. In addition, listeners are set on
750     * dependent app ui elements.
751     *
752     * @param previewStatusListener the listener that gets notified when SurfaceTexture
753     *                              changes
754     */
755    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
756        mPreviewStatusListener = previewStatusListener;
757        if (mPreviewStatusListener != null) {
758            onPreviewListenerChanged();
759        }
760    }
761
762    /**
763     * When the PreviewStatusListener changes, listeners need to be
764     * set on the following app ui elements:
765     * {@link com.android.camera.ui.PreviewOverlay},
766     * {@link com.android.camera.ui.BottomBar},
767     * {@link com.android.camera.ui.IndicatorIconController}.
768     */
769    private void onPreviewListenerChanged() {
770        // Set a listener for recognizing preview gestures.
771        GestureDetector.OnGestureListener gestureListener
772            = mPreviewStatusListener.getGestureListener();
773        if (gestureListener != null) {
774            mPreviewOverlay.setGestureListener(gestureListener);
775        }
776        View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
777        if (touchListener != null) {
778            mPreviewOverlay.setTouchListener(touchListener);
779        }
780
781        mTextureViewHelper.setAutoAdjustTransform(
782            mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
783    }
784
785    /**
786     * This method should be called in onCameraOpened.  It defines CameraAppUI
787     * specific changes that depend on the camera or camera settings.
788     */
789    public void onChangeCamera() {
790        ModuleController moduleController = mController.getCurrentModuleController();
791        applyModuleSpecs(moduleController.getHardwareSpec(),
792            moduleController.getBottomBarSpec());
793
794        if (mIndicatorIconController != null) {
795            // Sync the settings state with the indicator state.
796            mIndicatorIconController.syncIndicators();
797        }
798    }
799
800    /**
801     * Adds a listener to receive callbacks when preview area size changes.
802     */
803    public void addPreviewAreaSizeChangedListener(
804            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
805        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
806    }
807
808    /**
809     * Removes a listener that receives callbacks when preview area size changes.
810     */
811    public void removePreviewAreaSizeChangedListener(
812            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
813        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
814    }
815
816    /**
817     * This inflates generic_module layout, which contains all the shared views across
818     * modules. Then each module inflates their own views in the given view group. For
819     * now, this is called every time switching from a not-yet-refactored module to a
820     * refactored module. In the future, this should only need to be done once per app
821     * start.
822     */
823    public void prepareModuleUI() {
824        mCameraRootView.removeAllViews();
825        LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
826                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
827        inflater.inflate(R.layout.generic_module, mCameraRootView, true);
828
829        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
830        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
831        mTextureViewHelper = new TextureViewHelper(mTextureView);
832        mTextureViewHelper.setSurfaceTextureListener(this);
833        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
834
835        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
836        int unpressedColor = mController.getAndroidContext().getResources()
837            .getColor(R.color.bottombar_unpressed);
838        setBottomBarColor(unpressedColor);
839        int pressedColor = mController.getAndroidContext().getResources()
840            .getColor(R.color.bottombar_pressed);
841        setBottomBarPressedColor(pressedColor);
842
843        // Auto adjust the bottom bar for every module, so that panorama
844        // and photosphere can get a transparent bottom bar without any extra work.
845        // TODO: add and remove the bottom bar listener in onPreviewListenerChanged,
846        // based on whether the module needs to adjust the size of the bottom bar.
847        mTextureViewHelper.addPreviewAreaSizeChangedListener(mBottomBar);
848
849        mModeOptionsOverlay
850            = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
851        mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeOptionsOverlay);
852
853        // Sets the visibility of the bottom bar and the mode options.
854        resetBottomControls(mController.getCurrentModuleController(),
855            mController.getCurrentModuleIndex());
856
857        mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
858        mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
859
860        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
861        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
862        mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
863
864        mCaptureOverlay = (CaptureAnimationOverlay)
865                mCameraRootView.findViewById(R.id.capture_overlay);
866        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
867        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
868
869        if (mIndicatorIconController == null) {
870            mIndicatorIconController =
871                new IndicatorIconController(mController, mAppRootView);
872        }
873        mIndicatorIconController.setListener(mModeOptionsOverlay);
874
875        mController.getButtonManager().load(mCameraRootView);
876        mController.getButtonManager().setListener(mIndicatorIconController);
877        mController.getSettingsManager().addListener(mIndicatorIconController);
878    }
879
880    /**
881     * Called indirectly from each module in their initialization to get a view group
882     * to inflate the module specific views in.
883     *
884     * @return a view group for modules to attach views to
885     */
886    public FrameLayout getModuleRootView() {
887        // TODO: Change it to mModuleUI when refactor is done
888        return mCameraRootView;
889    }
890
891    /**
892     * Remove all the module specific views.
893     */
894    public void clearModuleUI() {
895        if (mModuleUI != null) {
896            mModuleUI.removeAllViews();
897        }
898        mTextureViewHelper.addPreviewAreaSizeChangedListener(null);
899
900        mPreviewStatusListener = null;
901        mPreviewOverlay.reset();
902    }
903
904    /**
905     * Gets called when preview is ready to start. It sets up one shot preview callback
906     * in order to receive a callback when the preview frame is available, so that
907     * the preview cover can be hidden to reveal preview.
908     *
909     * An alternative for getting the timing to hide preview cover is through
910     * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
911     * which is less accurate but therefore is the fallback for modules that manage
912     * their own preview callbacks (as setting one preview callback will override
913     * any other installed preview callbacks), or use camera2 API.
914     */
915    public void onPreviewReadyToStart() {
916        if (mModeCoverState == COVER_SHOWN) {
917            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
918            mController.setupOneShotPreviewListener();
919        }
920    }
921
922    /**
923     * Gets called when preview is started.
924     */
925    public void onPreviewStarted() {
926        if (mModeCoverState == COVER_SHOWN) {
927            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
928        }
929    }
930
931    /**
932     * Gets notified when next preview frame comes in.
933     */
934    public void onNewPreviewFrame() {
935        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
936        hideModeCover();
937        mModeCoverState = COVER_HIDDEN;
938    }
939
940    /**
941     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
942     *
943     * @param modeIndex mode index of the selected mode
944     */
945    @Override
946    public void onModeSelected(int modeIndex) {
947        mHideCoverRunnable = new Runnable() {
948            @Override
949            public void run() {
950                mModeListView.startModeSelectionAnimation();
951            }
952        };
953        mModeCoverState = COVER_SHOWN;
954
955        int lastIndex = mController.getCurrentModuleIndex();
956        mController.onModeSelected(modeIndex);
957        int currentIndex = mController.getCurrentModuleIndex();
958
959        if (lastIndex == currentIndex) {
960            hideModeCover();
961        }
962    }
963
964    /********************** Capture animation **********************/
965    /* TODO: This session is subject to UX changes. In addition to the generic
966       flash animation and post capture animation, consider designating a parameter
967       for specifying the type of animation, as well as an animation finished listener
968       so that modules can have more knowledge of the status of the animation. */
969
970    /**
971     * Starts the pre-capture animation.
972     */
973    public void startPreCaptureAnimation() {
974        mCaptureOverlay.startFlashAnimation();
975    }
976
977    /**
978     * Cancels the pre-capture animation.
979     */
980    public void cancelPreCaptureAnimation() {
981        mAnimationManager.cancelAnimations();
982    }
983
984    /**
985     * Cancels the post-capture animation.
986     */
987    public void cancelPostCaptureAnimation() {
988        mAnimationManager.cancelAnimations();
989    }
990
991    public FilmstripContentPanel getFilmstripContentPanel() {
992        return mFilmstripPanel;
993    }
994
995    /**
996     * @return The {@link com.android.camera.app.CameraAppUI.BottomControls} on the
997     * bottom of the filmstrip.
998     */
999    public BottomControls getFilmstripBottomControls() {
1000        return mFilmstripBottomControls;
1001    }
1002
1003    /**
1004     * @param listener The listener for bottom controls.
1005     */
1006    public void setFilmstripBottomControlsListener(BottomControls.Listener listener) {
1007        mFilmstripBottomControls.setListener(listener);
1008    }
1009
1010    /***************************SurfaceTexture Api and Listener*********************************/
1011
1012    /**
1013     * Return the shared surface texture.
1014     */
1015    public SurfaceTexture getSurfaceTexture() {
1016        return mSurface;
1017    }
1018
1019    /**
1020     * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1021     */
1022    public int getSurfaceWidth() {
1023        return mSurfaceWidth;
1024    }
1025
1026    /**
1027     * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1028     */
1029    public int getSurfaceHeight() {
1030        return mSurfaceHeight;
1031    }
1032
1033    @Override
1034    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1035        mSurface = surface;
1036        mSurfaceWidth = width;
1037        mSurfaceHeight = height;
1038        Log.v(TAG, "SurfaceTexture is available");
1039        if (mPreviewStatusListener != null) {
1040            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1041        }
1042    }
1043
1044    @Override
1045    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1046        mSurface = surface;
1047        mSurfaceWidth = width;
1048        mSurfaceHeight = height;
1049        if (mPreviewStatusListener != null) {
1050            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1051        }
1052    }
1053
1054    @Override
1055    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1056        mSurface = null;
1057        Log.v(TAG, "SurfaceTexture is destroyed");
1058        if (mPreviewStatusListener != null) {
1059            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1060        }
1061        return false;
1062    }
1063
1064    @Override
1065    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1066        mSurface = surface;
1067        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1068            CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1069            hideModeCover();
1070            mModeCoverState = COVER_HIDDEN;
1071        }
1072        if (mPreviewStatusListener != null) {
1073            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1074        }
1075    }
1076
1077    /****************************Grid lines api ******************************/
1078
1079    /**
1080     * Show a set of evenly spaced lines over the preview.  The number
1081     * of lines horizontally and vertically is determined by
1082     * {@link com.android.camera.ui.GridLines}.
1083     */
1084    public void showGridLines() {
1085        if (mGridLines != null) {
1086            mGridLines.setVisibility(View.VISIBLE);
1087        }
1088    }
1089
1090    /**
1091     * Hide the set of evenly spaced grid lines overlaying the preview.
1092     */
1093    public void hideGridLines() {
1094        if (mGridLines != null) {
1095            mGridLines.setVisibility(View.INVISIBLE);
1096        }
1097    }
1098
1099    /**
1100     * Return a callback which shows or hide the preview grid lines
1101     * depending on whether the grid lines setting is set on.
1102     */
1103    public ButtonManager.ButtonCallback getGridLinesCallback() {
1104        return new ButtonManager.ButtonCallback() {
1105            @Override
1106            public void onStateChanged(int state) {
1107                if (mController.getSettingsManager().areGridLinesOn()) {
1108                    showGridLines();
1109                } else {
1110                    hideGridLines();
1111                }
1112            }
1113        };
1114    }
1115
1116    /***************************Mode options api *****************************/
1117
1118    /**
1119     * Set the mode options visible.
1120     */
1121    public void showModeOptions() {
1122        mModeOptionsOverlay.setVisibility(View.VISIBLE);
1123    }
1124
1125    /**
1126     * Set the mode options invisible.  This is necessary for modes
1127     * that don't show a bottom bar for the capture UI.
1128     */
1129    public void hideModeOptions() {
1130        mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1131    }
1132
1133    /****************************Bottom bar api ******************************/
1134
1135    /**
1136     * Sets up the bottom bar and mode options with the correct
1137     * shutter button and visibility based on the current module.
1138     */
1139    public void resetBottomControls(ModuleController module, int moduleIndex) {
1140        if (areBottomControlsUsed(module)) {
1141            setBottomBarShutterIcon(moduleIndex);
1142        }
1143    }
1144
1145    /**
1146     * Show or hide the mode options and bottom bar, based on
1147     * whether the current module is using the bottom bar.  Returns
1148     * whether the mode options and bottom bar are used.
1149     */
1150    private boolean areBottomControlsUsed(ModuleController module) {
1151        if (module.isUsingBottomBar()) {
1152            showBottomBar();
1153            showModeOptions();
1154            return true;
1155        } else {
1156            hideBottomBar();
1157            hideModeOptions();
1158            return false;
1159        }
1160    }
1161
1162    /**
1163     * Set the bottom bar visible.
1164     */
1165    public void showBottomBar() {
1166        mBottomBar.setVisibility(View.VISIBLE);
1167    }
1168
1169    /**
1170     * Set the bottom bar invisible.
1171     */
1172    public void hideBottomBar() {
1173        mBottomBar.setVisibility(View.INVISIBLE);
1174    }
1175
1176    /**
1177     * Sets the color of the bottom bar.
1178     */
1179    public void setBottomBarColor(int colorId) {
1180        mBottomBar.setBackgroundColor(colorId);
1181    }
1182
1183    /**
1184     * Sets the pressed color of the bottom bar.
1185     */
1186    public void setBottomBarPressedColor(int colorId) {
1187        mBottomBar.setBackgroundPressedColor(colorId);
1188    }
1189
1190    /**
1191     * Sets the shutter button icon on the bottom bar, based on
1192     * the mode index.
1193     */
1194    public void setBottomBarShutterIcon(int modeIndex) {
1195        int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1196            mController.getAndroidContext());
1197        mBottomBar.setShutterButtonIcon(shutterIconId);
1198    }
1199
1200    public void animateBottomBarToCircle(int shutterIconId) {
1201        mBottomBar.animateToCircle(shutterIconId);
1202    }
1203
1204    public void animateBottomBarToFullSize(int shutterIconId) {
1205        mBottomBar.animateToFullSize(shutterIconId);
1206    }
1207
1208    /**
1209     * Set the visibility of the bottom bar.
1210     */
1211    // TODO: needed for when panorama is managed by the generic module ui.
1212    public void setBottomBarVisible(boolean visible) {
1213        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1214    }
1215
1216    /**
1217     * If the bottom bar is visible (hence has been drawn),
1218     * this sets a {@link #ShutterButton.OnShutterButtonListener}
1219     * on the global shutter button,
1220     */
1221    public void setBottomBarShutterListener(
1222            ShutterButton.OnShutterButtonListener listener) {
1223        ShutterButton shutterButton
1224            = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1225        if (shutterButton != null) {
1226            shutterButton.setOnShutterButtonListener(listener);
1227        }
1228    }
1229
1230    /**
1231     * Performs a transition to the capture layout of the bottom bar.
1232     */
1233    public void transitionToCapture() {
1234        ModuleController moduleController = mController.getCurrentModuleController();
1235        applyModuleSpecs(moduleController.getHardwareSpec(),
1236            moduleController.getBottomBarSpec());
1237        mBottomBar.transitionToCapture();
1238    }
1239
1240    /**
1241     * Performs a transition to the global intent layout.
1242     */
1243    public void transitionToIntentLayout() {
1244        ModuleController moduleController = mController.getCurrentModuleController();
1245        applyModuleSpecs(moduleController.getHardwareSpec(),
1246            moduleController.getBottomBarSpec());
1247        mBottomBar.transitionToIntentLayout();
1248    }
1249
1250    /**
1251     * Performs a transition to the global intent review layout.
1252     */
1253    public void transitionToIntentReviewLayout() {
1254        ModuleController moduleController = mController.getCurrentModuleController();
1255        applyModuleSpecs(moduleController.getHardwareSpec(),
1256            moduleController.getBottomBarSpec());
1257        mBottomBar.transitionToIntentReviewLayout();
1258    }
1259
1260    /**
1261     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1262     * to the bottom bar mode options based on limitations from a
1263     * {@link com.android.camera.hardware.HardwareSpec}.
1264     *
1265     * Options not supported by the hardware are either hidden
1266     * or disabled, depending on the option.
1267     *
1268     * Otherwise, the option is fully enabled and clickable.
1269     */
1270    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1271           final BottomBarUISpec bottomBarSpec) {
1272        if (hardwareSpec == null || bottomBarSpec == null) {
1273            return;
1274        }
1275
1276        ButtonManager buttonManager = mController.getButtonManager();
1277        SettingsManager settingsManager = mController.getSettingsManager();
1278
1279        /** Standard mode options */
1280        if (hardwareSpec.isFrontCameraSupported()) {
1281            if (bottomBarSpec.enableCamera) {
1282                buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
1283                    bottomBarSpec.cameraCallback);
1284            } else {
1285                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1286            }
1287        } else {
1288            // Hide camera icon if front camera not available.
1289            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1290        }
1291
1292        if (bottomBarSpec.hideFlash) {
1293            buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1294        } else {
1295            if (hardwareSpec.isFlashSupported()) {
1296                if (bottomBarSpec.enableFlash && settingsManager.isCameraBackFacing()) {
1297                    buttonManager.enableButton(ButtonManager.BUTTON_FLASH, bottomBarSpec.flashCallback);
1298                } else if (bottomBarSpec.enableTorchFlash && settingsManager.isCameraBackFacing()) {
1299                    buttonManager.enableButton(ButtonManager.BUTTON_TORCH, bottomBarSpec.flashCallback);
1300                } else {
1301                    buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1302                }
1303            } else {
1304                // Disable flash icon if not supported by the hardware.
1305                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1306            }
1307        }
1308
1309        if (bottomBarSpec.hideHdr) {
1310            // Force hide hdr or hdr plus icon.
1311            buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
1312        } else {
1313            if (hardwareSpec.isHdrPlusSupported()) {
1314                if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
1315                    buttonManager.enableButton(ButtonManager.BUTTON_HDRPLUS,
1316                        bottomBarSpec.hdrCallback);
1317                } else {
1318                    buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
1319                }
1320            } else if (hardwareSpec.isHdrSupported()) {
1321                if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
1322                    buttonManager.enableButton(ButtonManager.BUTTON_HDR,
1323                        bottomBarSpec.hdrCallback);
1324                } else {
1325                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1326                }
1327            } else {
1328                // Hide hdr plus or hdr icon if neither are supported.
1329                buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
1330            }
1331        }
1332
1333        if (bottomBarSpec.hideGridLines) {
1334            // Force hide grid lines icon.
1335            buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1336            hideGridLines();
1337        } else {
1338            if (bottomBarSpec.enableGridLines) {
1339                buttonManager.enableButton(ButtonManager.BUTTON_GRID_LINES,
1340                    bottomBarSpec.gridLinesCallback != null ?
1341                    bottomBarSpec.gridLinesCallback : getGridLinesCallback());
1342            } else {
1343                buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1344                hideGridLines();
1345            }
1346        }
1347
1348        if (bottomBarSpec.enablePanoHorizontal
1349                && PhotoSphereHelper.getPanoramaHorizontalDrawableId() > 0) {
1350            buttonManager.enablePushButton(ButtonManager.BUTTON_PANO_HORIZONTAL,
1351                bottomBarSpec.panoHorizontalCallback,
1352                PhotoSphereHelper.getPanoramaHorizontalDrawableId());
1353        }
1354
1355        if (bottomBarSpec.enablePanoVertical
1356                && PhotoSphereHelper.getPanoramaVerticalDrawableId() > 0) {
1357            buttonManager.enablePushButton(ButtonManager.BUTTON_PANO_VERTICAL,
1358                bottomBarSpec.panoVerticalCallback,
1359                PhotoSphereHelper.getPanoramaVerticalDrawableId());
1360        }
1361
1362        /** Intent UI */
1363        if (bottomBarSpec.showCancel) {
1364            buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
1365                bottomBarSpec.cancelCallback);
1366        }
1367        if (bottomBarSpec.showDone) {
1368            buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
1369                bottomBarSpec.doneCallback);
1370        }
1371        if (bottomBarSpec.showRetake) {
1372            buttonManager.enablePushButton(ButtonManager.BUTTON_RETAKE,
1373                bottomBarSpec.retakeCallback);
1374        }
1375    }
1376}
1377