Remove global UI elements based on supported camera parameters.

Bug: 12192238
Change-Id: I6373adeaa9c95385a2a4ca8d81cf7656550911f2
diff --git a/src/com/android/camera/ButtonManager.java b/src/com/android/camera/ButtonManager.java
index 22707bf..3eb7d0d 100644
--- a/src/com/android/camera/ButtonManager.java
+++ b/src/com/android/camera/ButtonManager.java
@@ -38,11 +38,12 @@
     public static final int BUTTON_TORCH = 1;
     public static final int BUTTON_CAMERA = 2;
     public static final int BUTTON_HDRPLUS = 3;
-    public static final int BUTTON_REFOCUS = 4;
-    public static final int BUTTON_CANCEL = 5;
-    public static final int BUTTON_DONE = 6;
-    public static final int BUTTON_RETAKE = 7;
-    public static final int BUTTON_REVIEW = 8;
+    public static final int BUTTON_HDR = 4;
+    public static final int BUTTON_REFOCUS = 5;
+    public static final int BUTTON_CANCEL = 6;
+    public static final int BUTTON_DONE = 7;
+    public static final int BUTTON_RETAKE = 8;
+    public static final int BUTTON_REVIEW = 9;
 
     /** For two state MultiToggleImageButtons, the off index. */
     public static final int OFF = 0;
@@ -55,7 +56,7 @@
     /** Bottom bar options buttons. */
     private MultiToggleImageButton mButtonFlash; // same as torch.
     private MultiToggleImageButton mButtonCamera;
-    private MultiToggleImageButton mButtonHdrPlus;
+    private MultiToggleImageButton mButtonHdr; // same as hdr plus.
     private MultiToggleImageButton mButtonRefocus;
 
     /** Intent UI buttons. */
@@ -126,7 +127,7 @@
             = (MultiToggleImageButton) root.findViewById(R.id.flash_toggle_button);
         mButtonCamera
             = (MultiToggleImageButton) root.findViewById(R.id.camera_toggle_button);
-        mButtonHdrPlus
+        mButtonHdr
             = (MultiToggleImageButton) root.findViewById(R.id.hdr_plus_toggle_button);
         mButtonRefocus
             = (MultiToggleImageButton) root.findViewById(R.id.refocus_toggle_button);
@@ -216,10 +217,15 @@
                 }
                 return mButtonCamera;
             case BUTTON_HDRPLUS:
