Reconcile with ics-mr0-release

Change-Id: Ic661c126d1de39077a150425601c8b32980e9d14
diff --git a/res/layout/effect_setting_popup.xml b/res/layout/effect_setting_popup.xml
index 5fa4ad2..3ef7baf 100644
--- a/res/layout/effect_setting_popup.xml
+++ b/res/layout/effect_setting_popup.xml
@@ -44,8 +44,9 @@
                         android:minHeight="@dimen/effect_setting_clear_text_min_height"
                         android:background="@drawable/bg_pressed"/>
                 <View style="@style/EffectTitleSeparator"/>
-                <TextView
+                <TextView android:id="@+id/effect_silly_faces_title"
                         android:text="@string/effect_silly_faces"
+                        android:visibility="gone"
                         style="@style/EffectSettingTypeTitle"/>
                 <com.android.camera.ui.ExpandedGridView android:id="@+id/effect_silly_faces"
                         style="@style/EffectSettingGrid"/>
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index a66a764..54f709f 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -122,6 +122,9 @@
     private Parameters mInitialParams;
     private boolean mFocusAreaSupported;
     private boolean mMeteringAreaSupported;
+    private boolean mAeLockSupported;
+    private boolean mAwbLockSupported;
+    private boolean mAeAwbLock;
 
     private MyOrientationEventListener mOrientationListener;
     // The degrees of the device rotated clockwise from its natural orientation.
@@ -1037,6 +1040,7 @@
             findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE);
         } else {
             mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
+            mThumbnailView.enableFilter(false);
             mThumbnailView.setVisibility(View.VISIBLE);
         }
 
@@ -1177,6 +1181,7 @@
         }
         if (mModePicker != null) mModePicker.setEnabled(enable);
         if (mZoomControl != null) mZoomControl.setEnabled(enable);
+        if (mThumbnailView != null) mThumbnailView.setEnabled(enable);
     }
 
     public static int roundOrientation(int orientation) {
@@ -1213,16 +1218,13 @@
         }
     }
 
-    private void setOrientationIndicator(int degree) {
-        if (mThumbnailView != null) mThumbnailView.setDegree(degree);
-        if (mModePicker != null) mModePicker.setDegree(degree);
-        if (mSharePopup != null) mSharePopup.setOrientation(degree);
-        if (mIndicatorControlContainer != null) mIndicatorControlContainer.setDegree(degree);
-        if (mZoomControl != null) mZoomControl.setDegree(degree);
-        if (mFocusIndicator != null) mFocusIndicator.setOrientation(degree);
-        if (mFaceView != null) mFaceView.setOrientation(degree);
-        if (mReviewCancelButton != null) mReviewCancelButton.setOrientation(degree);
-        if (mReviewDoneButton != null) mReviewDoneButton.setOrientation(degree);
+    private void setOrientationIndicator(int orientation) {
+        Rotatable[] indicators = {mThumbnailView, mModePicker, mSharePopup,
+                mIndicatorControlContainer, mZoomControl, mFocusIndicator, mFaceView,
+                mReviewCancelButton, mReviewDoneButton};
+        for (Rotatable indicator : indicators) {
+            if (indicator != null) indicator.setOrientation(orientation);
+        }
     }
 
     @Override
@@ -1357,7 +1359,19 @@
         // Do not do focus if there is not enough storage.
         if (pressed && !canTakePicture()) return;
 
+        // Lock AE and AWB so users can half-press shutter and recompose.
+        mAeAwbLock = pressed;
+        if (mAeAwbLock && (mAeLockSupported || mAwbLockSupported)) {
+            setCameraParameters(UPDATE_PARAM_PREFERENCE);
+        }
+
         mFocusManager.doFocus(pressed);
+
+        // Unlock AE and AWB after cancelAutoFocus. Camera API does not
+        // guarantee setParameters can be called during autofocus.
+        if (!mAeAwbLock && (mAeLockSupported || mAwbLockSupported)) {
+            setCameraParameters(UPDATE_PARAM_PREFERENCE);
+        }
     }
 
     @Override
@@ -1501,7 +1515,7 @@
             unregisterReceiver(mReceiver);
             mDidRegister = false;
         }
-        mLocationManager.recordLocation(false);
+        if (mLocationManager != null) mLocationManager.recordLocation(false);
         updateExposureOnScreenIndicator(0);
 
         mFocusManager.releaseSoundPlayer();
@@ -1743,6 +1757,7 @@
 
         setPreviewDisplay(mSurfaceHolder);
         setDisplayOrientation();
