Added test handler class and the main activity.
- CameraAnalyzerActivity is the main activity controlling the app.
- Java class CameraTests is the base class for tests in Java
- C++ class ImageTestHandler is the base class for handling tests
- JNI files for communicating between these files are also included.
Change-Id: Ifeea0eb2adb1e11bb6616d72ed67dff089256887
diff --git a/apps/CtsVerifier/include/colorchecker/imagetesthandler.h b/apps/CtsVerifier/include/colorchecker/imagetesthandler.h
new file mode 100644
index 0000000..d66a1d2
--- /dev/null
+++ b/apps/CtsVerifier/include/colorchecker/imagetesthandler.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef IMAGETESTHANDLER_H
+#define IMAGETESTHANDLER_H
+
+#include "vec2.h"
+#include "vec3.h"
+
+class ImageTestHandler{
+ public:
+ ImageTestHandler() {
+ initDebugImage();
+ }
+ ImageTestHandler(int debugHeight, int debugWidth) {
+ initDebugImage(debugHeight, debugWidth);
+ }
+ virtual ~ImageTestHandler() { delete[] mDebugOutput; }
+
+ virtual void processData() {}
+
+ int getDebugWidth() const { return mDebugWidth; }
+ int getDebugHeight() const { return mDebugHeight; }
+ const unsigned char* debug_output() const { return mDebugOutput; }
+ void copyDebugImage(int inputHeight, int inputWidth,
+ const unsigned char* inputImage);
+
+ void drawPoint(const Vec2i &point, const Vec3i &color);
+ void drawPoint(int row, int column, const Vec3i &color);
+ void drawLine(int angle, int radius, const Vec3i &color);
+
+ protected:
+ void clearDebugImage();
+
+ private:
+ void initDebugImage();
+ void initDebugImage(int debugHeight, int debugWidth);
+
+ unsigned char* mDebugOutput;
+ int mDebugWidth;
+ int mDebugHeight;
+};
+
+#endif
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp
new file mode 100644
index 0000000..51fd0c5
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "CameraTestsJNI"
+#include <utils/Log.h>
+#include "com_android_cts_verifier_camera_analyzer_CameraTests.h"
+#include "android/bitmap.h"
+#include "testingimage.h"
+#include "imagetesthandler.h"
+
+#include <string.h>
+
+jlong Java_com_android_cts_verifier_camera_analyzer_CameraTests_findNative(
+ JNIEnv* env,
+ jobject thiz,
+ jobject inputBitmap) {
+
+ ALOGV("JNI findNative starts!");
+
+ // Verify that we can handle the input bitmap
+ AndroidBitmapInfo inputInfo;
+ AndroidBitmap_getInfo(env, inputBitmap, &inputInfo);
+ if (inputInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
+ inputInfo.format != ANDROID_BITMAP_FORMAT_RGB_565) {
+ LOGE("Only RGBA_8888 and RGB_565 bitmaps are supported, type was %d.",
+ inputInfo.format);
+ }
+
+ // Get some references to the fields and class type of ColorChecker
+ jclass thizCls = env->GetObjectClass(thiz);
+ ALOGV("ColorChecker field and classes reference finished!");
+
+ // Get raw inputs and outputs ready
+ uint8_t *inputBuffer = NULL;
+ int result = AndroidBitmap_lockPixels(
+ env,
+ inputBitmap,
+ reinterpret_cast<void**>(&inputBuffer));
+
+ if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+ LOGE("Unable to lock input bitmap");
+ }
+
+ uint8_t *outputImage = NULL;
+ int outputWidth, outputHeight;
+
+ ALOGV("Input and output images created!");
+
+ // Find the color checker
+ bool success;
+ uint8_t *inputBufferRGBA = NULL;
+ int inputStride;
+ bool freeInputRGBA = false;
+ switch (inputInfo.format) {
+ case ANDROID_BITMAP_FORMAT_RGB_565: {
+ // First convert to RGBA_8888
+ inputBufferRGBA = new uint8_t[inputInfo.width *
+ inputInfo.height *
+ 4];
+ inputStride = inputInfo.width * 4;
+ uint8_t *outP = inputBufferRGBA;
+ for (int y = 0; y < inputInfo.height; y++ ) {
+ uint16_t *inP = (uint16_t*)(&inputBuffer[y * inputInfo.stride]);
+ for (int x = 0; x < inputInfo.width; x++) {
+ *(outP++) = ( ((*inP) >> 0) & 0x001F) << 3;
+ *(outP++) = ( ((*inP) >> 5) & 0x003F) << 2;
+ *(outP++) = ( ((*inP) >> 11) & 0x001F) << 3;
+ outP++;
+ inP++;
+ }
+ }
+ freeInputRGBA = true;
+
+ ALOGV("RGB_565 Format with width, height and stride as %d, %d, %d",
+ inputInfo.width, inputInfo.height, inputStride);
+ break;
+ }
+ case ANDROID_BITMAP_FORMAT_RGBA_8888: {
+ // Already in RGBA
+ inputBufferRGBA = inputBuffer;
+ inputStride = inputInfo.stride;
+ ALOGV("RGB_8888 Format with width, height and stride as %d, %d, %d",
+ inputInfo.width, inputInfo.height, inputStride);
+ break;
+ }
+ }
+
+ TestingImage *input_testing_image =
+ new TestingImage(inputBufferRGBA, inputInfo.height, inputInfo.width,
+ 4, inputStride, 120, 160);
+
+ long lp = (long)input_testing_image;
+
+ result = AndroidBitmap_unlockPixels(env, inputBitmap);
+ if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+ LOGE("Unable to unlock input bitmap");
+ }
+
+ if (freeInputRGBA) {
+ ALOGV("Deleteing inputbufferRGBA");
+ delete[] inputBufferRGBA;
+ }
+
+ return lp;
+ ALOGV("Input format switched!");
+}
+
+jlong Java_com_android_cts_verifier_camera_analyzer_CameraTests_createImageTestHandler(
+ JNIEnv* env,
+ jobject thiz,
+ jint debugHeight,
+ jint debugWidth) {
+
+ ImageTestHandler* testHandler =
+ new ImageTestHandler(debugHeight, debugWidth);
+ long handlerAddress = (long)testHandler;
+ return handlerAddress;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_CameraTests_cleanUpHandler(
+ JNIEnv* env,
+ jobject thiz,
+ jlong inputHandlerAddress) {
+
+ ImageTestHandler* testHandler = (ImageTestHandler*) (long) inputHandlerAddress;
+ delete testHandler;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_CameraTests_displayHandlerDebugOutput(
+ JNIEnv* env,
+ jobject thiz,
+ jlong inputHandlerAddress) {
+
+ jclass thizCls = env->GetObjectClass(thiz);
+ jfieldID outputId = env->GetFieldID(thizCls, "mDebugOutput",
+ "Landroid/graphics/Bitmap;");
+
+ ImageTestHandler* testHandler = (ImageTestHandler*) (long) inputHandlerAddress;
+ uint8_t *outputImage = new uint8_t[testHandler->getDebugHeight() *
+ testHandler->getDebugWidth() * 4];
+
+ const unsigned char *debugoutput = testHandler->debug_output();
+ memcpy(outputImage, debugoutput, testHandler->getDebugHeight() *
+ testHandler->getDebugWidth() * 4);
+
+ int outputWidth = testHandler->getDebugWidth();
+ int outputHeight = testHandler->getDebugHeight();
+ bool success = false;
+
+ if (outputImage == NULL) {
+ ALOGV("output Image is null!");
+ } else {
+ ALOGV("output Image is ready!");
+ }
+
+ // Create debug bitmap from output image data
+ if (outputImage != NULL) {
+ // Get method handles, create inputs to createBitmap
+ jclass bitmapClass =
+ env->FindClass("android/graphics/Bitmap");
+ jclass bitmapConfigClass =
+ env->FindClass("android/graphics/Bitmap$Config");
+
+ jmethodID createBitmap = env->GetStaticMethodID(
+ bitmapClass, "createBitmap",
+ "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
+
+ jmethodID getConfig = env->GetStaticMethodID(
+ bitmapConfigClass, "valueOf",
+ "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
+
+ // Create bitmap config and bitmap
+ jstring bitmapConfigValue = env->NewStringUTF("ARGB_8888");
+ jobject rgbaConfig = env->CallStaticObjectMethod(bitmapConfigClass,
+ getConfig,
+ bitmapConfigValue);
+ jobject outputBitmap = env->CallStaticObjectMethod(bitmapClass,
+ createBitmap,
+ outputWidth,
+ outputHeight,
+ rgbaConfig);
+ // Copy output image into it
+ uint8_t *outputBuffer;
+ int result = AndroidBitmap_lockPixels(
+ env,
+ outputBitmap,
+ reinterpret_cast<void**>(&outputBuffer) );
+
+ if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+ LOGE("Unable to lock output bitmap");
+ }
+
+ memcpy(outputBuffer, outputImage, outputWidth * outputHeight * 4);
+
+ result = AndroidBitmap_unlockPixels(env, outputBitmap);
+ if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+ LOGE("Unable to unlock output bitmap");
+ }
+
+ // Write new Bitmap reference into mDebugOutput class member
+ env->SetObjectField(thiz, outputId, outputBitmap);
+ ALOGV("Copied to outputBitmap");
+ delete [] outputImage;
+ env->DeleteLocalRef(outputBitmap);
+ env->DeleteLocalRef(rgbaConfig);
+ env->DeleteLocalRef(bitmapClass);
+ env->DeleteLocalRef(bitmapConfigClass);
+ }
+}
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h
new file mode 100644
index 0000000..e071dc1
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef JNI_CAMERAANALYZER_CAMERATESTS_H
+#define JNI_CAMERAANALYZER_CAMERATESTS_H
+
+#include <jni.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jlong JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_findNative(
+ JNIEnv *env,
+ jobject thiz,
+ jobject inputBitmap);
+
+JNIEXPORT jlong JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_createImageTestHandler(
+ JNIEnv* env,
+ jobject thiz,
+ jint debugHeight,
+ jint debugWidth);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_cleanUpHandler(
+ JNIEnv* env,
+ jobject thiz,
+ jlong inputHandlerAddress);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_displayHandlerDebugOutput(
+ JNIEnv* env,
+ jobject thiz,
+ jlong inputHandlerAddress);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp b/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp
new file mode 100644
index 0000000..1c5bc17
--- /dev/null
+++ b/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "ImageTestHandler"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <cmath>
+
+#include "vec2.h"
+#include "vec3.h"
+#include "imagetesthandler.h"
+
+void ImageTestHandler::initDebugImage() {
+ mDebugOutput = NULL;
+}
+
+// Initializes the debug image with a given height and width.
+void ImageTestHandler::initDebugImage(int debugHeight,
+ int debugWidth) {
+ mDebugOutput = NULL;
+ mDebugOutput = new unsigned char[debugHeight * debugWidth * 4];
+ memset(mDebugOutput, 0, debugHeight * debugWidth * 4);
+
+ mDebugHeight = debugHeight;
+ mDebugWidth = debugWidth;
+}
+
+// Copies an existing image to the debug image.
+void ImageTestHandler::copyDebugImage(int inputHeight, int inputWidth,
+ const unsigned char* inputImage) {
+ if ((inputHeight == mDebugHeight) && (inputWidth == mDebugWidth)) {
+ ALOGV("Copying debug images");
+ memcpy(mDebugOutput, inputImage, mDebugHeight * mDebugWidth * 4);
+ }
+}
+
+void ImageTestHandler::clearDebugImage() {
+ if (mDebugOutput != NULL) {
+ delete[] mDebugOutput;
+ mDebugOutput = new unsigned char[mDebugHeight * mDebugWidth * 4];
+ memset(mDebugOutput, 0, mDebugHeight * mDebugWidth * 4);
+ }
+}
+
+
+// Draws a point of a given color.
+void ImageTestHandler::drawPoint(int row, int column, const Vec3i &color) {
+ if ((row >= 0) && (column >= 0) &&
+ (column < mDebugWidth) && (row < mDebugHeight)) {
+ mDebugOutput[(row*mDebugWidth + column) * 4] = color.r();
+ mDebugOutput[(row*mDebugWidth + column) * 4+1] = color.g();
+ mDebugOutput[(row*mDebugWidth + column) * 4+2] = color.b();
+ mDebugOutput[(row*mDebugWidth + column) * 4+3] = 255;
+ }
+}
+
+// Draws a point in Vec2 format of a given color.
+void ImageTestHandler::drawPoint(const Vec2i &point, const Vec3i &color) {
+ drawPoint((int) point.y(), (int) point.x(), color);
+}
+
+// Draws a line of a given color.
+void ImageTestHandler::drawLine(int angle, int radius, const Vec3i &color) {
+ const int r = color.r();
+ const int g = color.g();
+ const int b = color.b();
+ const int a = 255;
+
+ int shiftedMin = -113;
+ int shiftedMax = 83;
+
+ float radiusDouble = static_cast<float>(radius);
+
+ float angleRad = static_cast<float>(angle) * M_PI / 180.0;
+
+ //ALOGV("draw line for (%d, %d)", angle, radius);
+ for (int i = shiftedMin; i <= shiftedMax; ++i) {
+ float j;
+
+ assert(angle != 0);
+ j = (i - radiusDouble / sin(angleRad)) * tan(angleRad);
+ float x = (static_cast<float>(i) + j) / sqrt(2.0);
+ float y = (j - static_cast<float>(i)) / sqrt(2.0);
+
+ drawPoint(x, y, color);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
new file mode 100644
index 0000000..8cce754
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
@@ -0,0 +1,480 @@
+/*
+ * 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.cts.verifier.camera.analyzer;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Button;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.content.Context;
+
+import java.io.IOException;
+import java.lang.Thread;
+import java.util.List;
+
+/**
+ * Controls the UI activities of the camera quality test app. It is created
+ * as soon as the app started. Users can launch different quality tests with
+ * the buttons in the UI. This class will manage the threading for different
+ * tests. Also it will provide debug output or debug text results for tests.
+ */
+public class CameraAnalyzerActivity extends PassFailButtons.Activity {
+
+ private static final String TAG = "CameraAnalyzer";
+ private SurfaceView mCameraView;
+ private ImageView mResultView;
+ private Button mFindCheckerButton;
+ private Button mExposureCompensationButton;
+ private Button mWhiteBalanceButton;
+ private Button mAutoLockButton;
+ private Button mMeteringButton;
+ private ListView mTestList;
+ private TwoColumnAdapter mAdapter;
+
+ private Camera mCamera;
+ private int mCameraIdx = 0;
+ private boolean mIsCameraOpen = false;
+
+ private PowerManager mPowerManager;
+ private PowerManager.WakeLock mWakeLock;
+
+ private boolean mProcessingPicture = false;
+ private boolean mTestInProgress = false;
+ private final Object mProcessingTest = new Object();
+ private CameraTests mCurrentTest = null;
+
+ private long mCheckerCenterAddress;
+ private long mCheckerRadiusAddress;
+
+ private String mResultReport = "";
+ static final String[] TESTS = new String[] {"Test1", "Test2"};
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ca_main);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.camera_analyzer, R.string.ca_info, -1);
+
+ mFindCheckerButton = (Button) findViewById(R.id.findcheckerboardbutton);
+ mExposureCompensationButton = (Button) findViewById(R.id.exposurecompensationbutton);
+ mWhiteBalanceButton = (Button) findViewById(R.id.whitebalancebutton);
+ mAutoLockButton = (Button) findViewById(R.id.lockbutton);
+ mMeteringButton = (Button) findViewById(R.id.meteringbutton);
+ mCameraView = (SurfaceView) findViewById(R.id.cameraview);
+ mResultView = (ImageView) findViewById(R.id.resultview);
+ mTestList = (ListView) findViewById(R.id.ca_tests);
+ mAdapter = new TwoColumnAdapter(this);
+
+ // Initialize the list view.
+ initializeAdapter();
+ mTestList.setAdapter(mAdapter);
+ mTestList.setOnItemClickListener(mListListener);
+
+ mFindCheckerButton.setOnClickListener(mFindCheckerListener);
+ mExposureCompensationButton.setOnClickListener(mExposureCompensationListener);
+ mWhiteBalanceButton.setOnClickListener(mWhiteBalanceListener);
+ mAutoLockButton.setOnClickListener(mAutoLockListener);
+ mMeteringButton.setOnClickListener(mMeteringListener);
+ mCameraView.getHolder().addCallback(mSurfaceChangeListener);
+
+ // Disables all test buttons except the color checker test one.
+ // They will be enabled after the color checker is located.
+ mExposureCompensationButton.setEnabled(false);
+ mWhiteBalanceButton.setEnabled(false);
+ mAutoLockButton.setEnabled(false);
+ mMeteringButton.setEnabled(false);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ openCamera(mCameraIdx);
+ Camera.Parameters params = mCamera.getParameters();
+ params.setPictureFormat(ImageFormat.JPEG);
+ params.setPictureSize(640, 480);
+ mCamera.setParameters(params);
+ Log.v(TAG, "Set resolution to 640*480");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ CameraTests.getCamera().release();
+ mIsCameraOpen = false;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.ca_menu, menu);
+
+ Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+ int cameraCount = Camera.getNumberOfCameras();
+ for (int camIdx = 0; camIdx < cameraCount; ++camIdx) {
+ MenuItem cameraMenuItem = menu.add(0, camIdx, Menu.NONE,
+ String.format("Open Camera %d", camIdx));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() != mCameraIdx) {
+ mCameraIdx = item.getItemId();
+ new SwitchCameraTask().execute(mCameraIdx);
+ }
+ return false;
+ }
+
+ private class SwitchCameraTask extends AsyncTask<Integer, Void, Void> {
+ @Override
+ protected Void doInBackground(Integer... camIdx) {
+ if (mTestInProgress) {
+ synchronized (mProcessingTest) {
+ try{
+ Log.v(TAG, "Waiting for test to finish");
+ mProcessingTest.wait();
+ } catch (InterruptedException e){
+ Log.v(TAG, "test wait fails!");
+ }
+ }
+ }
+
+ openCamera((int)camIdx[0]);
+ return null;
+ }
+ }
+
+ private synchronized void openCamera(int camIdx) {
+ if (mIsCameraOpen) {
+ CameraTests.getCamera().release();
+ Log.v(TAG, "Releasing the cameratests camera");
+ }
+ try {
+ mCamera = Camera.open(camIdx);
+ mIsCameraOpen = true;
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Failed to open the camera", e);
+ }
+
+ try {
+ mCamera.setPreviewDisplay(mCameraView.getHolder());
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to connect camera to display: " + e);
+ }
+ mCamera.startPreview();
+ CameraTests.setCamera(mCamera);
+ //ColorCheckerTest.getSingletonTest().updateCamera();
+ //WhiteBalanceTest.getSingletonTest().updateCamera();
+ //ExposureCompensationTest.getSingletonTest().updateCamera();
+ //MeteringTest.getSingletonTest().updateCamera();
+ //AutoLockTest.getSingletonTest().updateCamera();
+ }
+
+ public Camera getCameraInstance() {
+ return mCamera;
+ }
+
+ public void disableAll() {
+ mExposureCompensationButton.setEnabled(false);
+ mWhiteBalanceButton.setEnabled(false);
+ mAutoLockButton.setEnabled(false);
+ mMeteringButton.setEnabled(false);
+ mFindCheckerButton.setEnabled(false);
+ }
+
+ public void enableAll() {
+ mExposureCompensationButton.setEnabled(true);
+ mWhiteBalanceButton.setEnabled(true);
+ mAutoLockButton.setEnabled(true);
+ mMeteringButton.setEnabled(true);
+ mFindCheckerButton.setEnabled(true);
+ }
+
+ /**
+ * Provides an abstraction for the Camera tests. The camera tests will
+ * run in the background and the results will be shown in the UI thread
+ * after the tests are processed.
+ */
+ private class DebugOutputProcessingTask extends AsyncTask<Integer,
+ Integer,
+ Integer> {
+ @Override
+ protected Integer doInBackground(Integer... cameraTestIndex) {
+ Log.v(TAG, "Do in Background started!");
+
+ mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK, "CameraQualityTest");
+ mWakeLock.acquire();
+
+ mTestInProgress = true;
+
+ // Processes the camera tests one by one and publishes their
+ // debug output or debug text results after each test is done.
+ mCurrentTest.run((int)cameraTestIndex[0]);
+ publishProgress(cameraTestIndex);
+
+ Log.v(TAG, "Do in Background thread returns!");
+ return cameraTestIndex[0];
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... cameraTestIndex) {
+ Log.v(TAG, "Prepare to get debug output!");
+
+ // Copies the debug output image or text results to the UI.
+ mResultView.setImageBitmap(mCurrentTest.getDebugOutput());
+ mResultReport += (mCurrentTest.getTestName() + mCurrentTest.getDebugText());
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected void onPostExecute(Integer cameraTestIndex) {
+
+ // If the test is to find the color checker, copy the memory
+ // address of the found color checker centers and radius to the
+ // CameraTests class' static fields.
+ if (mCurrentTest.copyCheckerAddress()) {
+ mCheckerCenterAddress = CameraTests.getCheckerCenter();
+ mCheckerRadiusAddress = CameraTests.getCheckerRadius();
+ }
+
+ if (mCurrentTest.copyCheckerAddress() ||
+ !mCurrentTest.getTestName().contains("Color Checker")) {
+ // Enables the button of all other tests after the color checker
+ // is found. Now the user can start all other available tests.
+ enableAll();
+ }
+
+ mWakeLock.release();
+ mTestInProgress = false;
+ synchronized (mProcessingTest) {
+ mProcessingTest.notifyAll();
+ }
+
+ }
+ }
+
+ // Creates and runs a new test to find color checker in the captured image.
+ // It is invoked when users press the Find color checker button in the UI.
+ private View.OnClickListener mFindCheckerListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.v(TAG, "Running new color checker finding tests!");
+ /*ColorCheckerTest colorCheckerTest = ColorCheckerTest.getSingletonTest();
+
+ mCurrentTest = colorCheckerTest;
+ initializeAdapter();*/
+ }
+ };
+
+ // Creates and runs a new test to test the exposure compensation function.
+ // It is invoked when users press the Exposure Compensation Test button
+ // in the UI.
+ private View.OnClickListener mExposureCompensationListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.v(TAG, "Running new exposure compensation tests!");
+
+ /*ExposureCompensationTest exposureCompensationTest =
+ ExposureCompensationTest.getSingletonTest();
+
+ mCurrentTest = exposureCompensationTest;
+ initializeAdapter();*/
+
+ // Loads the memory address of the checker centers and radius
+ // from the this class and set the two values for the new test.
+ //ExposureCompensationTest.setCheckerAddress(mCheckerCenterAddress,
+ // mCheckerRadiusAddress);
+ }
+ };
+
+ // Creates and runs a new test to test the white balance function.
+ // It is invoked when users press the White Balance Test button in the UI.
+ private View.OnClickListener mWhiteBalanceListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.v(TAG, "Running new white balance tests!");
+
+ /*WhiteBalanceTest whiteBalanceTest = WhiteBalanceTest.getSingletonTest();
+
+ mCurrentTest = whiteBalanceTest;
+ initializeAdapter();*/
+
+ // Loads the memory address of the checker centers and radius
+ // from the this class and set the two values for the new test.
+ //WhiteBalanceTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+ }
+ };
+
+ // Creates and runs a new test to test the camera metering function.
+ // It is invoked when users press the Metering Test button in the UI.
+ private View.OnClickListener mMeteringListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.v(TAG, "Running new metering tests!");
+
+ /*MeteringTest meteringTest = MeteringTest.getSingletonTest();
+
+ mCurrentTest = meteringTest;
+ initializeAdapter();*/
+
+ // Loads the memory address of the checker centers and radius
+ // from the this class and set the two values for the new test.
+ //MeteringTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+ }
+ };
+
+ // Creates and runs new tests to test the camera auto exposure lock.
+ // It is invoked when users press the AWB and AE Lock Test button
+ // in the UI.
+ private View.OnClickListener mAutoLockListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.v(TAG, "Running New auto exposure/wb lock tests!");
+
+ // Loads the memory address of the checker centers and radius
+ // from the this class and set the two values for the new test.
+ //AutoLockTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+
+ // Construct all base case test scenearios for the Auto Lock test.
+ // Detailed documentation on each test can be found in native code.
+ /*AutoLockTest autoLockTest = AutoLockTest.getSingletonTest();
+ autoLockTest.setActivity(CameraAnalyzerActivity.this);
+
+ mCurrentTest = autoLockTest;
+ initializeAdapter();*/
+
+ }
+ };
+
+ // Creates a list listner that launches the experiment with the user's click
+ private AdapterView.OnItemClickListener mListListener = new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Log.v(TAG, String.format("Item %d selected!", position));
+ if (!mTestInProgress) {
+ DebugOutputProcessingTask captureTask = new DebugOutputProcessingTask();
+ disableAll();
+ captureTask.execute(position);
+ }
+ }
+ };
+
+ private SurfaceHolder.Callback mSurfaceChangeListener =
+ new SurfaceHolder.Callback() {
+
+ // Sets the aspect ratio of the camera preview to 4:3
+ @Override
+ public void surfaceChanged(SurfaceHolder holder,
+ int format,
+ int width,
+ int height) {
+ int x = mCameraView.getWidth();
+ int y = mCameraView.getHeight();
+ Log.v(TAG, String.format("Measures are %d, %d", x, y));
+
+ if ( x > 4.0 / 3.0 * y) {
+ android.view.ViewGroup.LayoutParams lp = mCameraView.getLayoutParams();
+ lp.height = y;
+ lp.width = (int)(4.0 / 3.0 * lp.height);
+ Log.v(TAG, String.format("params are %d, %d", lp.width, lp.height));
+ mCameraView.setLayoutParams(lp);
+ }
+
+ try {
+ mCamera.setPreviewDisplay(mCameraView.getHolder());
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to connect camera to display: " + e);
+ }
+ CameraTests.setCameraView(mCameraView);
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ };
+
+ @Override
+ public String getTestDetails() {
+ return mResultReport;
+ }
+
+ class TwoColumnAdapter extends ArrayAdapter<String> {
+ TwoColumnAdapter(Context context) {
+ super(context, R.layout.ca_row);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = getLayoutInflater();
+ View row = inflater.inflate(R.layout.ca_row, parent, false);
+ TextView nameField = (TextView) row.findViewById(R.id.caTestName);
+ TextView resultField = (TextView) row.findViewById(R.id.caTestResult);
+ if (mCurrentTest != null) {
+ nameField.setText(mCurrentTest.getTestName(position));
+ resultField.setText(mCurrentTest.getResult(position));
+ }
+ return row;
+ }
+ }
+
+ private void initializeAdapter() {
+ mAdapter.clear();
+ if (mCurrentTest != null) {
+ for (int i = 0; i < mCurrentTest.getNumTests(); ++i) {
+ mAdapter.add(mCurrentTest.getTestName(i));
+ }
+ }
+ }
+
+ public int getCameraIdx() {
+ return mCameraIdx;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java
new file mode 100644
index 0000000..7cea762
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java
@@ -0,0 +1,197 @@
+/*
+ * 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.cts.verifier.camera.analyzer;
+
+import android.graphics.Bitmap;
+import android.hardware.Camera;
+import android.os.Environment;
+import android.util.Log;
+import android.view.SurfaceView;
+
+import java.io.FileOutputStream;
+import java.io.File;
+import java.lang.Runnable;
+
+/**
+ * Provides an abstraction for all camera tests and allows communication
+ * between camera test classes with the UI thread. This base class provides
+ * functions to contruct and access debug output images. It can access and
+ * set the pointer address of checkerboard centers and radius. It also provides
+ * native methods to convert an image shot by the camera into a native
+ * character array. Another native method it provides is to create a native
+ * test handler with desired debug height and width.
+ */
+public abstract class CameraTests{
+
+ private static final String TAG = "CameraTests";
+
+ /** Memory address of the color checker centers. */
+ private static long sCheckerCenterAddress = 0;
+ /** Memory address of the color checker radius. */
+ private static long sCheckerRadiusAddress = 0;
+ /** The surface view linked with the camera preview. */
+ private static SurfaceView sCameraView;
+ /** Image debug output. */
+ private Bitmap mDebugOutput;
+ /** Shared camera instance. */
+ protected static Camera mTestCamera = null;
+
+ /**
+ * Constructs the base CameraTests class.
+ */
+ public CameraTests() {}
+
+ /**
+ * Returns debug Bitmap image. In the test to find the color checker,
+ * the debug image will be the captured image with a matched color checker
+ * overlay on top. In the exposure compensation test, the debug image
+ * will be the response curve of the camera.
+ * @return A low-resolution Bitmap to be displayed in the UI.
+ */
+ public Bitmap getDebugOutput() {
+ return mDebugOutput;
+ }
+
+ public String getDebugText() {
+ return "";
+ }
+
+ public abstract String getTestName();
+
+ /**
+ * Gets the detailed report for CTS output.
+ */
+ public String getResultText(){
+ return "Details not available \n";
+ }
+
+ /**
+ * Provides a polymorphism to start the run() method for all child classes.
+ */
+ public abstract void run(int index);
+
+ public SurfaceView getCameraView() {
+ return sCameraView;
+ }
+
+ public static void setCameraView(SurfaceView cameraView) {
+ sCameraView = cameraView;
+ }
+
+ /**
+ * Refreshes the camera instance when the activity opens a new camera.
+ */
+ public static void setCamera(Camera newCamera) {
+ mTestCamera = newCamera;
+ }
+
+ public static Camera getCamera() {
+ return mTestCamera;
+ }
+
+ /**
+ * Sets the memory address of the checker centers and checker radius.
+ *
+ * @param inputCenterAddress the new memory address of
+ * the color checker centers
+ * @param inputRadiusAddress the new memory address of
+ * the color checker radius
+ */
+ public static void setCheckerAddress(long inputCenterAddress, long inputRadiusAddress) {
+ sCheckerCenterAddress = inputCenterAddress;
+ sCheckerRadiusAddress = inputRadiusAddress;
+ }
+
+ /**
+ * Provides polymorphism to indicate whether the checker memory addresses
+ * should be copied.
+ *
+ * @return <code>true</code> if the class invoking the method needs to
+ * update the memory address of the color checker
+ * centers and radius;
+ * <code>false</code> if the class invoking the method does NOT
+ * update the memory address of the color checker
+ * centers and radius.
+ */
+ public boolean copyCheckerAddress() {
+ return false;
+ }
+
+ public void cleanUp() {
+ }
+
+ public static long getCheckerCenter() {
+ return sCheckerCenterAddress;
+ }
+
+ public static long getCheckerRadius() {
+ return sCheckerRadiusAddress;
+ }
+
+ public abstract String getTestName(int index);
+
+ public abstract String getResult(int index);
+
+ public abstract int getNumTests();
+
+ /**
+ * Provides a native method to convert the input Bitmap of the captured
+ * image into a native character array and constructs a native image class
+ * with this character array. This method currently supports conversion
+ * of Bitmaps in the formats of RGB_565 and RGB_8888.
+ *
+ * @param input the input Bitmap, which is decoded from the camrea data.
+ *
+ * @return the memory address of the native image class which contains
+ * the character array.
+ */
+ public native long findNative(Bitmap input);
+
+ /**
+ * Provides a native method to create a native image handler class. The
+ * native image handler class is the base class for all test classes and
+ * contains the debug output as a character array.
+ *
+ * @param outputHeight the desired height for the debug output
+ * @param outputWidth the desired width for the debug output
+ *
+ * @return the memory address of the native test handler class.
+ */
+ public native long createImageTestHandler(int outputHeight, int outputWidth);
+
+ /**
+ * Provides a native method to clean up the memory taken by the handler.
+ *
+ * @param handlerAddress the memory address of the native test handler,
+ * which contains the debug output.
+ */
+ public native void cleanUpHandler(long handlerAddress);
+
+ /**
+ * Provides a native method to convert the native debug output from
+ * character array back to Bitmap and copy it to the debug output of this
+ * class.
+ *
+ * @param handlerAddress the memory address of the native test handler,
+ * which contains the debug output.
+ */
+ public native void displayHandlerDebugOutput(long handlerAddress);
+
+ static {
+ System.loadLibrary("cameraanalyzer");
+ }
+}