-                if (mButtonHdrPlus == null) {
+                if (mButtonHdr == null) {
                     throw new IllegalStateException("Hdr button could not be found.");
                 }
-                return mButtonHdrPlus;
+                return mButtonHdr;
+            case BUTTON_HDR:
+                if (mButtonHdr == null) {
+                    throw new IllegalStateException("Hdr button could not be found.");
+                }
+                return mButtonHdr;
             case BUTTON_REFOCUS:
                 if (mButtonRefocus == null) {
                     throw new IllegalStateException("Refocus button could not be found.");
@@ -266,40 +272,27 @@
      * Enable a known button by id, with a state change callback and
      * a resource id that points to an array of drawables.
      */
-    public void enableButton(int buttonId, ButtonCallback cb, int resIdImages) {
+    public void enableButton(int buttonId, ButtonCallback cb) {
         MultiToggleImageButton button = getButtonOrError(buttonId);
         switch (buttonId) {
             case BUTTON_FLASH:
-                if (!mSettingsManager.isCameraBackFacing()) {
-                    disableButton(BUTTON_FLASH);
-                    return;
-                }
-                enableFlashButton(button, cb, resIdImages);
+                enableFlashButton(button, cb, R.array.camera_flashmode_icons);
                 break;
             case BUTTON_TORCH:
-                if (!mSettingsManager.isCameraBackFacing()) {
-                    disableButton(BUTTON_TORCH);
-                    return;
-                }
-                enableTorchButton(button, cb, resIdImages);
+                enableTorchButton(button, cb, R.array.video_flashmode_icons);
                 break;
             case BUTTON_CAMERA:
-                int modeIndex = mAppController.getCurrentModuleIndex();
-                if (modeIndex == sGcamIndex && mSettingsManager.isHdrPlusOn()) {
-                    disableButton(BUTTON_CAMERA);
-                    return;
-                }
-                enableCameraButton(button, cb, resIdImages);
+                enableCameraButton(button, cb, R.array.camera_id_icons);
                 break;
             case BUTTON_HDRPLUS:
-                if (!mSettingsManager.isCameraBackFacing()) {
-                    disableButton(BUTTON_HDRPLUS);
-                    return;
-                }
-                enableHdrPlusButton(button, cb, resIdImages);
+                enableHdrPlusButton(button, cb, R.array.pref_camera_hdr_plus_icons);
+                break;
+            case BUTTON_HDR:
+                // TODO: enableHdrButton
+                enableHdrPlusButton(button, cb, R.array.pref_camera_hdr_plus_icons);
                 break;
             case BUTTON_REFOCUS:
-                enableRefocusButton(button, cb, resIdImages);
+                enableRefocusButton(button, cb, R.array.refocus_icons);
                 break;
             default:
                 throw new IllegalArgumentException("button not known by id=" + buttonId);
@@ -472,22 +465,12 @@
                 if (cb != null) {
                     cb.onStateChanged(cameraId);
                 }
-                onCameraChanged();
+                mAppController.getCameraAppUI().onChangeCamera();
             }
         });
     }
 
     /**
-     * Re-sync the bottom bar buttons with the current module.
-     */
-    private void onCameraChanged() {
-        ModuleController moduleController = mAppController.getCurrentModuleController();
-        if (moduleController != null) {
-            moduleController.customizeButtons(this);
-        }
-    }
-
-    /**
      * Enable an hdr plus button.
      */
     private void enableHdrPlusButton(MultiToggleImageButton button,
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 6b67eb5..95002ec 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -1027,10 +1027,11 @@
 
         int modeIndex = -1;
         int photoIndex = getResources().getInteger(R.integer.camera_mode_photo);
+        int videoIndex = getResources().getInteger(R.integer.camera_mode_video);
         int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam);
         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
-            modeIndex = photoIndex;
+            modeIndex = videoIndex;
         } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
                         .getAction())) {
@@ -1068,7 +1069,6 @@
         syncBottomBarColor();
         syncBottomBarShutterIcon();
         mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
-        mCurrentModule.customizeButtons(getButtonManager());
 
         if (!mSecureCamera) {
             mFilmstripController.setDataAdapter(mDataAdapter);
@@ -1619,7 +1619,6 @@
         syncBottomBarColor();
         syncBottomBarShutterIcon();
         module.init(this, isSecureCamera(), isCaptureIntent());
-        module.customizeButtons(getButtonManager());
         module.resume();
         module.onPreviewVisibilityChanged(!mFilmstripVisible);
     }
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 6bfc07e..147ac65 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -50,6 +50,7 @@
 
 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
 import com.android.camera.app.AppController;
+import com.android.camera.app.CameraAppUI;
 import com.android.camera.app.CameraManager.CameraAFCallback;
 import com.android.camera.app.CameraManager.CameraAFMoveCallback;
 import com.android.camera.app.CameraManager.CameraPictureCallback;
@@ -62,6 +63,8 @@
 import com.android.camera.exif.ExifInterface;
 import com.android.camera.exif.ExifTag;
 import com.android.camera.exif.Rational;
+import com.android.camera.hardware.HardwareSpec;
+import com.android.camera.hardware.HardwareSpecImpl;
 import com.android.camera.module.ModuleController;
 import com.android.camera.settings.SettingsManager;
 import com.android.camera.ui.RotateTextToast;
@@ -340,7 +343,6 @@
 
         mActivity.getCameraProvider().requestCamera(mCameraId);
 
-        initializeControlByIntent();
         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
         mLocationManager = mActivity.getLocationManager();
         mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
@@ -353,8 +355,8 @@
     }
 
     private void initializeControlByIntent() {
-        mUI.initializeControlByIntent();
         if (mIsImageCaptureIntent) {
+            mActivity.getCameraAppUI().transitionToIntentLayout();
             setupCaptureParams();
         }
     }
@@ -407,6 +409,7 @@
 
     private void onCameraOpened() {
         openCameraCommon();
+        initializeControlByIntent();
     }
 
     private void switchCamera() {
@@ -479,15 +482,67 @@
             }
         };
 
+    private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            onCaptureCancelled();
+        }
+    };
+
+    private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            onCaptureDone();
+        }
+    };
+
+    private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mActivity.getCameraAppUI().transitionToIntentLayout();
+            onCaptureRetake();
+        }
+    };
+
     @Override