+        mAeAwbLock = false; // Unlock AE and AWB.
         setCameraParameters(UPDATE_PARAM_ALL);
         // If the focus mode is continuous autofocus, call cancelAutoFocus to
         // resume it because it may have been paused by autoFocus call.
@@ -1816,6 +1831,14 @@
     }
 
     private void updateCameraParametersPreference() {
+        if (mAeLockSupported) {
+            mParameters.setAutoExposureLock(mAeAwbLock);
+        }
+
+        if (mAwbLockSupported) {
+            mParameters.setAutoWhiteBalanceLock(mAeAwbLock);
+        }
+
         if (mFocusAreaSupported) {
             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
         }
@@ -2213,5 +2236,7 @@
                 && isSupported(Parameters.FOCUS_MODE_AUTO,
                         mInitialParams.getSupportedFocusModes()));
         mMeteringAreaSupported = (mInitialParams.getMaxNumMeteringAreas() > 0);
+        mAeLockSupported = mInitialParams.isAutoExposureLockSupported();
+        mAwbLockSupported = mInitialParams.isAutoWhiteBalanceLockSupported();
     }
 }
diff --git a/src/com/android/camera/ModePicker.java b/src/com/android/camera/ModePicker.java
index 528bc3d..ccde71d 100644
--- a/src/com/android/camera/ModePicker.java
+++ b/src/com/android/camera/ModePicker.java
@@ -17,6 +17,7 @@
 package com.android.camera;
 
 import com.android.camera.ui.PopupManager;
+import com.android.camera.ui.Rotatable;
 import com.android.camera.ui.RotateImageView;
 
 import android.content.Context;
@@ -36,7 +37,7 @@
  * a current mode indicator.
  */
 public class ModePicker extends RelativeLayout implements View.OnClickListener,
-    PopupManager.OnOtherPopupShowedListener {
+    PopupManager.OnOtherPopupShowedListener, Rotatable {
     public static final int MODE_CAMERA = 0;
     public static final int MODE_VIDEO = 1;
     public static final int MODE_PANORAMA = 2;
@@ -188,11 +189,11 @@
         return true;
     }
 
-    public void setDegree(int degree) {
+    public void setOrientation(int orientation) {
         for (int i = 0; i < MODE_NUM; ++i) {
-            mModeSelectionIcon[i].setDegree(degree);
+            mModeSelectionIcon[i].setOrientation(orientation);
             if (mCurrentModeFrame != null) {
-                mCurrentModeIcon[i].setDegree(degree);
+                mCurrentModeIcon[i].setOrientation(orientation);
             }
         }
     }
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index df8adb9..e8d8456 100755
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -538,19 +538,18 @@
         }
     }
 
