Home | History | Annotate | Download | only in camera
      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;
     18 
     19 import android.content.Context;
     20 import android.content.res.TypedArray;
     21 import android.view.LayoutInflater;
     22 import android.view.View;
     23 import android.widget.ImageButton;
     24 import android.widget.LinearLayout;
     25 
     26 import com.android.camera.app.AppController;
     27 import com.android.camera.app.CameraAppUI;
     28 import com.android.camera.settings.Keys;
     29 import com.android.camera.settings.SettingsManager;
     30 import com.android.camera.ui.RadioOptions;
     31 import com.android.camera.util.PhotoSphereHelper;
     32 import com.android.camera.widget.ModeOptions;
     33 import com.android.camera2.R;
     34 
     35 /**
     36  * A  class for generating pre-initialized
     37  * {@link #android.widget.ImageButton}s.
     38  */
     39 public class ButtonManager implements SettingsManager.OnSettingChangedListener {
     40     public static final int BUTTON_FLASH = 0;
     41     public static final int BUTTON_TORCH = 1;
     42     public static final int BUTTON_HDR_PLUS_FLASH = 2;
     43     public static final int BUTTON_CAMERA = 3;
     44     public static final int BUTTON_HDR_PLUS = 4;
     45     public static final int BUTTON_HDR = 5;
     46     public static final int BUTTON_CANCEL = 6;
     47     public static final int BUTTON_DONE = 7;
     48     public static final int BUTTON_RETAKE = 8;
     49     public static final int BUTTON_REVIEW = 9;
     50     public static final int BUTTON_GRID_LINES = 10;
     51     public static final int BUTTON_EXPOSURE_COMPENSATION = 11;
     52     public static final int BUTTON_COUNTDOWN = 12;
     53 
     54     /** For two state MultiToggleImageButtons, the off index. */
     55     public static final int OFF = 0;
     56     /** For two state MultiToggleImageButtons, the on index. */
     57     public static final int ON = 1;
     58 
     59     private static final int NO_RESOURCE = -1;
     60 
     61     /** A reference to the application's settings manager. */
     62     private final SettingsManager mSettingsManager;
     63 
     64     /** Bottom bar options toggle buttons. */
     65     private MultiToggleImageButton mButtonCamera;
     66     private MultiToggleImageButton mButtonFlash;
     67     private MultiToggleImageButton mButtonHdr;
     68     private MultiToggleImageButton mButtonGridlines;
     69     private MultiToggleImageButton mButtonCountdown;
     70 
     71     /** Intent UI buttons. */
     72     private ImageButton mButtonCancel;
     73     private ImageButton mButtonDone;
     74     private ImageButton mButtonRetake; // same as review.
     75 
     76     private ImageButton mButtonExposureCompensation;
     77     private ImageButton mExposureN2;
     78     private ImageButton mExposureN1;
     79     private ImageButton mExposure0;
     80     private ImageButton mExposureP1;
     81     private ImageButton mExposureP2;
     82     private RadioOptions mModeOptionsExposure;
     83     private RadioOptions mModeOptionsPano;
     84     private View mModeOptionsButtons;
     85     private ModeOptions mModeOptions;
     86 
     87     private int mMinExposureCompensation;
     88     private int mMaxExposureCompensation;
     89     private float mExposureCompensationStep;
     90 
     91     /** A listener for button enabled and visibility
     92         state changes. */
     93     private ButtonStatusListener mListener;
     94 
     95     /** An reference to the gcam mode index. */
     96     private static int sGcamIndex;
     97 
     98     /** Whether Camera Button can be enabled by generic operations. */
     99     private boolean mIsCameraButtonBlocked;
    100 
    101     private final AppController mAppController;
    102 
    103     /**
    104      * Get a new global ButtonManager.
    105      */
    106     public ButtonManager(AppController app) {
    107         mAppController = app;
    108 
    109         Context context = app.getAndroidContext();
    110         sGcamIndex = context.getResources().getInteger(R.integer.camera_mode_gcam);
    111 
    112         mSettingsManager = app.getSettingsManager();
    113         mSettingsManager.addListener(this);
    114     }
    115 
    116     /**
    117      * Load references to buttons under a root View.
    118      * Call this after the root clears/reloads all of its children
    119      * to prevent stale references button views.
    120      */
    121     public void load(View root) {
    122         getButtonsReferences(root);
    123     }
    124 
    125     /**
    126      * ButtonStatusListener provides callbacks for when button's
    127      * visibility changes and enabled status changes.
    128      */
    129     public interface ButtonStatusListener {
    130         /**
    131          * A button's visibility has changed.
    132          */
    133         public void onButtonVisibilityChanged(ButtonManager buttonManager, int buttonId);
    134 
    135         /**
    136          * A button's enabled state has changed.
    137          */
    138         public void onButtonEnabledChanged(ButtonManager buttonManager, int buttonId);
    139     }
    140 
    141     /**
    142      * Sets the ButtonStatusListener.
    143      */
    144     public void setListener(ButtonStatusListener listener) {
    145         mListener = listener;
    146     }
    147 
    148     /**
    149      * Gets references to all known buttons.
    150      */
    151     private void getButtonsReferences(View root) {
    152         mButtonCamera
    153             = (MultiToggleImageButton) root.findViewById(R.id.camera_toggle_button);
    154         mButtonFlash
    155             = (MultiToggleImageButton) root.findViewById(R.id.flash_toggle_button);
    156         mButtonHdr
    157             = (MultiToggleImageButton) root.findViewById(R.id.hdr_plus_toggle_button);
    158         mButtonGridlines
    159             = (MultiToggleImageButton) root.findViewById(R.id.grid_lines_toggle_button);
    160         mButtonCancel
    161             = (ImageButton) root.findViewById(R.id.cancel_button);
    162         mButtonDone
    163             = (ImageButton) root.findViewById(R.id.done_button);
    164         mButtonRetake
    165             = (ImageButton) root.findViewById(R.id.retake_button);
    166 
    167         mButtonExposureCompensation =
    168             (ImageButton) root.findViewById(R.id.exposure_button);
    169         mExposureN2 = (ImageButton) root.findViewById(R.id.exposure_n2);
    170         mExposureN1 = (ImageButton) root.findViewById(R.id.exposure_n1);
    171         mExposure0 = (ImageButton) root.findViewById(R.id.exposure_0);
    172         mExposureP1 = (ImageButton) root.findViewById(R.id.exposure_p1);
    173         mExposureP2 = (ImageButton) root.findViewById(R.id.exposure_p2);
    174         mModeOptionsExposure = (RadioOptions) root.findViewById(R.id.mode_options_exposure);
    175         mModeOptionsPano = (RadioOptions) root.findViewById(R.id.mode_options_pano);
    176         mModeOptionsButtons = root.findViewById(R.id.mode_options_buttons);
    177         mModeOptions = (ModeOptions) root.findViewById(R.id.mode_options);
    178 
    179         mButtonCountdown = (MultiToggleImageButton) root.findViewById(R.id.countdown_toggle_button);
    180     }
    181 
    182     @Override
    183     public void onSettingChanged(SettingsManager settingsManager, String key) {
    184         MultiToggleImageButton button = null;
    185         int index = 0;
    186 
    187         if (key.equals(Keys.KEY_FLASH_MODE)) {
    188             index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
    189                                                             Keys.KEY_FLASH_MODE);
    190             button = getButtonOrError(BUTTON_FLASH);
    191         } else if (key.equals(Keys.KEY_VIDEOCAMERA_FLASH_MODE)) {
    192             index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
    193                                                             Keys.KEY_VIDEOCAMERA_FLASH_MODE);
    194             button = getButtonOrError(BUTTON_TORCH);
    195         } else if (key.equals(Keys.KEY_HDR_PLUS_FLASH_MODE)) {
    196             index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
    197                                                             Keys.KEY_HDR_PLUS_FLASH_MODE);
    198             button = getButtonOrError(BUTTON_HDR_PLUS_FLASH);
    199         } else if (key.equals(Keys.KEY_CAMERA_ID)) {
    200             index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
    201                                                             Keys.KEY_CAMERA_ID);
    202             button = getButtonOrError(BUTTON_CAMERA);
    203         } else if (key.equals(Keys.KEY_CAMERA_HDR_PLUS)) {
    204             index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    205                                                             Keys.KEY_CAMERA_HDR_PLUS);
    206             button = getButtonOrError(BUTTON_HDR_PLUS);
    207         } else if (key.equals(Keys.KEY_CAMERA_HDR)) {
    208             index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    209                                                             Keys.KEY_CAMERA_HDR);
    210             button = getButtonOrError(BUTTON_HDR);
    211         } else if (key.equals(Keys.KEY_CAMERA_GRID_LINES)) {
    212             index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    213                                                             Keys.KEY_CAMERA_GRID_LINES);
    214             button = getButtonOrError(BUTTON_GRID_LINES);
    215         } else if (key.equals(Keys.KEY_CAMERA_PANO_ORIENTATION)) {
    216             updatePanoButtons();
    217         } else if (key.equals(Keys.KEY_EXPOSURE)) {
    218             updateExposureButtons();
    219         } else if (key.equals(Keys.KEY_COUNTDOWN_DURATION)) {
    220             index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    221                                                             Keys.KEY_COUNTDOWN_DURATION);
    222             button = getButtonOrError(BUTTON_COUNTDOWN);
    223         }
    224 
    225         if (button != null && button.getState() != index) {
    226             button.setState(Math.max(index, 0), false);
    227         }
    228     }
    229 
    230     /**
    231      * A callback executed in the state listener of a button.
    232      *
    233      * Used by a module to set specific behavior when a button's
    234      * state changes.
    235      */
    236     public interface ButtonCallback {
    237         public void onStateChanged(int state);
    238     }
    239 
    240     /**
    241      * Returns the appropriate {@link com.android.camera.MultiToggleImageButton}
    242      * based on button id.  An IllegalStateException will be throw if the
    243      * button could not be found in the view hierarchy.
    244      */
    245     private MultiToggleImageButton getButtonOrError(int buttonId) {
    246         switch (buttonId) {
    247             case BUTTON_FLASH:
    248                 if (mButtonFlash == null) {
    249                     throw new IllegalStateException("Flash button could not be found.");
    250                 }
    251                 return mButtonFlash;
    252             case BUTTON_TORCH:
    253                 if (mButtonFlash == null) {
    254                     throw new IllegalStateException("Torch button could not be found.");
    255                 }
    256                 return mButtonFlash;
    257             case BUTTON_HDR_PLUS_FLASH:
    258                 if (mButtonFlash == null) {
    259                     throw new IllegalStateException("Hdr plus torch button could not be found.");
    260                 }
    261                 return mButtonFlash;
    262             case BUTTON_CAMERA:
    263                 if (mButtonCamera == null) {
    264                     throw new IllegalStateException("Camera button could not be found.");
    265                 }
    266                 return mButtonCamera;
    267             case BUTTON_HDR_PLUS:
    268                 if (mButtonHdr == null) {
    269                     throw new IllegalStateException("Hdr plus button could not be found.");
    270                 }
    271                 return mButtonHdr;
    272             case BUTTON_HDR:
    273                 if (mButtonHdr == null) {
    274                     throw new IllegalStateException("Hdr button could not be found.");
    275                 }
    276                 return mButtonHdr;
    277             case BUTTON_GRID_LINES:
    278                 if (mButtonGridlines == null) {
    279                     throw new IllegalStateException("Grid lines button could not be found.");
    280                 }
    281                 return mButtonGridlines;
    282             case BUTTON_COUNTDOWN:
    283                 if (mButtonCountdown == null) {
    284                     throw new IllegalStateException("Countdown button could not be found.");
    285                 }
    286                 return mButtonCountdown;
    287             default:
    288                 throw new IllegalArgumentException("button not known by id=" + buttonId);
    289         }
    290     }
    291 
    292     /**
    293      * Returns the appropriate {@link android.widget.ImageButton}
    294      * based on button id.  An IllegalStateException will be throw if the
    295      * button could not be found in the view hierarchy.
    296      */
    297     private ImageButton getImageButtonOrError(int buttonId) {
    298         switch (buttonId) {
    299             case BUTTON_CANCEL:
    300                 if (mButtonCancel == null) {
    301                     throw new IllegalStateException("Cancel button could not be found.");
    302                 }
    303                 return mButtonCancel;
    304             case BUTTON_DONE:
    305                 if (mButtonDone == null) {
    306                     throw new IllegalStateException("Done button could not be found.");
    307                 }
    308                 return mButtonDone;
    309             case BUTTON_RETAKE:
    310                 if (mButtonRetake == null) {
    311                     throw new IllegalStateException("Retake button could not be found.");
    312                 }
    313                 return mButtonRetake;
    314             case BUTTON_REVIEW:
    315                 if (mButtonRetake == null) {
    316                     throw new IllegalStateException("Review button could not be found.");
    317                 }
    318                 return mButtonRetake;
    319             case BUTTON_EXPOSURE_COMPENSATION:
    320                 if (mButtonExposureCompensation == null) {
    321                     throw new IllegalStateException("Exposure Compensation button could not be found.");
    322                 }
    323                 return mButtonExposureCompensation;
    324             default:
    325                 throw new IllegalArgumentException("button not known by id=" + buttonId);
    326         }
    327     }
    328 
    329     /**
    330      * Initialize a known button by id with a state change callback, and then
    331      * enable the button.
    332      *
    333      * @param buttonId The id if the button to be initialized.
    334      * @param cb The callback to be executed after the button state change.
    335      */
    336     public void initializeButton(int buttonId, ButtonCallback cb) {
    337         initializeButton(buttonId, cb, null);
    338     }
    339 
    340     /**
    341      * Initialize a known button by id, with a state change callback and a state
    342      * pre-change callback, and then enable the button.
    343      *
    344      * @param buttonId The id if the button to be initialized.
    345      * @param cb The callback to be executed after the button state change.
    346      * @param preCb The callback to be executed before the button state change.
    347      */
    348     public void initializeButton(int buttonId, ButtonCallback cb, ButtonCallback preCb) {
    349         MultiToggleImageButton button = getButtonOrError(buttonId);
    350         switch (buttonId) {
    351             case BUTTON_FLASH:
    352                 initializeFlashButton(button, cb, preCb, R.array.camera_flashmode_icons);
    353                 break;
    354             case BUTTON_TORCH:
    355                 initializeTorchButton(button, cb, preCb, R.array.video_flashmode_icons);
    356                 break;
    357             case BUTTON_HDR_PLUS_FLASH:
    358                 initializeHdrPlusFlashButton(button, cb, preCb, R.array.camera_flashmode_icons);
    359                 break;
    360             case BUTTON_CAMERA:
    361                 initializeCameraButton(button, cb, preCb, R.array.camera_id_icons);
    362                 break;
    363             case BUTTON_HDR_PLUS:
    364                 initializeHdrPlusButton(button, cb, preCb, R.array.pref_camera_hdr_plus_icons);
    365                 break;
    366             case BUTTON_HDR:
    367                 initializeHdrButton(button, cb, preCb, R.array.pref_camera_hdr_icons);
    368                 break;
    369             case BUTTON_GRID_LINES:
    370                 initializeGridLinesButton(button, cb, preCb, R.array.grid_lines_icons);
    371                 break;
    372             case BUTTON_COUNTDOWN:
    373                 initializeCountdownButton(button, cb, preCb, R.array.countdown_duration_icons);
    374                 break;
    375             default:
    376                 throw new IllegalArgumentException("button not known by id=" + buttonId);
    377         }
    378 
    379         showButton(buttonId);
    380         enableButton(buttonId);
    381     }
    382 
    383     /**
    384      * Initialize a known button with a click listener and a drawable resource id,
    385      * and a content description resource id.
    386      * Sets the button visible.
    387      */
    388     public void initializePushButton(int buttonId, View.OnClickListener cb,
    389             int imageId, int contentDescriptionId) {
    390         ImageButton button = getImageButtonOrError(buttonId);
    391         button.setOnClickListener(cb);
    392         if (imageId != NO_RESOURCE) {
    393             button.setImageResource(imageId);
    394         }
    395         if (contentDescriptionId != NO_RESOURCE) {
    396             button.setContentDescription(mAppController
    397                     .getAndroidContext().getResources().getString(contentDescriptionId));
    398         }
    399 
    400         if (!button.isEnabled()) {
    401             button.setEnabled(true);
    402             if (mListener != null) {
    403                 mListener.onButtonEnabledChanged(this, buttonId);
    404             }
    405         }
    406         button.setTag(R.string.tag_enabled_id, buttonId);
    407 
    408         if (button.getVisibility() != View.VISIBLE) {
    409             button.setVisibility(View.VISIBLE);
    410             if (mListener != null) {
    411                 mListener.onButtonVisibilityChanged(this, buttonId);
    412             }
    413         }
    414     }
    415 
    416     /**
    417      * Initialize a known button with a click listener and a resource id.
    418      * Sets the button visible.
    419      */
    420     public void initializePushButton(int buttonId, View.OnClickListener cb,
    421             int imageId) {
    422         initializePushButton(buttonId, cb, imageId, NO_RESOURCE);
    423     }
    424 
    425     /**
    426      * Initialize a known button with a click listener. Sets the button visible.
    427      */
    428     public void initializePushButton(int buttonId, View.OnClickListener cb) {
    429         initializePushButton(buttonId, cb, NO_RESOURCE, NO_RESOURCE);
    430     }
    431 
    432     /**
    433      * Sets the camera button in its disabled (greyed out) state and blocks it
    434      * so no generic operation can enable it until it's explicitly re-enabled by
    435      * calling {@link #enableCameraButton()}.
    436      */
    437     public void disableCameraButtonAndBlock() {
    438         mIsCameraButtonBlocked = true;
    439         disableButton(BUTTON_CAMERA);
    440     }
    441 
    442     /**
    443      * Sets a button in its disabled (greyed out) state.
    444      */
    445     public void disableButton(int buttonId) {
    446         View button;
    447         if (buttonId == BUTTON_EXPOSURE_COMPENSATION) {
    448             button = getImageButtonOrError(buttonId);
    449         } else {
    450             button = getButtonOrError(buttonId);
    451         }
    452         // HDR and HDR+ buttons share the same button object,
    453         // but change actual image icons at runtime.
    454         // This extra check is to ensure the correct icons are used
    455         // in the case of the HDR[+] button being disabled at startup,
    456         // e.g. app startup with front-facing camera.
    457         // b/18104680
    458         if (buttonId == BUTTON_HDR_PLUS) {
    459             initializeHdrPlusButtonIcons((MultiToggleImageButton) button, R.array.pref_camera_hdr_plus_icons);
    460         } else if (buttonId == BUTTON_HDR) {
    461             initializeHdrButtonIcons((MultiToggleImageButton) button, R.array.pref_camera_hdr_icons);
    462         }
    463 
    464         if (button.isEnabled()) {
    465             button.setEnabled(false);
    466             if (mListener != null) {
    467                 mListener.onButtonEnabledChanged(this, buttonId);
    468             }
    469         }
    470         button.setTag(R.string.tag_enabled_id, null);
    471     }
    472 
    473     /**
    474      * Enables the camera button and removes the block that was set by
    475      * {@link #disableCameraButtonAndBlock()}.
    476      */
    477     public void enableCameraButton() {
    478         mIsCameraButtonBlocked = false;
    479         enableButton(BUTTON_CAMERA);
    480     }
    481 
    482     /**
    483      * Enables a button that has already been initialized.
    484      */
    485     public void enableButton(int buttonId) {
    486         // If Camera Button is blocked, ignore the request.
    487         if(buttonId == BUTTON_CAMERA && mIsCameraButtonBlocked) {
    488             return;
    489         }
    490         ImageButton button;
    491         // Manual exposure uses a regular image button instead of a
    492         // MultiToggleImageButton, so it requires special handling.
    493         // TODO: Redesign ButtonManager's button getter methods into one method.
    494         if (buttonId == BUTTON_EXPOSURE_COMPENSATION) {
    495             button = getImageButtonOrError(buttonId);
    496         } else {
    497             button = getButtonOrError(buttonId);
    498         }
    499         if (!button.isEnabled()) {
    500             button.setEnabled(true);
    501             if (mListener != null) {
    502                 mListener.onButtonEnabledChanged(this, buttonId);
    503             }
    504         }
    505         button.setTag(R.string.tag_enabled_id, buttonId);
    506     }
    507 
    508     /**
    509      * Disable click reactions for a button without affecting visual state.
    510      * For most cases you'll want to use {@link #disableButton(int)}.
    511      * @param buttonId The id of the button.
    512      */
    513     public void disableButtonClick(int buttonId) {
    514         ImageButton button = getButtonOrError(buttonId);
    515         if (button instanceof MultiToggleImageButton) {
    516             ((MultiToggleImageButton) button).setClickEnabled(false);
    517         }
    518     }
    519 
    520     /**
    521      * Enable click reactions for a button without affecting visual state.
    522      * For most cases you'll want to use {@link #enableButton(int)}.
    523      * @param buttonId The id of the button.
    524      */
    525     public void enableButtonClick(int buttonId) {
    526         ImageButton button = getButtonOrError(buttonId);
    527         if (button instanceof MultiToggleImageButton) {
    528             ((MultiToggleImageButton) button).setClickEnabled(true);
    529         }
    530     }
    531 
    532     /**
    533      * Hide a button by id.
    534      */
    535     public void hideButton(int buttonId) {
    536         View button;
    537         try {
    538             button = getButtonOrError(buttonId);
    539         } catch (IllegalArgumentException e) {
    540             button = getImageButtonOrError(buttonId);
    541         }
    542         if (button.getVisibility() == View.VISIBLE) {
    543             button.setVisibility(View.GONE);
    544             if (mListener != null) {
    545                 mListener.onButtonVisibilityChanged(this, buttonId);
    546             }
    547         }
    548     }
    549 
    550     /**
    551      * Show a button by id.
    552      */
    553     public void showButton(int buttonId) {
    554         View button;
    555         try {
    556             button = getButtonOrError(buttonId);
    557         } catch (IllegalArgumentException e) {
    558             button = getImageButtonOrError(buttonId);
    559         }
    560         if (button.getVisibility() != View.VISIBLE) {
    561             button.setVisibility(View.VISIBLE);
    562             if (mListener != null) {
    563                 mListener.onButtonVisibilityChanged(this, buttonId);
    564             }
    565         }
    566     }
    567 
    568 
    569     public void setToInitialState() {
    570         mModeOptions.setMainBar(ModeOptions.BAR_STANDARD);
    571     }
    572 
    573     public void setExposureCompensationCallback(final CameraAppUI.BottomBarUISpec
    574                                         .ExposureCompensationSetCallback cb) {
    575         if (cb == null) {
    576             mModeOptionsExposure.setOnOptionClickListener(null);
    577         } else {
    578             mModeOptionsExposure
    579                 .setOnOptionClickListener(new RadioOptions.OnOptionClickListener() {
    580                     @Override
    581                     public void onOptionClicked(View v) {
    582                         int comp = Integer.parseInt((String)(v.getTag()));
    583 
    584                         if (mExposureCompensationStep != 0.0f) {
    585                             int compValue =
    586                                 Math.round(comp / mExposureCompensationStep);
    587                             cb.setExposure(compValue);
    588                         }
    589                     }
    590                 });
    591         }
    592     }
    593 
    594     /**
    595      * Set the exposure compensation parameters supported by the current camera mode.
    596      * @param min Minimum exposure compensation value.
    597      * @param max Maximum exposure compensation value.
    598      * @param step Expsoure compensation step value.
    599      */
    600     public void setExposureCompensationParameters(int min, int max, float step) {
    601         mMaxExposureCompensation = max;
    602         mMinExposureCompensation = min;
    603         mExposureCompensationStep = step;
    604 
    605 
    606         setVisible(mExposureN2, (Math.round(min * step) <= -2));
    607         setVisible(mExposureN1, (Math.round(min * step) <= -1));
    608         setVisible(mExposureP1, (Math.round(max * step) >= 1));
    609         setVisible(mExposureP2, (Math.round(max * step) >= 2));
    610 
    611         updateExposureButtons();
    612     }
    613 
    614     private static void setVisible(View v, boolean visible) {
    615         if (visible) {
    616             v.setVisibility(View.VISIBLE);
    617         } else {
    618             v.setVisibility(View.INVISIBLE);
    619         }
    620     }
    621 
    622     /**
    623      * @return The exposure compensation step value.
    624      **/
    625     public float getExposureCompensationStep() {
    626         return mExposureCompensationStep;
    627     }
    628 
    629     /**
    630      * Check if a button is enabled with the given button id..
    631      */
    632     public boolean isEnabled(int buttonId) {
    633         View button;
    634         try {
    635             button = getButtonOrError(buttonId);
    636         } catch (IllegalArgumentException e) {
    637             button = getImageButtonOrError(buttonId);
    638         }
    639 
    640         Integer enabledId = (Integer) button.getTag(R.string.tag_enabled_id);
    641         if (enabledId != null) {
    642             return (enabledId.intValue() == buttonId) && button.isEnabled();
    643         } else {
    644             return false;
    645         }
    646     }
    647 
    648     /**
    649      * Check if a button is visible.
    650      */
    651     public boolean isVisible(int buttonId) {
    652         View button;
    653         try {
    654             button = getButtonOrError(buttonId);
    655         } catch (IllegalArgumentException e) {
    656             button = getImageButtonOrError(buttonId);
    657         }
    658         return (button.getVisibility() == View.VISIBLE);
    659     }
    660 
    661     /**
    662      * Initialize a flash button.
    663      */
    664     private void initializeFlashButton(MultiToggleImageButton button,
    665             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    666 
    667         if (resIdImages > 0) {
    668             button.overrideImageIds(resIdImages);
    669         }
    670         button.overrideContentDescriptions(R.array.camera_flash_descriptions);
    671 
    672         int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
    673                                                             Keys.KEY_FLASH_MODE);
    674         button.setState(index >= 0 ? index : 0, false);
    675 
    676         setPreChangeCallback(button, preCb);
    677 
    678         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    679             @Override
    680             public void stateChanged(View view, int state) {
    681                 mSettingsManager.setValueByIndex(mAppController.getCameraScope(),
    682                                                  Keys.KEY_FLASH_MODE, state);
    683                 if (cb != null) {
    684                     cb.onStateChanged(state);
    685                 }
    686             }
    687         });
    688     }
    689 
    690     /**
    691      * Initialize video torch button
    692      */
    693     private void initializeTorchButton(MultiToggleImageButton button,
    694             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    695 
    696         if (resIdImages > 0) {
    697             button.overrideImageIds(resIdImages);
    698         }
    699         button.overrideContentDescriptions(R.array.video_flash_descriptions);
    700 
    701         int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
    702                                                             Keys.KEY_VIDEOCAMERA_FLASH_MODE);
    703         button.setState(index >= 0 ? index : 0, false);
    704 
    705         setPreChangeCallback(button, preCb);
    706 
    707         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    708             @Override
    709             public void stateChanged(View view, int state) {
    710                 mSettingsManager.setValueByIndex(mAppController.getCameraScope(),
    711                                                  Keys.KEY_VIDEOCAMERA_FLASH_MODE, state);
    712                 if (cb != null) {
    713                     cb.onStateChanged(state);
    714                 }
    715             }
    716         });
    717     }
    718 
    719     /**
    720      * Initialize hdr plus flash button
    721      */
    722     private void initializeHdrPlusFlashButton(MultiToggleImageButton button,
    723             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    724 
    725         if (resIdImages > 0) {
    726             button.overrideImageIds(resIdImages);
    727         }
    728         button.overrideContentDescriptions(R.array.hdr_plus_flash_descriptions);
    729 
    730         int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
    731                                                             Keys.KEY_HDR_PLUS_FLASH_MODE);
    732         button.setState(index >= 0 ? index : 0, false);
    733 
    734         setPreChangeCallback(button, preCb);
    735 
    736         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    737             @Override
    738             public void stateChanged(View view, int state) {
    739                 mSettingsManager.setValueByIndex(mAppController.getModuleScope(),
    740                                                  Keys.KEY_HDR_PLUS_FLASH_MODE, state);
    741                 if (cb != null) {
    742                     cb.onStateChanged(state);
    743                 }
    744             }
    745         });
    746     }
    747 
    748     /**
    749      * Initialize a camera button.
    750      */
    751     private void initializeCameraButton(final MultiToggleImageButton button,
    752             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    753 
    754         if (resIdImages > 0) {
    755             button.overrideImageIds(resIdImages);
    756         }
    757 
    758         int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
    759                                                             Keys.KEY_CAMERA_ID);
    760         button.setState(index >= 0 ? index : 0, false);
    761 
    762         setPreChangeCallback(button, preCb);
    763 
    764         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    765             @Override
    766             public void stateChanged(View view, int state) {
    767                 mSettingsManager.setValueByIndex(mAppController.getModuleScope(),
    768                                                  Keys.KEY_CAMERA_ID, state);
    769                 int cameraId = mSettingsManager.getInteger(mAppController.getModuleScope(),
    770                                                            Keys.KEY_CAMERA_ID);
    771                 // This is a quick fix for ISE in Gcam module which can be
    772                 // found by rapid pressing camera switch button. The assumption
    773                 // here is that each time this button is clicked, the listener
    774                 // will do something and then enable this button again.
    775                 button.setEnabled(false);
    776                 if (cb != null) {
    777                     cb.onStateChanged(cameraId);
    778                 }
    779                 mAppController.getCameraAppUI().onChangeCamera();
    780             }
    781         });
    782     }
    783 
    784     /**
    785      * Initialize an hdr plus button.
    786      */
    787     private void initializeHdrPlusButton(MultiToggleImageButton button,
    788             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    789 
    790         initializeHdrPlusButtonIcons(button, resIdImages);
    791 
    792         int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    793                                                             Keys.KEY_CAMERA_HDR_PLUS);
    794         button.setState(index >= 0 ? index : 0, false);
    795 
    796         setPreChangeCallback(button, preCb);
    797 
    798         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    799             @Override
    800             public void stateChanged(View view, int state) {
    801                 mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
    802                                                  Keys.KEY_CAMERA_HDR_PLUS, state);
    803                 if (cb != null) {
    804                     cb.onStateChanged(state);
    805                 }
    806             }
    807         });
    808     }
    809 
    810     private void initializeHdrPlusButtonIcons(MultiToggleImageButton button, int resIdImages) {
    811         if (resIdImages > 0) {
    812             button.overrideImageIds(resIdImages);
    813         }
    814         button.overrideContentDescriptions(R.array.hdr_plus_descriptions);
    815     }
    816 
    817     /**
    818      * Initialize an hdr button.
    819      */
    820     private void initializeHdrButton(MultiToggleImageButton button,
    821             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    822 
    823         initializeHdrButtonIcons(button, resIdImages);
    824 
    825         int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    826                                                             Keys.KEY_CAMERA_HDR);
    827         button.setState(index >= 0 ? index : 0, false);
    828 
    829         setPreChangeCallback(button, preCb);
    830 
    831         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    832             @Override
    833             public void stateChanged(View view, int state) {
    834                 mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
    835                                                  Keys.KEY_CAMERA_HDR, state);
    836                 if (cb != null) {
    837                     cb.onStateChanged(state);
    838                 }
    839             }
    840         });
    841     }
    842 
    843     private void initializeHdrButtonIcons(MultiToggleImageButton button, int resIdImages) {
    844         if (resIdImages > 0) {
    845             button.overrideImageIds(resIdImages);
    846         }
    847         button.overrideContentDescriptions(R.array.hdr_descriptions);
    848     }
    849 
    850     /**
    851      * Initialize a countdown timer button.
    852      */
    853     private void initializeCountdownButton(MultiToggleImageButton button,
    854             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    855         if (resIdImages > 0) {
    856             button.overrideImageIds(resIdImages);
    857         }
    858 
    859         int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    860                                                             Keys.KEY_COUNTDOWN_DURATION);
    861         button.setState(index >= 0 ? index : 0, false);
    862 
    863         setPreChangeCallback(button, preCb);
    864 
    865         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    866             @Override
    867             public void stateChanged(View view, int state) {
    868                 mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
    869                                                  Keys.KEY_COUNTDOWN_DURATION, state);
    870                 if(cb != null) {
    871                     cb.onStateChanged(state);
    872                 }
    873             }
    874         });
    875     }
    876 
    877     /**
    878      * Update the visual state of the manual exposure buttons
    879      */
    880     public void updateExposureButtons() {
    881         int compValue = mSettingsManager.getInteger(mAppController.getCameraScope(),
    882                                                     Keys.KEY_EXPOSURE);
    883         if (mExposureCompensationStep != 0.0f) {
    884             int comp = Math.round(compValue * mExposureCompensationStep);
    885             mModeOptionsExposure.setSelectedOptionByTag(String.valueOf(comp));
    886         }
    887     }
    888 
    889     /**
    890      * Initialize a grid lines button.
    891      */
    892     private void initializeGridLinesButton(MultiToggleImageButton button,
    893             final ButtonCallback cb, final ButtonCallback preCb, int resIdImages) {
    894 
    895         if (resIdImages > 0) {
    896             button.overrideImageIds(resIdImages);
    897         }
    898         button.overrideContentDescriptions(R.array.grid_lines_descriptions);
    899 
    900         setPreChangeCallback(button, preCb);
    901 
    902         button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
    903             @Override
    904             public void stateChanged(View view, int state) {
    905                 mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
    906                                                  Keys.KEY_CAMERA_GRID_LINES, state);
    907                 if (cb != null) {
    908                     cb.onStateChanged(state);
    909                 }
    910             }
    911         });
    912 
    913         int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    914                                                             Keys.KEY_CAMERA_GRID_LINES);
    915         button.setState(index >= 0 ? index : 0, true);
    916     }
    917 
    918     public boolean isPanoEnabled() {
    919         return mModeOptions.getMainBar() == ModeOptions.BAR_PANO;
    920     }
    921 
    922    /**
    923      * Initialize a panorama orientation buttons.
    924      */
    925     public void initializePanoOrientationButtons(final ButtonCallback cb) {
    926         int resIdImages = PhotoSphereHelper.getPanoramaOrientationOptionArrayId();
    927         int resIdDescriptions = PhotoSphereHelper.getPanoramaOrientationDescriptions();
    928         if (resIdImages > 0) {
    929             TypedArray imageIds = null;
    930             TypedArray descriptionIds = null;
    931             try {
    932                 mModeOptions.setMainBar(ModeOptions.BAR_PANO);
    933                 imageIds = mAppController
    934                     .getAndroidContext().getResources().obtainTypedArray(resIdImages);
    935                 descriptionIds = mAppController
    936                     .getAndroidContext().getResources().obtainTypedArray(resIdDescriptions);
    937                 mModeOptionsPano.removeAllViews();
    938                 final boolean isHorizontal =
    939                     (mModeOptionsPano.getOrientation() == LinearLayout.HORIZONTAL);
    940                 final int numImageIds = imageIds.length();
    941                 for (int index = 0; index < numImageIds; index++) {
    942                     int i;
    943                     // if in portrait orientation (pano bar horizonal), order buttons normally
    944                     // if in landscape orientation (pano bar vertical), reverse button order
    945                     if (isHorizontal) {
    946                         i = index;
    947                     } else {
    948                         i = numImageIds - index - 1;
    949                     }
    950 
    951                     int imageId = imageIds.getResourceId(i, 0);
    952                     if (imageId > 0) {
    953                         ImageButton imageButton = (ImageButton) LayoutInflater
    954                             .from(mAppController.getAndroidContext())
    955                             .inflate(R.layout.mode_options_imagebutton_template,
    956                                      mModeOptionsPano, false);
    957                         imageButton.setImageResource(imageId);
    958                         imageButton.setTag(String.valueOf(i));
    959                         mModeOptionsPano.addView(imageButton);
    960 
    961                         int descriptionId = descriptionIds.getResourceId(i, 0);
    962                         if (descriptionId > 0) {
    963                             imageButton.setContentDescription(
    964                                     mAppController.getAndroidContext().getString(descriptionId));
    965                         }
    966                     }
    967                 }
    968                 mModeOptionsPano.updateListeners();
    969                 mModeOptionsPano
    970                     .setOnOptionClickListener(new RadioOptions.OnOptionClickListener() {
    971                         @Override
    972                         public void onOptionClicked(View v) {
    973                             if (cb != null) {
    974                                 int state = Integer.parseInt((String)v.getTag());
    975                                 mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
    976                                                                  Keys.KEY_CAMERA_PANO_ORIENTATION,
    977                                                                  state);
    978                                 cb.onStateChanged(state);
    979                             }
    980                         }
    981                     });
    982                 updatePanoButtons();
    983             } finally {
    984                 if (imageIds != null) {
    985                     imageIds.recycle();
    986                 }
    987                 if (descriptionIds != null) {
    988                     descriptionIds.recycle();
    989                 }
    990             }
    991         }
    992     }
    993 
    994     private void updatePanoButtons() {
    995         int modeIndex = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
    996                                                                 Keys.KEY_CAMERA_PANO_ORIENTATION);
    997         mModeOptionsPano.setSelectedOptionByTag(String.valueOf(modeIndex));
    998     }
    999 
   1000     private void setPreChangeCallback(MultiToggleImageButton button, final ButtonCallback preCb) {
   1001         button.setOnPreChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
   1002             @Override
   1003             public void stateChanged(View view, int state) {
   1004                 if(preCb != null) {
   1005                     preCb.onStateChanged(state);
   1006                 }
   1007             }
   1008         });
   1009     }
   1010 }
   1011