-    public void customizeButtons(ButtonManager buttonManager) {
-        mUI.customizeButtons(buttonManager, mCameraCallback, mHdrPlusCallback,
-            mRefocusCallback);
+    public HardwareSpec getHardwareSpec() {
+        return new HardwareSpecImpl(mParameters);
+    }
+
+    @Override
+    public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
+        CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
+
+        bottomBarSpec.enableCamera = true;
+        bottomBarSpec.cameraCallback = mCameraCallback;
+        bottomBarSpec.enableFlash = true;
+
+        if (mActivity.getCurrentModuleIndex() ==
+                mActivity.getResources().getInteger(R.integer.camera_mode_photo)) {
+            bottomBarSpec.hideHdr= true;
+            bottomBarSpec.hideRefocus = true;
+        } else {
+            bottomBarSpec.enableHdr = true;
+            bottomBarSpec.hdrCallback = mHdrPlusCallback;
+            bottomBarSpec.enableRefocus = true;
+            bottomBarSpec.refocusCallback = mRefocusCallback;
+        }
+
+        if (isImageCaptureIntent()) {
+            bottomBarSpec.showCancel = true;
+            bottomBarSpec.cancelCallback = mCancelCallback;
+            bottomBarSpec.showDone = true;
+            bottomBarSpec.doneCallback = mDoneCallback;
+            bottomBarSpec.showRetake = true;
+            bottomBarSpec.retakeCallback = mRetakeCallback;
+        }
+
+        return bottomBarSpec;
     }
 
     // either open a new camera or switch cameras
     private void openCameraCommon() {
-        mUI.onCameraOpened(mParameters, mCameraCallback, mHdrPlusCallback, mRefocusCallback);
+        mUI.onCameraOpened(mParameters);
         if (mIsImageCaptureIntent) {
             // Set hdr plus to default: off.
             SettingsManager settingsManager = mActivity.getSettingsManager();
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index dc67353..29ef001 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -28,7 +28,6 @@
 import android.view.MotionEvent;
 import android.view.TextureView;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 
@@ -70,32 +69,6 @@
     private float mAspectRatio = UNSET;
     private final Object mSurfaceTextureLock = new Object();
 
-    private ButtonManager.ButtonCallback mCameraCallback;
-    private ButtonManager.ButtonCallback mHdrCallback;
-    private ButtonManager.ButtonCallback mRefocusCallback;
-
-    private final OnClickListener mCancelCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mController.onCaptureCancelled();
-        }
-    };
-    private final OnClickListener mDoneCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mController.onCaptureDone();
-        }
-    };
-    private final OnClickListener mRetakeCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            customizeButtons(mActivity.getButtonManager(), mCameraCallback, mHdrCallback,
-                mRefocusCallback);
-            mActivity.getCameraAppUI().transitionToIntentLayout();
-            mController.onCaptureRetake();
-        }
-    };
-
     private final GestureDetector.OnGestureListener mPreviewGestureListener
             = new GestureDetector.SimpleOnGestureListener() {
         @Override
@@ -263,70 +236,16 @@
         // TODO init toggle buttons on bottom bar here
     }
 
-    public void onCameraOpened(Camera.Parameters params,
-            ButtonManager.ButtonCallback cameraCallback,
-            ButtonManager.ButtonCallback hdrCallback,
-            ButtonManager.ButtonCallback refocusCallback) {
-
-        mCameraCallback = cameraCallback;
-        mHdrCallback = hdrCallback;
-        mRefocusCallback = refocusCallback;
-
+    public void onCameraOpened(Camera.Parameters params) {
         initializeZoom(params);
     }
 
-    /**
-     * Customize the mode options such that flash and camera
-     * switching are enabled in simple photo mode, and flash, camera, refocus,
-     * and hdr are enabled in advanced photo mode.
-     */
-    public void customizeButtons(ButtonManager buttonManager,
-            ButtonManager.ButtonCallback cameraCallback,
-            ButtonManager.ButtonCallback hdrCallback,
-            ButtonManager.ButtonCallback refocusCallback) {
-
-        buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
-            cameraCallback, R.array.camera_id_icons);
-        buttonManager.enableButton(ButtonManager.BUTTON_FLASH,
-            null, R.array.camera_flashmode_icons);
-
-        if (mActivity.getCurrentModuleIndex() ==
-                mActivity.getResources().getInteger(R.integer.camera_mode_photo)) {
-            // Simple photo mode.
-            buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
-            buttonManager.hideButton(ButtonManager.BUTTON_REFOCUS);
-        } else {
-            // Advanced photo mode.
-            buttonManager.enableButton(ButtonManager.BUTTON_HDRPLUS,
-                hdrCallback, R.array.pref_camera_hdr_plus_icons);
-            buttonManager.enableButton(ButtonManager.BUTTON_REFOCUS,
-                refocusCallback, R.array.refocus_icons);
-        }
-
-        if (mController.isImageCaptureIntent()) {
-            buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
-                mCancelCallback);
-            buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
-                mDoneCallback);
-            buttonManager.enablePushButton(ButtonManager.BUTTON_RETAKE,
-                mRetakeCallback);
-        }
-    }
-
     public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) {
         // Decode jpeg byte array and then animate the jpeg
         DecodeTask task = new DecodeTask(jpegData, orientation, mirror);
         task.execute();
     }
 
