TestingCamera: Autofocus, recording, logging, code cleanup
- Add logging into onscreen textview
- Clean up code (whitespace, refactor some long methods)
- Add AF mode selection, AF trigger, AF cancel
- Add recording support
Bug: 6243944
Change-Id: I799a99e24009677b558875a2d980bb980dd8d004
diff --git a/apps/TestingCamera/AndroidManifest.xml b/apps/TestingCamera/AndroidManifest.xml
index bb48524..cfed74b 100644
--- a/apps/TestingCamera/AndroidManifest.xml
+++ b/apps/TestingCamera/AndroidManifest.xml
@@ -22,8 +22,11 @@
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
- <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="16"/>
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:icon="@mipmap/launcher_testingcamera"
android:label="@string/app_name"
diff --git a/apps/TestingCamera/project.properties b/apps/TestingCamera/project.properties
index e484b3c..9b84a6b 100644
--- a/apps/TestingCamera/project.properties
+++ b/apps/TestingCamera/project.properties
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=Google Inc.:Google APIs:16
+target=android-16
diff --git a/apps/TestingCamera/res/layout/main.xml b/apps/TestingCamera/res/layout/main.xml
index 06f3628..4478379 100644
--- a/apps/TestingCamera/res/layout/main.xml
+++ b/apps/TestingCamera/res/layout/main.xml
@@ -32,12 +32,14 @@
android:layout_height="0px"
android:layout_weight="6"
/>
+
<TextView
- android:id="@+id/log"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:layout_weight="1"
- />
+ android:id="@+id/log"
+ android:layout_width="fill_parent"
+ android:layout_height="0px"
+ android:layout_weight="1.5"
+ android:freezesText="true" />
+
</LinearLayout>
<ScrollView
@@ -74,8 +76,10 @@
<Button
android:id="@+id/info_button"
+ style="?android:attr/buttonStyleSmall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:layout_weight="1"
android:text="@string/show_info" />
@@ -106,6 +110,7 @@
android:id="@+id/start_preview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:layout_weight="1"
android:textColorLink="@android:color/holo_blue_light"
android:textOff="@string/preview_off_label"
@@ -121,6 +126,47 @@
android:background="@color/horiz_rule_color" />
<TextView
+ android:id="@+id/af_mode_spinner_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/af_mode_prompt"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <Spinner
+ android:id="@+id/af_mode_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/af_button"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:text="@string/trigger_autofocus" />
+
+ <Button
+ android:id="@+id/af_cancel_button"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:text="@string/cancel_autofocus" />
+
+ <View
+ android:id="@+id/horizontal_rule_3"
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_marginBottom="@dimen/horiz_rule_btm_margin"
+ android:layout_marginTop="@dimen/horiz_rule_top_margin"
+ android:layout_weight="1"
+ android:background="@color/horiz_rule_color" />
+
+ <TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -136,13 +182,14 @@
<Button
android:id="@+id/take_picture"
+ style="?android:attr/buttonStyleSmall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/take_picture_label" />
<View
- android:id="@+id/horizontal_rule_3"
+ android:id="@+id/horizontal_rule_4"
android:layout_width="fill_parent"
android:layout_height="1dip"
android:layout_marginBottom="@dimen/horiz_rule_btm_margin"
diff --git a/apps/TestingCamera/res/values/strings.xml b/apps/TestingCamera/res/values/strings.xml
index 69c57da..5ce8262 100644
--- a/apps/TestingCamera/res/values/strings.xml
+++ b/apps/TestingCamera/res/values/strings.xml
@@ -22,7 +22,7 @@
<string name="preview_off_label">Preview Off</string>
<string name="take_picture_label">Take picture</string>
<string name="preview_resolution_prompt">Preview size</string>
- <string name="camera_selection_prompt">Camera ID</string>
+ <string name="camera_selection_prompt">Active camera</string>
<string name="default_camera_entry">No cameras found</string>
<string name="snapshot_text_default">Snapshot text</string>
<string name="snapshot_size_spinner_label">Still capture size</string>
@@ -34,4 +34,7 @@
<string name="info_ok_button_label">OK</string>
<string name="snapshot_ok_label">OK</string>
<string name="still_image_description">Captured still image</string>
+ <string name="af_mode_prompt">Autofocus mode</string>
+ <string name="trigger_autofocus">Trigger autofocus</string>
+ <string name="cancel_autofocus">Cancel Autofocus</string>
</resources>
diff --git a/apps/TestingCamera/src/com/android/testingcamera/InfoDialogFragment.java b/apps/TestingCamera/src/com/android/testingcamera/InfoDialogFragment.java
index 9499778..da5496a 100644
--- a/apps/TestingCamera/src/com/android/testingcamera/InfoDialogFragment.java
+++ b/apps/TestingCamera/src/com/android/testingcamera/InfoDialogFragment.java
@@ -37,7 +37,6 @@
return view;
}
- @Override
public void onClick(View v) {
this.dismiss();
}
diff --git a/apps/TestingCamera/src/com/android/testingcamera/SnapshotDialogFragment.java b/apps/TestingCamera/src/com/android/testingcamera/SnapshotDialogFragment.java
index 28ba3aa..9b13b95 100644
--- a/apps/TestingCamera/src/com/android/testingcamera/SnapshotDialogFragment.java
+++ b/apps/TestingCamera/src/com/android/testingcamera/SnapshotDialogFragment.java
@@ -41,9 +41,7 @@
return view;
}
- @Override
public void onClick(View v) {
- // TODO Auto-generated method stub
this.dismiss();
}
diff --git a/apps/TestingCamera/src/com/android/testingcamera/TestingCamera.java b/apps/TestingCamera/src/com/android/testingcamera/TestingCamera.java
index 5cf7a5e..d3403c7 100644
--- a/apps/TestingCamera/src/com/android/testingcamera/TestingCamera.java
+++ b/apps/TestingCamera/src/com/android/testingcamera/TestingCamera.java
@@ -22,13 +22,17 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
+import android.hardware.Camera.Parameters;
import android.media.CamcorderProfile;
+import android.media.MediaRecorder;
import android.os.Bundle;
+import android.os.Environment;
import android.view.View;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
@@ -37,19 +41,30 @@
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;
+import android.text.Layout;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.TreeSet;
/**
* A simple test application for the camera API.
*
* The goal of this application is to allow all camera API features to be
- * excercised, and all information provided by the API to be shown.
+ * exercised, and all information provided by the API to be shown.
*/
public class TestingCamera extends Activity implements SurfaceHolder.Callback {
@@ -61,33 +76,46 @@
private Button mInfoButton;
private Spinner mPreviewSizeSpinner;
private ToggleButton mPreviewToggle;
+ private Spinner mAutofocusModeSpinner;
+ private Button mAutofocusButton;
+ private Button mCancelAutofocusButton;
private Spinner mSnapshotSizeSpinner;
private Button mTakePictureButton;
private Spinner mCamcorderProfileSpinner;
private ToggleButton mRecordToggle;
+ private TextView mLogView;
+
+ private Set<View> mPreviewOnlyControls = new HashSet<View>();
+
/** Camera state */
private int mCameraId = 0;
private Camera mCamera;
private Camera.Parameters mParams;
private List<Camera.Size> mPreviewSizes;
private int mPreviewSize = 0;
+ private List<String> mAfModes;
+ private int mAfMode = 0;
private List<Camera.Size> mSnapshotSizes;
private int mSnapshotSize = 0;
private List<CamcorderProfile> mCamcorderProfiles;
private int mCamcorderProfile = 0;
+ private MediaRecorder mRecorder;
+
private static final int CAMERA_UNINITIALIZED = 0;
private static final int CAMERA_OPEN = 1;
private static final int CAMERA_PREVIEW = 2;
private static final int CAMERA_TAKE_PICTURE = 3;
+ private static final int CAMERA_RECORD = 4;
private int mState = CAMERA_UNINITIALIZED;
/** Misc variables */
private static final String TAG = "TestingCamera";
- // Activity methods
+ /** Activity lifecycle */
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -109,20 +137,33 @@
mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview);
mPreviewToggle.setOnClickListener(mPreviewToggleListener);
+ mAutofocusModeSpinner = (Spinner) findViewById(R.id.af_mode_spinner);
+ mAutofocusModeSpinner.setOnItemSelectedListener(mAutofocusModeListener);
+
+ mAutofocusButton = (Button) findViewById(R.id.af_button);
+ mAutofocusButton.setOnClickListener(mAutofocusButtonListener);
+ mPreviewOnlyControls.add(mAutofocusButton);
+
+ mCancelAutofocusButton = (Button) findViewById(R.id.af_cancel_button);
+ mCancelAutofocusButton.setOnClickListener(mCancelAutofocusButtonListener);
+ mPreviewOnlyControls.add(mCancelAutofocusButton);
+
mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner);
mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener);
mTakePictureButton = (Button) findViewById(R.id.take_picture);
mTakePictureButton.setOnClickListener(mTakePictureListener);
+ mPreviewOnlyControls.add(mTakePictureButton);
mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner);
mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener);
mRecordToggle = (ToggleButton) findViewById(R.id.start_record);
mRecordToggle.setOnClickListener(mRecordToggleListener);
+ mPreviewOnlyControls.add(mRecordToggle);
- // TODO: Implement recording support
- mRecordToggle.setVisibility(View.GONE);
+ mLogView = (TextView) findViewById(R.id.log);
+ mLogView.setMovementMethod(new ScrollingMovementMethod());
int numCameras = Camera.getNumberOfCameras();
String[] cameraNames = new String[numCameras];
@@ -138,6 +179,7 @@
@Override
public void onResume() {
super.onResume();
+ log("onResume: Setting up camera");
mPreviewHolder = null;
setUpCamera();
}
@@ -145,41 +187,43 @@
@Override
public void onPause() {
super.onPause();
-
+ log("onPause: Releasing camera");
mCamera.release();
mState = CAMERA_UNINITIALIZED;
}
- // SurfaceHolder.Callback methods
- @Override
+ /** SurfaceHolder.Callback methods */
public void surfaceChanged(SurfaceHolder holder,
int format,
int width,
int height) {
if (mPreviewHolder != null) return;
- Log.d(TAG, "Surface holder available: " + width + " x " + height);
+ log("Surface holder available: " + width + " x " + height);
mPreviewHolder = holder;
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
- Log.e(TAG, "Unable to set up preview!");
+ logE("Unable to set up preview!");
}
- resizePreview(mPreviewSizes.get(mPreviewSize).width,
- mPreviewSizes.get(mPreviewSize).height);
}
- @Override
public void surfaceCreated(SurfaceHolder holder) {
}
- @Override
public void surfaceDestroyed(SurfaceHolder holder) {
-
+ mPreviewHolder = null;
}
- // UI listeners
+ /** UI controls enable/disable */
+ private void enablePreviewOnlyControls(boolean enabled) {
+ for (View v : mPreviewOnlyControls) {
+ v.setEnabled(enabled);
+ }
+ }
+
+ /** UI listeners */
private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
new AdapterView.OnItemSelectedListener() {
@@ -191,13 +235,12 @@
}
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
};
private OnClickListener mInfoButtonListener = new OnClickListener() {
- @Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
InfoDialogFragment infoDialog = new InfoDialogFragment();
@@ -208,13 +251,11 @@
private AdapterView.OnItemSelectedListener mPreviewSizeListener =
new AdapterView.OnItemSelectedListener() {
- @Override
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
if (pos == mPreviewSize) return;
- Log.d(TAG, "Switching preview sizes");
-
if (mState == CAMERA_PREVIEW) {
+ log("Stopping preview to switch resolutions");
mCamera.stopPreview();
}
@@ -223,15 +264,18 @@
int height = mPreviewSizes.get(mPreviewSize).height;
mParams.setPreviewSize(width, height);
+ log("Setting preview size to " + width + "x" + height);
+
mCamera.setParameters(mParams);
- resizePreview(width, height);
if (mState == CAMERA_PREVIEW) {
+ log("Restarting preview");
+ resizePreview(width, height);
mCamera.startPreview();
}
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
};
@@ -240,42 +284,92 @@
new View.OnClickListener() {
public void onClick(View v) {
if (mState == CAMERA_TAKE_PICTURE) {
- Log.e(TAG, "Can't change preview state while taking picture!");
+ logE("Can't change preview state while taking picture!");
return;
}
if (mPreviewToggle.isChecked()) {
- Log.d(TAG, "Starting preview");
-
+ log("Starting preview");
+ resizePreview(mPreviewSizes.get(mPreviewSize).width,
+ mPreviewSizes.get(mPreviewSize).height);
mCamera.startPreview();
mState = CAMERA_PREVIEW;
-
- mTakePictureButton.setEnabled(true);
+ enablePreviewOnlyControls(true);
} else {
- Log.d(TAG, "Stopping preview");
+ log("Stopping preview");
mCamera.stopPreview();
mState = CAMERA_OPEN;
- mTakePictureButton.setEnabled(false);
+ enablePreviewOnlyControls(false);
}
}
};
+ private OnItemSelectedListener mAutofocusModeListener =
+ new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> parent,
+ View view, int pos, long id) {
+ if (pos == mAfMode) return;
+
+ mAfMode = pos;
+ String focusMode = mAfModes.get(mAfMode);
+ log("Setting focus mode to " + focusMode);
+ mParams.setFocusMode(focusMode);
+
+ mCamera.setParameters(mParams);
+ }
+
+ public void onNothingSelected(AdapterView<?> arg0) {
+
+ }
+ };
+
+ private OnClickListener mAutofocusButtonListener =
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ log("Triggering autofocus");
+ mCamera.autoFocus(mAutofocusCallback);
+ }
+ };
+
+ private OnClickListener mCancelAutofocusButtonListener =
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ log("Cancelling autofocus");
+ mCamera.cancelAutoFocus();
+ }
+ };
+
+ private Camera.AutoFocusCallback mAutofocusCallback =
+ new Camera.AutoFocusCallback() {
+ public void onAutoFocus(boolean success, Camera camera) {
+ log("Autofocus completed: " + (success ? "success" : "failure") );
+ }
+ };
+
+ private Camera.AutoFocusMoveCallback mAutofocusMoveCallback =
+ new Camera.AutoFocusMoveCallback() {
+ public void onAutoFocusMoving(boolean start, Camera camera) {
+ log("Autofocus movement: " + (start ? "starting" : "stopped") );
+ }
+ };
+
private AdapterView.OnItemSelectedListener mSnapshotSizeListener =
- new AdapterView.OnItemSelectedListener() {
+ new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
if (pos == mSnapshotSize) return;
- Log.d(TAG, "Switching snapshot sizes");
mSnapshotSize = pos;
int width = mSnapshotSizes.get(mSnapshotSize).width;
int height = mSnapshotSizes.get(mSnapshotSize).height;
+ log("Setting snapshot size to " + width + " x " + height);
+
mParams.setPictureSize(width, height);
mCamera.setParameters(mParams);
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
};
@@ -283,17 +377,15 @@
private View.OnClickListener mTakePictureListener =
new View.OnClickListener() {
public void onClick(View v) {
- Log.d(TAG, "Taking picture");
+ log("Taking picture");
if (mState == CAMERA_PREVIEW) {
mState = CAMERA_TAKE_PICTURE;
-
- mTakePictureButton.setEnabled(false);
- mPreviewToggle.setEnabled(false);
+ enablePreviewOnlyControls(false);
mPreviewToggle.setChecked(false);
mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb);
} else {
- Log.e(TAG, "Can't take picture while not running preview!");
+ logE("Can't take picture while not running preview!");
}
}
};
@@ -302,41 +394,53 @@
new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
- mCamcorderProfile = pos;
+ if (pos == mCamcorderProfile) return;
+
+ log("Setting camcorder profile to " + ((TextView)view).getText());
+ mCamcorderProfile = pos;
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
};
private View.OnClickListener mRecordToggleListener =
- new View.OnClickListener() {
+ new View.OnClickListener() {
public void onClick(View v) {
+ mPreviewToggle.setEnabled(false);
+ if (mState == CAMERA_PREVIEW) {
+ startRecording();
+ } else if (mState == CAMERA_RECORD) {
+ stopRecording();
+ } else {
+ logE("Can't toggle recording in current state!");
+ }
+ mPreviewToggle.setEnabled(true);
}
};
private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() {
public void onShutter() {
- Log.d(TAG, "Shutter cb fired");
+ log("Shutter callback received");
}
};
private Camera.PictureCallback mRawCb = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
- Log.d(TAG, "Raw cb fired");
+ log("Raw callback received");
}
};
private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
- Log.d(TAG, "Postview cb fired");
+ log("Postview callback received");
}
};
private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
- Log.d(TAG, "JPEG cb fired");
+ log("JPEG picture callback received");
FragmentManager fm = getFragmentManager();
SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment();
@@ -353,67 +457,36 @@
// Internal methods
void setUpCamera() {
- Log.d(TAG, "Setting up camera " + mCameraId);
+ log("Setting up camera " + mCameraId);
+ logIndent(1);
if (mState >= CAMERA_OPEN) {
- Log.d(TAG, "Closing old camera");
+ log("Closing old camera");
mCamera.release();
mState = CAMERA_UNINITIALIZED;
}
- Log.d(TAG, "Opening camera " + mCameraId);
+ log("Opening camera " + mCameraId);
mCamera = Camera.open(mCameraId);
mState = CAMERA_OPEN;
mParams = mCamera.getParameters();
// Set up preview size selection
- mPreviewSizes = mParams.getSupportedPreviewSizes();
- String[] availableSizeNames = new String[mPreviewSizes.size()];
- int i = 0;
- for (Camera.Size previewSize: mPreviewSizes) {
- availableSizeNames[i++] =
- Integer.toString(previewSize.width) + " x " +
- Integer.toString(previewSize.height);
- }
- mPreviewSizeSpinner.setAdapter(
- new ArrayAdapter<String>(
- this, R.layout.spinner_item, availableSizeNames));
+ log("Configuring camera");
+ logIndent(1);
- mPreviewSize = 0;
-
- int width = mPreviewSizes.get(mPreviewSize).width;
- int height = mPreviewSizes.get(mPreviewSize).height;
- mParams.setPreviewSize(width, height);
-
- // Set up snapshot size selection
-
- mSnapshotSizes = mParams.getSupportedPictureSizes();
-
- availableSizeNames = new String[mSnapshotSizes.size()];
- i = 0;
- for (Camera.Size snapshotSize : mSnapshotSizes) {
- availableSizeNames[i++] =
- Integer.toString(snapshotSize.width) + " x " +
- Integer.toString(snapshotSize.height);
- }
- mSnapshotSizeSpinner.setAdapter(
- new ArrayAdapter<String>(
- this, R.layout.spinner_item, availableSizeNames));
-
- mSnapshotSize = 0;
-
- width = mSnapshotSizes.get(mSnapshotSize).width;
- height = mSnapshotSizes.get(mSnapshotSize).height;
- mParams.setPictureSize(width, height);
-
- // Set up camcorder profile selection
+ updatePreviewSizes(mParams);
+ updateAfModes(mParams);
+ updateSnapshotSizes(mParams);
updateCamcorderProfile(mCameraId);
- // Update parameters based on above defaults
-
+ // Update parameters based on above updates
mCamera.setParameters(mParams);
+ mCamera.setAutoFocusMoveCallback(mAutofocusMoveCallback);
+
if (mPreviewHolder != null) {
+ log("Setting preview display");
try {
mCamera.setPreviewDisplay(mPreviewHolder);
} catch(IOException e) {
@@ -421,71 +494,139 @@
}
}
+ logIndent(-1);
+
mPreviewToggle.setEnabled(true);
mPreviewToggle.setChecked(false);
- mTakePictureButton.setEnabled(false);
+ enablePreviewOnlyControls(false);
+ int width = mPreviewSizes.get(mPreviewSize).width;
+ int height = mPreviewSizes.get(mPreviewSize).height;
resizePreview(width, height);
if (mPreviewToggle.isChecked()) {
- Log.d(TAG, "Starting preview" );
- mCamera.startPreview();
+ log("Starting preview" );
+ mCamera.startPreview();
mState = CAMERA_PREVIEW;
}
+ logIndent(-1);
+ }
+
+ private void updateAfModes(Parameters params) {
+ mAfModes = params.getSupportedFocusModes();
+
+ mAutofocusModeSpinner.setAdapter(
+ new ArrayAdapter<String>(this, R.layout.spinner_item,
+ mAfModes.toArray(new String[0])));
+
+ mAfMode = 0;
+
+ params.setFocusMode(mAfModes.get(mAfMode));
+
+ log("Setting AF mode to " + mAfModes.get(mAfMode));
+ }
+
+ private void updatePreviewSizes(Camera.Parameters params) {
+ mPreviewSizes = params.getSupportedPreviewSizes();
+
+ String[] availableSizeNames = new String[mPreviewSizes.size()];
+ int i = 0;
+ for (Camera.Size previewSize: mPreviewSizes) {
+ availableSizeNames[i++] =
+ Integer.toString(previewSize.width) + " x " +
+ Integer.toString(previewSize.height);
+ }
+ mPreviewSizeSpinner.setAdapter(
+ new ArrayAdapter<String>(
+ this, R.layout.spinner_item, availableSizeNames));
+
+ mPreviewSize = 0;
+
+ int width = mPreviewSizes.get(mPreviewSize).width;
+ int height = mPreviewSizes.get(mPreviewSize).height;
+ params.setPreviewSize(width, height);
+ log("Setting preview size to " + width + " x " + height);
+ }
+
+ private void updateSnapshotSizes(Camera.Parameters params) {
+ String[] availableSizeNames;
+ mSnapshotSizes = params.getSupportedPictureSizes();
+
+ availableSizeNames = new String[mSnapshotSizes.size()];
+ int i = 0;
+ for (Camera.Size snapshotSize : mSnapshotSizes) {
+ availableSizeNames[i++] =
+ Integer.toString(snapshotSize.width) + " x " +
+ Integer.toString(snapshotSize.height);
+ }
+ mSnapshotSizeSpinner.setAdapter(
+ new ArrayAdapter<String>(
+ this, R.layout.spinner_item, availableSizeNames));
+
+ mSnapshotSize = 0;
+
+ int snapshotWidth = mSnapshotSizes.get(mSnapshotSize).width;
+ int snapshotHeight = mSnapshotSizes.get(mSnapshotSize).height;
+ params.setPictureSize(snapshotWidth, snapshotHeight);
+ log("Setting snapshot size to " + snapshotWidth + " x " + snapshotHeight);
}
private void updateCamcorderProfile(int cameraId) {
// Have to query all of these individually,
final int PROFILES[] = new int[] {
- CamcorderProfile.QUALITY_1080P,
- CamcorderProfile.QUALITY_480P,
- CamcorderProfile.QUALITY_720P,
- CamcorderProfile.QUALITY_CIF,
- CamcorderProfile.QUALITY_HIGH,
- CamcorderProfile.QUALITY_LOW,
- CamcorderProfile.QUALITY_QCIF,
- CamcorderProfile.QUALITY_QVGA,
- CamcorderProfile.QUALITY_TIME_LAPSE_1080P,
- CamcorderProfile.QUALITY_TIME_LAPSE_480P,
- CamcorderProfile.QUALITY_TIME_LAPSE_720P,
- CamcorderProfile.QUALITY_TIME_LAPSE_CIF,
- CamcorderProfile.QUALITY_TIME_LAPSE_HIGH,
- CamcorderProfile.QUALITY_TIME_LAPSE_LOW,
- CamcorderProfile.QUALITY_TIME_LAPSE_QCIF,
- CamcorderProfile.QUALITY_TIME_LAPSE_QVGA
+ CamcorderProfile.QUALITY_1080P,
+ CamcorderProfile.QUALITY_480P,
+ CamcorderProfile.QUALITY_720P,
+ CamcorderProfile.QUALITY_CIF,
+ CamcorderProfile.QUALITY_HIGH,
+ CamcorderProfile.QUALITY_LOW,
+ CamcorderProfile.QUALITY_QCIF,
+ CamcorderProfile.QUALITY_QVGA,
+ CamcorderProfile.QUALITY_TIME_LAPSE_1080P,
+ CamcorderProfile.QUALITY_TIME_LAPSE_480P,
+ CamcorderProfile.QUALITY_TIME_LAPSE_720P,
+ CamcorderProfile.QUALITY_TIME_LAPSE_CIF,
+ CamcorderProfile.QUALITY_TIME_LAPSE_HIGH,
+ CamcorderProfile.QUALITY_TIME_LAPSE_LOW,
+ CamcorderProfile.QUALITY_TIME_LAPSE_QCIF,
+ CamcorderProfile.QUALITY_TIME_LAPSE_QVGA
};
final String PROFILE_NAMES[] = new String[] {
- "1080P",
- "480P",
- "720P",
- "CIF",
- "HIGH",
- "LOW",
- "QCIF",
- "QVGA",
- "TIME_LAPSE_1080P",
- "TIME_LAPSE_480P",
- "TIME_LAPSE_720P",
- "TIME_LAPSE_CIF",
- "TIME_LAPSE_HIGH",
- "TIME_LAPSE_LOW",
- "TIME_LAPSE_QCIF",
- "TIME_LAPSE_QVGA"
+ "1080P",
+ "480P",
+ "720P",
+ "CIF",
+ "HIGH",
+ "LOW",
+ "QCIF",
+ "QVGA",
+ "TIME_LAPSE_1080P",
+ "TIME_LAPSE_480P",
+ "TIME_LAPSE_720P",
+ "TIME_LAPSE_CIF",
+ "TIME_LAPSE_HIGH",
+ "TIME_LAPSE_LOW",
+ "TIME_LAPSE_QCIF",
+ "TIME_LAPSE_QVGA"
};
List<String> availableCamcorderProfileNames = new ArrayList<String>();
mCamcorderProfiles = new ArrayList<CamcorderProfile>();
for (int i = 0; i < PROFILES.length; i++) {
- if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) {
- availableCamcorderProfileNames.add(PROFILE_NAMES[i]);
- mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i]));
- }
+ if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) {
+ availableCamcorderProfileNames.add(PROFILE_NAMES[i]);
+ mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i]));
+ }
}
String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]);
mCamcorderProfileSpinner.setAdapter(
- new ArrayAdapter<String>(
- this, R.layout.spinner_item, nameArray));
+ new ArrayAdapter<String>(
+ this, R.layout.spinner_item, nameArray));
+
+ mCamcorderProfile = 0;
+ log("Setting camcorder profile to " + nameArray[mCamcorderProfile]);
+
}
void resizePreview(int width, int height) {
@@ -498,4 +639,161 @@
}
}
+
+ static final int MEDIA_TYPE_IMAGE = 0;
+ static final int MEDIA_TYPE_VIDEO = 1;
+ private File getOutputMediaFile(int type){
+ // To be safe, you should check that the SDCard is mounted
+ // using Environment.getExternalStorageState() before doing this.
+
+ String state = Environment.getExternalStorageState();
+ if (!Environment.MEDIA_MOUNTED.equals(state)) {
+ return null;
+ }
+
+ File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES), "TestingCamera");
+ // This location works best if you want the created images to be shared
+ // between applications and persist after your app has been uninstalled.
+
+ // Create the storage directory if it does not exist
+ if (! mediaStorageDir.exists()){
+ if (! mediaStorageDir.mkdirs()){
+ logE("Failed to create directory for pictures/video");
+ return null;
+ }
+ }
+
+ // Create a media file name
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ File mediaFile;
+ if (type == MEDIA_TYPE_IMAGE){
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "IMG_"+ timeStamp + ".jpg");
+ } else if(type == MEDIA_TYPE_VIDEO) {
+ mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+ "VID_"+ timeStamp + ".mp4");
+ } else {
+ return null;
+ }
+
+ return mediaFile;
+ }
+
+ private void startRecording() {
+ log("Starting recording");
+ logIndent(1);
+ log("Configuring MediaRecoder");
+ mCamera.unlock();
+ mRecorder = new MediaRecorder();
+ mRecorder.setOnErrorListener(mRecordingErrorListener);
+ mRecorder.setOnInfoListener(mRecordingInfoListener);
+ mRecorder.setCamera(mCamera);
+ mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+ mRecorder.setProfile(mCamcorderProfiles.get(mCamcorderProfile));
+ File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
+ log("File name:" + outputFile.toString());
+ mRecorder.setOutputFile(outputFile.toString());
+
+ boolean ready = false;
+ log("Preparing MediaRecorder");
+ try {
+ mRecorder.prepare();
+ ready = true;
+ } catch (Exception e) {
+ StringWriter writer = new StringWriter();
+ e.printStackTrace(new PrintWriter(writer));
+ logE("Exception preparing MediaRecorder:\n" + writer.toString());
+ }
+
+ if (ready) {
+ try {
+ log("Starting MediaRecorder");
+ mRecorder.start();
+ mState = CAMERA_RECORD;
+ log("Recording active");
+ } catch (Exception e) {
+ StringWriter writer = new StringWriter();
+ e.printStackTrace(new PrintWriter(writer));
+ logE("Exception starting MediaRecorder:\n" + writer.toString());
+ }
+ } else {
+ mPreviewToggle.setChecked(false);
+ }
+ logIndent(-1);
+ }
+
+ private MediaRecorder.OnErrorListener mRecordingErrorListener =
+ new MediaRecorder.OnErrorListener() {
+ public void onError(MediaRecorder mr, int what, int extra) {
+ logE("MediaRecorder reports error: " + what + ", extra "
+ + extra);
+ if (mState == CAMERA_RECORD) {
+ stopRecording();
+ }
+ }
+ };
+
+ private MediaRecorder.OnInfoListener mRecordingInfoListener =
+ new MediaRecorder.OnInfoListener() {
+ public void onInfo(MediaRecorder mr, int what, int extra) {
+ log("MediaRecorder reports info: " + what + ", extra "
+ + extra);
+ }
+ };
+
+ private void stopRecording() {
+ log("Stopping recording");
+ if (mRecorder != null) {
+ mRecorder.stop();
+ mCamera.lock();
+ mState = CAMERA_PREVIEW;
+ mRecorder.release();
+ mRecorder = null;
+ } else {
+ logE("Recorder is unexpectedly null!");
+ }
+ }
+
+ private int mLogIndentLevel = 0;
+ private String mLogIndent = "\t";
+ /** Increment or decrement log indentation level */
+ private void logIndent(int delta) {
+ mLogIndentLevel += delta;
+ if (mLogIndentLevel < 0) mLogIndentLevel = 0;
+ char[] mLogIndentArray = new char[mLogIndentLevel + 1];
+ for (int i = -1; i < mLogIndentLevel; i++) {
+ mLogIndentArray[i + 1] = '\t';
+ }
+ mLogIndent = new String(mLogIndentArray);
+ }
+
+ SimpleDateFormat mDateFormatter = new SimpleDateFormat("HH:mm:ss.SSS");
+ /** Log both to log text view and to device logcat */
+ private void log(String logLine) {
+ Log.d(TAG, logLine);
+ logAndScrollToBottom(logLine, mLogIndent);
+ }
+
+ private void logE(String logLine) {
+ Log.e(TAG, logLine);
+ logAndScrollToBottom(logLine, mLogIndent + "!!! ");
+ }
+
+ private void logAndScrollToBottom(String logLine, String logIndent) {
+ StringBuffer logEntry = new StringBuffer(32);
+ logEntry.append("\n").append(mDateFormatter.format(new Date())).append(logIndent);
+ logEntry.append(logLine);
+ mLogView.append(logEntry);
+ final Layout layout = mLogView.getLayout();
+ if (layout != null){
+ int scrollDelta = layout.getLineBottom(mLogView.getLineCount() - 1)
+ - mLogView.getScrollY() - mLogView.getHeight();
+ if(scrollDelta > 0) {
+ mLogView.scrollBy(0, scrollDelta);
+ }
+ }
+ }
+
}
\ No newline at end of file