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