-    public void initializeControlByIntent() {
-        if (mController.isImageCaptureIntent()) {
-            customizeButtons(mActivity.getButtonManager(), mCameraCallback, mHdrCallback,
-                mRefocusCallback);
-            mActivity.getCameraAppUI().transitionToIntentLayout();
-        }
-    }
-
     // called from onResume but only the first time
     public void initializeFirstTime() {
 
@@ -379,10 +298,7 @@
         mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror);
         mDecodeTaskForReview.execute();
 
-        customizeButtons(mActivity.getButtonManager(), mCameraCallback, mHdrCallback,
-            mRefocusCallback);
         mActivity.getCameraAppUI().transitionToIntentReviewLayout();
-
         pauseFaceDetection();
     }
 
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 963326e..adf3043 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -61,6 +61,8 @@
 import com.android.camera.app.MemoryManager;
 import com.android.camera.app.MemoryManager.MemoryListener;
 import com.android.camera.exif.ExifInterface;
+import com.android.camera.hardware.HardwareSpec;
+import com.android.camera.hardware.HardwareSpecImpl;
 import com.android.camera.module.ModuleController;
 import com.android.camera.settings.SettingsManager;
 import com.android.camera.util.AccessibilityUtils;
@@ -355,6 +357,12 @@
         return true;
     }
 
+    private void initializeControlByIntent() {
+        if (isVideoCaptureIntent()) {
+            mActivity.getCameraAppUI().transitionToIntentLayout();
+        }
+    }
+
     @Override
     public void onSingleTapUp(View view, int x, int y) {
         if (mPaused || mCameraDevice == null) {
@@ -467,9 +475,53 @@
             }
         };
 
+    private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            onReviewCancelClicked(v);
+        }
+    };
+
+    private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            onReviewDoneClicked(v);
+        }
+    };
+    private final View.OnClickListener mReviewCallback = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mActivity.getCameraAppUI().transitionToIntentLayout();
+            onReviewPlayClicked(v);
+        }
+    };
+
     @Override
-    public void customizeButtons(ButtonManager buttonManager) {
-        mUI.customizeButtons(buttonManager, mFlashCallback, mCameraCallback);
+    public HardwareSpec getHardwareSpec() {
+        return new HardwareSpecImpl(mParameters);
+    }
+
+    @Override
+    public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
+        CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
+
+        bottomBarSpec.enableCamera = true;
+        bottomBarSpec.cameraCallback = mCameraCallback;
+        bottomBarSpec.enableTorchFlash = true;
+        bottomBarSpec.flashCallback = mFlashCallback;
+        bottomBarSpec.hideHdr = true;
+        bottomBarSpec.hideRefocus = true;
+
+        if (isVideoCaptureIntent()) {
+            bottomBarSpec.showCancel = true;
+            bottomBarSpec.cancelCallback = mCancelCallback;
+            bottomBarSpec.showDone = true;
+            bottomBarSpec.doneCallback = mDoneCallback;
+            bottomBarSpec.showReview = true;
+            bottomBarSpec.reviewCallback = mReviewCallback;
+        }
+
+        return bottomBarSpec;
     }
 
     @Override
@@ -484,7 +536,7 @@
         startPreview();
         initializeVideoSnapshot();
         mUI.initializeZoom(mParameters);
-        mUI.onCameraOpened(mFlashCallback, mCameraCallback);
+        initializeControlByIntent();
     }
 
     private void startPlayVideoActivity() {
diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java
index 0956cd1..715b469 100644
--- a/src/com/android/camera/VideoUI.java
+++ b/src/com/android/camera/VideoUI.java
@@ -26,7 +26,6 @@
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -72,29 +71,6 @@
     private float mSurfaceTextureUncroppedWidth;
     private float mSurfaceTextureUncroppedHeight;
 
-    private ButtonManager.ButtonCallback mFlashCallback;
-    private ButtonManager.ButtonCallback mCameraCallback;
-
-    private final OnClickListener mCancelCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mController.onReviewCancelClicked(v);
-        }
-    };
-    private final OnClickListener mDoneCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mController.onReviewDoneClicked(v);
-        }
-    };
-    private final OnClickListener mReviewCallback = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
-            mActivity.getCameraAppUI().transitionToIntentLayout();
-            mController.onReviewPlayClicked(v);
-        }
-    };
 
     private float mAspectRatio = UNSET;
     private final AnimationManager mAnimationManager;
