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