diff options
author | Angus Kong <shkong@google.com> | 2011-08-08 17:24:47 +0800 |
---|---|---|
committer | Angus Kong <shkong@google.com> | 2011-08-11 16:27:48 +0800 |
commit | 142402d57c1689c1342d096c976b9b0826f8ce1a (patch) | |
tree | 650ac2d29ec8c84347ced31e6e6c817e760097c4 | |
parent | a18605333f037e5c8ff95584bc932e539ece249e (diff) | |
download | LegacyCamera-142402d57c1689c1342d096c976b9b0826f8ce1a.zip LegacyCamera-142402d57c1689c1342d096c976b9b0826f8ce1a.tar.gz LegacyCamera-142402d57c1689c1342d096c976b9b0826f8ce1a.tar.bz2 |
"Retake" and "Ok" buttons can function correctly.
1. "Retake" button brings user back to preview stage without saving the final result.
2. "Ok" button brings user back to preview stage after saving the final result.
bug:5031609
bug:5142100
bug:5134202
bug:5133871
Change-Id: I234e242182765cc5624b2fc8444bc38e5a9edf9f
-rw-r--r-- | proguard.flags | 4 | ||||
-rw-r--r-- | res/layout/pano_capture.xml | 1 | ||||
-rw-r--r-- | res/layout/pano_control.xml | 1 | ||||
-rw-r--r-- | res/layout/pano_review.xml | 2 | ||||
-rw-r--r-- | src/com/android/camera/panorama/MosaicFrameProcessor.java | 37 | ||||
-rw-r--r-- | src/com/android/camera/panorama/OnClickAttr.java | 31 | ||||
-rw-r--r-- | src/com/android/camera/panorama/PanoramaActivity.java | 155 |
7 files changed, 176 insertions, 55 deletions
diff --git a/proguard.flags b/proguard.flags index 5dcd2b9..a3dc864 100644 --- a/proguard.flags +++ b/proguard.flags @@ -11,3 +11,7 @@ -keep class com.android.camera.VideoCamera { public boolean isRecording(); } + +-keep class * extends android.app.Activity { + @com.android.camera.panorama.OnClickAttr <methods>; +} diff --git a/res/layout/pano_capture.xml b/res/layout/pano_capture.xml index a12e6e6..0ce34b9 100644 --- a/res/layout/pano_capture.xml +++ b/res/layout/pano_capture.xml @@ -41,6 +41,7 @@ <Button android:id="@+id/pano_capture_stop_button" android:text="@string/pano_capture_stop" + android:onClick="onStopButtonClicked" android:textSize="24dp" android:layout_width="180dp" android:layout_height="180dp" /> diff --git a/res/layout/pano_control.xml b/res/layout/pano_control.xml index e3ab1d4..4a1175a 100644 --- a/res/layout/pano_control.xml +++ b/res/layout/pano_control.xml @@ -29,6 +29,7 @@ android:layout_centerInParent="true" android:clickable="true" android:focusable="true" + android:onClick="onShutterButtonClicked" android:background="@drawable/btn_shutter"/> <include layout="@layout/mode_picker"/> diff --git a/res/layout/pano_review.xml b/res/layout/pano_review.xml index b778daa..40dee1e 100644 --- a/res/layout/pano_review.xml +++ b/res/layout/pano_review.xml @@ -39,11 +39,13 @@ <Button android:id="@+id/pano_review_retake_button" android:text="@string/review_retake" + android:onClick="onRetakeButtonClicked" android:textSize="24dp" android:layout_width="180dp" android:layout_height="180dp" /> <Button android:id="@+id/pano_review_ok_button" android:text="@string/review_ok" + android:onClick="onOkButtonClicked" android:textSize="24dp" android:layout_width="180dp" android:layout_height="180dp" /> diff --git a/src/com/android/camera/panorama/MosaicFrameProcessor.java b/src/com/android/camera/panorama/MosaicFrameProcessor.java index c5eeac5..6496f11 100644 --- a/src/com/android/camera/panorama/MosaicFrameProcessor.java +++ b/src/com/android/camera/panorama/MosaicFrameProcessor.java @@ -18,6 +18,9 @@ package com.android.camera.panorama; import android.util.Log; +/** + * Class to handle the processing of each frame by Mosaicer. + */ public class MosaicFrameProcessor { private static final boolean LOGV = true; private static final String TAG = "MosaicFrameProcessor"; @@ -54,7 +57,6 @@ public class MosaicFrameProcessor { private int mPreviewHeight; private int mPreviewBufferSize; - public interface ProgressListener { public void onProgress(boolean isFinished, float translationRate, int traversedAngleX, int traversedAngleY); @@ -72,14 +74,20 @@ public class MosaicFrameProcessor { mProgressListener = listener; } - public void onResume() { + public void initialize() { setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); + reset(); } - public void onPause() { - releaseMosaicer(); + public void clear() { + mMosaicer.freeMosaicMemory(); + + for (int i = 0; i < NUM_FRAMES_IN_BUFFER; i++) { + mFrames[i] = null; + } } + private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) { Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize); mMosaicer.allocateMosaicMemory(previewWidth, previewHeight); @@ -94,12 +102,15 @@ public class MosaicFrameProcessor { } } - private void releaseMosaicer() { - mMosaicer.freeMosaicMemory(); - - for (int i = 0; i < NUM_FRAMES_IN_BUFFER; i++) { - mFrames[i] = null; - } + public void reset() { + // reset() can be called even if MosaicFrameProcessor is not initialized. + // Only counters will be changed. + mTotalFrameCount = 0; + mFillIn = 0; + mLastProcessedFrameTimestamp = 0; + mLastProcessFrameIdx = -1; + mCurrProcessFrameIdx = -1; + mMosaicer.reset(); } public void createMosaic(boolean highRes) { @@ -114,6 +125,12 @@ public class MosaicFrameProcessor { // updates the UI to show progress. // When done, processes and displays the final mosaic. public void processFrame(byte[] data) { + if (mFrames[mFillIn] == null) { + // clear() is called and buffers are cleared, stop computation. + // This can happen when the onPause() is called in the activity, but still some frames + // are not processed yet and thus the callback may be invoked. + return; + } long t1 = System.currentTimeMillis(); mFrameTimestamp[mFillIn] = t1; System.arraycopy(data, 0, mFrames[mFillIn], 0, data.length); diff --git a/src/com/android/camera/panorama/OnClickAttr.java b/src/com/android/camera/panorama/OnClickAttr.java new file mode 100644 index 0000000..bfc7c4e --- /dev/null +++ b/src/com/android/camera/panorama/OnClickAttr.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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.panorama; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Interface for OnClickAttr annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface OnClickAttr { +} diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java index 11fb6d1..0b1ec60 100644 --- a/src/com/android/camera/panorama/PanoramaActivity.java +++ b/src/com/android/camera/panorama/PanoramaActivity.java @@ -28,6 +28,8 @@ import com.android.camera.Util; import android.app.Activity; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -39,7 +41,6 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -48,7 +49,6 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; -import android.widget.Button; import android.widget.ImageView; import java.io.ByteArrayOutputStream; @@ -64,13 +64,16 @@ public class PanoramaActivity extends Activity implements public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720; private static final int MSG_FINAL_MOSAIC_READY = 1; + private static final int MSG_RESET_TO_PREVIEW = 2; private static final String TAG = "PanoramaActivity"; private static final int PREVIEW_STOPPED = 0; private static final int PREVIEW_ACTIVE = 1; - // Ratio of nanosecond to second private static final float NS2S = 1.0f / 1000000000.0f; + + private boolean mPausing; + private View mPanoControlLayout; private View mCaptureLayout; private View mReviewLayout; @@ -78,9 +81,10 @@ public class PanoramaActivity extends Activity implements private ImageView mReview; private CaptureView mCaptureView; private MosaicRendererSurfaceView mRealTimeMosaicView; - private ShutterButton mShutterButton; - private Button mStopButton; + + private byte[] mFinalJpegData; + private int mPreviewWidth; private int mPreviewHeight; private Camera mCameraDevice; @@ -94,6 +98,8 @@ public class PanoramaActivity extends Activity implements private Handler mMainHandler; private SurfaceHolder mSurfaceHolder; + private boolean mThreadRunning; + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -114,9 +120,15 @@ public class PanoramaActivity extends Activity implements public void handleMessage(Message msg) { switch (msg.what) { case MSG_FINAL_MOSAIC_READY: - Uri uri = (Uri) msg.obj; - showFinalMosaic(uri); + mThreadRunning = false; + showFinalMosaic((Bitmap) msg.obj); + break; + case MSG_RESET_TO_PREVIEW: + mThreadRunning = false; + resetToPreview(); + break; } + clearMosaicFrameProcessorIfNeeded(); } }; } @@ -130,6 +142,7 @@ public class PanoramaActivity extends Activity implements private void releaseCamera() { if (mCameraDevice != null) { + mCameraDevice.setPreviewCallbackWithBuffer(null); CameraHolder.instance().release(); mCameraDevice = null; mCameraState = PREVIEW_STOPPED; @@ -267,23 +280,25 @@ public class PanoramaActivity extends Activity implements }); mCaptureLayout.setVisibility(View.VISIBLE); - mPreview.setVisibility(View.GONE); + mPreview.setVisibility(View.INVISIBLE); // will be re-used, invisible is better than gone. mRealTimeMosaicView.setVisibility(View.VISIBLE); mPanoControlLayout.setVisibility(View.GONE); } private void stopCapture() { mMosaicFrameProcessor.setProgressListener(null); - mCameraDevice.stopPreview(); - mCameraDevice.setPreviewCallbackWithBuffer(null); + stopPreview(); // TODO: show some dialog for long computation. - Thread t = new Thread() { - @Override - public void run() { - generateAndStoreFinalMosaic(false); - } - }; - t.start(); + if (!mThreadRunning) { + mThreadRunning = true; + Thread t = new Thread() { + @Override + public void run() { + generateAndStoreFinalMosaic(false); + } + }; + t.start(); + } } private void updateProgress(float translationRate, int traversedAngleX, int traversedAngleY) { @@ -323,16 +338,10 @@ public class PanoramaActivity extends Activity implements mShutterButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + if (mPausing || mThreadRunning) return; startCapture(); } }); - mStopButton = (Button) findViewById(R.id.pano_capture_stop_button); - mStopButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - stopCapture(); - } - }); mPanoControlLayout = (View) findViewById(R.id.pano_control_layout); @@ -342,23 +351,85 @@ public class PanoramaActivity extends Activity implements mModePicker.setCurrentMode(ModePicker.MODE_PANORAMA); } - private void showFinalMosaic(Uri uri) { - mReview.setImageURI(uri); - mCaptureLayout.setVisibility(View.INVISIBLE); - mPreview.setVisibility(View.INVISIBLE); - mReviewLayout.setVisibility(View.VISIBLE); - mCaptureView.setStatusText(""); - mCaptureView.setSweepAngle(0); + @OnClickAttr + public void onStopButtonClicked(View v) { + if (mPausing || mThreadRunning) return; + stopCapture(); + } + + @OnClickAttr + public void onOkButtonClicked(View v) { + if (mPausing || mThreadRunning) return; + mThreadRunning = true; + Thread t = new Thread() { + @Override + public void run() { + saveFinalMosaic(); + } + }; + t.start(); + } + + @OnClickAttr + public void onRetakeButtonClicked(View v) { + if (mPausing || mThreadRunning) return; + resetToPreview(); + } + + private void resetToPreview() { + mPreview.setVisibility(View.VISIBLE); + mPanoControlLayout.setVisibility(View.VISIBLE); + mRealTimeMosaicView.setVisibility(View.GONE); + mCaptureLayout.setVisibility(View.GONE); + mReviewLayout.setVisibility(View.GONE); + mMosaicFrameProcessor.reset(); + if (!mPausing) startPreview(); + } + + private void showFinalMosaic(Bitmap bitmap) { + if (bitmap != null) { + mReview.setImageBitmap(bitmap); + mCaptureLayout.setVisibility(View.GONE); + mPreview.setVisibility(View.INVISIBLE); + mReviewLayout.setVisibility(View.VISIBLE); + mCaptureView.setStatusText(""); + mCaptureView.setSweepAngle(0); + } + } + + private void saveFinalMosaic() { + if (mFinalJpegData != null) { + Storage.addImage(getContentResolver(), mCurrentImagePath, mTimeTaken, null, 0, + mFinalJpegData); + mFinalJpegData = null; + } + mMainHandler.sendMessage(mMainHandler.obtainMessage(MSG_RESET_TO_PREVIEW)); + } + + private void clearMosaicFrameProcessorIfNeeded() { + if (!mPausing || mThreadRunning) return; + mMosaicFrameProcessor.clear(); + } + + private void initMosaicFrameProcessorIfNeeded() { + if (mPausing || mThreadRunning) return; + if (mMosaicFrameProcessor == null) { + // Start the activity for the first time. + mMosaicFrameProcessor = new MosaicFrameProcessor(DEFAULT_SWEEP_ANGLE - 5, + mPreviewWidth, mPreviewHeight, getPreviewBufSize()); + } + mMosaicFrameProcessor.initialize(); } @Override protected void onPause() { super.onPause(); releaseCamera(); - mMosaicFrameProcessor.onPause(); + mPausing = true; mCaptureView.onPause(); mRealTimeMosaicView.onPause(); mSensorManager.unregisterListener(mListener); + clearMosaicFrameProcessorIfNeeded(); System.gc(); } @@ -366,6 +437,7 @@ public class PanoramaActivity extends Activity implements protected void onResume() { super.onResume(); + mPausing = false; /* * It is not necessary to get accelerometer events at a very high rate, * by using a slower rate (SENSOR_DELAY_UI), we get an automatic @@ -376,16 +448,10 @@ public class PanoramaActivity extends Activity implements mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI); setupCamera(); + // Camera must be initialized before MosaicFrameProcessor is initialized. The preview size + // has to be decided by camera device. + initMosaicFrameProcessorIfNeeded(); startPreview(); - - if (mMosaicFrameProcessor == null) { - // Start the activity for the first time. - mMosaicFrameProcessor = new MosaicFrameProcessor(DEFAULT_SWEEP_ANGLE - 5, - mPreviewWidth, mPreviewHeight, getPreviewBufSize()); - mMosaicFrameProcessor.onResume(); - } else { - mMosaicFrameProcessor.onResume(); - } mCaptureView.onResume(); mRealTimeMosaicView.onResume(); } @@ -449,11 +515,10 @@ public class PanoramaActivity extends Activity implements Log.e(TAG, "Exception in storing final mosaic", e); return; } - Uri uri = Storage.addImage( - getContentResolver(), mCurrentImagePath, mTimeTaken, null, 0, - out.toByteArray()); - mMainHandler.sendMessage(mMainHandler.obtainMessage(MSG_FINAL_MOSAIC_READY, uri)); + mFinalJpegData = out.toByteArray(); + Bitmap bitmap = BitmapFactory.decodeByteArray(mFinalJpegData, 0, mFinalJpegData.length); + mMainHandler.sendMessage(mMainHandler.obtainMessage(MSG_FINAL_MOSAIC_READY, bitmap)); // Now's a good time to run the GC. Since we won't do any explicit // allocation during the test, the GC should stay dormant and not // influence our results. |