@@ -142,7 +118,6 @@
         mSurfaceTexture = mTextureView.getSurfaceTexture();
 
         initializeMiscControls();
-        initializeControlByIntent();
         mAnimationManager = new AnimationManager();
         mFocusUI = (FocusOverlay) mRootView.findViewById(R.id.focus_overlay);
     }
@@ -153,38 +128,6 @@
         mSurfaceView.getHolder().addCallback(this);
     }
 
-    /**
-     * Customize the mode options such that flash and camera
-     * switching are enabled.
-     */
-    public void customizeButtons(ButtonManager buttonManager,
-                                   ButtonManager.ButtonCallback flashCallback,
-                                   ButtonManager.ButtonCallback cameraCallback) {
-
-        buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
-            cameraCallback, R.array.camera_id_icons);
-        buttonManager.enableButton(ButtonManager.BUTTON_TORCH,
-            flashCallback, R.array.video_flashmode_icons);
-        buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
-        buttonManager.hideButton(ButtonManager.BUTTON_REFOCUS);
-
-        if (mController.isVideoCaptureIntent()) {
-            buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
-                mCancelCallback);
-            buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
-                mDoneCallback);
-            buttonManager.enablePushButton(ButtonManager.BUTTON_REVIEW,
-                mReviewCallback, R.drawable.ic_play);
-        }
-    }
-
-    private void initializeControlByIntent() {
-        if (mController.isVideoCaptureIntent()) {
-            customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
-            mActivity.getCameraAppUI().transitionToIntentLayout();
-        }
-    }
-
     public void setPreviewSize(int width, int height) {
         if (width == 0 || height == 0) {
             Log.w(TAG, "Preview size should not be 0.");
@@ -267,12 +210,6 @@
         mTextureView.setVisibility(View.GONE);
     }
 
-    public void onCameraOpened(ButtonManager.ButtonCallback flashCallback,
-            ButtonManager.ButtonCallback cameraCallback) {
-        mFlashCallback = flashCallback;
-        mCameraCallback = cameraCallback;
-    }
-
     private void initializeMiscControls() {
         mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
         mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
@@ -331,7 +268,6 @@
     }
 
     public void showReviewControls() {
-        customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
         mActivity.getCameraAppUI().transitionToIntentReviewLayout();
         mReviewImage.setVisibility(View.VISIBLE);
     }
diff --git a/src/com/android/camera/app/CameraAppUI.java b/src/com/android/camera/app/CameraAppUI.java
index 4fa4068..6bb6d19 100644
--- a/src/com/android/camera/app/CameraAppUI.java
+++ b/src/com/android/camera/app/CameraAppUI.java
@@ -32,9 +32,14 @@
 import android.widget.FrameLayout;
 
 import com.android.camera.AnimationManager;
-import com.android.camera.ShutterButton;
+import com.android.camera.ButtonManager;
 import com.android.camera.TextureViewHelper;
+import com.android.camera.ShutterButton;
+import com.android.camera.app.CameraProvider;
 import com.android.camera.filmstrip.FilmstripContentPanel;
+import com.android.camera.hardware.HardwareSpec;
+import com.android.camera.module.ModuleController;
+import com.android.camera.settings.SettingsManager;
 import com.android.camera.ui.BottomBar;
 import com.android.camera.ui.CaptureAnimationOverlay;
 import com.android.camera.ui.MainActivityLayout;
@@ -171,6 +176,157 @@
         }
     }
 
