ButtonManager.java revision 66d3d0d0633324893b89e1ddec14a4710d8034c3
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
17package com.android.camera;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.view.LayoutInflater;
22import android.view.View;
23import android.widget.ImageButton;
24import android.widget.LinearLayout;
25
26import com.android.camera.app.AppController;
27import com.android.camera.app.CameraAppUI;
28import com.android.camera.settings.Keys;
29import com.android.camera.settings.SettingsManager;
30import com.android.camera.ui.RadioOptions;
31import com.android.camera.util.PhotoSphereHelper;
32import com.android.camera.widget.ModeOptions;
33import com.android.camera2.R;
34
35/**
36 * A  class for generating pre-initialized
37 * {@link #android.widget.ImageButton}s.
38 */
39public 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        if (button.isEnabled()) {
421            button.setEnabled(false);
422            if (mListener != null) {
423                mListener.onButtonEnabledChanged(this, buttonId);
424            }
425        }
426        button.setTag(R.string.tag_enabled_id, null);
427
428        if (button.getVisibility() != View.VISIBLE) {
429            button.setVisibility(View.VISIBLE);
430            if (mListener != null) {
431                mListener.onButtonVisibilityChanged(this, buttonId);
432            }
433        }
434    }
435
436    /**
437     * Enables a button that has already been initialized.
438     */
439    public void enableButton(int buttonId) {
440        ImageButton button = getButtonOrError(buttonId);
441        if (!button.isEnabled()) {
442            button.setEnabled(true);
443            if (mListener != null) {
444                mListener.onButtonEnabledChanged(this, buttonId);
445            }
446        }
447        button.setTag(R.string.tag_enabled_id, buttonId);
448
449        if (button.getVisibility() != View.VISIBLE) {
450            button.setVisibility(View.VISIBLE);
451            if (mListener != null) {
452                mListener.onButtonVisibilityChanged(this, buttonId);
453            }
454        }
455    }
456
457    /**
458     * Disable click reactions for a button without affecting visual state.
459     * For most cases you'll want to use {@link #disableButton(int)}.
460     * @param buttonId The id of the button.
461     */
462    public void disableButtonClick(int buttonId) {
463        ImageButton button = getButtonOrError(buttonId);
464        if (button instanceof MultiToggleImageButton) {
465            ((MultiToggleImageButton) button).setClickEnabled(false);
466        }
467    }
468
469    /**
470     * Enable click reactions for a button without affecting visual state.
471     * For most cases you'll want to use {@link #enableButton(int)}.
472     * @param buttonId The id of the button.
473     */
474    public void enableButtonClick(int buttonId) {
475        ImageButton button = getButtonOrError(buttonId);
476        if (button instanceof MultiToggleImageButton) {
477            ((MultiToggleImageButton) button).setClickEnabled(true);
478        }
479    }
480
481    /**
482     * Hide a button by id.
483     */
484    public void hideButton(int buttonId) {
485        View button;
486        try {
487            button = getButtonOrError(buttonId);
488        } catch (IllegalArgumentException e) {
489            button = getImageButtonOrError(buttonId);
490        }
491        if (button.getVisibility() == View.VISIBLE) {
492            button.setVisibility(View.GONE);
493            if (mListener != null) {
494                mListener.onButtonVisibilityChanged(this, buttonId);
495            }
496        }
497    }
498
499    public void setToInitialState() {
500        mModeOptions.setMainBar(ModeOptions.BAR_STANDARD);
501    }
502
503    public void setExposureCompensationCallback(final CameraAppUI.BottomBarUISpec
504                                        .ExposureCompensationSetCallback cb) {
505        if (cb == null) {
506            mModeOptionsExposure.setOnOptionClickListener(null);
507        } else {
508            mModeOptionsExposure
509                .setOnOptionClickListener(new RadioOptions.OnOptionClickListener() {
510                    @Override
511                    public void onOptionClicked(View v) {
512                        int comp = Integer.parseInt((String)(v.getTag()));
513
514                        if (mExposureCompensationStep != 0.0f) {
515                            int compValue =
516                                Math.round(comp / mExposureCompensationStep);
517                            cb.setExposure(compValue);
518                        }
519                    }
520                });
521        }
522    }
523
524    /**
525     * Set the exposure compensation parameters supported by the current camera mode.
526     * @param min Minimum exposure compensation value.
527     * @param max Maximum exposure compensation value.
528     * @param step Expsoure compensation step value.
529     */
530    public void setExposureCompensationParameters(int min, int max, float step) {
531        mMaxExposureCompensation = max;
532        mMinExposureCompensation = min;
533        mExposureCompensationStep = step;
534
535
536        setVisible(mExposureN2, (Math.round(min * step) <= -2));
537        setVisible(mExposureN1, (Math.round(min * step) <= -1));
538        setVisible(mExposureP1, (Math.round(max * step) >= 1));
539        setVisible(mExposureP2, (Math.round(max * step) >= 2));
540
541        updateExposureButtons();
542    }
543
544    private static void setVisible(View v, boolean visible) {
545        if (visible) {
546            v.setVisibility(View.VISIBLE);
547        } else {
548            v.setVisibility(View.INVISIBLE);
549        }
550    }
551
552    /**
553     * @return The exposure compensation step value.
554     **/
555    public float getExposureCompensationStep() {
556        return mExposureCompensationStep;
557    }
558
559    /**
560     * Check if a button is enabled with the given button id..
561     */
562    public boolean isEnabled(int buttonId) {
563        View button;
564        try {
565            button = getButtonOrError(buttonId);
566        } catch (IllegalArgumentException e) {
567            button = getImageButtonOrError(buttonId);
568        }
569
570        Integer enabledId = (Integer) button.getTag(R.string.tag_enabled_id);
571        if (enabledId != null) {
572            return (enabledId.intValue() == buttonId) && button.isEnabled();
573        } else {
574            return false;
575        }
576    }
577
578    /**
579     * Check if a button is visible.
580     */
581    public boolean isVisible(int buttonId) {
582        View button;
583        try {
584            button = getButtonOrError(buttonId);
585        } catch (IllegalArgumentException e) {
586            button = getImageButtonOrError(buttonId);
587        }
588        return (button.getVisibility() == View.VISIBLE);
589    }
590
591    /**
592     * Initialize a flash button.
593     */
594    private void initializeFlashButton(MultiToggleImageButton button,
595            final ButtonCallback cb, int resIdImages) {
596
597        if (resIdImages > 0) {
598            button.overrideImageIds(resIdImages);
599        }
600        button.overrideContentDescriptions(R.array.camera_flash_descriptions);
601
602        int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
603                                                            Keys.KEY_FLASH_MODE);
604        button.setState(index >= 0 ? index : 0, false);
605
606        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
607            @Override
608            public void stateChanged(View view, int state) {
609                mSettingsManager.setValueByIndex(mAppController.getCameraScope(),
610                                                 Keys.KEY_FLASH_MODE, state);
611                if (cb != null) {
612                    cb.onStateChanged(state);
613                }
614            }
615        });
616    }
617
618    /**
619     * Initialize video torch button
620     */
621    private void initializeTorchButton(MultiToggleImageButton button,
622            final ButtonCallback cb, int resIdImages) {
623
624        if (resIdImages > 0) {
625            button.overrideImageIds(resIdImages);
626        }
627        button.overrideContentDescriptions(R.array.video_flash_descriptions);
628
629        int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getCameraScope(),
630                                                            Keys.KEY_VIDEOCAMERA_FLASH_MODE);
631        button.setState(index >= 0 ? index : 0, false);
632
633        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
634            @Override
635            public void stateChanged(View view, int state) {
636                mSettingsManager.setValueByIndex(mAppController.getCameraScope(),
637                                                 Keys.KEY_VIDEOCAMERA_FLASH_MODE, state);
638                if (cb != null) {
639                    cb.onStateChanged(state);
640                }
641            }
642        });
643    }
644
645    /**
646     * Initialize hdr plus flash button
647     */
648    private void initializeHdrPlusFlashButton(MultiToggleImageButton button,
649            final ButtonCallback cb, int resIdImages) {
650
651        if (resIdImages > 0) {
652            button.overrideImageIds(resIdImages);
653        }
654        button.overrideContentDescriptions(R.array.hdr_plus_flash_descriptions);
655
656        int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
657                                                            Keys.KEY_HDR_PLUS_FLASH_MODE);
658        button.setState(index >= 0 ? index : 0, false);
659
660        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
661            @Override
662            public void stateChanged(View view, int state) {
663                mSettingsManager.setValueByIndex(mAppController.getModuleScope(),
664                                                 Keys.KEY_HDR_PLUS_FLASH_MODE, state);
665                if (cb != null) {
666                    cb.onStateChanged(state);
667                }
668            }
669        });
670    }
671
672    /**
673     * Initialize a camera button.
674     */
675    private void initializeCameraButton(final MultiToggleImageButton button,
676            final ButtonCallback cb, int resIdImages) {
677
678        if (resIdImages > 0) {
679            button.overrideImageIds(resIdImages);
680        }
681
682        int index = mSettingsManager.getIndexOfCurrentValue(mAppController.getModuleScope(),
683                                                            Keys.KEY_CAMERA_ID);
684        button.setState(index >= 0 ? index : 0, false);
685
686        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
687            @Override
688            public void stateChanged(View view, int state) {
689                mSettingsManager.setValueByIndex(mAppController.getModuleScope(),
690                                                 Keys.KEY_CAMERA_ID, state);
691                int cameraId = mSettingsManager.getInteger(mAppController.getModuleScope(),
692                                                           Keys.KEY_CAMERA_ID);
693                // This is a quick fix for ISE in Gcam module which can be
694                // found by rapid pressing camera switch button. The assumption
695                // here is that each time this button is clicked, the listener
696                // will do something and then enable this button again.
697                button.setEnabled(false);
698                if (cb != null) {
699                    cb.onStateChanged(cameraId);
700                }
701                mAppController.getCameraAppUI().onChangeCamera();
702            }
703        });
704    }
705
706    /**
707     * Initialize an hdr plus button.
708     */
709    private void initializeHdrPlusButton(MultiToggleImageButton button,
710            final ButtonCallback cb, int resIdImages) {
711
712        if (resIdImages > 0) {
713            button.overrideImageIds(resIdImages);
714        }
715        button.overrideContentDescriptions(R.array.hdr_plus_descriptions);
716
717        int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
718                                                            Keys.KEY_CAMERA_HDR_PLUS);
719        button.setState(index >= 0 ? index : 0, false);
720
721        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
722            @Override
723            public void stateChanged(View view, int state) {
724                mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
725                                                 Keys.KEY_CAMERA_HDR_PLUS, state);
726                if (cb != null) {
727                    cb.onStateChanged(state);
728                }
729            }
730        });
731    }
732
733    /**
734     * Initialize an hdr button.
735     */
736    private void initializeHdrButton(MultiToggleImageButton button,
737            final ButtonCallback cb, int resIdImages) {
738
739        if (resIdImages > 0) {
740            button.overrideImageIds(resIdImages);
741        }
742        button.overrideContentDescriptions(R.array.hdr_descriptions);
743
744        int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
745                                                            Keys.KEY_CAMERA_HDR);
746        button.setState(index >= 0 ? index : 0, false);
747
748        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
749            @Override
750            public void stateChanged(View view, int state) {
751                mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
752                                                 Keys.KEY_CAMERA_HDR, state);
753                if (cb != null) {
754                    cb.onStateChanged(state);
755                }
756            }
757        });
758    }
759
760    /**
761     * Initialize a countdown timer button.
762     */
763    private void initializeCountdownButton(MultiToggleImageButton button,
764            final ButtonCallback cb, int resIdImages) {
765        if (resIdImages > 0) {
766            button.overrideImageIds(resIdImages);
767        }
768
769        int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
770                                                            Keys.KEY_COUNTDOWN_DURATION);
771        button.setState(index >= 0 ? index : 0, false);
772
773        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
774            @Override
775            public void stateChanged(View view, int state) {
776                mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
777                                                 Keys.KEY_COUNTDOWN_DURATION, state);
778                if(cb != null) {
779                    cb.onStateChanged(state);
780                }
781            }
782        });
783    }
784
785    /**
786     * Update the visual state of the manual exposure buttons
787     */
788    public void updateExposureButtons() {
789        int compValue = mSettingsManager.getInteger(mAppController.getCameraScope(),
790                                                    Keys.KEY_EXPOSURE);
791        if (mExposureCompensationStep != 0.0f) {
792            int comp = Math.round(compValue * mExposureCompensationStep);
793            mModeOptionsExposure.setSelectedOptionByTag(String.valueOf(comp));
794        }
795    }
796
797    /**
798     * Initialize a grid lines button.
799     */
800    private void initializeGridLinesButton(MultiToggleImageButton button,
801            final ButtonCallback cb, int resIdImages) {
802
803        if (resIdImages > 0) {
804            button.overrideImageIds(resIdImages);
805        }
806        button.overrideContentDescriptions(R.array.grid_lines_descriptions);
807
808        button.setOnStateChangeListener(new MultiToggleImageButton.OnStateChangeListener() {
809            @Override
810            public void stateChanged(View view, int state) {
811                mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
812                                                 Keys.KEY_CAMERA_GRID_LINES, state);
813                if (cb != null) {
814                    cb.onStateChanged(state);
815                }
816            }
817        });
818
819        int index = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
820                                                            Keys.KEY_CAMERA_GRID_LINES);
821        button.setState(index >= 0 ? index : 0, true);
822    }
823
824    public boolean isPanoEnabled() {
825        return mModeOptions.getMainBar() == ModeOptions.BAR_PANO;
826    }
827
828   /**
829     * Initialize a panorama orientation buttons.
830     */
831    public void initializePanoOrientationButtons(final ButtonCallback cb) {
832        int resIdImages = PhotoSphereHelper.getPanoramaOrientationOptionArrayId();
833        int resIdDescriptions = PhotoSphereHelper.getPanoramaOrientationDescriptions();
834        if (resIdImages > 0) {
835            TypedArray imageIds = null;
836            TypedArray descriptionIds = null;
837            try {
838                mModeOptions.setMainBar(ModeOptions.BAR_PANO);
839                imageIds = mAppController
840                    .getAndroidContext().getResources().obtainTypedArray(resIdImages);
841                descriptionIds = mAppController
842                    .getAndroidContext().getResources().obtainTypedArray(resIdDescriptions);
843                mModeOptionsPano.removeAllViews();
844                final boolean isHorizontal =
845                    (mModeOptionsPano.getOrientation() == LinearLayout.HORIZONTAL);
846                final int numImageIds = imageIds.length();
847                for (int index = 0; index < numImageIds; index++) {
848                    int i;
849                    // if in portrait orientation (pano bar horizonal), order buttons normally
850                    // if in landscape orientation (pano bar vertical), reverse button order
851                    if (isHorizontal) {
852                        i = index;
853                    } else {
854                        i = numImageIds - index - 1;
855                    }
856
857                    int imageId = imageIds.getResourceId(i, 0);
858                    if (imageId > 0) {
859                        ImageButton imageButton = (ImageButton) LayoutInflater
860                            .from(mAppController.getAndroidContext())
861                            .inflate(R.layout.mode_options_imagebutton_template,
862                                     mModeOptionsPano, false);
863                        imageButton.setImageResource(imageId);
864                        imageButton.setTag(String.valueOf(i));
865                        mModeOptionsPano.addView(imageButton);
866
867                        int descriptionId = descriptionIds.getResourceId(i, 0);
868                        if (descriptionId > 0) {
869                            imageButton.setContentDescription(
870                                    mAppController.getAndroidContext().getString(descriptionId));
871                        }
872                    }
873                }
874                mModeOptionsPano.updateListeners();
875                mModeOptionsPano
876                    .setOnOptionClickListener(new RadioOptions.OnOptionClickListener() {
877                        @Override
878                        public void onOptionClicked(View v) {
879                            if (cb != null) {
880                                int state = Integer.parseInt((String)v.getTag());
881                                mSettingsManager.setValueByIndex(SettingsManager.SCOPE_GLOBAL,
882                                                                 Keys.KEY_CAMERA_PANO_ORIENTATION,
883                                                                 state);
884                                cb.onStateChanged(state);
885                            }
886                        }
887                    });
888                updatePanoButtons();
889            } finally {
890                if (imageIds != null) {
891                    imageIds.recycle();
892                }
893                if (descriptionIds != null) {
894                    descriptionIds.recycle();
895                }
896            }
897        }
898    }
899
900    private void updatePanoButtons() {
901        int modeIndex = mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
902                                                                Keys.KEY_CAMERA_PANO_ORIENTATION);
903        mModeOptionsPano.setSelectedOptionByTag(String.valueOf(modeIndex));
904    }
905}
906