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      */
ButtonManager(AppController app)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      */
load(View root)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          */
onButtonVisibilityChanged(ButtonManager buttonManager, int buttonId)129         public void onButtonVisibilityChanged(ButtonManager buttonManager, int buttonId);
130 
131         /**
132          * A button's enabled state has changed.
133          */
onButtonEnabledChanged(ButtonManager buttonManager, int buttonId)134         public void onButtonEnabledChanged(ButtonManager buttonManager, int buttonId);
135     }
136 
137     /**
138      * Sets the ButtonStatusListener.
139      */
setListener(ButtonStatusListener listener)140     public void setListener(ButtonStatusListener listener) {
141         mListener = listener;
142     }
143 
144     /**
145      * Gets references to all known buttons.
146      */
getButtonsReferences(View root)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
onSettingChanged(SettingsManager settingsManager, String key)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 {
onStateChanged(int state)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      */
getButtonOrError(int buttonId)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      */
getImageButtonOrError(int buttonId)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      */
initializeButton(int buttonId, ButtonCallback cb)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      */
initializePushButton(int buttonId, View.OnClickListener cb, int imageId)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      */
initializePushButton(int buttonId, View.OnClickListener cb)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      */
disableButton(int buttonId)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      */
enableButton(int buttonId)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      */
disableButtonClick(int buttonId)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      */
enableButtonClick(int buttonId)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      */
hideButton(int buttonId)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 
setToInitialState()512     public void setToInitialState() {
513         mModeOptions.setMainBar(ModeOptions.BAR_STANDARD);
514     }
515 
setExposureCompensationCallback(final CameraAppUI.BottomBarUISpec .ExposureCompensationSetCallback cb)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      */
setExposureCompensationParameters(int min, int max, float step)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 
setVisible(View v, boolean visible)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      **/
getExposureCompensationStep()568     public float getExposureCompensationStep() {
569         return mExposureCompensationStep;
570     }
571 
572     /**
573      * Check if a button is enabled with the given button id..
574      */
isEnabled(int buttonId)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      */
isVisible(int buttonId)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      */
initializeFlashButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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      */
initializeTorchButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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      */
initializeHdrPlusFlashButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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      */
initializeCameraButton(final MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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      */
initializeHdrPlusButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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 
initializeHdrPlusButtonIcons(MultiToggleImageButton button, int resIdImages)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      */
initializeHdrButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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 
initializeHdrButtonIcons(MultiToggleImageButton button, int resIdImages)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      */
initializeCountdownButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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      */
updateExposureButtons()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      */
initializeGridLinesButton(MultiToggleImageButton button, final ButtonCallback cb, int resIdImages)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 
isPanoEnabled()845     public boolean isPanoEnabled() {
846         return mModeOptions.getMainBar() == ModeOptions.BAR_PANO;
847     }
848 
849    /**
850      * Initialize a panorama orientation buttons.
851      */
initializePanoOrientationButtons(final ButtonCallback cb)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 
updatePanoButtons()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