diff options
-rw-r--r-- | res/layout/effect_setting_popup.xml | 3 | ||||
-rw-r--r-- | src/com/android/camera/Camera.java | 32 | ||||
-rw-r--r-- | src/com/android/camera/panorama/MosaicFrameProcessor.java | 29 | ||||
-rwxr-xr-x | src/com/android/camera/panorama/PanoramaActivity.java | 71 | ||||
-rw-r--r-- | src/com/android/camera/ui/EffectSettingPopup.java | 114 |
5 files changed, 128 insertions, 121 deletions
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 8cc5816..685319f 100644 --- a/src/com/android/camera/Camera.java +++ b/src/com/android/camera/Camera.java @@ -122,6 +122,9 @@ public class Camera extends ActivityBase implements FocusManager.Listener, 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 @@ public class Camera extends ActivityBase implements FocusManager.Listener, 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 @@ public class Camera extends ActivityBase implements FocusManager.Listener, } if (mModePicker != null) mModePicker.setEnabled(enable); if (mZoomControl != null) mZoomControl.setEnabled(enable); + if (mThumbnailView != null) mThumbnailView.setEnabled(enable); } public static int roundOrientation(int orientation) { @@ -1357,7 +1362,19 @@ public class Camera extends ActivityBase implements FocusManager.Listener, // 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 +1518,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener, unregisterReceiver(mReceiver); mDidRegister = false; } - mLocationManager.recordLocation(false); + if (mLocationManager != null) mLocationManager.recordLocation(false); updateExposureOnScreenIndicator(0); mFocusManager.releaseSoundPlayer(); @@ -1743,6 +1760,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener, 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 +1834,14 @@ public class Camera extends ActivityBase implements FocusManager.Listener, } private void updateCameraParametersPreference() { + if (mAeLockSupported) { + mParameters.setAutoExposureLock(mAeAwbLock); + } + + if (mAwbLockSupported) { + mParameters.setAutoWhiteBalanceLock(mAeAwbLock); + } + if (mFocusAreaSupported) { mParameters.setFocusAreas(mFocusManager.getFocusAreas()); } @@ -2078,7 +2104,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener, 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(); @@ -2213,5 +2239,7 @@ public class Camera extends ActivityBase implements FocusManager.Listener, && 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 @@ public class MosaicFrameProcessor { 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 @@ public class MosaicFrameProcessor { // 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 @@ public class MosaicFrameProcessor { 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..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; 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; @@ -543,6 +541,7 @@ public class PanoramaActivity extends ActivityBase implements 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 +553,14 @@ public class PanoramaActivity extends ActivityBase implements 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 +580,7 @@ public class PanoramaActivity extends ActivityBase implements mCaptureIndicator.setVisibility(View.GONE); hideTooFastIndication(); hideDirectionIndicators(); + mThumbnailView.setEnabled(true); mMosaicFrameProcessor.setProgressListener(null); stopCameraPreview(); @@ -625,7 +626,7 @@ public class PanoramaActivity extends ActivityBase implements mRightIndicator.setEnabled(false); } - private void updateProgress(float panningRate) { + private void updateProgress(float panningRate, float progressX, float progressY) { mMosaicView.setReady(); mMosaicView.requestRender(); @@ -637,6 +638,7 @@ public class PanoramaActivity extends ActivityBase implements } else { hideTooFastIndication(); } + mPanoProgressBar.setProgress((int) (progressX * mHorizontalViewAngle)); } private void createContentView() { @@ -679,6 +681,7 @@ public class PanoramaActivity extends ActivityBase implements 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); @@ -909,7 +912,6 @@ public class PanoramaActivity extends ActivityBase implements releaseCamera(); mMosaicView.onPause(); clearMosaicFrameProcessorIfNeeded(); - mSensorManager.unregisterListener(mListener); mOrientationEventListener.disable(); System.gc(); } @@ -920,14 +922,6 @@ public class PanoramaActivity extends ActivityBase implements 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(); @@ -938,55 +932,6 @@ public class PanoramaActivity extends ActivityBase implements 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 @@ import java.util.HashMap; // 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 @@ public class EffectSettingPopup extends AbstractSettingPopup implements 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 @@ public class EffectSettingPopup extends AbstractSettingPopup implements // 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 @@ public class EffectSettingPopup extends AbstractSettingPopup implements 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 @@ public class EffectSettingPopup extends AbstractSettingPopup implements // 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 class EffectSettingPopup extends AbstractSettingPopup implements 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 @@ public class EffectSettingPopup extends AbstractSettingPopup implements @Override public void onClick(View v) { // Clear the effect. - mPreference.setValueIndex(0); + mPreference.setValue(mNoEffect); reloadPreference(); if (mListener != null) mListener.onSettingChanged(); } |