Merge "Disable Thumbnailview when Panorama capture is inProgress." into ics-mr0
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 f4ab623..685319f 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.
@@ -1359,7 +1362,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
@@ -1503,7 +1518,7 @@
             unregisterReceiver(mReceiver);
             mDidRegister = false;
         }
-        mLocationManager.recordLocation(false);
+        if (mLocationManager != null) mLocationManager.recordLocation(false);
         updateExposureOnScreenIndicator(0);
 
         mFocusManager.releaseSoundPlayer();
@@ -1745,6 +1760,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.
@@ -1818,6 +1834,14 @@
     }
 
     private void updateCameraParametersPreference() {
+        if (mAeLockSupported) {
+            mParameters.setAutoExposureLock(mAeAwbLock);
+        }
+
+        if (mAwbLockSupported) {
+            mParameters.setAutoWhiteBalanceLock(mAeAwbLock);
+        }
+
         if (mFocusAreaSupported) {
             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
         }
@@ -2080,7 +2104,7 @@
 
     private boolean switchToOtherMode(int mode) {
         if (isFinishing()) return false;
-        mImageSaver.waitDone();
+        if (mImageSaver != null) mImageSaver.waitDone();
         MenuHelper.gotoMode(mode, Camera.this);
         mHandler.removeMessages(FIRST_TIME_INIT);
         finish();
@@ -2215,5 +2239,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/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 06ed3ea..25c565f 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;
@@ -555,13 +553,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);
                 }
             }
         });
@@ -627,7 +626,7 @@
         mRightIndicator.setEnabled(false);
     }
 
-    private void updateProgress(float panningRate) {
+    private void updateProgress(float panningRate, float progressX, float progressY) {
         mMosaicView.setReady();
         mMosaicView.requestRender();
 
@@ -639,6 +638,7 @@
         } else {
             hideTooFastIndication();
         }
+        mPanoProgressBar.setProgress((int) (progressX * mHorizontalViewAngle));
     }
 
     private void createContentView() {
@@ -912,7 +912,6 @@
         releaseCamera();
         mMosaicView.onPause();
         clearMosaicFrameProcessorIfNeeded();
-        mSensorManager.unregisterListener(mListener);
         mOrientationEventListener.disable();
         System.gc();
     }
@@ -923,14 +922,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();
@@ -941,55 +932,6 @@
         mMosaicView.onResume();
     }
 
-    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/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();
     }