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.content.res.Resources;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Canvas;
     22 import android.graphics.Matrix;
     23 import android.graphics.RectF;
     24 import android.graphics.SurfaceTexture;
     25 import android.hardware.display.DisplayManager;
     26 import android.util.CameraPerformanceTracker;
     27 import android.view.GestureDetector;
     28 import android.view.LayoutInflater;
     29 import android.view.MotionEvent;
     30 import android.view.TextureView;
     31 import android.view.View;
     32 import android.view.ViewConfiguration;
     33 import android.view.ViewGroup;
     34 import android.widget.FrameLayout;
     35 import android.widget.ImageButton;
     36 
     37 import com.android.camera.AccessibilityUtil;
     38 import com.android.camera.AnimationManager;
     39 import com.android.camera.ButtonManager;
     40 import com.android.camera.CaptureLayoutHelper;
     41 import com.android.camera.ShutterButton;
     42 import com.android.camera.TextureViewHelper;
     43 import com.android.camera.debug.Log;
     44 import com.android.camera.filmstrip.FilmstripContentPanel;
     45 import com.android.camera.hardware.HardwareSpec;
     46 import com.android.camera.module.ModuleController;
     47 import com.android.camera.settings.Keys;
     48 import com.android.camera.settings.SettingsManager;
     49 import com.android.camera.ui.AbstractTutorialOverlay;
     50 import com.android.camera.ui.BottomBar;
     51 import com.android.camera.ui.CaptureAnimationOverlay;
     52 import com.android.camera.ui.GridLines;
     53 import com.android.camera.ui.MainActivityLayout;
     54 import com.android.camera.ui.ModeListView;
     55 import com.android.camera.ui.ModeTransitionView;
     56 import com.android.camera.ui.PreviewOverlay;
     57 import com.android.camera.ui.PreviewStatusListener;
     58 import com.android.camera.ui.StickyBottomCaptureLayout;
     59 import com.android.camera.ui.TouchCoordinate;
     60 import com.android.camera.ui.focus.FocusRing;
     61 import com.android.camera.util.AndroidServices;
     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.RoundedThumbnailView;
     71 import com.android.camera2.R;
     72 
     73 /**
     74  * CameraAppUI centralizes control of views shared across modules. Whereas module
     75  * specific views will be handled in each Module UI. For example, we can now
     76  * bring the flash animation and capture animation up from each module to app
     77  * level, as these animations are largely the same for all modules.
     78  *
     79  * This class also serves to disambiguate touch events. It recognizes all the
     80  * swipe gestures that happen on the preview by attaching a touch listener to
     81  * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
     82  * of how swipe from each direction should be handled, it can then redirect these
     83  * events to appropriate recipient views.
     84  */
     85 public class CameraAppUI implements ModeListView.ModeSwitchListener,
     86                                     TextureView.SurfaceTextureListener,
     87                                     ModeListView.ModeListOpenListener,
     88                                     SettingsManager.OnSettingChangedListener,
     89                                     ShutterButton.OnShutterButtonListener {
     90 
     91     /**
     92      * The bottom controls on the filmstrip.
     93      */
     94     public static interface BottomPanel {
     95         /** Values for the view state of the button. */
     96         public final int VIEWER_NONE = 0;
     97         public final int VIEWER_PHOTO_SPHERE = 1;
     98         public final int VIEWER_REFOCUS = 2;
     99         public final int VIEWER_OTHER = 3;
    100 
    101         /**
    102          * Sets a new or replaces an existing listener for bottom control events.
    103          */
    104         void setListener(Listener listener);
    105 
    106         /**
    107          * Sets cling for external viewer button.
    108          */
    109         void setClingForViewer(int viewerType, Cling cling);
    110 
    111         /**
    112          * Clears cling for external viewer button.
    113          */
    114         void clearClingForViewer(int viewerType);
    115 
    116         /**
    117          * Returns a cling for the specified viewer type.
    118          */
    119         Cling getClingForViewer(int viewerType);
    120 
    121         /**
    122          * Set if the bottom controls are visible.
    123          * @param visible {@code true} if visible.
    124          */
    125         void setVisible(boolean visible);
    126 
    127         /**
    128          * @param visible Whether the button is visible.
    129          */
    130         void setEditButtonVisibility(boolean visible);
    131 
    132         /**
    133          * @param enabled Whether the button is enabled.
    134          */
    135         void setEditEnabled(boolean enabled);
    136 
    137         /**
    138          * Sets the visibility of the view-photosphere button.
    139          *
    140          * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
    141          *            {@link #VIEWER_REFOCUS}.
    142          */
    143         void setViewerButtonVisibility(int state);
    144 
    145         /**
    146          * @param enabled Whether the button is enabled.
    147          */
    148         void setViewEnabled(boolean enabled);
    149 
    150         /**
    151          * @param enabled Whether the button is enabled.
    152          */
    153         void setTinyPlanetEnabled(boolean enabled);
    154 
    155         /**
    156          * @param visible Whether the button is visible.
    157          */
    158         void setDeleteButtonVisibility(boolean visible);
    159 
    160         /**
    161          * @param enabled Whether the button is enabled.
    162          */
    163         void setDeleteEnabled(boolean enabled);
    164 
    165         /**
    166          * @param visible Whether the button is visible.
    167          */
    168         void setShareButtonVisibility(boolean visible);
    169 
    170         /**
    171          * @param enabled Whether the button is enabled.
    172          */
    173         void setShareEnabled(boolean enabled);
    174 
    175         /**
    176          * Sets the texts for progress UI.
    177          *
    178          * @param text The text to show.
    179          */
    180         void setProgressText(CharSequence text);
    181 
    182         /**
    183          * Sets the progress.
    184          *
    185          * @param progress The progress value. Should be between 0 and 100.
    186          */
    187         void setProgress(int progress);
    188 
    189         /**
    190          * Replaces the progress UI with an error message.
    191          */
    192         void showProgressError(CharSequence message);
    193 
    194         /**
    195          * Hide the progress error message.
    196          */
    197         void hideProgressError();
    198 
    199         /**
    200          * Shows the progress.
    201          */
    202         void showProgress();
    203 
    204         /**
    205          * Hides the progress.
    206          */
    207         void hideProgress();
    208 
    209         /**
    210          * Shows the controls.
    211          */
    212         void showControls();
    213 
    214         /**
    215          * Hides the controls.
    216          */
    217         void hideControls();
    218 
    219         /**
    220          * Classes implementing this interface can listen for events on the bottom
    221          * controls.
    222          */
    223         public static interface Listener {
    224             /**
    225              * Called when the user pressed the "view" button to e.g. view a photo
    226              * sphere or RGBZ image.
    227              */
    228             public void onExternalViewer();
    229 
    230             /**
    231              * Called when the "edit" button is pressed.
    232              */
    233             public void onEdit();
    234 
    235             /**
    236              * Called when the "tiny planet" button is pressed.
    237              */
    238             public void onTinyPlanet();
    239 
    240             /**
    241              * Called when the "delete" button is pressed.
    242              */
    243             public void onDelete();
    244 
    245             /**
    246              * Called when the "share" button is pressed.
    247              */
    248             public void onShare();
    249 
    250             /**
    251              * Called when the progress error message is clicked.
    252              */
    253             public void onProgressErrorClicked();
    254         }
    255     }
    256 
    257     /**
    258      * BottomBarUISpec provides a structure for modules
    259      * to specify their ideal bottom bar mode options layout.
    260      *
    261      * Once constructed by a module, this class should be
    262      * treated as read only.
    263      *
    264      * The application then edits this spec according to
    265      * hardware limitations and displays the final bottom
    266      * bar ui.
    267      */
    268     public static class BottomBarUISpec {
    269         /** Mode options UI */
    270 
    271         /**
    272          * Set true if the camera option should be enabled.
    273          * If not set or false, and multiple cameras are supported,
    274          * the camera option will be disabled.
    275          *
    276          * If multiple cameras are not supported, this preference
    277          * is ignored and the camera option will not be visible.
    278          */
    279         public boolean enableCamera;
    280 
    281         /**
    282          * Set true if the camera option should not be visible, regardless
    283          * of hardware limitations.
    284          */
    285         public boolean hideCamera;
    286 
    287         /**
    288          * Set true if the photo flash option should be enabled.
    289          * If not set or false, the photo flash option will be
    290          * disabled.
    291          *
    292          * If the hardware does not support multiple flash values,
    293          * this preference is ignored and the flash option will
    294          * be disabled.  It will not be made invisible in order to
    295          * preserve a consistent experience across devices and between
    296          * front and back cameras.
    297          */
    298         public boolean enableFlash;
    299 
    300         /**
    301          * Set true if the video flash option should be enabled.
    302          * Same disable rules apply as the photo flash option.
    303          */
    304         public boolean enableTorchFlash;
    305 
    306         /**
    307          * Set true if the HDR+ flash option should be enabled.
    308          * Same disable rules apply as the photo flash option.
    309          */
    310         public boolean enableHdrPlusFlash;
    311 
    312         /**
    313          * Set true if flash should not be visible, regardless of
    314          * hardware limitations.
    315          */
    316         public boolean hideFlash;
    317 
    318         /**
    319          * Set true if the hdr/hdr+ option should be enabled.
    320          * If not set or false, the hdr/hdr+ option will be disabled.
    321          *
    322          * Hdr or hdr+ will be chosen based on hardware limitations,
    323          * with hdr+ prefered.
    324          *
    325          * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
    326          * will not be visible.
    327          */
    328         public boolean enableHdr;
    329 
    330         /**
    331          * Set true if hdr/hdr+ should not be visible, regardless of
    332          * hardware limitations.
    333          */
    334         public boolean hideHdr;
    335 
    336         /**
    337          * Set true if grid lines should be visible.  Not setting this
    338          * causes grid lines to be disabled.  This option is agnostic to
    339          * the hardware.
    340          */
    341         public boolean enableGridLines;
    342 
    343         /**
    344          * Set true if grid lines should not be visible.
    345          */
    346         public boolean hideGridLines;
    347 
    348         /**
    349          * Set true if the panorama orientation option should be visible.
    350          *
    351          * This option is not constrained by hardware limitations.
    352          */
    353         public boolean enablePanoOrientation;
    354 
    355         /**
    356          * Set true if manual exposure compensation should be visible.
    357          *
    358          * This option is not constrained by hardware limitations.
    359          * For example, this is false in HDR+ mode.
    360          */
    361         public boolean enableExposureCompensation;
    362 
    363         /**
    364          * Set true if the device and module support exposure compensation.
    365          * Used only to show exposure button in disabled (greyed out) state.
    366          */
    367         public boolean isExposureCompensationSupported;
    368 
    369         /** Intent UI */
    370 
    371         /**
    372          * Set true if the intent ui cancel option should be visible.
    373          */
    374         public boolean showCancel;
    375         /**
    376          * Set true if the intent ui done option should be visible.
    377          */
    378         public boolean showDone;
    379         /**
    380          * Set true if the intent ui retake option should be visible.
    381          */
    382         public boolean showRetake;
    383         /**
    384          * Set true if the intent ui review option should be visible.
    385          */
    386         public boolean showReview;
    387 
    388         /** Mode options callbacks */
    389 
    390         /**
    391          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    392          * that will be executed when the camera option is pressed. This
    393          * callback can be null.
    394          */
    395         public ButtonManager.ButtonCallback cameraCallback;
    396 
    397         /**
    398          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    399          * that will be executed when the flash option is pressed. This
    400          * callback can be null.
    401          */
    402         public ButtonManager.ButtonCallback flashCallback;
    403 
    404         /**
    405          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    406          * that will be executed when the hdr/hdr+ option is pressed. This
    407          * callback can be null.
    408          */
    409         public ButtonManager.ButtonCallback hdrCallback;
    410 
    411         /**
    412          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    413          * that will be executed when the grid lines option is pressed. This
    414          * callback can be null.
    415          */
    416         public ButtonManager.ButtonCallback gridLinesCallback;
    417 
    418         /**
    419          * A {@link com.android.camera.ButtonManager.ButtonCallback}
    420          * that will execute when the panorama orientation option is pressed.
    421          * This callback can be null.
    422          */
    423         public ButtonManager.ButtonCallback panoOrientationCallback;
    424 
    425         /** Intent UI callbacks */
    426 
    427         /**
    428          * A {@link android.view.View.OnClickListener} that will execute
    429          * when the cancel option is pressed. This callback can be null.
    430          */
    431         public View.OnClickListener cancelCallback;
    432 
    433         /**
    434          * A {@link android.view.View.OnClickListener} that will execute
    435          * when the done option is pressed. This callback can be null.
    436          */
    437         public View.OnClickListener doneCallback;
    438 
    439         /**
    440          * A {@link android.view.View.OnClickListener} that will execute
    441          * when the retake option is pressed. This callback can be null.
    442          */
    443         public View.OnClickListener retakeCallback;
    444 
    445         /**
    446          * A {@link android.view.View.OnClickListener} that will execute
    447          * when the review option is pressed. This callback can be null.
    448          */
    449         public View.OnClickListener reviewCallback;
    450 
    451         /**
    452          * A ExposureCompensationSetCallback that will execute
    453          * when an expsosure button is pressed. This callback can be null.
    454          */
    455         public interface ExposureCompensationSetCallback {
    456             public void setExposure(int value);
    457         }
    458         public ExposureCompensationSetCallback exposureCompensationSetCallback;
    459 
    460         /**
    461          * Exposure compensation parameters.
    462          */
    463         public int minExposureCompensation;
    464         public int maxExposureCompensation;
    465         public float exposureCompensationStep;
    466 
    467         /**
    468          * Whether self-timer is enabled.
    469          */
    470         public boolean enableSelfTimer = false;
    471 
    472         /**
    473          * Whether the option for self-timer should show. If true and
    474          * {@link #enableSelfTimer} is false, then the option should be shown
    475          * disabled.
    476          */
    477         public boolean showSelfTimer = false;
    478     }
    479 
    480 
    481     private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
    482 
    483     private final AppController mController;
    484     private final boolean mIsCaptureIntent;
    485     private final AnimationManager mAnimationManager;
    486 
    487     // Swipe states:
    488     private final static int IDLE = 0;
    489     private final static int SWIPE_UP = 1;
    490     private final static int SWIPE_DOWN = 2;
    491     private final static int SWIPE_LEFT = 3;
    492     private final static int SWIPE_RIGHT = 4;
    493     private boolean mSwipeEnabled = true;
    494 
    495     // Shared Surface Texture properities.
    496     private SurfaceTexture mSurface;
    497     private int mSurfaceWidth;
    498     private int mSurfaceHeight;
    499 
    500     // Touch related measures:
    501     private final int mSlop;
    502     private final static int SWIPE_TIME_OUT_MS = 500;
    503 
    504     // Mode cover states:
    505     private final static int COVER_HIDDEN = 0;
    506     private final static int COVER_SHOWN = 1;
    507     private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
    508     private final static int COVER_WILL_HIDE_AFTER_NEXT_TEXTURE_UPDATE = 3;
    509     private final static int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 4;
    510 
    511     /**
    512      * Preview down-sample rate when taking a screenshot.
    513      */
    514     private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
    515 
    516     // App level views:
    517     private final FrameLayout mCameraRootView;
    518     private final ModeTransitionView mModeTransitionView;
    519     private final MainActivityLayout mAppRootView;
    520     private final ModeListView mModeListView;
    521     private final FilmstripLayout mFilmstripLayout;
    522     private TextureView mTextureView;
    523     private FrameLayout mModuleUI;
    524     private ShutterButton mShutterButton;
    525     private ImageButton mCountdownCancelButton;
    526     private BottomBar mBottomBar;
    527     private ModeOptionsOverlay mModeOptionsOverlay;
    528     private IndicatorIconController mIndicatorIconController;
    529     private FocusRing mFocusRing;
    530     private FrameLayout mTutorialsPlaceHolderWrapper;
    531     private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
    532     private TextureViewHelper mTextureViewHelper;
    533     private final GestureDetector mGestureDetector;
    534     private DisplayManager.DisplayListener mDisplayListener;
    535     private int mLastRotation;
    536     private int mSwipeState = IDLE;
    537     private PreviewOverlay mPreviewOverlay;
    538     private GridLines mGridLines;
    539     private CaptureAnimationOverlay mCaptureOverlay;
    540     private PreviewStatusListener mPreviewStatusListener;
    541     private int mModeCoverState = COVER_HIDDEN;
    542     private final FilmstripBottomPanel mFilmstripBottomControls;
    543     private final FilmstripContentPanel mFilmstripPanel;
    544     private Runnable mHideCoverRunnable;
    545     private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
    546             = new View.OnLayoutChangeListener() {
    547         @Override
    548         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
    549                 int oldTop, int oldRight, int oldBottom) {
    550             if (mPreviewStatusListener != null) {
    551                 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
    552                         oldTop, oldRight, oldBottom);
    553             }
    554         }
    555     };
    556     private View mModeOptionsToggle;
    557     private final RoundedThumbnailView mRoundedThumbnailView;
    558     private final CaptureLayoutHelper mCaptureLayoutHelper;
    559     private final View mAccessibilityAffordances;
    560     private AccessibilityUtil mAccessibilityUtil;
    561 
    562     private boolean mDisableAllUserInteractions;
    563     /** Whether to prevent capture indicator from being triggered. */
    564     private boolean mSuppressCaptureIndicator;
    565 
    566     /** Supported HDR mode (none, hdr, hdr+). */
    567     private String mHdrSupportMode;
    568 
    569     /** Used to track the last scope used to update the bottom bar UI. */
    570     private String mCurrentCameraScope;
    571     private String mCurrentModuleScope;
    572 
    573     /**
    574      * Provides current preview frame and the controls/overlay from the module that
    575      * are shown on top of the preview.
    576      */
    577     public interface CameraModuleScreenShotProvider {
    578         /**
    579          * Returns the current preview frame down-sampled using the given down-sample
    580          * factor.
    581          *
    582          * @param downSampleFactor the down sample factor for down sampling the
    583          *                         preview frame. (e.g. a down sample factor of
    584          *                         2 means to scale down the preview frame to 1/2
    585          *                         the width and height.)
    586          * @return down-sampled preview frame
    587          */
    588         public Bitmap getPreviewFrame(int downSampleFactor);
    589 
    590         /**
    591          * @return the controls and overlays that are currently showing on top of
    592          *         the preview drawn into a bitmap with no scaling applied.
    593          */
    594         public Bitmap getPreviewOverlayAndControls();
    595 
    596         /**
    597          * Returns a bitmap containing the current screenshot.
    598          *
    599          * @param previewDownSampleFactor the downsample factor applied on the
    600          *                                preview frame when taking the screenshot
    601          */
    602         public Bitmap getScreenShot(int previewDownSampleFactor);
    603     }
    604 
    605     /**
    606      * This listener gets called when the size of the window (excluding the system
    607      * decor such as status bar and nav bar) has changed.
    608      */
    609     public interface NonDecorWindowSizeChangedListener {
    610         public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
    611     }
    612 
    613     private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
    614             new CameraModuleScreenShotProvider() {
    615                 @Override
    616                 public Bitmap getPreviewFrame(int downSampleFactor) {
    617                     if (mCameraRootView == null || mTextureView == null) {
    618                         return null;
    619                     }
    620                     // Gets the bitmap from the preview TextureView.
    621                     Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
    622                     return preview;
    623                 }
    624 
    625                 @Override
    626                 public Bitmap getPreviewOverlayAndControls() {
    627                     Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
    628                             mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
    629                     Canvas canvas = new Canvas(overlays);
    630                     mCameraRootView.draw(canvas);
    631                     return overlays;
    632                 }
    633 
    634                 @Override
    635                 public Bitmap getScreenShot(int previewDownSampleFactor) {
    636                     Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
    637                             mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
    638                     Canvas canvas = new Canvas(screenshot);
    639                     canvas.drawARGB(255, 0, 0, 0);
    640                     Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
    641                     if (preview != null) {
    642                         canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
    643                     }
    644                     Bitmap overlay = getPreviewOverlayAndControls();
    645                     if (overlay != null) {
    646                         canvas.drawBitmap(overlay, 0f, 0f, null);
    647                     }
    648                     return screenshot;
    649                 }
    650             };
    651 
    652     private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
    653 
    654     public long getCoverHiddenTime() {
    655         return mCoverHiddenTime;
    656     }
    657 
    658     /**
    659      * This resets the preview to have no applied transform matrix.
    660      */
    661     public void clearPreviewTransform() {
    662         mTextureViewHelper.clearTransform();
    663     }
    664 
    665     public void updatePreviewAspectRatio(float aspectRatio) {
    666         mTextureViewHelper.updateAspectRatio(aspectRatio);
    667     }
    668 
    669     /**
    670      * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
    671      * its TextureView.  This is necessary to get the expected behavior for the TextureView's
    672      * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
    673      * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
    674      * size).
    675      *
    676      * b/17286155 - Tracking a fix for this in HardwareLayer.
    677      */
    678     public void setDefaultBufferSizeToViewDimens() {
    679         if (mSurface == null || mTextureView == null) {
    680             Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
    681             return;
    682         }
    683         mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
    684     }
    685 
    686     /**
    687      * Updates the preview matrix without altering it.
    688      *
    689      * @param matrix
    690      * @param aspectRatio the desired aspect ratio for the preview.
    691      */
    692     public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
    693         mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
    694     }
    695 
    696     /**
    697      * @return the rect that will display the preview.
    698      */
    699     public RectF getFullscreenRect() {
    700         return mTextureViewHelper.getFullscreenRect();
    701     }
    702 
    703     /**
    704      * This is to support modules that calculate their own transform matrix because
    705      * they need to use a transform matrix to rotate the preview.
    706      *
    707      * @param matrix transform matrix to be set on the TextureView
    708      */
    709     public void updatePreviewTransform(Matrix matrix) {
    710         mTextureViewHelper.updateTransform(matrix);
    711     }
    712 
    713     public interface AnimationFinishedListener {
    714         public void onAnimationFinished(boolean success);
    715     }
    716 
    717     private class MyTouchListener implements View.OnTouchListener {
    718         private boolean mScaleStarted = false;
    719         @Override
    720         public boolean onTouch(View v, MotionEvent event) {
    721             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
    722                 mScaleStarted = false;
    723             } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
    724                 mScaleStarted = true;
    725             }
    726             return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
    727         }
    728     }
    729 
    730     /**
    731      * This gesture listener finds out the direction of the scroll gestures and
    732      * sends them to CameraAppUI to do further handling.
    733      */
    734     private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
    735         private MotionEvent mDown;
    736 
    737         @Override
    738         public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
    739             if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
    740                     || mSwipeState != IDLE
    741                     || mIsCaptureIntent
    742                     || !mSwipeEnabled) {
    743                 return false;
    744             }
    745 
    746             int deltaX = (int) (ev.getX() - mDown.getX());
    747             int deltaY = (int) (ev.getY() - mDown.getY());
    748             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
    749                 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
    750                     // Calculate the direction of the swipe.
    751                     if (deltaX >= Math.abs(deltaY)) {
    752                         // Swipe right.
    753                         setSwipeState(SWIPE_RIGHT);
    754                     } else if (deltaX <= -Math.abs(deltaY)) {
    755                         // Swipe left.
    756                         setSwipeState(SWIPE_LEFT);
    757                     }
    758                 }
    759             }
    760             return true;
    761         }
    762 
    763         private void setSwipeState(int swipeState) {
    764             mSwipeState = swipeState;
    765             // Notify new swipe detected.
    766             onSwipeDetected(swipeState);
    767         }
    768 
    769         @Override
    770         public boolean onDown(MotionEvent ev) {
    771             mDown = MotionEvent.obtain(ev);
    772             mSwipeState = IDLE;
    773             return false;
    774         }
    775     }
    776 
    777     public CameraAppUI(AppController controller, MainActivityLayout appRootView,
    778             boolean isCaptureIntent) {
    779         mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
    780         mController = controller;
    781         mIsCaptureIntent = isCaptureIntent;
    782 
    783         mAppRootView = appRootView;
    784         mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
    785         mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
    786         mModeTransitionView = (ModeTransitionView)
    787                 mAppRootView.findViewById(R.id.mode_transition_view);
    788         mFilmstripBottomControls = new FilmstripBottomPanel(controller,
    789                 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
    790         mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
    791         mGestureDetector = new GestureDetector(controller.getAndroidContext(),
    792                 new MyGestureListener());
    793         Resources res = controller.getAndroidContext().getResources();
    794         mCaptureLayoutHelper = new CaptureLayoutHelper(
    795                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
    796                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
    797                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
    798         mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
    799         if (mModeListView != null) {
    800             mModeListView.setModeSwitchListener(this);
    801             mModeListView.setModeListOpenListener(this);
    802             mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
    803             mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
    804             boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
    805                     SettingsManager.SCOPE_GLOBAL,
    806                     Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
    807             mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
    808         } else {
    809             Log.e(TAG, "Cannot find mode list in the view hierarchy");
    810         }
    811         mAnimationManager = new AnimationManager();
    812         mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
    813         mRoundedThumbnailView.setCallback(new RoundedThumbnailView.Callback() {
    814             @Override
    815             public void onHitStateFinished() {
    816                 mFilmstripLayout.showFilmstrip();
    817             }
    818         });
    819 
    820         mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
    821         initDisplayListener();
    822         mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
    823         View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
    824         modeListToggle.setOnClickListener(new View.OnClickListener() {
    825             @Override
    826             public void onClick(View view) {
    827                 openModeList();
    828             }
    829         });
    830         View filmstripToggle = mAppRootView.findViewById(
    831                 R.id.accessibility_filmstrip_toggle_button);
    832         filmstripToggle.setOnClickListener(new View.OnClickListener() {
    833             @Override
    834             public void onClick(View view) {
    835                 showFilmstrip();
    836             }
    837         });
    838 
    839         mSuppressCaptureIndicator = false;
    840     }
    841 
    842 
    843     /**
    844      * Freeze what is currently shown on screen until the next preview frame comes
    845      * in.
    846      */
    847     public void freezeScreenUntilPreviewReady() {
    848         Log.v(TAG, "freezeScreenUntilPreviewReady");
    849         mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
    850                 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
    851         mHideCoverRunnable = new Runnable() {
    852             @Override
    853             public void run() {
    854                 mModeTransitionView.hideImageCover();
    855             }
    856         };
    857         mModeCoverState = COVER_SHOWN;
    858     }
    859 
    860     /**
    861      * Creates a cling for the specific viewer and links the cling to the corresponding
    862      * button for layout position.
    863      *
    864      * @param viewerType defines which viewer the cling is for.
    865      */
    866     public void setupClingForViewer(int viewerType) {
    867         if (viewerType == BottomPanel.VIEWER_REFOCUS) {
    868             FrameLayout filmstripContent = (FrameLayout) mAppRootView
    869                     .findViewById(R.id.camera_filmstrip_content_layout);
    870             if (filmstripContent != null) {
    871                 // Creates refocus cling.
    872                 LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater();
    873                 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
    874                 // Sets instruction text in the cling.
    875                 refocusCling.setText(mController.getAndroidContext().getResources()
    876                         .getString(R.string.cling_text_for_refocus_editor_button));
    877 
    878                 // Adds cling into view hierarchy.
    879                 int clingWidth = mController.getAndroidContext()
    880                         .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
    881                 filmstripContent.addView(refocusCling, clingWidth,
    882                         ViewGroup.LayoutParams.WRAP_CONTENT);
    883                 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
    884             }
    885         }
    886     }
    887 
    888     /**
    889      * Clears the listeners for the cling and remove it from the view hierarchy.
    890      *
    891      * @param viewerType defines which viewer the cling is for.
    892      */
    893     public void clearClingForViewer(int viewerType) {
    894         Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
    895         if (clingToBeRemoved == null) {
    896             // No cling is created for the specific viewer type.
    897             return;
    898         }
    899         mFilmstripBottomControls.clearClingForViewer(viewerType);
    900         clingToBeRemoved.setVisibility(View.GONE);
    901         mAppRootView.removeView(clingToBeRemoved);
    902     }
    903 
    904     /**
    905      * Enable or disable swipe gestures. We want to disable them e.g. while we
    906      * record a video.
    907      */
    908     public void setSwipeEnabled(boolean enabled) {
    909         mSwipeEnabled = enabled;
    910         // TODO: This can be removed once we come up with a new design for handling swipe
    911         // on shutter button and mode options. (More details: b/13751653)
    912         mAppRootView.setSwipeEnabled(enabled);
    913     }
    914 
    915     public void onDestroy() {
    916         AndroidServices.instance().provideDisplayManager()
    917                 .unregisterDisplayListener(mDisplayListener);
    918     }
    919 
    920     /**
    921      * Initializes the display listener to listen to display changes such as
    922      * 180-degree rotation change, which will not have an onConfigurationChanged
    923      * callback.
    924      */
    925     private void initDisplayListener() {
    926         if (ApiHelper.HAS_DISPLAY_LISTENER) {
    927             mLastRotation = CameraUtil.getDisplayRotation();
    928 
    929             mDisplayListener = new DisplayManager.DisplayListener() {
    930                 @Override
    931                 public void onDisplayAdded(int arg0) {
    932                     // Do nothing.
    933                 }
    934 
    935                 @Override
    936                 public void onDisplayChanged(int displayId) {
    937                     int rotation = CameraUtil.getDisplayRotation(
    938                     );
    939                     if ((rotation - mLastRotation + 360) % 360 == 180
    940                             && mPreviewStatusListener != null) {
    941                         mPreviewStatusListener.onPreviewFlipped();
    942                         mStickyBottomCaptureLayout.requestLayout();
    943                         mModeListView.requestLayout();
    944                         mTextureView.requestLayout();
    945                     }
    946                     mLastRotation = rotation;
    947                 }
    948 
    949                 @Override
    950                 public void onDisplayRemoved(int arg0) {
    951                     // Do nothing.
    952                 }
    953             };
    954 
    955             AndroidServices.instance().provideDisplayManager()
    956                   .registerDisplayListener(mDisplayListener, null);
    957         }
    958     }
    959 
    960     /**
    961      * Redirects touch events to appropriate recipient views based on swipe direction.
    962      * More specifically, swipe up and swipe down will be handled by the view that handles
    963      * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
    964      * to mode list in order to bring up mode list.
    965      */
    966     private void onSwipeDetected(int swipeState) {
    967         if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
    968             // TODO: Polish quick switch after this release.
    969             // Quick switch between modes.
    970             int currentModuleIndex = mController.getCurrentModuleIndex();
    971             final int moduleToTransitionTo =
    972                     mController.getQuickSwitchToModuleId(currentModuleIndex);
    973             if (currentModuleIndex != moduleToTransitionTo) {
    974                 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
    975                 int shadeColorId = R.color.camera_gray_background;
    976                 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
    977                         mController.getAndroidContext());
    978 
    979                 AnimationFinishedListener listener = new AnimationFinishedListener() {
    980                     @Override
    981                     public void onAnimationFinished(boolean success) {
    982                         if (success) {
    983                             mHideCoverRunnable = new Runnable() {
    984                                 @Override
    985                                 public void run() {
    986                                     mModeTransitionView.startPeepHoleAnimation();
    987                                 }
    988                             };
    989                             mModeCoverState = COVER_SHOWN;
    990                             // Go to new module when the previous operation is successful.
    991                             mController.onModeSelected(moduleToTransitionTo);
    992                         }
    993                     }
    994                 };
    995             }
    996         } else if (swipeState == SWIPE_LEFT) {
    997             // Pass the touch sequence to filmstrip layout.
    998             mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
    999         } else if (swipeState == SWIPE_RIGHT) {
   1000             // Pass the touch to mode switcher
   1001             mAppRootView.redirectTouchEventsTo(mModeListView);
   1002         }
   1003     }
   1004 
   1005     /**
   1006      * Gets called when activity resumes in preview.
   1007      */
   1008     public void resume() {
   1009         // Show mode theme cover until preview is ready
   1010         showModeCoverUntilPreviewReady();
   1011 
   1012         // Hide action bar first since we are in full screen mode first, and
   1013         // switch the system UI to lights-out mode.
   1014         mFilmstripPanel.hide();
   1015 
   1016         // Show UI that is meant to only be used when spoken feedback is
   1017         // enabled.
   1018         mAccessibilityAffordances.setVisibility(
   1019                 (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) ? View.VISIBLE
   1020                         : View.GONE);
   1021     }
   1022 
   1023     /**
   1024      * Opens the mode list (e.g. because of the menu button being pressed) and
   1025      * adapts the rest of the UI.
   1026      */
   1027     public void openModeList() {
   1028         mModeOptionsOverlay.closeModeOptions();
   1029         mModeListView.onMenuPressed();
   1030     }
   1031 
   1032     public void showAccessibilityZoomUI(float maxZoom) {
   1033         mAccessibilityUtil.showZoomUI(maxZoom);
   1034     }
   1035 
   1036     public void hideAccessibilityZoomUI() {
   1037         mAccessibilityUtil.hideZoomUI();
   1038     }
   1039 
   1040     /**
   1041      * A cover view showing the mode theme color and mode icon will be visible on
   1042      * top of preview until preview is ready (i.e. camera preview is started and
   1043      * the first frame has been received).
   1044      */
   1045     private void showModeCoverUntilPreviewReady() {
   1046         int modeId = mController.getCurrentModuleIndex();
   1047         int colorId = R.color.camera_gray_background;;
   1048         int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
   1049         mModeTransitionView.setupModeCover(colorId, iconId);
   1050         mHideCoverRunnable = new Runnable() {
   1051             @Override
   1052             public void run() {
   1053                 mModeTransitionView.hideModeCover(null);
   1054                 if (!mDisableAllUserInteractions) {
   1055                     showShimmyDelayed();
   1056                 }
   1057             }
   1058         };
   1059         mModeCoverState = COVER_SHOWN;
   1060     }
   1061 
   1062     private void showShimmyDelayed() {
   1063         if (!mIsCaptureIntent) {
   1064             // Show shimmy in SHIMMY_DELAY_MS
   1065             mModeListView.showModeSwitcherHint();
   1066         }
   1067     }
   1068 
   1069     private void hideModeCover() {
   1070         if (mHideCoverRunnable != null) {
   1071             mAppRootView.post(mHideCoverRunnable);
   1072             mHideCoverRunnable = null;
   1073         }
   1074         mModeCoverState = COVER_HIDDEN;
   1075         if (mCoverHiddenTime < 0) {
   1076             mCoverHiddenTime = System.currentTimeMillis();
   1077         }
   1078     }
   1079 
   1080 
   1081     public void onPreviewVisiblityChanged(int visibility) {
   1082         if (visibility == ModuleController.VISIBILITY_HIDDEN) {
   1083             setIndicatorBottomBarWrapperVisible(false);
   1084             mAccessibilityAffordances.setVisibility(View.GONE);
   1085         } else {
   1086             setIndicatorBottomBarWrapperVisible(true);
   1087             if (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) {
   1088                 mAccessibilityAffordances.setVisibility(View.VISIBLE);
   1089             } else {
   1090                 mAccessibilityAffordances.setVisibility(View.GONE);
   1091             }
   1092         }
   1093     }
   1094 
   1095     /**
   1096      * Call to stop the preview from being rendered. Sets the entire capture
   1097      * root view to invisible which includes the preview plus focus indicator
   1098      * and any other auxiliary views for capture modes.
   1099      */
   1100     public void pausePreviewRendering() {
   1101         mCameraRootView.setVisibility(View.INVISIBLE);
   1102     }
   1103 
   1104     /**
   1105      * Call to begin rendering the preview and auxiliary views again.
   1106      */
   1107     public void resumePreviewRendering() {
   1108         mCameraRootView.setVisibility(View.VISIBLE);
   1109     }
   1110 
   1111     /**
   1112      * Returns the transform associated with the preview view.
   1113      *
   1114      * @param m the Matrix in which to copy the current transform.
   1115      * @return The specified matrix if not null or a new Matrix instance
   1116      *         otherwise.
   1117      */
   1118     public Matrix getPreviewTransform(Matrix m) {
   1119         return mTextureView.getTransform(m);
   1120     }
   1121 
   1122     @Override
   1123     public void onOpenFullScreen() {
   1124         // Do nothing.
   1125     }
   1126 
   1127     @Override
   1128     public void onModeListOpenProgress(float progress) {
   1129         // When the mode list is in transition, ensure the large layers are
   1130         // hardware accelerated.
   1131         if (progress >= 1.0f || progress <= 0.0f) {
   1132             // Convert hardware layers back to default layer types when animation stops
   1133             // to prevent accidental artifacting.
   1134             if(mModeOptionsToggle.getLayerType() == View.LAYER_TYPE_HARDWARE ||
   1135                   mShutterButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
   1136                 Log.v(TAG, "Disabling hardware layer for the Mode Options Toggle Button.");
   1137                 mModeOptionsToggle.setLayerType(View.LAYER_TYPE_NONE, null);
   1138                 Log.v(TAG, "Disabling hardware layer for the Shutter Button.");
   1139                 mShutterButton.setLayerType(View.LAYER_TYPE_NONE, null);
   1140             }
   1141         } else {
   1142             if(mModeOptionsToggle.getLayerType() != View.LAYER_TYPE_HARDWARE ||
   1143                   mShutterButton.getLayerType() != View.LAYER_TYPE_HARDWARE) {
   1144                 Log.v(TAG, "Enabling hardware layer for the Mode Options Toggle Button.");
   1145                 mModeOptionsToggle.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   1146                 Log.v(TAG, "Enabling hardware layer for the Shutter Button.");
   1147                 mShutterButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
   1148             }
   1149         }
   1150 
   1151         progress = 1 - progress;
   1152         float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
   1153         mModeOptionsToggle.setAlpha(interpolatedProgress);
   1154         // Change shutter button alpha linearly based on the mode list open progress:
   1155         // set the alpha to disabled alpha when list is fully open, to enabled alpha
   1156         // when the list is fully closed.
   1157         mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
   1158                 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
   1159     }
   1160 
   1161     @Override
   1162     public void onModeListClosed() {
   1163         // Convert hardware layers back to default layer types when animation stops
   1164         // to prevent accidental artifacting.
   1165         if(mModeOptionsToggle.getLayerType() == View.LAYER_TYPE_HARDWARE ||
   1166               mShutterButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
   1167             Log.v(TAG, "Disabling hardware layer for the Mode Options Toggle Button.");
   1168             mModeOptionsToggle.setLayerType(View.LAYER_TYPE_NONE, null);
   1169             Log.v(TAG, "Disabling hardware layer for the Shutter Button.");
   1170             mShutterButton.setLayerType(View.LAYER_TYPE_NONE, null);
   1171         }
   1172 
   1173         // Make sure the alpha on mode options ellipse is reset when mode drawer
   1174         // is closed.
   1175         mModeOptionsToggle.setAlpha(1f);
   1176         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
   1177     }
   1178 
   1179     /**
   1180      * Called when the back key is pressed.
   1181      *
   1182      * @return Whether the UI responded to the key event.
   1183      */
   1184     public boolean onBackPressed() {
   1185         if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
   1186             return mFilmstripLayout.onBackPressed();
   1187         } else {
   1188             return mModeListView.onBackPressed();
   1189         }
   1190     }
   1191 
   1192     /**
   1193      * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
   1194      * listens to SurfaceTexture changes. In addition, listeners are set on
   1195      * dependent app ui elements.
   1196      *
   1197      * @param previewStatusListener the listener that gets notified when SurfaceTexture
   1198      *                              changes
   1199      */
   1200     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
   1201         mPreviewStatusListener = previewStatusListener;
   1202         if (mPreviewStatusListener != null) {
   1203             onPreviewListenerChanged();
   1204         }
   1205     }
   1206 
   1207     /**
   1208      * When the PreviewStatusListener changes, listeners need to be
   1209      * set on the following app ui elements:
   1210      * {@link com.android.camera.ui.PreviewOverlay},
   1211      * {@link com.android.camera.ui.BottomBar},
   1212      * {@link com.android.camera.ui.IndicatorIconController}.
   1213      */
   1214     private void onPreviewListenerChanged() {
   1215         // Set a listener for recognizing preview gestures.
   1216         GestureDetector.OnGestureListener gestureListener
   1217             = mPreviewStatusListener.getGestureListener();
   1218         if (gestureListener != null) {
   1219             mPreviewOverlay.setGestureListener(gestureListener);
   1220         }
   1221         View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
   1222         if (touchListener != null) {
   1223             mPreviewOverlay.setTouchListener(touchListener);
   1224         }
   1225 
   1226         mTextureViewHelper.setAutoAdjustTransform(
   1227                 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
   1228     }
   1229 
   1230     /**
   1231      * This method should be called in onCameraOpened.  It defines CameraAppUI
   1232      * specific changes that depend on the camera or camera settings.
   1233      */
   1234     public void onChangeCamera() {
   1235         ModuleController moduleController = mController.getCurrentModuleController();
   1236         HardwareSpec hardwareSpec = moduleController.getHardwareSpec();
   1237 
   1238         /**
   1239          * The current UI requires that the flash option visibility in front-
   1240          * facing camera be
   1241          *   * disabled if back facing camera supports flash
   1242          *   * hidden if back facing camera does not support flash
   1243          * We save whether back facing camera supports flash because we cannot
   1244          * get this in front facing camera without a camera switch.
   1245          *
   1246          * If this preference is cleared, we also need to clear the camera
   1247          * facing setting so we default to opening the camera in back facing
   1248          * camera, and can save this flash support value again.
   1249          */
   1250         if (hardwareSpec != null) {
   1251             if (!mController.getSettingsManager().isSet(SettingsManager.SCOPE_GLOBAL,
   1252                     Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA)) {
   1253                 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
   1254                         Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA,
   1255                         hardwareSpec.isFlashSupported());
   1256             }
   1257             /** Similar logic applies to the HDR option. */
   1258             if (!mController.getSettingsManager().isSet(SettingsManager.SCOPE_GLOBAL,
   1259                     Keys.KEY_HDR_SUPPORT_MODE_BACK_CAMERA)) {
   1260                 String hdrSupportMode;
   1261                 if (hardwareSpec.isHdrPlusSupported()) {
   1262                     hdrSupportMode = getResourceString(
   1263                             R.string.pref_camera_hdr_supportmode_hdr_plus);
   1264                 } else if (hardwareSpec.isHdrSupported()) {
   1265                     hdrSupportMode = getResourceString(R.string.pref_camera_hdr_supportmode_hdr);
   1266                 } else {
   1267                     hdrSupportMode = getResourceString(R.string.pref_camera_hdr_supportmode_none);
   1268                 }
   1269                 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
   1270                         Keys.KEY_HDR_SUPPORT_MODE_BACK_CAMERA, hdrSupportMode);
   1271             }
   1272         }
   1273 
   1274         applyModuleSpecs(hardwareSpec, moduleController.getBottomBarSpec(),
   1275                 true /*skipScopeCheck*/);
   1276         syncModeOptionIndicators();
   1277     }
   1278 
   1279     /**
   1280      * Updates the mode option indicators according to the current settings.
   1281      */
   1282     public void syncModeOptionIndicators() {
   1283         if (mIndicatorIconController != null) {
   1284             // Sync the settings state with the indicator state.
   1285             mIndicatorIconController.syncIndicators();
   1286         }
   1287     }
   1288 
   1289     /**
   1290      * Adds a listener to receive callbacks when preview area changes.
   1291      */
   1292     public void addPreviewAreaChangedListener(
   1293             PreviewStatusListener.PreviewAreaChangedListener listener) {
   1294         mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
   1295     }
   1296 
   1297     /**
   1298      * Removes a listener that receives callbacks when preview area changes.
   1299      */
   1300     public void removePreviewAreaChangedListener(
   1301             PreviewStatusListener.PreviewAreaChangedListener listener) {
   1302         mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
   1303     }
   1304 
   1305     /**
   1306      * This inflates generic_module layout, which contains all the shared views across
   1307      * modules. Then each module inflates their own views in the given view group. For
   1308      * now, this is called every time switching from a not-yet-refactored module to a
   1309      * refactored module. In the future, this should only need to be done once per app
   1310      * start.
   1311      */
   1312     public void prepareModuleUI() {
   1313         mController.getSettingsManager().addListener(this);
   1314         mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
   1315         mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
   1316         mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
   1317                 mController.getCameraProvider(), mController);
   1318         mTextureViewHelper.setSurfaceTextureListener(this);
   1319         mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
   1320 
   1321         mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
   1322         int unpressedColor = mController.getAndroidContext().getResources()
   1323             .getColor(R.color.camera_gray_background);
   1324         setBottomBarColor(unpressedColor);
   1325         updateModeSpecificUIColors();
   1326 
   1327         mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
   1328 
   1329         mModeOptionsOverlay
   1330             = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
   1331 
   1332         // Sets the visibility of the bottom bar and the mode options.
   1333         resetBottomControls(mController.getCurrentModuleController(),
   1334             mController.getCurrentModuleIndex());
   1335         mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
   1336 
   1337         mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
   1338         addShutterListener(mController.getCurrentModuleController());
   1339         addShutterListener(mModeOptionsOverlay);
   1340         addShutterListener(this);
   1341 
   1342         mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
   1343         mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
   1344 
   1345         mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
   1346         mPreviewOverlay.setOnTouchListener(new MyTouchListener());
   1347         mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
   1348         mAccessibilityUtil = new AccessibilityUtil(mPreviewOverlay, mAccessibilityAffordances);
   1349 
   1350         mCaptureOverlay = (CaptureAnimationOverlay)
   1351                 mCameraRootView.findViewById(R.id.capture_overlay);
   1352         mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
   1353         mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
   1354 
   1355         if (mIndicatorIconController == null) {
   1356             mIndicatorIconController =
   1357                 new IndicatorIconController(mController, mAppRootView);
   1358         }
   1359 
   1360         mController.getButtonManager().load(mCameraRootView);
   1361         mController.getButtonManager().setListener(mIndicatorIconController);
   1362         mController.getSettingsManager().addListener(mIndicatorIconController);
   1363 
   1364         mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
   1365         mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
   1366         mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
   1367                 .findViewById(R.id.tutorials_placeholder_wrapper);
   1368         mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
   1369                 .findViewById(R.id.sticky_bottom_capture_layout);
   1370         mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
   1371         mCountdownCancelButton = (ImageButton) mStickyBottomCaptureLayout
   1372                 .findViewById(R.id.shutter_cancel_button);
   1373 
   1374         mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
   1375         mTextureViewHelper.addAspectRatioChangedListener(
   1376                 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
   1377                     @Override
   1378                     public void onPreviewAspectRatioChanged(float aspectRatio) {
   1379                         mModeOptionsOverlay.requestLayout();
   1380                         mBottomBar.requestLayout();
   1381                     }
   1382                 }
   1383         );
   1384     }
   1385 
   1386     /**
   1387      * Called indirectly from each module in their initialization to get a view group
   1388      * to inflate the module specific views in.
   1389      *
   1390      * @return a view group for modules to attach views to
   1391      */
   1392     public FrameLayout getModuleRootView() {
   1393         // TODO: Change it to mModuleUI when refactor is done
   1394         return mCameraRootView;
   1395     }
   1396 
   1397     /**
   1398      * Remove all the module specific views.
   1399      */
   1400     public void clearModuleUI() {
   1401         if (mModuleUI != null) {
   1402             mModuleUI.removeAllViews();
   1403         }
   1404         removeShutterListener(mController.getCurrentModuleController());
   1405         mTutorialsPlaceHolderWrapper.removeAllViews();
   1406         mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
   1407 
   1408         setShutterButtonEnabled(true);
   1409         mPreviewStatusListener = null;
   1410         mPreviewOverlay.reset();
   1411 
   1412         Log.v(TAG, "mFocusRing.stopFocusAnimations()");
   1413         mFocusRing.stopFocusAnimations();
   1414     }
   1415 
   1416     /**
   1417      * Gets called when preview is ready to start. It sets up one shot preview callback
   1418      * in order to receive a callback when the preview frame is available, so that
   1419      * the preview cover can be hidden to reveal preview.
   1420      *
   1421      * An alternative for getting the timing to hide preview cover is through
   1422      * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
   1423      * which is less accurate but therefore is the fallback for modules that manage
   1424      * their own preview callbacks (as setting one preview callback will override
   1425      * any other installed preview callbacks), or use camera2 API.
   1426      */
   1427     public void onPreviewReadyToStart() {
   1428         if (mModeCoverState == COVER_SHOWN) {
   1429             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
   1430             mController.setupOneShotPreviewListener();
   1431         }
   1432     }
   1433 
   1434     /**
   1435      * Gets called when preview is started.
   1436      */
   1437     public void onPreviewStarted() {
   1438         Log.v(TAG, "onPreviewStarted");
   1439         if (mModeCoverState == COVER_SHOWN) {
   1440             // This is a work around of the face detection failure in b/20724126.
   1441             // In particular, we need to drop the first preview frame in order to
   1442             // make face detection work and also need to hide this preview frame to
   1443             // avoid potential janks. We do this only for L, Nexus 6 and Haleakala.
   1444             if (ApiHelper.isLorLMr1() && ApiHelper.IS_NEXUS_6) {
   1445                 mModeCoverState = COVER_WILL_HIDE_AFTER_NEXT_TEXTURE_UPDATE;
   1446             } else {
   1447                 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
   1448             }
   1449         }
   1450         enableModeOptions();
   1451     }
   1452 
   1453     /**
   1454      * Gets notified when next preview frame comes in.
   1455      */
   1456     public void onNewPreviewFrame() {
   1457         Log.v(TAG, "onNewPreviewFrame");
   1458         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
   1459         hideModeCover();
   1460     }
   1461 
   1462     @Override
   1463     public void onShutterButtonClick() {
   1464         /*
   1465          * Set the mode options toggle unclickable, generally
   1466          * throughout the app, whenever the shutter button is clicked.
   1467          *
   1468          * This could be done in the OnShutterButtonListener of the
   1469          * ModeOptionsOverlay, but since it is very important that we
   1470          * can clearly see when the toggle becomes clickable again,
   1471          * keep all of that logic at this level.
   1472          */
   1473         // disableModeOptions();
   1474     }
   1475 
   1476     @Override
   1477     public void onShutterCoordinate(TouchCoordinate coord) {
   1478         // Do nothing.
   1479     }
   1480 
   1481     @Override
   1482     public void onShutterButtonFocus(boolean pressed) {
   1483         // noop
   1484     }
   1485 
   1486     @Override
   1487     public void onShutterButtonLongPressed() {
   1488         // noop
   1489     }
   1490 
   1491     /**
   1492      * Set the mode options toggle clickable.
   1493      */
   1494     public void enableModeOptions() {
   1495         /*
   1496          * For modules using camera 1 api, this gets called in
   1497          * onSurfaceTextureUpdated whenever the preview gets stopped and
   1498          * started after each capture.  This also takes care of the
   1499          * case where the mode options might be unclickable when we
   1500          * switch modes
   1501          *
   1502          * For modules using camera 2 api, they're required to call this
   1503          * method when a capture is "completed".  Unfortunately this differs
   1504          * per module implementation.
   1505          */
   1506         if (!mDisableAllUserInteractions) {
   1507             mModeOptionsOverlay.setToggleClickable(true);
   1508         }
   1509     }
   1510 
   1511     /**
   1512      * Set the mode options toggle not clickable.
   1513      */
   1514     public void disableModeOptions() {
   1515         mModeOptionsOverlay.setToggleClickable(false);
   1516     }
   1517 
   1518     public void setDisableAllUserInteractions(boolean disable) {
   1519         if (disable) {
   1520             disableModeOptions();
   1521             setShutterButtonEnabled(false);
   1522             setSwipeEnabled(false);
   1523             mModeListView.hideAnimated();
   1524         } else {
   1525             enableModeOptions();
   1526             setShutterButtonEnabled(true);
   1527             setSwipeEnabled(true);
   1528         }
   1529         mDisableAllUserInteractions = disable;
   1530     }
   1531 
   1532     @Override
   1533     public void onModeButtonPressed(int modeIndex) {
   1534         // TODO: Make CameraActivity listen to ModeListView's events.
   1535         int pressedModuleId = mController.getModuleId(modeIndex);
   1536         int currentModuleId = mController.getCurrentModuleIndex();
   1537         if (pressedModuleId != currentModuleId) {
   1538             hideCaptureIndicator();
   1539         }
   1540     }
   1541 
   1542     /**
   1543      * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
   1544      *
   1545      * @param modeIndex mode index of the selected mode
   1546      */
   1547     @Override
   1548     public void onModeSelected(int modeIndex) {
   1549         mHideCoverRunnable = new Runnable() {
   1550             @Override
   1551             public void run() {
   1552                 mModeListView.startModeSelectionAnimation();
   1553             }
   1554         };
   1555         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
   1556         mModeCoverState = COVER_SHOWN;
   1557 
   1558         int lastIndex = mController.getCurrentModuleIndex();
   1559         // Actual mode teardown / new mode initialization happens here
   1560         mController.onModeSelected(modeIndex);
   1561         int currentIndex = mController.getCurrentModuleIndex();
   1562 
   1563         if (lastIndex == currentIndex) {
   1564             hideModeCover();
   1565         }
   1566 
   1567         updateModeSpecificUIColors();
   1568     }
   1569 
   1570     private void updateModeSpecificUIColors() {
   1571         setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
   1572     }
   1573 
   1574     @Override
   1575     public void onSettingsSelected() {
   1576         mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
   1577                                              Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
   1578         mModeListView.setShouldShowSettingsCling(false);
   1579         mController.onSettingsSelected();
   1580     }
   1581 
   1582     @Override
   1583     public int getCurrentModeIndex() {
   1584         return mController.getCurrentModuleIndex();
   1585     }
   1586 
   1587     /********************** Capture animation **********************/
   1588     /* TODO: This session is subject to UX changes. In addition to the generic
   1589        flash animation and post capture animation, consider designating a parameter
   1590        for specifying the type of animation, as well as an animation finished listener
   1591        so that modules can have more knowledge of the status of the animation. */
   1592 
   1593     /**
   1594      * Turns on or off the capture indicator suppression.
   1595      */
   1596     public void setShouldSuppressCaptureIndicator(boolean suppress) {
   1597         mSuppressCaptureIndicator = suppress;
   1598     }
   1599 
   1600     /**
   1601      * Starts the capture indicator pop-out animation.
   1602      *
   1603      * @param accessibilityString An accessibility String to be announced during the peek animation.
   1604      */
   1605     public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
   1606         if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
   1607             return;
   1608         }
   1609         mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
   1610     }
   1611 
   1612     /**
   1613      * Updates the thumbnail image in the capture indicator.
   1614      *
   1615      * @param thumbnailBitmap The thumbnail image to be shown.
   1616      */
   1617     public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
   1618         if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
   1619             return;
   1620         }
   1621         mRoundedThumbnailView.setThumbnail(thumbnailBitmap, rotation);
   1622     }
   1623 
   1624     /**
   1625      * Hides the capture indicator.
   1626      */
   1627     public void hideCaptureIndicator() {
   1628         mRoundedThumbnailView.hideThumbnail();
   1629     }
   1630 
   1631     /**
   1632      * Starts the flash animation.
   1633      */
   1634     public void startFlashAnimation(boolean shortFlash) {
   1635         mCaptureOverlay.startFlashAnimation(shortFlash);
   1636     }
   1637 
   1638     /**
   1639      * Cancels the pre-capture animation.
   1640      */
   1641     public void cancelPreCaptureAnimation() {
   1642         mAnimationManager.cancelAnimations();
   1643     }
   1644 
   1645     /**
   1646      * Cancels the post-capture animation.
   1647      */
   1648     public void cancelPostCaptureAnimation() {
   1649         mAnimationManager.cancelAnimations();
   1650     }
   1651 
   1652     public FilmstripContentPanel getFilmstripContentPanel() {
   1653         return mFilmstripPanel;
   1654     }
   1655 
   1656     /**
   1657      * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
   1658      * bottom of the filmstrip.
   1659      */
   1660     public BottomPanel getFilmstripBottomControls() {
   1661         return mFilmstripBottomControls;
   1662     }
   1663 
   1664     public void showBottomControls() {
   1665         mFilmstripBottomControls.show();
   1666     }
   1667 
   1668     public void hideBottomControls() {
   1669         mFilmstripBottomControls.hide();
   1670     }
   1671 
   1672     /**
   1673      * @param listener The listener for bottom controls.
   1674      */
   1675     public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
   1676         mFilmstripBottomControls.setListener(listener);
   1677     }
   1678 
   1679     /***************************SurfaceTexture Api and Listener*********************************/
   1680 
   1681     /**
   1682      * Return the shared surface texture.
   1683      */
   1684     public SurfaceTexture getSurfaceTexture() {
   1685         return mSurface;
   1686     }
   1687 
   1688     /**
   1689      * Return the shared {@link android.graphics.SurfaceTexture}'s width.
   1690      */
   1691     public int getSurfaceWidth() {
   1692         return mSurfaceWidth;
   1693     }
   1694 
   1695     /**
   1696      * Return the shared {@link android.graphics.SurfaceTexture}'s height.
   1697      */
   1698     public int getSurfaceHeight() {
   1699         return mSurfaceHeight;
   1700     }
   1701 
   1702     @Override
   1703     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
   1704         mSurface = surface;
   1705         mSurfaceWidth = width;
   1706         mSurfaceHeight = height;
   1707         Log.v(TAG, "SurfaceTexture is available");
   1708         if (mPreviewStatusListener != null) {
   1709             mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
   1710         }
   1711         enableModeOptions();
   1712     }
   1713 
   1714     @Override
   1715     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
   1716         mSurface = surface;
   1717         mSurfaceWidth = width;
   1718         mSurfaceHeight = height;
   1719         if (mPreviewStatusListener != null) {
   1720             mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
   1721         }
   1722     }
   1723 
   1724     @Override
   1725     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
   1726         mSurface = null;
   1727         Log.v(TAG, "SurfaceTexture is destroyed");
   1728         if (mPreviewStatusListener != null) {
   1729             return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
   1730         }
   1731         return false;
   1732     }
   1733 
   1734     @Override
   1735     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
   1736         mSurface = surface;
   1737         if (mPreviewStatusListener != null) {
   1738             mPreviewStatusListener.onSurfaceTextureUpdated(surface);
   1739         }
   1740         // Do not show the first preview frame. Due to the bug b/20724126, we need to have
   1741         // a WAR to request a preview frame followed by 5-frame ZSL burst before the repeating
   1742         // preview and ZSL streams. Need to hide the first preview frame since it is janky.
   1743         // We do this only for L, Nexus 6 and Haleakala.
   1744         if (mModeCoverState == COVER_WILL_HIDE_AFTER_NEXT_TEXTURE_UPDATE) {
   1745             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
   1746         } else if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE){
   1747             Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
   1748             CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
   1749             hideModeCover();
   1750         }
   1751     }
   1752 
   1753     /****************************Grid lines api ******************************/
   1754 
   1755     /**
   1756      * Show a set of evenly spaced lines over the preview.  The number
   1757      * of lines horizontally and vertically is determined by
   1758      * {@link com.android.camera.ui.GridLines}.
   1759      */
   1760     public void showGridLines() {
   1761         if (mGridLines != null) {
   1762             mGridLines.setVisibility(View.VISIBLE);
   1763         }
   1764     }
   1765 
   1766     /**
   1767      * Hide the set of evenly spaced grid lines overlaying the preview.
   1768      */
   1769     public void hideGridLines() {
   1770         if (mGridLines != null) {
   1771             mGridLines.setVisibility(View.INVISIBLE);
   1772         }
   1773     }
   1774 
   1775     /**
   1776      * Return a callback which shows or hide the preview grid lines
   1777      * depending on whether the grid lines setting is set on.
   1778      */
   1779     public ButtonManager.ButtonCallback getGridLinesCallback() {
   1780         return new ButtonManager.ButtonCallback() {
   1781             @Override
   1782             public void onStateChanged(int state) {
   1783                 if (!mController.isPaused()) {
   1784                     if (Keys.areGridLinesOn(mController.getSettingsManager())) {
   1785                         showGridLines();
   1786                     } else {
   1787                         hideGridLines();
   1788                     }
   1789                 }
   1790             }
   1791         };
   1792     }
   1793 
   1794     /***************************Mode options api *****************************/
   1795 
   1796     /**
   1797      * Set the mode options visible.
   1798      */
   1799     public void showModeOptions() {
   1800         /* Make mode options clickable. */
   1801         enableModeOptions();
   1802         mModeOptionsOverlay.setVisibility(View.VISIBLE);
   1803     }
   1804 
   1805     /**
   1806      * Set the mode options invisible.  This is necessary for modes
   1807      * that don't show a bottom bar for the capture UI.
   1808      */
   1809     public void hideModeOptions() {
   1810         mModeOptionsOverlay.setVisibility(View.INVISIBLE);
   1811     }
   1812 
   1813     /****************************Bottom bar api ******************************/
   1814 
   1815     /**
   1816      * Sets up the bottom bar and mode options with the correct
   1817      * shutter button and visibility based on the current module.
   1818      */
   1819     public void resetBottomControls(ModuleController module, int moduleIndex) {
   1820         if (areBottomControlsUsed(module)) {
   1821             setBottomBarShutterIcon(moduleIndex);
   1822             mCaptureLayoutHelper.setShowBottomBar(true);
   1823         } else {
   1824             mCaptureLayoutHelper.setShowBottomBar(false);
   1825         }
   1826     }
   1827 
   1828     /**
   1829      * Show or hide the mode options and bottom bar, based on
   1830      * whether the current module is using the bottom bar.  Returns
   1831      * whether the mode options and bottom bar are used.
   1832      */
   1833     private boolean areBottomControlsUsed(ModuleController module) {
   1834         if (module.isUsingBottomBar()) {
   1835             showBottomBar();
   1836             showModeOptions();
   1837             return true;
   1838         } else {
   1839             hideBottomBar();
   1840             hideModeOptions();
   1841             return false;
   1842         }
   1843     }
   1844 
   1845     /**
   1846      * Set the bottom bar visible.
   1847      */
   1848     public void showBottomBar() {
   1849         mBottomBar.setVisibility(View.VISIBLE);
   1850     }
   1851 
   1852     /**
   1853      * Set the bottom bar invisible.
   1854      */
   1855     public void hideBottomBar() {
   1856         mBottomBar.setVisibility(View.INVISIBLE);
   1857     }
   1858 
   1859     /**
   1860      * Sets the color of the bottom bar.
   1861      */
   1862     public void setBottomBarColor(int colorId) {
   1863         mBottomBar.setBackgroundColor(colorId);
   1864     }
   1865 
   1866     /**
   1867      * Sets the pressed color of the bottom bar for a camera mode index.
   1868      */
   1869     public void setBottomBarColorsForModeIndex(int index) {
   1870         mBottomBar.setColorsForModeIndex(index);
   1871     }
   1872 
   1873     /**
   1874      * Sets the shutter button icon on the bottom bar, based on
   1875      * the mode index.
   1876      */
   1877     public void setBottomBarShutterIcon(int modeIndex) {
   1878         int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
   1879             mController.getAndroidContext());
   1880         mBottomBar.setShutterButtonIcon(shutterIconId);
   1881     }
   1882 
   1883     public void animateBottomBarToVideoStop(int shutterIconId) {
   1884         mBottomBar.animateToVideoStop(shutterIconId);
   1885     }
   1886 
   1887     public void animateBottomBarToFullSize(int shutterIconId) {
   1888         mBottomBar.animateToFullSize(shutterIconId);
   1889     }
   1890 
   1891     public void setShutterButtonEnabled(final boolean enabled) {
   1892         if (!mDisableAllUserInteractions) {
   1893             mBottomBar.post(new Runnable() {
   1894                 @Override
   1895                 public void run() {
   1896                     mBottomBar.setShutterButtonEnabled(enabled);
   1897                 }
   1898             });
   1899         }
   1900     }
   1901 
   1902     public void setShutterButtonImportantToA11y(boolean important) {
   1903         mBottomBar.setShutterButtonImportantToA11y(important);
   1904     }
   1905 
   1906     public boolean isShutterButtonEnabled() {
   1907         return mBottomBar.isShutterButtonEnabled();
   1908     }
   1909 
   1910     public void setIndicatorBottomBarWrapperVisible(boolean visible) {
   1911         mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
   1912     }
   1913 
   1914     /**
   1915      * Set the visibility of the bottom bar.
   1916      */
   1917     // TODO: needed for when panorama is managed by the generic module ui.
   1918     public void setBottomBarVisible(boolean visible) {
   1919         mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
   1920     }
   1921 
   1922     /**
   1923      * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
   1924      */
   1925     public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
   1926         mShutterButton.addOnShutterButtonListener(listener);
   1927     }
   1928 
   1929     /**
   1930      * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
   1931      */
   1932     public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
   1933         mShutterButton.removeOnShutterButtonListener(listener);
   1934     }
   1935 
   1936     /**
   1937      * Sets or replaces the "cancel shutter" button listener.
   1938      * <p>
   1939      * TODO: Make this part of the interface the same way shutter button
   1940      * listeners are.
   1941      */
   1942     public void setCancelShutterButtonListener(View.OnClickListener listener) {
   1943         mCountdownCancelButton.setOnClickListener(listener);
   1944     }
   1945 
   1946     /**
   1947      * Performs a transition to the capture layout of the bottom bar.
   1948      */
   1949     public void transitionToCapture() {
   1950         ModuleController moduleController = mController.getCurrentModuleController();
   1951         applyModuleSpecs(moduleController.getHardwareSpec(),
   1952             moduleController.getBottomBarSpec());
   1953         mBottomBar.transitionToCapture();
   1954     }
   1955 
   1956     /**
   1957      * Displays the Cancel button instead of the capture button.
   1958      */
   1959     public void transitionToCancel() {
   1960         ModuleController moduleController = mController.getCurrentModuleController();
   1961         applyModuleSpecs(moduleController.getHardwareSpec(),
   1962                 moduleController.getBottomBarSpec());
   1963         mBottomBar.transitionToCancel();
   1964     }
   1965 
   1966     /**
   1967      * Performs a transition to the global intent layout.
   1968      */
   1969     public void transitionToIntentCaptureLayout() {
   1970         ModuleController moduleController = mController.getCurrentModuleController();
   1971         applyModuleSpecs(moduleController.getHardwareSpec(),
   1972             moduleController.getBottomBarSpec());
   1973         mBottomBar.transitionToIntentCaptureLayout();
   1974         showModeOptions();
   1975     }
   1976 
   1977     /**
   1978      * Performs a transition to the global intent review layout.
   1979      */
   1980     public void transitionToIntentReviewLayout() {
   1981         ModuleController moduleController = mController.getCurrentModuleController();
   1982         applyModuleSpecs(moduleController.getHardwareSpec(),
   1983             moduleController.getBottomBarSpec());
   1984         mBottomBar.transitionToIntentReviewLayout();
   1985         hideModeOptions();
   1986 
   1987         // Hide the preview snapshot since the screen is frozen when users tap
   1988         // shutter button in capture intent.
   1989         hideModeCover();
   1990     }
   1991 
   1992     /**
   1993      * @return whether UI is in intent review mode
   1994      */
   1995     public boolean isInIntentReview() {
   1996         return mBottomBar.isInIntentReview();
   1997     }
   1998 
   1999     @Override
   2000     public void onSettingChanged(SettingsManager settingsManager, String key) {
   2001         // Update the mode options based on the hardware spec,
   2002         // when hdr changes to prevent flash from getting out of sync.
   2003         if (key.equals(Keys.KEY_CAMERA_HDR)) {
   2004             ModuleController moduleController = mController.getCurrentModuleController();
   2005             applyModuleSpecs(moduleController.getHardwareSpec(),
   2006                              moduleController.getBottomBarSpec(),
   2007                              true /*skipScopeCheck*/);
   2008         }
   2009     }
   2010 
   2011     /**
   2012      * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
   2013      * to the bottom bar mode options based on limitations from a
   2014      * {@link com.android.camera.hardware.HardwareSpec}.
   2015      *
   2016      * Options not supported by the hardware are either hidden
   2017      * or disabled, depending on the option.
   2018      *
   2019      * Otherwise, the option is fully enabled and clickable.
   2020      */
   2021     public void applyModuleSpecs(HardwareSpec hardwareSpec,
   2022             BottomBarUISpec bottomBarSpec) {
   2023         applyModuleSpecs(hardwareSpec, bottomBarSpec, false /*skipScopeCheck*/);
   2024     }
   2025 
   2026     private void applyModuleSpecs(final HardwareSpec hardwareSpec,
   2027            final BottomBarUISpec bottomBarSpec, boolean skipScopeCheck) {
   2028         if (hardwareSpec == null || bottomBarSpec == null) {
   2029             return;
   2030         }
   2031 
   2032         ButtonManager buttonManager = mController.getButtonManager();
   2033         SettingsManager settingsManager = mController.getSettingsManager();
   2034 
   2035         buttonManager.setToInitialState();
   2036 
   2037         if (skipScopeCheck
   2038                 || !mController.getModuleScope().equals(mCurrentModuleScope)
   2039                 || !mController.getCameraScope().equals(mCurrentCameraScope)) {
   2040 
   2041             // Scope dependent options, update only if the module or the
   2042             // camera scope changed or scope-check skip was requested.
   2043             mCurrentModuleScope = mController.getModuleScope();
   2044             mCurrentCameraScope = mController.getCameraScope();
   2045 
   2046             mHdrSupportMode = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
   2047                     Keys.KEY_HDR_SUPPORT_MODE_BACK_CAMERA);
   2048 
   2049             /** Standard mode options */
   2050             if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
   2051                     hardwareSpec.isFrontCameraSupported()) {
   2052                 if (bottomBarSpec.enableCamera) {
   2053                     int hdrButtonId = ButtonManager.BUTTON_HDR;
   2054                     if (mHdrSupportMode.equals(getResourceString(
   2055                             R.string.pref_camera_hdr_supportmode_hdr_plus))) {
   2056                         hdrButtonId = ButtonManager.BUTTON_HDR_PLUS;
   2057                     }
   2058                     buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
   2059                             bottomBarSpec.cameraCallback,
   2060                             getDisableButtonCallback(hdrButtonId));
   2061                 } else {
   2062                     buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
   2063                 }
   2064             } else {
   2065                 // Hide camera icon if front camera not available.
   2066                 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
   2067             }
   2068 
   2069             boolean flashBackCamera = settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   2070                     Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
   2071             if (bottomBarSpec.hideFlash
   2072                     || !flashBackCamera) {
   2073                 // Hide both flash and torch button in flash disable logic
   2074                 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
   2075                 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
   2076             } else {
   2077                 if (hardwareSpec.isFlashSupported()) {
   2078                     if (bottomBarSpec.enableFlash) {
   2079                         buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
   2080                                 bottomBarSpec.flashCallback);
   2081                     } else if (bottomBarSpec.enableTorchFlash) {
   2082                         buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
   2083                                 bottomBarSpec.flashCallback);
   2084                     } else if (bottomBarSpec.enableHdrPlusFlash) {
   2085                         buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
   2086                                 bottomBarSpec.flashCallback);
   2087                     } else {
   2088                         // Disable both flash and torch button in flash disable
   2089                         // logic. Need to ensure it's visible, it may be hidden
   2090                         // from previous non-flash mode.
   2091                         buttonManager.showButton(ButtonManager.BUTTON_FLASH);
   2092                         buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
   2093                         buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
   2094                     }
   2095                 } else {
   2096                     // Flash not supported but another module does.
   2097                     // Disable flash button. Need to ensure it's visible,
   2098                     // it may be hidden from previous non-flash mode.
   2099                     buttonManager.showButton(ButtonManager.BUTTON_FLASH);
   2100                     buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
   2101                     buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
   2102                 }
   2103             }
   2104 
   2105             if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
   2106                 // Force hide hdr or hdr plus icon.
   2107                 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
   2108             } else {
   2109                 if (hardwareSpec.isHdrPlusSupported()) {
   2110                     mHdrSupportMode = getResourceString(
   2111                             R.string.pref_camera_hdr_supportmode_hdr_plus);
   2112                     if (bottomBarSpec.enableHdr) {
   2113                         buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
   2114                                 bottomBarSpec.hdrCallback,
   2115                                 getDisableButtonCallback(ButtonManager.BUTTON_CAMERA));
   2116                     } else {
   2117                         buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
   2118                     }
   2119                 } else if (hardwareSpec.isHdrSupported()) {
   2120                     mHdrSupportMode = getResourceString(R.string.pref_camera_hdr_supportmode_hdr);
   2121                     if (bottomBarSpec.enableHdr) {
   2122                         buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
   2123                                 bottomBarSpec.hdrCallback,
   2124                                 getDisableButtonCallback(ButtonManager.BUTTON_CAMERA));
   2125                     } else {
   2126                         buttonManager.disableButton(ButtonManager.BUTTON_HDR);
   2127                     }
   2128                 } else {
   2129                     // Hide hdr plus or hdr icon if neither are supported overall.
   2130                     if (mHdrSupportMode.isEmpty() || mHdrSupportMode
   2131                             .equals(getResourceString(R.string.pref_camera_hdr_supportmode_none))) {
   2132                         buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
   2133                     } else {
   2134                         // Disable HDR button. Need to ensure it's visible,
   2135                         // it may be hidden from previous non HDR mode (eg. Video).
   2136                         int buttonId = ButtonManager.BUTTON_HDR;
   2137                         if (mHdrSupportMode.equals(
   2138                                 getResourceString(R.string.pref_camera_hdr_supportmode_hdr_plus))) {
   2139                             buttonId = ButtonManager.BUTTON_HDR_PLUS;
   2140                         }
   2141                         buttonManager.showButton(buttonId);
   2142                         buttonManager.disableButton(buttonId);
   2143                     }
   2144                 }
   2145             }
   2146 
   2147         }
   2148         if (bottomBarSpec.hideGridLines) {
   2149             // Force hide grid lines icon.
   2150             buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
   2151             hideGridLines();
   2152         } else {
   2153             if (bottomBarSpec.enableGridLines) {
   2154                 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
   2155                         bottomBarSpec.gridLinesCallback != null ?
   2156                                 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
   2157                 );
   2158             } else {
   2159                 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
   2160                 hideGridLines();
   2161             }
   2162         }
   2163 
   2164         if (bottomBarSpec.enableSelfTimer) {
   2165             buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
   2166         } else {
   2167             if (bottomBarSpec.showSelfTimer) {
   2168                 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
   2169             } else {
   2170                 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
   2171             }
   2172         }
   2173 
   2174         if (bottomBarSpec.enablePanoOrientation
   2175                 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
   2176             buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
   2177         }
   2178 
   2179 
   2180 
   2181         // If manual exposure is enabled both in SettingsManager and
   2182         // BottomBarSpec,then show the exposure button.
   2183         // If manual exposure is disabled in the BottomBarSpec (eg. HDR+
   2184         // enabled), but the device/module has the feature, then disable the exposure
   2185         // button.
   2186         // Otherwise, hide the button.
   2187         if (bottomBarSpec.enableExposureCompensation
   2188                 && !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0)
   2189                 && mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
   2190                         Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
   2191             buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION,
   2192                     new View.OnClickListener() {
   2193                         @Override
   2194                         public void onClick(View v) {
   2195                             mModeOptionsOverlay.showExposureOptions();
   2196                         }
   2197                     });
   2198             buttonManager.setExposureCompensationParameters(
   2199                     bottomBarSpec.minExposureCompensation,
   2200                     bottomBarSpec.maxExposureCompensation,
   2201                     bottomBarSpec.exposureCompensationStep);
   2202 
   2203             buttonManager.setExposureCompensationCallback(
   2204                     bottomBarSpec.exposureCompensationSetCallback);
   2205             buttonManager.updateExposureButtons();
   2206         } else if (mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
   2207                 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)
   2208                 && bottomBarSpec.isExposureCompensationSupported) {
   2209             buttonManager.disableButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
   2210         } else {
   2211             buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
   2212             buttonManager.setExposureCompensationCallback(null);
   2213         }
   2214 
   2215         /** Intent UI */
   2216         if (bottomBarSpec.showCancel) {
   2217             buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
   2218                     bottomBarSpec.cancelCallback);
   2219         }
   2220         if (bottomBarSpec.showDone) {
   2221             buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
   2222                     bottomBarSpec.doneCallback);
   2223         }
   2224         if (bottomBarSpec.showRetake) {
   2225             buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
   2226                     bottomBarSpec.retakeCallback,
   2227                     R.drawable.ic_back,
   2228                     R.string.retake_button_description);
   2229         }
   2230         if (bottomBarSpec.showReview) {
   2231             buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
   2232                     bottomBarSpec.reviewCallback,
   2233                     R.drawable.ic_play,
   2234                     R.string.review_button_description);
   2235         }
   2236     }
   2237 
   2238     /**
   2239      * Returns a {@link com.android.camera.ButtonManager.ButtonCallback} that
   2240      * will disable the button identified by the parameter.
   2241      *
   2242      * @param conflictingButton The button id to be disabled.
   2243      */
   2244     private ButtonManager.ButtonCallback getDisableButtonCallback(final int conflictingButton) {
   2245         return new ButtonManager.ButtonCallback() {
   2246             @Override
   2247             public void onStateChanged(int state) {
   2248                 mController.getButtonManager().disableButton(conflictingButton);
   2249             }
   2250         };
   2251     }
   2252 
   2253     private String getResourceString(int stringId) {
   2254         try {
   2255             return mController.getAndroidContext().getResources().getString(stringId);
   2256         } catch (Resources.NotFoundException e) {
   2257             // String not found, returning empty string.
   2258             return "";
   2259         }
   2260     }
   2261 
   2262     /**
   2263      * Shows the given tutorial on the screen.
   2264      */
   2265     public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
   2266         tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
   2267     }
   2268 
   2269     /**
   2270      * Whether the capture ratio selector dialog must be shown on this device.
   2271      * */
   2272     public boolean shouldShowAspectRatioDialog() {
   2273         final boolean isAspectRatioPreferenceSet = mController.getSettingsManager().getBoolean(
   2274                 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
   2275         final boolean isAspectRatioDevice =
   2276                 ApiHelper.IS_NEXUS_4 || ApiHelper.IS_NEXUS_5 || ApiHelper.IS_NEXUS_6;
   2277         return isAspectRatioDevice && !isAspectRatioPreferenceSet;
   2278     }
   2279 
   2280 
   2281     /***************************Filmstrip api *****************************/
   2282 
   2283     public void showFilmstrip() {
   2284         mModeListView.onBackPressed();
   2285         mFilmstripLayout.showFilmstrip();
   2286     }
   2287 
   2288     public void hideFilmstrip() {
   2289         mFilmstripLayout.hideFilmstrip();
   2290     }
   2291 
   2292     public int getFilmstripVisibility() {
   2293         return mFilmstripLayout.getVisibility();
   2294     }
   2295 }
   2296