summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAngus Kong <shkong@google.com>2011-08-08 17:24:47 +0800
committerAngus Kong <shkong@google.com>2011-08-11 16:27:48 +0800
commit142402d57c1689c1342d096c976b9b0826f8ce1a (patch)
tree650ac2d29ec8c84347ced31e6e6c817e760097c4
parenta18605333f037e5c8ff95584bc932e539ece249e (diff)
downloadpackages_apps_LegacyCamera-142402d57c1689c1342d096c976b9b0826f8ce1a.zip
packages_apps_LegacyCamera-142402d57c1689c1342d096c976b9b0826f8ce1a.tar.gz
packages_apps_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.flags4
-rw-r--r--res/layout/pano_capture.xml1
-rw-r--r--res/layout/pano_control.xml1
-rw-r--r--res/layout/pano_review.xml2
-rw-r--r--src/com/android/camera/panorama/MosaicFrameProcessor.java37
-rw-r--r--src/com/android/camera/panorama/OnClickAttr.java31
-rw-r--r--src/com/android/camera/panorama/PanoramaActivity.java155
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.