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