+    /**
+     * BottomBarUISpec provides a structure for modules
+     * to specify their ideal bottom bar mode options layout.
+     *
+     * Once constructed by a module, this class should be
+     * treated as read only.
+     *
+     * The application then edits this spec according to
+     * hardware limitations and displays the final bottom
+     * bar ui.
+     */
+    public static class BottomBarUISpec {
+        /** Mode options UI */
+
+        /**
+         * Set true if the camera option should be enabled.
+         * If not set or false, and multiple cameras are supported,
+         * the camera option will be disabled.
+         *
+         * If multiple cameras are not supported, this preference
+         * is ignored and the camera option will not be visible.
+         */
+        public boolean enableCamera;
+
+        /**
+         * Set true if the photo flash option should be enabled.
+         * If not set or false, the photo flash option will be
+         * disabled.
+         *
+         * If the hardware does not support multiple flash values,
+         * this preference is ignored and the flash option will
+         * be disabled.  It will not be made invisible in order to
+         * preserve a consistent experience across devices and between
+         * front and back cameras.
+         */
+        public boolean enableFlash;
+
+        /**
+         * Set true if the video flash option should be enabled.
+         * Same disable rules apply as the photo flash option.
+         */
+        public boolean enableTorchFlash;
+
+        /**
+         * Set true if the hdr/hdr+ option should be enabled.
+         * If not set or false, the hdr/hdr+ option will be disabled.
+         *
+         * Hdr or hdr+ will be chosen based on hardware limitations,
+         * with hdr+ prefered.
+         *
+         * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
+         * will not be visible.
+         */
+        public boolean enableHdr;
+
+        /**
+         * Set true if hdr/hdr+ should not be visible, regardless of
+         * hardware limitations.
+         */
+        public boolean hideHdr;
+
+        /**
+         * Set true if the refocus option should be enabled.
+         * If not set or false, the refocus option will be disabled.
+         *
+         * This option is not constrained by hardware limitations.
+         */
+        public boolean enableRefocus;
+
+        /**
+         * Set true if refocus should not be visible.
+         */
+        public boolean hideRefocus;
+
+        /** Intent UI */
+
+        /**
+         * Set true if the intent ui cancel option should be visible.
+         */
+        public boolean showCancel;
+        /**
+         * Set true if the intent ui done option should be visible.
+         */
+        public boolean showDone;
+        /**
+         * Set true if the intent ui retake option should be visible.
+         */
+        public boolean showRetake;
+        /**
+         * Set true if the intent ui review option should be visible.
+         */
+        public boolean showReview;
+
+        /** Mode options callbacks */
+
+        /**
+         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
+         * that will be executed when the camera option is pressed. This
+         * callback can be null.
+         */
+        public ButtonManager.ButtonCallback cameraCallback;
+
+        /**
+         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
+         * that will be executed when the flash option is pressed. This
+         * callback can be null.
+         */
+        public ButtonManager.ButtonCallback flashCallback;
+
+        /**
+         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
+         * that will be executed when the hdr/hdr+ option is pressed. This
+         * callback can be null.
+         */
+        public ButtonManager.ButtonCallback hdrCallback;
+
+        /**
+         * A {@link android.com.android.camera.ButtonManager.ButtonCallback}
+         * that will be executed when the refocus option is pressed. This
+         * callback can be null.
+         */
+        public ButtonManager.ButtonCallback refocusCallback;
+
+        /** Intent UI callbacks */
+
+        /**
+         * A {@link android.view.View.OnClickListener} that will execute
+         * when the cancel option is pressed. This callback can be null.
+         */
+        public View.OnClickListener cancelCallback;
+
+        /**
+         * A {@link android.view.View.OnClickListener} that will execute
+         * when the done option is pressed. This callback can be null.
+         */
+        public View.OnClickListener doneCallback;
+
+        /**
+         * A {@link android.view.View.OnClickListener} that will execute
+         * when the retake option is pressed. This callback can be null.
+         */
+        public View.OnClickListener retakeCallback;
+
+        /**
+         * A {@link android.view.View.OnClickListener} that will execute
+         * when the review option is pressed. This callback can be null.
+         */
+        public View.OnClickListener reviewCallback;
+    }
+
+
     private final static String TAG = "CameraAppUI";
 
     private final AppController mController;