-    private void setOrientationIndicator(int degree) {
-        if (mThumbnailView != null) mThumbnailView.setDegree(degree);
-        if (mModePicker != null) mModePicker.setDegree(degree);
-        if (mSharePopup != null) mSharePopup.setOrientation(degree);
-        if (mBgLearningMessageRotater != null) mBgLearningMessageRotater.setOrientation(degree);
-        if (mIndicatorControlContainer != null) mIndicatorControlContainer.setDegree(degree);
-        if (mReviewDoneButton != null) mReviewDoneButton.setOrientation(degree);
-        if (mReviewPlayButton != null) mReviewPlayButton.setOrientation(degree);
-        if (mReviewCancelButton!= null) mReviewCancelButton.setOrientation(degree);
+    private void setOrientationIndicator(int orientation) {
+        Rotatable[] indicators = {mThumbnailView, mModePicker, mSharePopup,
+                mBgLearningMessageRotater, mIndicatorControlContainer,
+                mReviewDoneButton, mReviewPlayButton, mReviewCancelButton};
+        for (Rotatable indicator : indicators) {
+            if (indicator != null) indicator.setOrientation(orientation);
+        }
+
         // We change the orientation of the linearlayout only for phone UI because when in portrait
         // the width is not enough.
         if (mLabelsLinearLayout != null) {
-            if (((degree / 90) & 1) == 1) {
+            if (((orientation / 90) & 1) == 1) {
                 mLabelsLinearLayout.setOrientation(mLabelsLinearLayout.VERTICAL);
             } else {
                 mLabelsLinearLayout.setOrientation(mLabelsLinearLayout.HORIZONTAL);
diff --git a/src/com/android/camera/panorama/MosaicFrameProcessor.java b/src/com/android/camera/panorama/MosaicFrameProcessor.java
index 7660f5e..300e4e3 100644
--- a/src/com/android/camera/panorama/MosaicFrameProcessor.java
+++ b/src/com/android/camera/panorama/MosaicFrameProcessor.java
@@ -65,7 +65,8 @@
     private int mPreviewBufferSize;
 
     public interface ProgressListener {
-        public void onProgress(boolean isFinished, float panningRateX, float panningRateY);
+        public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
+                float progressX, float progressY);
     }
 
     public MosaicFrameProcessor(int previewWidth, int previewHeight, int bufSize) {
@@ -173,11 +174,15 @@
 
                 // Publish progress of the ongoing processing
                 if (mProgressListener != null) {
-                    mProgressListener.onProgress(false, mPanningRateX, mPanningRateY);
+                    mProgressListener.onProgress(false, mPanningRateX, mPanningRateY,
+                            mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
+                            mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
                 }
             } else {
                 if (mProgressListener != null) {
-                    mProgressListener.onProgress(true, mPanningRateX, mPanningRateY);
+                    mProgressListener.onProgress(true, mPanningRateX, mPanningRateY,
+                            mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
+                            mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
                 }
             }
         }
@@ -211,20 +216,20 @@
         mTotalTranslationY += mDeltaY[idx];
         mTotalDeltaTime += mDeltaTime[idx];
 
-        mTranslationLastX = translationCurrX;
-        mTranslationLastY = translationCurrY;
-        mLastProcessedFrameTimestamp = now;
-        mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
-
         // The panning rate is measured as the rate of the translation percentage in
         // image width/height. Take the horizontal panning rate for example, the image width
         // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR).
         // To get the horizontal translation percentage, the horizontal translation,
         // (translationCurrX - mTranslationLastX), is divided by the
         // image width. We then get the rate by dividing the translation percentage with deltaTime.
-        mPanningRateX = mTotalTranslationX / (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR)
-                / mTotalDeltaTime;
-        mPanningRateY = mTotalTranslationY / (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR)
-                / mTotalDeltaTime;
+        mPanningRateX = mTotalTranslationX /
+                (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
+        mPanningRateY = mTotalTranslationY /
+                (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
+
+        mTranslationLastX = translationCurrX;
+        mTranslationLastY = translationCurrY;
+        mLastProcessedFrameTimestamp = now;
+        mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
     }
 }
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index f11bc3a..ab4f0af 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -49,8 +49,6 @@
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.Size;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.net.Uri;
 import android.os.Bundle;
@@ -321,12 +319,6 @@
                 });
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
-        updateThumbnailButton();
-    }
-
     private void setupCamera() {
         openCamera();
         Parameters parameters = mCameraDevice.getParameters();
@@ -543,6 +535,7 @@
         mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan_recording);
         mCaptureIndicator.setVisibility(View.VISIBLE);
         showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
+        mThumbnailView.setEnabled(false);
 
         mCompassValueXStart = mCompassValueXStartBuffer;
         mCompassValueYStart = mCompassValueYStartBuffer;
@@ -554,13 +547,14 @@
 
         mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
             @Override
-            public void onProgress(boolean isFinished, float panningRateX, float panningRateY) {
+            public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
+                    float progressX, float progressY) {
                 if (isFinished
                         || (mMaxAngleX - mMinAngleX >= DEFAULT_SWEEP_ANGLE)
                         || (mMaxAngleY - mMinAngleY >= DEFAULT_SWEEP_ANGLE)) {
                     stopCapture(false);
                 } else {
-                    updateProgress(panningRateX);
+                    updateProgress(panningRateX, progressX, progressY);
                 }
             }
         });
@@ -580,6 +574,7 @@
         mCaptureIndicator.setVisibility(View.GONE);
         hideTooFastIndication();
         hideDirectionIndicators();
+        mThumbnailView.setEnabled(true);
 
         mMosaicFrameProcessor.setProgressListener(null);
         stopCameraPreview();
@@ -625,7 +620,7 @@
         mRightIndicator.setEnabled(false);
     }
 
-    private void updateProgress(float panningRate) {
+    private void updateProgress(float panningRate, float progressX, float progressY) {
         mMosaicView.setReady();
         mMosaicView.requestRender();
 
@@ -637,6 +632,7 @@
         } else {
             hideTooFastIndication();
         }
+        mPanoProgressBar.setProgress((int) (progressX * mHorizontalViewAngle));
     }
 
     private void createContentView() {
@@ -679,6 +675,7 @@
         mCaptureIndicator = (TextView) findViewById(R.id.pano_capture_indicator);
 
         mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
+        mThumbnailView.enableFilter(false);
 
         mReviewLayout = (View) findViewById(R.id.pano_review_layout);
         mReview = (ImageView) findViewById(R.id.pano_reviewarea);
@@ -746,6 +743,14 @@
         t.start();
     }
 
