Home | History | Annotate | Download | only in app
      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          */
    106         void setListener(Listener listener);
    107 
    108         /**
    109          * Sets cling for external viewer button.
    110          */
    111         void setClingForViewer(int viewerType, Cling cling);
    112 
    113         /**
    114          * Clears cling for external viewer button.
    115          */
    116         void clearClingForViewer(int viewerType);
    117 
    118         /**
    119          * Returns a cling for the specified viewer type.
    120          */
    121         Cling getClingForViewer(int viewerType);
    122 
    123         /**
    124          * Set if the bottom controls are visible.
    125          * @param visible {@code true} if visible.
    126          */
    127         void setVisible(boolean visible);
    128 
    129         /**
    130          * @param visible Whether the button is visible.
    131          */
    132         void setEditButtonVisibility(boolean visible);
    133 
    134         /**
    135          * @param enabled Whether the button is enabled.
    136          */
    137         void setEditEnabled(boolean enabled);
    138 
    139         /**
    140          * Sets the visibility of the view-photosphere button.
    141          *
    142          * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
    143          *            {@link #VIEWER_REFOCUS}.
    144          */
    145         void setViewerButtonVisibility(int state);
    146 
    147         /**
    148          * @param enabled Whether the button is enabled.
    149          */
    150         void setViewEnabled(boolean enabled);
    151 
    152         /**
    153          * @param enabled Whether the button is enabled.
    154          */
    155         void setTinyPlanetEnabled(boolean enabled);
    156 
    157         /**
    158          * @param visible Whether the button is visible.
    159          */
    160         void setDeleteButtonVisibility(boolean visible);
    161 
    162         /**
    163          * @param enabled Whether the button is enabled.
    164          */
    165         void setDeleteEnabled(boolean enabled);
    166 
    167         /**
    168          * @param visible Whether the button is visible.
    169          */
    170         void setShareButtonVisibility(boolean visible);
    171 
    172         /**
    173          * @param enabled Whether the button is enabled.
    174          */
    175         void setShareEnabled(boolean enabled);
    176 
    177         /**
    178          * Sets the texts for progress UI.
    179          *
    180          * @param text The text to show.
    181          */
    182         void setProgressText(CharSequence text);
    183 
    184         /**
    185          * Sets the progress.
    186          *
    187          * @param progress The progress value. Should be between 0 and 100.
    188          */
    189         void setProgress(int progress);
    190 
    191         /**
    192          * Replaces the progress UI with an error message.
    193          */
    194         void showProgressError(CharSequence message);
    195 
    196         /**
    197          * Hide the progress error message.
    198          */
    199         void hideProgressError();
    200 
    201         /**
    202          * Shows the progress.
    203          */
    204         void showProgress();
    205 
    206         /**
    207          * Hides the progress.
    208          */
    209         void hideProgress();
    210 
    211         /**
    212          * Shows the controls.
    213          */
    214         void showControls();
    215 
    216         /**
    217          * Hides the controls.
    218          */
    219         void hideControls();
    220 
    221         /**
    222          * Classes implementing this interface can listen for events on the bottom
    223          * controls.
    224          */
    225         public static interface Listener {
    226             /**
    227              * Called when the user pressed the "view" button to e.g. view a photo
    228              * sphere or RGBZ image.
    229              */
    230             public void onExternalViewer();
    231 
    232             /**
    233              * Called when the "edit" button is pressed.
    234              */
    235             public void onEdit();
    236 
    237             /**
    238              * Called when the "tiny planet" button is pressed.
    239              */
    240             public void onTinyPlanet();
    241 
    242             /**
    243              * Called when the "delete" button is pressed.
    244              */
    245             public void onDelete();
    246 
    247             /**
    248              * Called when the "share" button is pressed.
    249              */
    250             public void onShare();
    251 
    252             /**
    253              * Called when the progress error message is clicked.
    254              */
    255             public void onProgressErrorClicked();
    256         }
    257     }
    258 
    259     /**
    260      * BottomBarUISpec provides a structure for modules
    261      * to specify their ideal bottom bar mode options layout.
    262      *
    263      * Once constructed by a module, this class should be
    264      * treated as read only.
    265      *
    266      * The application then edits this spec according to
    267      * hardware limitations and displays the final bottom
    268      * bar ui.
    269      */
    270     public static class BottomBarUISpec {
    271         /** Mode options UI */
    272 
    273         /**
    274          * Set true if the camera option should be enabled.
    275          * If not set or false, and multiple cameras are supported,
    276          * the camera option will be disabled.
    277          *
    278          * If multiple cameras are not supported, this preference
    279          * is ignored and the camera option will not be visible.
    280          */
    281         public boolean enableCamera;
    282 
    283         /**
    284          * Set true if the camera option should not be visible, regardless
    285          * of hardware limitations.
    286          */
    287         public boolean hideCamera;
    288 
    289         /**
    290          * Set true if the photo flash option should be enabled.
    291          * If not set or false, the photo flash option will be
    292          * disabled.
    293          *
    294          * If the hardware does not support multiple flash values,
    295          * this preference is ignored and the flash option will
    296          * be disabled.  It will not be made invisible in order to
    297          * preserve a consistent experience across devices and between
    298          * front and back cameras.
    299          */
    300         public boolean enableFlash;
    301 
    302         /**
    303          * Set true if the video flash option should be enabled.
    304          * Same disable rules apply as the photo flash option.
    305          */
    306         public boolean enableTorchFlash;
    307 
    308         /**
    309          * Set true if the HDR+ flash option should be enabled.
    310          * Same disable rules apply as the photo flash option.
    311          */
    312         public boolean enableHdrPlusFlash;
    313 
    314         /**
    315          * Set true if flash should not be visible, regardless of
    316          * hardware limitations.
    317          */
    318         public boolean hideFlash;
    319 
    320         /**
    321          * Set true if the hdr/hdr+ option should be enabled.
    322          * If not set or false, the hdr/hdr+ option will be disabled.
    323          *
    324          * Hdr or hdr+ will be chosen based on hardware limitations,
    325          * with hdr+ prefered.
    326          *
    327          * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
    328          * will not be visible.
    329          */
    330         public boolean enableHdr;
    331 
    332         /**
    333          * Set true if hdr/hdr+ should not be visible, regardless of
    334          * hardware limitations.
    335          */
    336         public boolean hideHdr;
    337 
    338         /**
    339          * Set true if grid lines should be visible.  Not setting this
    340          * causes grid lines to be disabled.  This option is agnostic to
    341          * the hardware.
    342          */
    343         public boolean enableGridLines;
    344 
    345         /**
    346          * Set true if grid lines should not be visible.
    347          */
    348         public boolean hideGridLines;
    349 
    350         /**
    351          * Set true if the panorama orientation option should be visible.
    352          *
    353          * This option is not constrained by hardware limitations.
    354          */
    355         public boolean enablePanoOrientation;
    356 
    357         public boolean enableExposureCompensation;
    358 
    359         /** Intent UI */
    360 
    361         /**
    362          * Set true if the intent ui cancel option should be visible.
    363          */
    364         public boolean showCancel;
    365         /**
    366          * Set true if the intent ui done option should be visible.
    367          */
    368         public boolean showDone;
    369         /**
    370          * Set true if the intent ui retake option should be visible.
    371          */
    372         public boolean showRetake;
    373         /**
    374          * Set true if the intent ui review option should be visible.
    375          */
    376         public boolean showReview;
    377 
    378         /** Mode options callbacks */
    379 
    380         /**
    381          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    382          * that will be executed when the camera option is pressed. This
    383          * callback can be null.
    384          */
    385         public ButtonManager.ButtonCallback cameraCallback;
    386 
    387         /**
    388          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    389          * that will be executed when the flash option is pressed. This
    390          * callback can be null.
    391          */
    392         public ButtonManager.ButtonCallback flashCallback;
    393 
    394         /**
    395          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    396          * that will be executed when the hdr/hdr+ option is pressed. This
    397          * callback can be null.
    398          */
    399         public ButtonManager.ButtonCallback hdrCallback;
    400 
    401         /**
    402          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    403          * that will be executed when the grid lines option is pressed. This
    404          * callback can be null.
    405          */
    406         public ButtonManager.ButtonCallback gridLinesCallback;
    407 
    408         /**
    409          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    410          * that will execute when the panorama orientation option is pressed.
    411          * This callback can be null.
    412          */
    413         public ButtonManager.ButtonCallback panoOrientationCallback;
    414 
    415         /** Intent UI callbacks */
    416 
    417         /**
    418          * A {@link android.view.View.OnClickListener} that will execute
    419          * when the cancel option is pressed. This callback can be null.
    420          */
    421         public View.OnClickListener cancelCallback;
    422 
    423         /**
    424          * A {@link android.view.View.OnClickListener} that will execute
    425          * when the done option is pressed. This callback can be null.
    426          */
    427         public View.OnClickListener doneCallback;
    428 
    429         /**
    430          * A {@link android.view.View.OnClickListener} that will execute
    431          * when the retake option is pressed. This callback can be null.
    432          */
    433         public View.OnClickListener retakeCallback;
    434 
    435         /**
    436          * A {@link android.view.View.OnClickListener} that will execute
    437          * when the review option is pressed. This callback can be null.
    438          */
    439         public View.OnClickListener reviewCallback;
    440 
    441         /**
    442          * A ExposureCompensationSetCallback that will execute
    443          * when an expsosure button is pressed. This callback can be null.
    444          */
    445         public interface ExposureCompensationSetCallback {
    446             public void setExposure(int value);
    447         }
    448         public ExposureCompensationSetCallback exposureCompensationSetCallback;
    449 
    450         /**
    451          * Exposure compensation parameters.
    452          */
    453         public int minExposureCompensation;
    454         public int maxExposureCompensation;
    455         public float exposureCompensationStep;
    456 
    457         /**
    458          * Whether self-timer is enabled.
    459          */
    460         public boolean enableSelfTimer = false;
    461 
    462         /**
    463          * Whether the option for self-timer should show. If true and
    464          * {@link #enableSelfTimer} is false, then the option should be shown
    465          * disabled.
    466          */
    467         public boolean showSelfTimer = false;
    468     }
    469 
    470 
    471     private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
    472 
    473     private final AppController mController;
    474     private final boolean mIsCaptureIntent;
    475     private final AnimationManager mAnimationManager;
    476 
    477     // Swipe states:
    478     private final static int IDLE = 0;
    479     private final static int SWIPE_UP = 1;
    480     private final static int SWIPE_DOWN = 2;
    481     private final static int SWIPE_LEFT = 3;
    482     private final static int SWIPE_RIGHT = 4;
    483     private boolean mSwipeEnabled = true;
    484 
    485     // Shared Surface Texture properities.
    486     private SurfaceTexture mSurface;
    487     private int mSurfaceWidth;
    488     private int mSurfaceHeight;
    489 
    490     // Touch related measures:
    491     private final int mSlop;
    492     private final static int SWIPE_TIME_OUT_MS = 500;
    493 
    494     // Mode cover states:
    495     private final static int COVER_HIDDEN = 0;
    496     private final static int COVER_SHOWN = 1;
    497     private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
    498     private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
    499 
    500     /**
    501      * Preview down-sample rate when taking a screenshot.
    502      */
    503     private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
    504 
    505     // App level views:
    506     private final FrameLayout mCameraRootView;
    507     private final ModeTransitionView mModeTransitionView;
    508     private final MainActivityLayout mAppRootView;
    509     private final ModeListView mModeListView;
    510     private final FilmstripLayout mFilmstripLayout;
    511     private TextureView mTextureView;
    512     private FrameLayout mModuleUI;
    513     private ShutterButton mShutterButton;
    514     private View mLetterBoxer1;
    515     private View mLetterBoxer2;
    516     private BottomBar mBottomBar;
    517     private ModeOptionsOverlay mModeOptionsOverlay;
    518     private IndicatorIconController mIndicatorIconController;
    519     private View mFocusOverlay;
    520     private FrameLayout mTutorialsPlaceHolderWrapper;
    521     private BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper;
    522     private TextureViewHelper mTextureViewHelper;
    523     private final GestureDetector mGestureDetector;
    524     private DisplayManager.DisplayListener mDisplayListener;
    525     private int mLastRotation;
    526     private int mSwipeState = IDLE;
    527     private PreviewOverlay mPreviewOverlay;
    528     private GridLines mGridLines;
    529     private CaptureAnimationOverlay mCaptureOverlay;
    530     private PreviewStatusListener mPreviewStatusListener;
    531     private int mModeCoverState = COVER_HIDDEN;
    532     private final FilmstripBottomPanel mFilmstripBottomControls;
    533     private final FilmstripContentPanel mFilmstripPanel;
    534     private Runnable mHideCoverRunnable;
    535     private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
    536             = new View.OnLayoutChangeListener() {
    537         @Override
    538         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
    539                 int oldTop, int oldRight, int oldBottom) {
    540             if (mPreviewStatusListener != null) {
    541                 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
    542                         oldTop, oldRight, oldBottom);
    543             }
    544         }
    545     };
    546     private View mModeOptionsToggle;
    547     private final PeekView mPeekView;
    548     private final CaptureLayoutHelper mCaptureLayoutHelper;
    549     private boolean mAccessibilityEnabled;
    550     private final View mAccessibilityAffordances;
    551 
    552     /**
    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          */
    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          */
    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          */
    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 {
    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 
    633     public long getCoverHiddenTime() {
    634         return mCoverHiddenTime;
    635     }
    636 
    637     /**
    638      * This resets the preview to have no applied transform matrix.
    639      */
    640     public void clearPreviewTransform() {
    641         mTextureViewHelper.clearTransform();
    642     }
    643 
    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      */
    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      */
    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      */
    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      */
    688     public void updatePreviewTransform(Matrix matrix) {
    689         mTextureViewHelper.updateTransform(matrix);
    690     }
    691 
    692     public interface AnimationFinishedListener {
    693         public void onAnimationFinished(boolean success);
    694     }
    695 
    696     private class MyTouchListener implements View.OnTouchListener {
    697         private boolean mScaleStarted = false;
    698         @Override
    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
    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 
    742         private void setSwipeState(int swipeState) {
    743             mSwipeState = swipeState;
    744             // Notify new swipe detected.
    745             onSwipeDetected(swipeState);
    746         }
    747 
    748         @Override
    749         public boolean onDown(MotionEvent ev) {
    750             mDown = MotionEvent.obtain(ev);
    751             mSwipeState = IDLE;
    752             return false;
    753         }
    754     }
    755 
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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 
   1036     private void showShimmyDelayed() {
   1037         if (!mIsCaptureIntent) {
   1038             // Show shimmy in SHIMMY_DELAY_MS
   1039             mModeListView.showModeSwitcherHint();
   1040         }
   1041     }
   1042 
   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 
   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      */
   1072     public void pausePreviewRendering() {
   1073         mTextureView.setVisibility(View.INVISIBLE);
   1074     }
   1075 
   1076     /**
   1077      * Call to begin rendering the preview again.
   1078      */
   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      */
   1090     public Matrix getPreviewTransform(Matrix m) {
   1091         return mTextureView.getTransform(m);
   1092     }
   1093 
   1094     @Override
   1095     public void onOpenFullScreen() {
   1096         // Do nothing.
   1097     }
   1098 
   1099     @Override
   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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   1327     public void onPreviewReadyToStart() {
   1328         if (mModeCoverState == COVER_SHOWN) {
   1329             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
   1330             mController.setupOneShotPreviewListener();
   1331         }
   1332     }
   1333 
   1334     /**
   1335      * Gets called when preview is started.
   1336      */
   1337     public void onPreviewStarted() {
   1338         Log.v(TAG, "onPreviewStarted");
   1339         if (mModeCoverState == COVER_SHOWN) {
   1340             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
   1341         }
   1342         enableModeOptions();
   1343     }
   1344 
   1345     /**
   1346      * Gets notified when next preview frame comes in.
   1347      */
   1348     public void onNewPreviewFrame() {
   1349         Log.v(TAG, "onNewPreviewFrame");
   1350         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
   1351         hideModeCover();
   1352     }
   1353 
   1354     /**
   1355      * Set the mode options toggle clickable.
   1356      */
   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
   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
   1387     public void onShutterCoordinate(TouchCoordinate coord) {
   1388         // Do nothing.
   1389     }
   1390 
   1391     @Override
   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
   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 
   1424     private void updateModeSpecificUIColors() {
   1425         setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
   1426     }
   1427 
   1428     @Override
   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
   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      */
   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      */
   1468     public void startPreCaptureAnimation(boolean shortFlash) {
   1469         mCaptureOverlay.startFlashAnimation(shortFlash);
   1470     }
   1471 
   1472     /**
   1473      * Cancels the pre-capture animation.
   1474      */
   1475     public void cancelPreCaptureAnimation() {
   1476         mAnimationManager.cancelAnimations();
   1477     }
   1478 
   1479     /**
   1480      * Cancels the post-capture animation.
   1481      */
   1482     public void cancelPostCaptureAnimation() {
   1483         mAnimationManager.cancelAnimations();
   1484     }
   1485 
   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      */
   1494     public BottomPanel getFilmstripBottomControls() {
   1495         return mFilmstripBottomControls;
   1496     }
   1497 
   1498     public void showBottomControls() {
   1499         mFilmstripBottomControls.show();
   1500     }
   1501 
   1502     public void hideBottomControls() {
   1503         mFilmstripBottomControls.hide();
   1504     }
   1505 
   1506     /**
   1507      * @param listener The listener for bottom controls.
   1508      */
   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      */
   1518     public SurfaceTexture getSurfaceTexture() {
   1519         return mSurface;
   1520     }
   1521 
   1522     /**
   1523      * Return the shared {@link android.graphics.SurfaceTexture}'s width.
   1524      */
   1525     public int getSurfaceWidth() {
   1526         return mSurfaceWidth;
   1527     }
   1528 
   1529     /**
   1530      * Return the shared {@link android.graphics.SurfaceTexture}'s height.
   1531      */
   1532     public int getSurfaceHeight() {
   1533         return mSurfaceHeight;
   1534     }
   1535 
   1536     @Override
   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
   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
   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
   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      */
   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      */
   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      */
   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