@@ -594,6 +750,12 @@
      * specific changes that depend on the camera or camera settings.
      */
     public void onChangeCamera() {
+        ModuleController moduleController = mController.getCurrentModuleController();
+        if (moduleController.isUsingBottomBar()) {
+            applyModuleSpecs(moduleController.getHardwareSpec(),
+                moduleController.getBottomBarSpec());
+        }
+
         if (mIndicatorIconController != null) {
             // Sync the settings state with the indicator state.
             mIndicatorIconController.syncIndicators();
@@ -840,6 +1002,7 @@
         mBottomBar.setBackgroundPressedColor(colorId);
     }
 
+    // TODO: refactor this out so it can controlled by the app.
     /**
      * Sets the shutter button icon on the bottom bar
      */
@@ -881,13 +1044,123 @@
      * Performs a transition to the global intent layout.
      */
     public void transitionToIntentLayout() {
-        mBottomBar.transitionToIntentLayout();
+        ModuleController moduleController = mController.getCurrentModuleController();
+        if (moduleController.isUsingBottomBar()) {
+            applyModuleSpecs(moduleController.getHardwareSpec(),
+                moduleController.getBottomBarSpec());
+            mBottomBar.transitionToIntentLayout();
+        }
     }
 
     /**
      * Performs a transition to the global intent review layout.
      */
     public void transitionToIntentReviewLayout() {
-        mBottomBar.transitionToIntentReviewLayout();
+        ModuleController moduleController = mController.getCurrentModuleController();
+        if (moduleController.isUsingBottomBar()) {
+            applyModuleSpecs(moduleController.getHardwareSpec(),
+                moduleController.getBottomBarSpec());
+            mBottomBar.transitionToIntentReviewLayout();
+        }
+    }
+
+    /**
+     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
+     * to the bottom bar mode options based on limitations from a
+     * {@link com.android.camera.hardware.HardwareSpec}.
+     *
+     * Options not supported by the hardware are either hidden
+     * or disabled, depending on the option.
+     *
+     * Otherwise, the option is fully enabled and clickable.
+     */
+    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
+           final BottomBarUISpec bottomBarSpec) {
+        if (hardwareSpec == null) {
+            throw new IllegalArgumentException();
+        }
+        if (bottomBarSpec == null) {
+            throw new IllegalArgumentException();
+        }
+
+        ButtonManager buttonManager = mController.getButtonManager();
+        SettingsManager settingsManager = mController.getSettingsManager();
+
+        /** Standard mode options */
+        if (hardwareSpec.isFrontCameraSupported()) {
+            if (bottomBarSpec.enableCamera) {
+                buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
+                    bottomBarSpec.cameraCallback);
+            } else {
+                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
+            }
+        } else {
+            // Hide camera icon if front camera not available.
+            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
+        }
+
+        if (hardwareSpec.isFlashSupported()) {
+            if (bottomBarSpec.enableFlash && settingsManager.isCameraBackFacing()) {
+                buttonManager.enableButton(ButtonManager.BUTTON_FLASH, bottomBarSpec.flashCallback);
+            } else if (bottomBarSpec.enableTorchFlash && settingsManager.isCameraBackFacing()) {
+                buttonManager.enableButton(ButtonManager.BUTTON_TORCH, bottomBarSpec.flashCallback);
+            } else {
+                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
+            }
+        } else {
+            // Disable flash icon if not supported by the hardware.
+            buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
+        }
+
+        if (bottomBarSpec.hideHdr) {
+            // Force hide hdr or hdr plus icon.
+            buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
+        } else {
+            if (hardwareSpec.isHdrPlusSupported()) {
+                if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
+                    buttonManager.enableButton(ButtonManager.BUTTON_HDRPLUS,
+                        bottomBarSpec.hdrCallback);
+                } else {
+                    buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
+                }
+            } else if (hardwareSpec.isHdrSupported()) {
+                if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
+                    buttonManager.enableButton(ButtonManager.BUTTON_HDR,
+                        bottomBarSpec.hdrCallback);
+                } else {
+                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
+                }
+            } else {
+                // Hide hdr plus or hdr icon if neither are supported.
+                buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
+            }
+        }
+
+        if (bottomBarSpec.hideRefocus) {
+            buttonManager.hideButton(ButtonManager.BUTTON_REFOCUS);
+        } else {
+            if (bottomBarSpec.enableRefocus) {
+                buttonManager.enableButton(ButtonManager.BUTTON_REFOCUS,
+                    bottomBarSpec.refocusCallback);
+            } else {
+                // Disable refocus icon when not enabled, not dependent
+                // on hardware spec.
+                buttonManager.disableButton(ButtonManager.BUTTON_REFOCUS);
+            }
+        }
+
+        /** Intent UI */
+        if (bottomBarSpec.showCancel) {
+            buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
+                bottomBarSpec.cancelCallback);
+        }
+        if (bottomBarSpec.showDone) {
+            buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
+                bottomBarSpec.doneCallback);
+        }
+        if (bottomBarSpec.showRetake) {
+            buttonManager.enablePushButton(ButtonManager.BUTTON_RETAKE,
+                bottomBarSpec.retakeCallback);
+        }
     }
 }