+    private void initThumbnailButton() {
+        // Load the thumbnail from the disk.
+        if (mThumbnail == null) {
+            mThumbnail = Thumbnail.loadFrom(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
+        }
+        updateThumbnailButton();
+    }
+
     private void updateThumbnailButton() {
         // Update last image if URI is invalid and the storage is ready.
         ContentResolver contentResolver = getContentResolver();
@@ -909,7 +914,6 @@
         releaseCamera();
         mMosaicView.onPause();
         clearMosaicFrameProcessorIfNeeded();
-        mSensorManager.unregisterListener(mListener);
         mOrientationEventListener.disable();
         System.gc();
     }
@@ -920,14 +924,6 @@
 
         mPausing = false;
         mOrientationEventListener.enable();
-        /*
-         * It is not necessary to get accelerometer events at a very high rate,
-         * by using a game rate (SENSOR_DELAY_UI), we get an automatic
-         * low-pass filter, which "extracts" the gravity component of the
-         * acceleration. As an added benefit, we use less power and CPU
-         * resources.
-         */
-        mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI);
 
         mCaptureState = CAPTURE_STATE_VIEWFINDER;
         setupCamera();
@@ -936,57 +932,10 @@
         // has to be decided by camera device.
         initMosaicFrameProcessorIfNeeded();
         mMosaicView.onResume();
+
+        initThumbnailButton();
     }
 
