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