diff --git a/src/com/android/camera/hardware/HardwareSpec.java b/src/com/android/camera/hardware/HardwareSpec.java
new file mode 100644
index 0000000..93c1660
--- /dev/null
+++ b/src/com/android/camera/hardware/HardwareSpec.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera.hardware;
+
+/**
+ * HardwareSpec is a interface for specifying whether
+ * high-level features are supported by the camera device
+ * hardware limitations.
+ */
+public interface HardwareSpec {
+
+    /**
+     * Returns whether a front facing camera is available
+     * on the current hardware.
+     */
+    public boolean isFrontCameraSupported();
+
+    /**
+     * Returns whether hdr scene mode is supported on the
+     * current hardware.
+     */
+    public boolean isHdrSupported();
+
+    /**
+     * Returns whether hdr plus is supported on the current
+     * hardware.
+     */
+    public boolean isHdrPlusSupported();
+
+    /**
+     * Returns whether flash is supported and has more than
+     * one supported setting.  If flash is supported but is
+     * always off, this method should return false.
+     */
+    public boolean isFlashSupported();
+}
\ No newline at end of file
diff --git a/src/com/android/camera/hardware/HardwareSpecImpl.java b/src/com/android/camera/hardware/HardwareSpecImpl.java
new file mode 100644
index 0000000..180a44e
--- /dev/null
+++ b/src/com/android/camera/hardware/HardwareSpecImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera.hardware;
+
+import android.hardware.Camera;
+
+import com.android.camera.util.CameraUtil;
+import com.android.camera.util.GcamHelper;
+
+import java.util.List;
+
+/**
+ * HardwareSpecImpl is the default implementation of
+ * {@link com.android.camera.hardware.HardwareSpec} for
+ * a camera device opened using the {@link android.hardware.Camera}
+ * api.
+ */
+public class HardwareSpecImpl implements HardwareSpec {
+
+    private final boolean mIsFrontCameraSupported;
+    private final boolean mIsHdrSupported;
+    private final boolean mIsHdrPlusSupported;
+    private final boolean mIsFlashSupported;
+
+    /**
+     * Compute the supported values for all
+     * {@link com.android.camera.hardware.HardwareSpec} methods
+     * based on {@link android.hardware.Camera.Parameters}.
+     */
+    public HardwareSpecImpl(Camera.Parameters parameters) {
+        // Cache whether front camera is supported.
+        mIsFrontCameraSupported = (Camera.getNumberOfCameras() > 1);
+
+        // Cache whether hdr is supported.
+        mIsHdrSupported = CameraUtil.isCameraHdrSupported(parameters);
+
+        // Cache whether hdr plus is supported.
+        mIsHdrPlusSupported = GcamHelper.hasGcamCapture();
+
+        // Cache whether flash is supported.
+        mIsFlashSupported = isFlashSupported(parameters);
+    }
+
+    @Override
+    public boolean isFrontCameraSupported() {
+        return mIsFrontCameraSupported;
+    }
+
+    @Override
+    public boolean isHdrSupported() {
+        return mIsHdrSupported;
+    }
+
+    @Override
+    public boolean isHdrPlusSupported() {
+        return mIsHdrPlusSupported;
+    }
+
+    @Override
+    public boolean isFlashSupported() {
+        return mIsFlashSupported;
+    }
+
+    /**
+     * Returns whether flash is supported and flash has more than
+     * one possible value.
+     */
+    private boolean isFlashSupported(Camera.Parameters parameters) {
+        List<String> supportedFlashModes = parameters.getSupportedFlashModes();
+        return !(supportedFlashModes == null || (supportedFlashModes.size() == 1));
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/camera/module/ModuleController.java b/src/com/android/camera/module/ModuleController.java
index a5b3886..e126ad8 100644
--- a/src/com/android/camera/module/ModuleController.java
+++ b/src/com/android/camera/module/ModuleController.java
@@ -20,7 +20,9 @@
 
 import com.android.camera.ButtonManager;
 import com.android.camera.app.AppController;
+import com.android.camera.app.CameraAppUI.BottomBarUISpec;
 import com.android.camera.app.CameraManager;
+import com.android.camera.hardware.HardwareSpec;
 
 /**
  * The controller at app level.
@@ -106,10 +108,18 @@
     public void onCameraAvailable(CameraManager.CameraProxy cameraProxy);
 
     /**
-     * Called when the module needs to customize global buttons according
-     * to its supported feature set.
+     * Returns a {@link com.android.camera.hardware.HardwareSpec}
+     * based on the module's open camera device.
      */
-    public void customizeButtons(ButtonManager buttonManager);
+    public HardwareSpec getHardwareSpec();
+
+    /**
+     * Returns a {@link com.android.camera.app.CameraAppUI.BottomBarUISpec}
+     * which represents the module's ideal bottom bar layout of the
+     * mode options.  The app edits the final layout based on the
+     * {@link com.android.camera.hardware.HardwareSpec}.
+     */
+    public BottomBarUISpec getBottomBarSpec();
 
     /**
      * Used by the app on configuring the bottom bar color and visibility.