-    private void updateCompassValue() {
-        if (mCaptureState == CAPTURE_STATE_VIEWFINDER) return;
-        // By what angle has the camera moved since start of capture?
-        mTraversedAngleX = (int) (mCompassValueX - mCompassValueXStart);
-        mTraversedAngleY = (int) (mCompassValueY - mCompassValueYStart);
-        mMinAngleX = Math.min(mMinAngleX, mTraversedAngleX);
-        mMaxAngleX = Math.max(mMaxAngleX, mTraversedAngleX);
-        mMinAngleY = Math.min(mMinAngleY, mTraversedAngleY);
-        mMaxAngleY = Math.max(mMaxAngleY, mTraversedAngleY);
-
-        // Use orientation to identify if the user is panning to the right or the left.
-        switch (mDeviceOrientation) {
-            case 0:
-                mPanoProgressBar.setProgress(-mTraversedAngleX);
-                break;
-            case 90:
-                mPanoProgressBar.setProgress(mTraversedAngleY);
-                break;
-            case 180:
-                mPanoProgressBar.setProgress(mTraversedAngleX);
-                break;
-            case 270:
-                mPanoProgressBar.setProgress(-mTraversedAngleY);
-                break;
-        }
-        mPanoProgressBar.invalidate();
-    }
-
-    private final SensorEventListener mListener = new SensorEventListener() {
-        public void onSensorChanged(SensorEvent event) {
-            if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
-                if (mTimestamp != 0) {
-                    final float dT = (event.timestamp - mTimestamp) * NS2S;
-                    mCompassValueX += event.values[1] * dT * 180.0f / Math.PI;
-                    mCompassValueY += event.values[0] * dT * 180.0f / Math.PI;
-                    mCompassValueXStartBuffer = mCompassValueX;
-                    mCompassValueYStartBuffer = mCompassValueY;
-                    updateCompassValue();
-                }
-                mTimestamp = event.timestamp;
-
-            }
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        }
-    };
-
     public MosaicJpeg generateFinalMosaic(boolean highRes) {
         if (mMosaicFrameProcessor.createMosaic(highRes) == Mosaic.MOSAIC_RET_CANCELLED) {
             return null;
diff --git a/src/com/android/camera/ui/AbstractIndicatorButton.java b/src/com/android/camera/ui/AbstractIndicatorButton.java
index a661586..0ff7b19 100644
--- a/src/com/android/camera/ui/AbstractIndicatorButton.java
+++ b/src/com/android/camera/ui/AbstractIndicatorButton.java
@@ -106,10 +106,10 @@
     }
 
     @Override
-    public void setDegree(int degree) {
-        super.setDegree(degree);
+    public void setOrientation(int orientation) {
+        super.setOrientation(orientation);
         if (mPopup != null) {
-            mPopup.setOrientation(degree);
+            mPopup.setOrientation(orientation);
         }
     }
 
diff --git a/src/com/android/camera/ui/EffectSettingPopup.java b/src/com/android/camera/ui/EffectSettingPopup.java
index 606524b..c4a4d49 100644
--- a/src/com/android/camera/ui/EffectSettingPopup.java
+++ b/src/com/android/camera/ui/EffectSettingPopup.java
@@ -36,19 +36,30 @@
 // effects.
 public class EffectSettingPopup extends AbstractSettingPopup implements
         AdapterView.OnItemClickListener, View.OnClickListener {
-    private final String TAG = "EffectSettingPopup";
+    private static final String TAG = "EffectSettingPopup";
+    private String mNoEffect;
     private IconListPreference mPreference;
     private Listener mListener;
     private View mClearEffects;
     private GridView mSillyFacesGrid;
     private GridView mBackgroundGrid;
 
+    // Data for silly face items. (text, image, and preference value)
+    ArrayList<HashMap<String, Object>> mSillyFacesItem =
+            new ArrayList<HashMap<String, Object>>();
+
+    // Data for background replacer items. (text, image, and preference value)
+    ArrayList<HashMap<String, Object>> mBackgroundItem =
+            new ArrayList<HashMap<String, Object>>();
+
+
     static public interface Listener {
         public void onSettingChanged();
     }
 
     public EffectSettingPopup(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mNoEffect = context.getString(R.string.pref_video_effect_default);
     }
 
     @Override
@@ -64,6 +75,7 @@
         mPreference = preference;
         Context context = getContext();
         CharSequence[] entries = mPreference.getEntries();
+        CharSequence[] entryValues = mPreference.getEntryValues();
         int[] iconIds = mPreference.getImageIds();
         if (iconIds == null) {
             iconIds = mPreference.getLargeIconIds();
@@ -72,39 +84,45 @@
         // Set title.
         mTitle.setText(mPreference.getTitle());
 
-        // Prepare goofy face GridView.
-        ArrayList<HashMap<String, Object>> sillyFacesItem =
-                new ArrayList<HashMap<String, Object>>();
-        // The first is clear effect. Skip it.
-        for(int i = 1; i < EffectsRecorder.NUM_OF_GF_EFFECTS + 1; ++i) {
+        for(int i = 0; i < entries.length; ++i) {
+            String value = entryValues[i].toString();
+            if (value.equals(mNoEffect)) continue;  // no effect, skip it.
             HashMap<String, Object> map = new HashMap<String, Object>();
+            map.put("value", value);
             map.put("text", entries[i].toString());
             if (iconIds != null) map.put("image", iconIds[i]);
-            sillyFacesItem.add(map);
+            if (value.startsWith("goofy_face")) {
+                mSillyFacesItem.add(map);
+            } else if (value.startsWith("backdropper")) {
+                mBackgroundItem.add(map);
+            }
         }
-        SimpleAdapter sillyFacesItemAdapter = new SimpleAdapter(context,
-                sillyFacesItem, R.layout.effect_setting_item,
-                new String[] {"text", "image"},
-                new int[] {R.id.text, R.id.image});
-        mSillyFacesGrid.setAdapter(sillyFacesItemAdapter);
-        mSillyFacesGrid.setOnItemClickListener(this);
 
-        // Prepare background replacer GridView.
-        ArrayList<HashMap<String, Object>> backgroundItem =
-                new ArrayList<HashMap<String, Object>>();
-        for(int i = EffectsRecorder.NUM_OF_GF_EFFECTS + 1; i < entries.length; ++i) {
-            HashMap<String, Object> map = new HashMap<String, Object>();
-            map.put("text", entries[i].toString());
-            if (iconIds != null) map.put("image", iconIds[i]);
-            backgroundItem.add(map);
+        boolean hasSillyFaces = mSillyFacesItem.size() > 0;
+        boolean hasBackground = mBackgroundItem.size() > 0;
+
+        // Initialize goofy face if it is supported.
+        if (hasSillyFaces) {
+            findViewById(R.id.effect_silly_faces_title).setVisibility(View.VISIBLE);
+            mSillyFacesGrid.setVisibility(View.VISIBLE);
+            SimpleAdapter sillyFacesItemAdapter = new SimpleAdapter(context,
+                    mSillyFacesItem, R.layout.effect_setting_item,
+                    new String[] {"text", "image"},
+                    new int[] {R.id.text, R.id.image});
+            mSillyFacesGrid.setAdapter(sillyFacesItemAdapter);
+            mSillyFacesGrid.setOnItemClickListener(this);
         }
-        // Initialize background replacer if it is supported.
-        if (backgroundItem.size() > 0) {
+
+        if (hasSillyFaces && hasBackground) {
             findViewById(R.id.effect_background_separator).setVisibility(View.VISIBLE);
+        }
+
+        // Initialize background replacer if it is supported.
+        if (hasBackground) {
             findViewById(R.id.effect_background_title).setVisibility(View.VISIBLE);
             mBackgroundGrid.setVisibility(View.VISIBLE);
             SimpleAdapter backgroundItemAdapter = new SimpleAdapter(context,
-                    backgroundItem, R.layout.effect_setting_item,
+                    mBackgroundItem, R.layout.effect_setting_item,
                     new String[] {"text", "image"},
                     new int[] {R.id.text, R.id.image});
             mBackgroundGrid.setAdapter(backgroundItemAdapter);
@@ -120,8 +138,8 @@
             if (getVisibility() != View.VISIBLE) {
                 // Do not show or hide "Clear effects" button when the popup
                 // is already visible. Otherwise it looks strange.
-                int index = mPreference.findIndexOfValue(mPreference.getValue());
-                mClearEffects.setVisibility((index <= 0) ? View.GONE : View.VISIBLE);
+                boolean noEffect = mPreference.getValue().equals(mNoEffect);
+                mClearEffects.setVisibility(noEffect ? View.GONE : View.VISIBLE);
             }
             reloadPreference();
         }
@@ -131,19 +149,28 @@
     // The value of the preference may have changed. Update the UI.
     @Override
     public void reloadPreference() {
-        int index = mPreference.findIndexOfValue(mPreference.getValue());
-        if (index >= 0) {
-            mBackgroundGrid.setItemChecked(mBackgroundGrid.getCheckedItemPosition(), false);
-            mSillyFacesGrid.setItemChecked(mSillyFacesGrid.getCheckedItemPosition(), false);
-            if (index >= 1 && index < EffectsRecorder.NUM_OF_GF_EFFECTS + 1) {
-                mSillyFacesGrid.setItemChecked(index - 1, true);
-            } else if (index >= EffectsRecorder.NUM_OF_GF_EFFECTS + 1) {
-                mBackgroundGrid.setItemChecked(index - EffectsRecorder.NUM_OF_GF_EFFECTS - 1, true);
+        mBackgroundGrid.setItemChecked(mBackgroundGrid.getCheckedItemPosition(), false);
+        mSillyFacesGrid.setItemChecked(mSillyFacesGrid.getCheckedItemPosition(), false);
+
+        String value = mPreference.getValue();
+        if (value.equals(mNoEffect)) return;
+
+        for (int i = 0; i < mSillyFacesItem.size(); i++) {
+            if (value.equals(mSillyFacesItem.get(i).get("value"))) {
+                mSillyFacesGrid.setItemChecked(i, true);
+                return;
             }
-        } else {
-            Log.e(TAG, "Invalid preference value.");
-            mPreference.print();
         }
+
+        for (int i = 0; i < mBackgroundItem.size(); i++) {
+            if (value.equals(mBackgroundItem.get(i).get("value"))) {
+                mBackgroundGrid.setItemChecked(i, true);
+                return;
+            }
+        }
+
+        Log.e(TAG, "Invalid preference value: " + value);
+        mPreference.print();
     }
 
     public void setSettingChangedListener(Listener listener) {
@@ -154,10 +181,11 @@
     public void onItemClick(AdapterView<?> parent, View view,
             int index, long id) {
         if (parent == mSillyFacesGrid) {
-            // The first one is clear effect.
-            mPreference.setValueIndex(index + 1);
-        } else { // Background replace grid.
-            mPreference.setValueIndex(index + EffectsRecorder.NUM_OF_GF_EFFECTS + 1);
+            String value = (String) mSillyFacesItem.get(index).get("value");
+            mPreference.setValue(value);
+        } else if (parent == mBackgroundGrid) {
+            String value = (String) mBackgroundItem.get(index).get("value");
+            mPreference.setValue(value);
         }
         reloadPreference();
         if (mListener != null) mListener.onSettingChanged();
@@ -166,7 +194,7 @@
     @Override
     public void onClick(View v) {
         // Clear the effect.
-        mPreference.setValueIndex(0);
+        mPreference.setValue(mNoEffect);
         reloadPreference();
         if (mListener != null) mListener.onSettingChanged();
     }
diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java
index 167d8b9..5724d9c 100644
--- a/src/com/android/camera/ui/FaceView.java
+++ b/src/com/android/camera/ui/FaceView.java
@@ -29,7 +29,7 @@
 import android.util.Log;
 import android.view.View;
 
-public class FaceView extends View implements FocusIndicator {
+public class FaceView extends View implements FocusIndicator, Rotatable {
     private final String TAG = "FaceView";
     private final boolean LOGV = false;
     // The value for android.hardware.Camera.setDisplayOrientation.
diff --git a/src/com/android/camera/ui/IndicatorControl.java b/src/com/android/camera/ui/IndicatorControl.java
index 86b0cc9..cac38b8 100644
--- a/src/com/android/camera/ui/IndicatorControl.java
+++ b/src/com/android/camera/ui/IndicatorControl.java
@@ -35,7 +35,7 @@
  * A view that contains camera setting indicators.
  */
 public abstract class IndicatorControl extends RelativeLayout implements
-        IndicatorButton.Listener, OtherSettingsPopup.Listener {
+        IndicatorButton.Listener, OtherSettingsPopup.Listener, Rotatable {
     private static final String TAG = "IndicatorControl";
     public static final int MODE_CAMERA = 0;
     public static final int MODE_VIDEO = 1;
@@ -45,7 +45,7 @@
     protected CameraPicker mCameraPicker;
 
     private PreferenceGroup mPreferenceGroup;
-    private int mDegree = 0;
+    private int mOrientation = 0;
 
     protected int mCurrentMode = MODE_CAMERA;
 
@@ -61,15 +61,13 @@
         super(context, attrs);
     }
 
-    public void setDegree(int degree) {
-        mDegree = degree;
+    public void setOrientation(int orientation) {
+        mOrientation = orientation;
         int count = getChildCount();
         for (int i = 0 ; i < count ; ++i) {
             View view = getChildAt(i);
-            if (view instanceof RotateImageView) {
-                ((RotateImageView) view).setDegree(degree);
-            } else if (view instanceof ZoomControl) {
-                ((ZoomControl) view).setDegree(degree);
+            if (view instanceof Rotatable) {
+                ((Rotatable) view).setOrientation(orientation);
             }
         }
     }
diff --git a/src/com/android/camera/ui/IndicatorControlBarContainer.java b/src/com/android/camera/ui/IndicatorControlBarContainer.java
index 6b60ace..1e860ad 100644
--- a/src/com/android/camera/ui/IndicatorControlBarContainer.java
+++ b/src/com/android/camera/ui/IndicatorControlBarContainer.java
@@ -75,9 +75,9 @@
                 secondLevelKeys, secondLevelOtherSettingKeys);
     }
 
-    public void setDegree(int degree) {
-        mIndicatorControlBar.setDegree(degree);
-        mSecondLevelIndicatorControlBar.setDegree(degree);
+    public void setOrientation(int orientation) {
+        mIndicatorControlBar.setOrientation(orientation);
+        mSecondLevelIndicatorControlBar.setOrientation(orientation);
     }
 
     @Override
diff --git a/src/com/android/camera/ui/IndicatorControlWheelContainer.java b/src/com/android/camera/ui/IndicatorControlWheelContainer.java
index 14539da..29074c2 100644
--- a/src/com/android/camera/ui/IndicatorControlWheelContainer.java
+++ b/src/com/android/camera/ui/IndicatorControlWheelContainer.java
@@ -191,9 +191,9 @@
     }
 
     @Override
-    public void setDegree(int degree) {
-        mIndicatorControlWheel.setDegree(degree);
-        mZoomControlWheel.setDegree(degree);
+    public void setOrientation(int orientation) {
+        mIndicatorControlWheel.setOrientation(orientation);
+        mZoomControlWheel.setOrientation(orientation);
     }
 
     public void startTimeLapseAnimation(int timeLapseInterval, long startTime) {
diff --git a/src/com/android/camera/ui/RotateImageView.java b/src/com/android/camera/ui/RotateImageView.java
index 390d705..f47a26b 100644
--- a/src/com/android/camera/ui/RotateImageView.java
+++ b/src/com/android/camera/ui/RotateImageView.java
@@ -16,8 +16,6 @@
 
 package com.android.camera.ui;
 
-import com.android.camera.ui.Rotatable;
-
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -66,12 +64,8 @@
         return mTargetDegree;
     }
 
-    public void setOrientation(int orientation) {
-        setDegree(orientation);
-    }
-
     // Rotate the view counter-clockwise
-    public void setDegree(int degree) {
+    public void setOrientation(int degree) {
         // make sure in the range of [0, 359]
         degree = degree >= 0 ? degree % 360 : degree % 360 + 360;
         if (degree == mTargetDegree) return;
diff --git a/src/com/android/camera/ui/RotateLayout.java b/src/com/android/camera/ui/RotateLayout.java
index 24815f8..e66e71a 100644
--- a/src/com/android/camera/ui/RotateLayout.java
+++ b/src/com/android/camera/ui/RotateLayout.java
@@ -16,8 +16,6 @@
 
 package com.android.camera.ui;
 
-import com.android.camera.ui.Rotatable;
-
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
diff --git a/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java b/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
index c9037d9..45bbca8 100644
--- a/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
+++ b/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
@@ -39,7 +39,7 @@
     private View mDivider; // the divider line
     private View mIndicatorHighlight; // the side highlight bar
     private View mPopupedIndicator;
-    int mDegree = 0;
+    int mOrientation = 0;
     int mSelectedIndex = -1;
     // There are some views in the ViewGroup before adding the indicator buttons,
     // such as Close icon, divider line and the hightlight bar, we need to
@@ -64,7 +64,7 @@
         setPreferenceGroup(group);
         mNonIndicatorButtonCount = getChildCount();
         addControls(keys, otherSettingKeys);
-        if (mDegree != 0) setDegree(mDegree);
+        if (mOrientation != 0) setOrientation(mOrientation);
     }
 
     public void onClick(View view) {
@@ -151,9 +151,9 @@
     }
 
     @Override
-    public void setDegree(int degree) {
-        mDegree = degree;
-        super.setDegree(degree);
+    public void setOrientation(int orientation) {
+        mOrientation = orientation;
+        super.setOrientation(orientation);
     }
 
     @Override
diff --git a/src/com/android/camera/ui/SharePopup.java b/src/com/android/camera/ui/SharePopup.java
index 84bada4..92abb8a 100644
--- a/src/com/android/camera/ui/SharePopup.java
+++ b/src/com/android/camera/ui/SharePopup.java
@@ -50,7 +50,7 @@
 
 // A popup window that contains a big thumbnail and a list of apps to share.
 public class SharePopup extends PopupWindow implements View.OnClickListener,
-        View.OnTouchListener, AdapterView.OnItemClickListener {
+        View.OnTouchListener, AdapterView.OnItemClickListener, Rotatable {
     private static final String TAG = "SharePopup";
     private static final String ADAPTER_COLUMN_ICON = "icon";
     private Context mContext;
diff --git a/src/com/android/camera/ui/ZoomControl.java b/src/com/android/camera/ui/ZoomControl.java
index 1809d18..f2971cd 100644
--- a/src/com/android/camera/ui/ZoomControl.java
+++ b/src/com/android/camera/ui/ZoomControl.java
@@ -29,7 +29,7 @@
  * A view that contains camera zoom control which could adjust the zoom in/out
  * if the camera supports zooming.
  */
-public abstract class ZoomControl extends RelativeLayout {
+public abstract class ZoomControl extends RelativeLayout implements Rotatable {
     // The states of zoom button.
     public static final int ZOOM_IN = 0;
     public static final int ZOOM_OUT = 1;
@@ -42,7 +42,7 @@
     protected ImageView mZoomOut;
     protected ImageView mZoomSlider;
     protected int mSliderPosition = 0;
-    protected int mDegree;
+    protected int mOrientation;
     private Handler mHandler;
 
     public interface OnZoomChangedListener {
@@ -208,13 +208,13 @@
         return true;
     }
 
-    public void setDegree(int degree) {
-        mDegree = degree;
+    public void setOrientation(int orientation) {
+        mOrientation = orientation;
         int count = getChildCount();
         for (int i = 0 ; i < count ; ++i) {
             View view = getChildAt(i);
             if (view instanceof RotateImageView) {
-                ((RotateImageView) view).setDegree(degree);
+                ((RotateImageView) view).setOrientation(orientation);
             }
         }
     }
diff --git a/src/com/android/camera/ui/ZoomControlBar.java b/src/com/android/camera/ui/ZoomControlBar.java
index 08042d4..2e14e53 100644
--- a/src/com/android/camera/ui/ZoomControlBar.java
+++ b/src/com/android/camera/ui/ZoomControlBar.java
@@ -59,7 +59,7 @@
         // landscape mode, the zoom-in bottom should be on the top, so the
         // position should be reversed.
         int pos; // the relative position in the zoom slider bar
-        if (mDegree == 180) {
+        if (mOrientation == 180) {
             pos = y - mTotalIconHeight;
         } else {
             pos = mHeight - mTotalIconHeight - y;
@@ -114,10 +114,10 @@
     }
 
     @Override
-    public void setDegree(int degree) {
+    public void setOrientation(int orientation) {
         // layout for the left-hand camera control
-        if ((degree == 180) || (mDegree == 180)) requestLayout();
-        super.setDegree(degree);
+        if ((orientation == 180) || (mOrientation == 180)) requestLayout();
+        super.setOrientation(orientation);
     }
 
     @Override
@@ -135,7 +135,7 @@
         } else {
             sliderPosition = (int) ((double) mSliderLength * mZoomIndex / mZoomMax);
         }
-        if (mDegree == 180) {
+        if (mOrientation == 180) {
             mZoomOut.layout(0, 0, width, mIconHeight);
             mZoomIn.layout(0, mHeight - mIconHeight, width, mHeight);
             pos = mBar.getTop() + sliderPosition;