• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onResume()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
onPause()143     public void onPause() {
144         super.onPause();
145         CameraTests.getCamera().release();
146         mIsCameraOpen = false;
147     }
148 
149     @Override
onCreateOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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
doInBackground(Integer... camIdx)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 
openCamera(int camIdx)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 
getCameraInstance()218     public Camera getCameraInstance() {
219         return mCamera;
220     }
221 
disableAll()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 
enableAll()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
doInBackground(Integer... cameraTestIndex)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
onProgressUpdate(Integer... cameraTestIndex)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
onPostExecute(Integer cameraTestIndex)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
getTestDetails()447     public String getTestDetails() {
448         return mResultReport;
449     }
450 
451     class TwoColumnAdapter extends ArrayAdapter<String> {
TwoColumnAdapter(Context context)452         TwoColumnAdapter(Context context) {
453             super(context, R.layout.ca_row);
454         }
455 
456         @Override
getView(int position, View convertView, ViewGroup parent)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 
initializeAdapter()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 
getCameraIdx()497     public int getCameraIdx() {
498         return mCameraIdx;
499     }
500 }
501