Home | History | Annotate | Download | only in analyzer
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.cts.verifier.camera.analyzer;
     17 
     18 import com.android.cts.verifier.PassFailButtons;
     19 import com.android.cts.verifier.R;
     20 
     21 import android.app.Activity;
     22 import android.graphics.Bitmap;
     23 import android.graphics.BitmapFactory;
     24 import android.graphics.Color;
     25 import android.graphics.ImageFormat;
     26 import android.hardware.Camera;
     27 import android.hardware.Camera.CameraInfo;
     28 import android.hardware.Camera.Size;
     29 import android.os.AsyncTask;
     30 import android.os.Bundle;
     31 import android.text.Html;
     32 import android.text.method.ScrollingMovementMethod;
     33 import android.util.Log;
     34 import android.view.LayoutInflater;
     35 import android.view.Menu;
     36 import android.view.MenuInflater;
     37 import android.view.MenuItem;
     38 import android.view.SurfaceHolder;
     39 import android.view.SurfaceView;
     40 import android.view.View;
     41 import android.view.ViewGroup;
     42 import android.widget.AdapterView;
     43 import android.widget.ArrayAdapter;
     44 import android.widget.ImageView;
     45 import android.widget.ListView;
     46 import android.widget.TextView;
     47 import android.widget.Button;
     48 import android.os.PowerManager;
     49 import android.os.PowerManager.WakeLock;
     50 import android.content.Context;
     51 
     52 import java.io.IOException;
     53 import java.lang.Thread;
     54 import java.util.List;
     55 
     56 /**
     57  * Controls the UI activities of the camera quality test app. It is created
     58  * as soon as the app started. Users can launch different quality tests with
     59  * the buttons in the UI. This class will manage the threading for different
     60  * tests. Also it will provide debug output or debug text results for tests.
     61  */
     62 public class CameraAnalyzerActivity extends PassFailButtons.Activity {
     63 
     64     private static final String TAG = "CameraAnalyzer";
     65     private SurfaceView mCameraView;
     66     private ImageView mResultView;
     67     private Button mFindCheckerButton;
     68     private Button mExposureCompensationButton;
     69     private Button mWhiteBalanceButton;
     70     private Button mAutoLockButton;
     71     private Button mMeteringButton;
     72     private ListView mTestList;
     73     private TwoColumnAdapter mAdapter;
     74 
     75     private Camera mCamera;
     76     private int mCameraIdx = 0;
     77     private boolean mIsCameraOpen = false;
     78 
     79     private PowerManager mPowerManager;
     80     private PowerManager.WakeLock mWakeLock;
     81 
     82     private boolean mProcessingPicture = false;
     83     private boolean mTestInProgress = false;
     84     private final Object mProcessingTest = new Object();
     85     private CameraTests mCurrentTest = null;
     86 
     87     private long mCheckerCenterAddress;
     88     private long mCheckerRadiusAddress;
     89 
     90     private String mResultReport = "";
     91     static final String[] TESTS = new String[] {"Test1", "Test2"};
     92 
     93     @Override
     94     public void onCreate(Bundle savedInstanceState) {
     95         super.onCreate(savedInstanceState);
     96         setContentView(R.layout.ca_main);
     97         setPassFailButtonClickListeners();
     98         setInfoResources(R.string.camera_analyzer, R.string.ca_info, -1);
     99 
    100         mFindCheckerButton = (Button) findViewById(R.id.findcheckerboardbutton);
    101         mExposureCompensationButton = (Button) findViewById(R.id.exposurecompensationbutton);
    102         mWhiteBalanceButton = (Button) findViewById(R.id.whitebalancebutton);
    103         mAutoLockButton = (Button) findViewById(R.id.lockbutton);
    104         mMeteringButton = (Button) findViewById(R.id.meteringbutton);
    105         mCameraView = (SurfaceView) findViewById(R.id.cameraview);
    106         mResultView = (ImageView) findViewById(R.id.resultview);
    107         mTestList = (ListView) findViewById(R.id.ca_tests);
    108         mAdapter = new TwoColumnAdapter(this);
    109 
    110         // Initialize the list view.
    111         initializeAdapter();
    112         mTestList.setAdapter(mAdapter);
    113         mTestList.setOnItemClickListener(mListListener);
    114 
    115         mFindCheckerButton.setOnClickListener(mFindCheckerListener);
    116         mExposureCompensationButton.setOnClickListener(mExposureCompensationListener);
    117         mWhiteBalanceButton.setOnClickListener(mWhiteBalanceListener);
    118         mAutoLockButton.setOnClickListener(mAutoLockListener);
    119         mMeteringButton.setOnClickListener(mMeteringListener);
    120         mCameraView.getHolder().addCallback(mSurfaceChangeListener);
    121 
    122         // Disables all test buttons except the color checker test one.
    123         // They will be enabled after the color checker is located.
    124         mExposureCompensationButton.setEnabled(false);
    125         mWhiteBalanceButton.setEnabled(false);
    126         mAutoLockButton.setEnabled(false);
    127         mMeteringButton.setEnabled(false);
    128     }
    129 
    130     @Override
    131     public void onResume() {
    132         super.onResume();
    133 
    134         openCamera(mCameraIdx);
    135         Camera.Parameters params = mCamera.getParameters();
    136         params.setPictureFormat(ImageFormat.JPEG);
    137         params.setPictureSize(640, 480);
    138         mCamera.setParameters(params);
    139         Log.v(TAG, "Set resolution to 640*480");
    140     }
    141 
    142     @Override
    143     public void onPause() {
    144         super.onPause();
    145         CameraTests.getCamera().release();
    146         mIsCameraOpen = false;
    147     }
    148 
    149     @Override
    150     public boolean onCreateOptionsMenu(Menu menu) {
    151         MenuInflater inflater = getMenuInflater();
    152         inflater.inflate(R.menu.ca_menu, menu);
    153 
    154         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    155         int cameraCount = Camera.getNumberOfCameras();
    156         for (int camIdx = 0; camIdx < cameraCount; ++camIdx) {
    157             MenuItem cameraMenuItem = menu.add(0, camIdx, Menu.NONE,
    158                                                String.format("Open Camera %d", camIdx));
    159         }
    160       return true;
    161     }
    162 
    163     @Override
    164     public boolean onOptionsItemSelected(MenuItem item) {
    165         if (item.getItemId() != mCameraIdx) {
    166             mCameraIdx = item.getItemId();
    167             new SwitchCameraTask().execute(mCameraIdx);
    168         }
    169         return false;
    170     }
    171 
    172     private class SwitchCameraTask extends AsyncTask<Integer, Void, Void> {
    173         @Override
    174         protected Void doInBackground(Integer... camIdx) {
    175             if (mTestInProgress) {
    176                 synchronized (mProcessingTest) {
    177                     try{
    178                         Log.v(TAG, "Waiting for test to finish");
    179                         mProcessingTest.wait();
    180                     } catch (InterruptedException e){
    181                          Log.v(TAG, "test wait fails!");
    182                     }
    183                 }
    184             }
    185 
    186             openCamera((int)camIdx[0]);
    187             return null;
    188         }
    189     }
    190 
    191     private synchronized void openCamera(int camIdx) {
    192         if (mIsCameraOpen) {
    193             CameraTests.getCamera().release();
    194             Log.v(TAG, "Releasing the cameratests camera");
    195         }
    196         try {
    197             mCamera = Camera.open(camIdx);
    198             mIsCameraOpen = true;
    199         } catch (RuntimeException e) {
    200             throw new RuntimeException("Failed to open the camera", e);
    201         }
    202 
    203         try {
    204             mCamera.setPreviewDisplay(mCameraView.getHolder());
    205         } catch (IOException e) {
    206             throw new RuntimeException("Unable to connect camera to display: " + e);
    207         }
    208         mCamera.startPreview();
    209         CameraTests.setCamera(mCamera);
    210 
    211         ColorCheckerTest.getSingletonTest().updateCamera();
    212         WhiteBalanceTest.getSingletonTest().updateCamera();
    213         ExposureCompensationTest.getSingletonTest().updateCamera();
    214         MeteringTest.getSingletonTest().updateCamera();
    215         AutoLockTest.getSingletonTest().updateCamera();
    216     }
    217 
    218     public Camera getCameraInstance() {
    219         return mCamera;
    220     }
    221 
    222     public void disableAll() {
    223         mExposureCompensationButton.setEnabled(false);
    224         mWhiteBalanceButton.setEnabled(false);
    225         mAutoLockButton.setEnabled(false);
    226         mMeteringButton.setEnabled(false);
    227         mFindCheckerButton.setEnabled(false);
    228     }
    229 
    230     public void enableAll() {
    231         mExposureCompensationButton.setEnabled(true);
    232         mWhiteBalanceButton.setEnabled(true);
    233         mAutoLockButton.setEnabled(true);
    234         mMeteringButton.setEnabled(true);
    235         mFindCheckerButton.setEnabled(true);
    236     }
    237 
    238     /**
    239      * Provides an abstraction for the Camera tests. The camera tests will
    240      * run in the background and the results will be shown in the UI thread
    241      * after the tests are processed.
    242      */
    243     private class DebugOutputProcessingTask extends AsyncTask<Integer,
    244                                                               Integer,
    245                                                               Integer> {
    246         @Override
    247         protected Integer doInBackground(Integer... cameraTestIndex) {
    248             Log.v(TAG, "Do in Background started!");
    249 
    250             mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    251             mWakeLock = mPowerManager.newWakeLock(
    252                     PowerManager.SCREEN_DIM_WAKE_LOCK, "CameraQualityTest");
    253             mWakeLock.acquire();
    254 
    255             mTestInProgress = true;
    256 
    257             // Processes the camera tests one by one and publishes their
    258             // debug output or debug text results after each test is done.
    259             mCurrentTest.run((int)cameraTestIndex[0]);
    260             publishProgress(cameraTestIndex);
    261 
    262             Log.v(TAG, "Do in Background thread returns!");
    263             return cameraTestIndex[0];
    264         }
    265 
    266         @Override
    267         protected void onProgressUpdate(Integer... cameraTestIndex) {
    268             Log.v(TAG, "Prepare to get debug output!");
    269 
    270             // Copies the debug output image or text results to the UI.
    271             mResultView.setImageBitmap(mCurrentTest.getDebugOutput());
    272             mResultReport += (mCurrentTest.getTestName() + mCurrentTest.getDebugText());
    273             mAdapter.notifyDataSetChanged();
    274         }
    275 
    276         @Override
    277         protected void onPostExecute(Integer cameraTestIndex) {
    278 
    279             // If the test is to find the color checker, copy the memory
    280             // address of the found color checker centers and radius to the
    281             // CameraTests class' static fields.
    282             if (mCurrentTest.copyCheckerAddress()) {
    283                 mCheckerCenterAddress = CameraTests.getCheckerCenter();
    284                 mCheckerRadiusAddress = CameraTests.getCheckerRadius();
    285             }
    286 
    287             if (mCurrentTest.copyCheckerAddress() ||
    288                 !mCurrentTest.getTestName().contains("Color Checker")) {
    289                 // Enables the button of all other tests after the color checker
    290                 // is found. Now the user can start all other available tests.
    291                 enableAll();
    292             }
    293 
    294             mWakeLock.release();
    295             mTestInProgress = false;
    296             synchronized (mProcessingTest) {
    297                 mProcessingTest.notifyAll();
    298             }
    299 
    300         }
    301     }
    302 
    303     // Creates and runs a new test to find color checker in the captured image.
    304     // It is invoked when users press the Find color checker button in the UI.
    305     private View.OnClickListener mFindCheckerListener = new View.OnClickListener() {
    306         @Override
    307         public void onClick(View v) {
    308             Log.v(TAG, "Running new color checker finding tests!");
    309             ColorCheckerTest colorCheckerTest = ColorCheckerTest.getSingletonTest();
    310 
    311             mCurrentTest = colorCheckerTest;
    312             initializeAdapter();
    313         }
    314     };
    315 
    316     // Creates and runs a new test to test the exposure compensation function.
    317     // It is invoked when users press the Exposure Compensation Test button
    318     // in the UI.
    319     private View.OnClickListener mExposureCompensationListener = new View.OnClickListener() {
    320         @Override
    321         public void onClick(View v) {
    322             Log.v(TAG, "Running new exposure compensation tests!");
    323 
    324             ExposureCompensationTest exposureCompensationTest =
    325                     ExposureCompensationTest.getSingletonTest();
    326 
    327             mCurrentTest = exposureCompensationTest;
    328             initializeAdapter();
    329 
    330             // Loads the memory address of the checker centers and radius
    331             // from the this class and set the two values for the new test.
    332             ExposureCompensationTest.setCheckerAddress(mCheckerCenterAddress,
    333                                                    mCheckerRadiusAddress);
    334         }
    335     };
    336 
    337     // Creates and runs a new test to test the white balance function.
    338     // It is invoked when users press the White Balance Test button in the UI.
    339     private View.OnClickListener mWhiteBalanceListener = new View.OnClickListener() {
    340         @Override
    341         public void onClick(View v) {
    342             Log.v(TAG, "Running new white balance tests!");
    343 
    344             WhiteBalanceTest whiteBalanceTest = WhiteBalanceTest.getSingletonTest();
    345 
    346             mCurrentTest = whiteBalanceTest;
    347             initializeAdapter();
    348 
    349             // Loads the memory address of the checker centers and radius
    350             // from the this class and set the two values for the new test.
    351             WhiteBalanceTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
    352         }
    353     };
    354 
    355     // Creates and runs a new test to test the camera metering function.
    356     // It is invoked when users press the Metering Test button in the UI.
    357     private View.OnClickListener mMeteringListener = new View.OnClickListener() {
    358         @Override
    359         public void onClick(View v) {
    360             Log.v(TAG, "Running new metering tests!");
    361 
    362             MeteringTest meteringTest = MeteringTest.getSingletonTest();
    363 
    364             mCurrentTest = meteringTest;
    365             initializeAdapter();
    366 
    367             // Loads the memory address of the checker centers and radius
    368             // from the this class and set the two values for the new test.
    369             MeteringTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
    370         }
    371     };
    372 
    373     // Creates and runs new tests to test the camera auto exposure lock.
    374     // It is invoked when users press the AWB and AE Lock Test button
    375     // in the UI.
    376     private View.OnClickListener mAutoLockListener = new View.OnClickListener() {
    377         @Override
    378         public void onClick(View v) {
    379             Log.v(TAG, "Running New auto exposure/wb lock tests!");
    380 
    381             // Loads the memory address of the checker centers and radius
    382             // from the this class and set the two values for the new test.
    383             AutoLockTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
    384 
    385             // Construct all base case test scenearios for the Auto Lock test.
    386             // Detailed documentation on each test can be found in native code.
    387             AutoLockTest autoLockTest = AutoLockTest.getSingletonTest();
    388             autoLockTest.setActivity(CameraAnalyzerActivity.this);
    389 
    390             mCurrentTest = autoLockTest;
    391             initializeAdapter();
    392 
    393         }
    394     };
    395 
    396     // Creates a list listner that launches the experiment with the user's click
    397     private AdapterView.OnItemClickListener mListListener = new AdapterView.OnItemClickListener() {
    398         @Override
    399         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    400             Log.v(TAG, String.format("Item %d selected!", position));
    401             if (!mTestInProgress) {
    402                 DebugOutputProcessingTask captureTask = new DebugOutputProcessingTask();
    403                 disableAll();
    404                 captureTask.execute(position);
    405             }
    406         }
    407     };
    408 
    409     private SurfaceHolder.Callback mSurfaceChangeListener =
    410             new SurfaceHolder.Callback() {
    411 
    412         // Sets the aspect ratio of the camera preview to 4:3
    413         @Override
    414         public void surfaceChanged(SurfaceHolder holder,
    415                                    int format,
    416                                    int width,
    417                                    int height) {
    418             int x = mCameraView.getWidth();
    419             int y = mCameraView.getHeight();
    420             Log.v(TAG, String.format("Measures are %d, %d", x, y));
    421 
    422             if ( x > 4.0 / 3.0 * y) {
    423                 android.view.ViewGroup.LayoutParams lp = mCameraView.getLayoutParams();
    424                 lp.height =  y;
    425                 lp.width = (int)(4.0 / 3.0 * lp.height);
    426                 Log.v(TAG, String.format("params are %d, %d", lp.width, lp.height));
    427                 mCameraView.setLayoutParams(lp);
    428             }
    429 
    430             try {
    431                 mCamera.setPreviewDisplay(mCameraView.getHolder());
    432             } catch (IOException e) {
    433                 throw new RuntimeException("Unable to connect camera to display: " + e);
    434             }
    435             CameraTests.setCameraView(mCameraView);
    436             mCamera.startPreview();
    437         }
    438 
    439         @Override
    440         public void surfaceCreated(SurfaceHolder holder) {}
    441 
    442         @Override
    443         public void surfaceDestroyed(SurfaceHolder holder) {}
    444     };
    445 
    446     @Override
    447     public String getTestDetails() {
    448         return mResultReport;
    449     }
    450 
    451     class TwoColumnAdapter extends ArrayAdapter<String> {
    452         TwoColumnAdapter(Context context) {
    453             super(context, R.layout.ca_row);
    454         }
    455 
    456         @Override
    457         public View getView(int position, View convertView, ViewGroup parent) {
    458             LayoutInflater inflater = getLayoutInflater();
    459             View row = inflater.inflate(R.layout.ca_row, parent, false);
    460             ImageView iconField = (ImageView) row.findViewById(R.id.caTestIcon);
    461             TextView nameField = (TextView) row.findViewById(R.id.caTestName);
    462             TextView resultField = (TextView) row.findViewById(R.id.caTestResult);
    463             if (mCurrentTest != null) {
    464                 nameField.setText(mCurrentTest.getTestName(position));
    465                 int result = mCurrentTest.getResult(position);
    466                 switch (result) {
    467                     case CameraTests.CAMERA_TEST_SUCCESS:
    468                         resultField.setText("Success");
    469                         iconField.setBackgroundColor(Color.rgb(0x99,0xCC,00));
    470                         resultField.setTextColor(Color.rgb(0x99,0xCC,00));
    471                         break;
    472                     case CameraTests.CAMERA_TEST_FAILURE:
    473                         resultField.setText("Failed!");
    474                         iconField.setBackgroundColor(Color.rgb(0xFF,0x44,0x44));
    475                         resultField.setTextColor(Color.rgb(0xFF,0x44,0x44));
    476                         break;
    477                     case CameraTests.CAMERA_TEST_NOT_RUN:
    478                         resultField.setText("Tap to run");
    479                         iconField.setBackgroundColor(Color.rgb(0x00,0x99,0xCC));
    480                         resultField.setTextColor(Color.rgb(0x33,0xB5,0xE5));
    481                         break;
    482                 }
    483             }
    484             return row;
    485         }
    486     }
    487 
    488     private void initializeAdapter() {
    489         mAdapter.clear();
    490         if (mCurrentTest != null) {
    491             for (int i = 0; i < mCurrentTest.getNumTests(); ++i) {
    492                 mAdapter.add(mCurrentTest.getTestName(i));
    493             }
    494         }
    495     }
    496 
    497     public int getCameraIdx() {
    498         return mCameraIdx;
    499     }
    500 }
    501