TestingCamera.java revision 4ae1a87883045d820ffd6e1b8a95c13b1919e00a
1/*
2 * Copyright (C) 2012 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
17package com.android.testingcamera;
18
19import android.app.Activity;
20import android.app.Dialog;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.hardware.Camera;
24import android.media.CamcorderProfile;
25import android.os.Bundle;
26import android.view.View;
27import android.view.SurfaceHolder;
28import android.view.SurfaceView;
29import android.view.View.OnClickListener;
30import android.widget.AdapterView;
31import android.widget.ArrayAdapter;
32import android.widget.Button;
33import android.widget.ImageView;
34import android.widget.LinearLayout.LayoutParams;
35import android.widget.Spinner;
36import android.widget.CompoundButton;
37import android.widget.TextView;
38import android.widget.ToggleButton;
39import android.support.v4.app.FragmentActivity;
40import android.support.v4.app.FragmentManager;
41import android.util.Log;
42
43import java.io.IOException;
44import java.util.ArrayList;
45import java.util.Comparator;
46import java.util.List;
47import java.util.TreeSet;
48
49/**
50 * A simple test application for the camera API.
51 *
52 * The goal of this application is to allow all camera API features to be
53 * excercised, and all information provided by the API to be shown.
54 */
55public class TestingCamera extends FragmentActivity implements SurfaceHolder.Callback {
56
57    /** UI elements */
58    private SurfaceView mPreviewView;
59    private SurfaceHolder mPreviewHolder;
60
61    private Spinner mCameraSpinner;
62    private Button mInfoButton;
63    private Spinner mPreviewSizeSpinner;
64    private ToggleButton mPreviewToggle;
65    private Spinner mSnapshotSizeSpinner;
66    private Button  mTakePictureButton;
67    private Spinner mCamcorderProfileSpinner;
68    private ToggleButton mRecordToggle;
69
70    /** Camera state */
71    private int mCameraId = 0;
72    private Camera mCamera;
73    private Camera.Parameters mParams;
74    private List<Camera.Size> mPreviewSizes;
75    private int mPreviewSize = 0;
76    private List<Camera.Size> mSnapshotSizes;
77    private int mSnapshotSize = 0;
78    private List<CamcorderProfile> mCamcorderProfiles;
79    private int mCamcorderProfile = 0;
80
81    private static final int CAMERA_UNINITIALIZED = 0;
82    private static final int CAMERA_OPEN = 1;
83    private static final int CAMERA_PREVIEW = 2;
84    private static final int CAMERA_TAKE_PICTURE = 3;
85    private int mState = CAMERA_UNINITIALIZED;
86
87    /** Misc variables */
88
89    private static final String TAG = "TestingCamera";
90
91    // Activity methods
92    @Override
93    public void onCreate(Bundle savedInstanceState) {
94        super.onCreate(savedInstanceState);
95
96        setContentView(R.layout.main);
97
98        mPreviewView = (SurfaceView)findViewById(R.id.preview);
99        mPreviewView.getHolder().addCallback(this);
100
101        mCameraSpinner = (Spinner) findViewById(R.id.camera_spinner);
102        mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
103
104        mInfoButton = (Button) findViewById(R.id.info_button);
105        mInfoButton.setOnClickListener(mInfoButtonListener);
106
107        mPreviewSizeSpinner = (Spinner) findViewById(R.id.preview_size_spinner);
108        mPreviewSizeSpinner.setOnItemSelectedListener(mPreviewSizeListener);
109
110        mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview);
111        mPreviewToggle.setOnClickListener(mPreviewToggleListener);
112
113        mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner);
114        mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener);
115
116        mTakePictureButton = (Button) findViewById(R.id.take_picture);
117        mTakePictureButton.setOnClickListener(mTakePictureListener);
118
119        mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner);
120        mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener);
121
122        mRecordToggle = (ToggleButton) findViewById(R.id.start_record);
123        mRecordToggle.setOnClickListener(mRecordToggleListener);
124
125        int numCameras = Camera.getNumberOfCameras();
126        String[] cameraNames = new String[numCameras];
127        for (int i = 0; i < numCameras; i++) {
128            cameraNames[i] = "Camera " + i;
129        }
130
131        mCameraSpinner.setAdapter(
132                new ArrayAdapter<String>(this,
133                        R.layout.spinner_item, cameraNames));
134    }
135
136    @Override
137    public void onResume() {
138        super.onResume();
139        mPreviewHolder = null;
140        setUpCamera();
141    }
142
143    @Override
144    public void onPause() {
145        super.onPause();
146
147        mCamera.release();
148        mState = CAMERA_UNINITIALIZED;
149    }
150
151    // SurfaceHolder.Callback methods
152    @Override
153    public void surfaceChanged(SurfaceHolder holder,
154            int format,
155            int width,
156            int height) {
157        if (mPreviewHolder != null) return;
158
159        Log.d(TAG, "Surface holder available: " + width + " x " + height);
160        mPreviewHolder = holder;
161        try {
162            mCamera.setPreviewDisplay(holder);
163        } catch (IOException e) {
164            Log.e(TAG, "Unable to set up preview!");
165        }
166        resizePreview(mPreviewSizes.get(mPreviewSize).width,
167                mPreviewSizes.get(mPreviewSize).height);
168    }
169
170    @Override
171    public void surfaceCreated(SurfaceHolder holder) {
172
173    }
174
175    @Override
176    public void surfaceDestroyed(SurfaceHolder holder) {
177
178    }
179
180    // UI listeners
181
182    private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
183                new AdapterView.OnItemSelectedListener() {
184        public void onItemSelected(AdapterView<?> parent,
185                        View view, int pos, long id) {
186                if (mCameraId != pos) {
187                        mCameraId = pos;
188                        setUpCamera();
189                }
190        }
191
192        public void onNothingSelected(AdapterView parent) {
193
194        }
195    };
196
197    private OnClickListener mInfoButtonListener = new OnClickListener() {
198
199                @Override
200                public void onClick(View v) {
201                        FragmentManager fm = getSupportFragmentManager();
202                InfoDialogFragment infoDialog = new InfoDialogFragment();
203                infoDialog.updateInfo(mCameraId, mCamera);
204                infoDialog.show(fm, "info_dialog_fragment");
205                }
206
207    };
208
209    private AdapterView.OnItemSelectedListener mPreviewSizeListener =
210            new AdapterView.OnItemSelectedListener() {
211                public void onItemSelected(AdapterView<?> parent,
212                        View view, int pos, long id) {
213                    if (pos == mPreviewSize) return;
214                    Log.d(TAG, "Switching preview sizes");
215
216                    if (mState == CAMERA_PREVIEW) {
217                        mCamera.stopPreview();
218                    }
219
220                    mPreviewSize = pos;
221                    int width = mPreviewSizes.get(mPreviewSize).width;
222                    int height = mPreviewSizes.get(mPreviewSize).height;
223                    mParams.setPreviewSize(width, height);
224
225                    mCamera.setParameters(mParams);
226                    resizePreview(width, height);
227
228                    if (mState == CAMERA_PREVIEW) {
229                        mCamera.startPreview();
230                    }
231                }
232
233                public void onNothingSelected(AdapterView parent) {
234
235                }
236            };
237
238    private View.OnClickListener mPreviewToggleListener =
239            new View.OnClickListener() {
240        public void onClick(View v) {
241            if (mState == CAMERA_TAKE_PICTURE) {
242                Log.e(TAG, "Can't change preview state while taking picture!");
243                return;
244            }
245            if (mPreviewToggle.isChecked()) {
246                Log.d(TAG, "Starting preview");
247
248                mCamera.startPreview();
249                mState = CAMERA_PREVIEW;
250
251                mTakePictureButton.setEnabled(true);
252            } else {
253                Log.d(TAG, "Stopping preview");
254                mCamera.stopPreview();
255                mState = CAMERA_OPEN;
256
257                mTakePictureButton.setEnabled(false);
258            }
259        }
260    };
261
262    private AdapterView.OnItemSelectedListener mSnapshotSizeListener =
263            new AdapterView.OnItemSelectedListener() {
264                public void onItemSelected(AdapterView<?> parent,
265                        View view, int pos, long id) {
266                    if (pos == mSnapshotSize) return;
267                    Log.d(TAG, "Switching snapshot sizes");
268
269                    mSnapshotSize = pos;
270                    int width = mSnapshotSizes.get(mSnapshotSize).width;
271                    int height = mSnapshotSizes.get(mSnapshotSize).height;
272                    mParams.setPictureSize(width, height);
273
274                    mCamera.setParameters(mParams);
275                }
276
277                public void onNothingSelected(AdapterView parent) {
278
279                }
280            };
281
282    private View.OnClickListener mTakePictureListener =
283            new View.OnClickListener() {
284        public void onClick(View v) {
285            Log.d(TAG, "Taking picture");
286            if (mState == CAMERA_PREVIEW) {
287                mState = CAMERA_TAKE_PICTURE;
288
289                mTakePictureButton.setEnabled(false);
290                mPreviewToggle.setEnabled(false);
291                mPreviewToggle.setChecked(false);
292
293                mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb);
294            } else {
295                Log.e(TAG, "Can't take picture while not running preview!");
296            }
297        }
298    };
299
300    private AdapterView.OnItemSelectedListener mCamcorderProfileListener =
301                new AdapterView.OnItemSelectedListener() {
302        public void onItemSelected(AdapterView<?> parent,
303                        View view, int pos, long id) {
304                mCamcorderProfile = pos;
305        }
306
307        public void onNothingSelected(AdapterView parent) {
308
309        }
310    };
311
312    private View.OnClickListener mRecordToggleListener =
313                new View.OnClickListener() {
314        public void onClick(View v) {
315        }
316    };
317
318    private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() {
319        public void onShutter() {
320            Log.d(TAG, "Shutter cb fired");
321        }
322    };
323
324    private Camera.PictureCallback mRawCb = new Camera.PictureCallback() {
325        public void onPictureTaken(byte[] data, Camera camera) {
326            Log.d(TAG, "Raw cb fired");
327        }
328    };
329
330    private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() {
331        public void onPictureTaken(byte[] data, Camera camera) {
332            Log.d(TAG, "Postview cb fired");
333        }
334    };
335
336    private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() {
337        public void onPictureTaken(byte[] data, Camera camera) {
338            Log.d(TAG, "JPEG cb fired");
339            FragmentManager fm = getSupportFragmentManager();
340                SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment();
341
342            Bitmap img = BitmapFactory.decodeByteArray(data, 0, data.length);
343            snapshotDialog.updateImage(img);
344            snapshotDialog.show(fm, "snapshot_dialog_fragment");
345
346            mPreviewToggle.setEnabled(true);
347
348            mState = CAMERA_OPEN;
349        }
350    };
351
352    // Internal methods
353
354    void setUpCamera() {
355        Log.d(TAG, "Setting up camera " + mCameraId);
356        if (mState >= CAMERA_OPEN) {
357            Log.d(TAG, "Closing old camera");
358            mCamera.release();
359            mState = CAMERA_UNINITIALIZED;
360        }
361        Log.d(TAG, "Opening camera " + mCameraId);
362        mCamera = Camera.open(mCameraId);
363        mState = CAMERA_OPEN;
364
365        mParams = mCamera.getParameters();
366
367        // Set up preview size selection
368        mPreviewSizes = mParams.getSupportedPreviewSizes();
369
370        String[] availableSizeNames = new String[mPreviewSizes.size()];
371        int i = 0;
372        for (Camera.Size previewSize: mPreviewSizes) {
373            availableSizeNames[i++] =
374                    Integer.toString(previewSize.width) + " x " +
375                    Integer.toString(previewSize.height);
376        }
377        mPreviewSizeSpinner.setAdapter(
378            new ArrayAdapter<String>(
379                this, R.layout.spinner_item, availableSizeNames));
380
381        mPreviewSize = 0;
382
383        int width = mPreviewSizes.get(mPreviewSize).width;
384        int height = mPreviewSizes.get(mPreviewSize).height;
385        mParams.setPreviewSize(width, height);
386
387        // Set up snapshot size selection
388
389        mSnapshotSizes = mParams.getSupportedPictureSizes();
390
391        availableSizeNames = new String[mSnapshotSizes.size()];
392        i = 0;
393        for (Camera.Size snapshotSize : mSnapshotSizes) {
394            availableSizeNames[i++] =
395                    Integer.toString(snapshotSize.width) + " x " +
396                    Integer.toString(snapshotSize.height);
397        }
398        mSnapshotSizeSpinner.setAdapter(
399            new ArrayAdapter<String>(
400                this, R.layout.spinner_item, availableSizeNames));
401
402        mSnapshotSize = 0;
403
404        width = mSnapshotSizes.get(mSnapshotSize).width;
405        height = mSnapshotSizes.get(mSnapshotSize).height;
406        mParams.setPictureSize(width, height);
407
408        // Set up camcorder profile selection
409        updateCamcorderProfile(mCameraId);
410
411        // Update parameters based on above defaults
412
413        mCamera.setParameters(mParams);
414
415        if (mPreviewHolder != null) {
416            try {
417                mCamera.setPreviewDisplay(mPreviewHolder);
418            } catch(IOException e) {
419                Log.e(TAG, "Unable to set up preview!");
420            }
421        }
422
423        mPreviewToggle.setEnabled(true);
424        mPreviewToggle.setChecked(false);
425        mTakePictureButton.setEnabled(false);
426
427        resizePreview(width, height);
428        if (mPreviewToggle.isChecked()) {
429            Log.d(TAG, "Starting preview" );
430             mCamera.startPreview();
431            mState = CAMERA_PREVIEW;
432        }
433    }
434
435    private void updateCamcorderProfile(int cameraId) {
436        // Have to query all of these individually,
437        final int PROFILES[] = new int[] {
438                        CamcorderProfile.QUALITY_1080P,
439                        CamcorderProfile.QUALITY_480P,
440                        CamcorderProfile.QUALITY_720P,
441                        CamcorderProfile.QUALITY_CIF,
442                        CamcorderProfile.QUALITY_HIGH,
443                        CamcorderProfile.QUALITY_LOW,
444                        CamcorderProfile.QUALITY_QCIF,
445                        CamcorderProfile.QUALITY_QVGA,
446                        CamcorderProfile.QUALITY_TIME_LAPSE_1080P,
447                        CamcorderProfile.QUALITY_TIME_LAPSE_480P,
448                        CamcorderProfile.QUALITY_TIME_LAPSE_720P,
449                        CamcorderProfile.QUALITY_TIME_LAPSE_CIF,
450                        CamcorderProfile.QUALITY_TIME_LAPSE_HIGH,
451                        CamcorderProfile.QUALITY_TIME_LAPSE_LOW,
452                        CamcorderProfile.QUALITY_TIME_LAPSE_QCIF,
453                        CamcorderProfile.QUALITY_TIME_LAPSE_QVGA
454        };
455
456        final String PROFILE_NAMES[] = new String[] {
457                        "1080P",
458                        "480P",
459                        "720P",
460                        "CIF",
461                        "HIGH",
462                        "LOW",
463                        "QCIF",
464                        "QVGA",
465                        "TIME_LAPSE_1080P",
466                        "TIME_LAPSE_480P",
467                        "TIME_LAPSE_720P",
468                        "TIME_LAPSE_CIF",
469                        "TIME_LAPSE_HIGH",
470                        "TIME_LAPSE_LOW",
471                        "TIME_LAPSE_QCIF",
472                        "TIME_LAPSE_QVGA"
473        };
474
475        List<String> availableCamcorderProfileNames = new ArrayList<String>();
476        mCamcorderProfiles = new ArrayList<CamcorderProfile>();
477
478        for (int i = 0; i < PROFILES.length; i++) {
479                if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) {
480                        availableCamcorderProfileNames.add(PROFILE_NAMES[i]);
481                        mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i]));
482                }
483        }
484        String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]);
485        mCamcorderProfileSpinner.setAdapter(
486                        new ArrayAdapter<String>(
487                                        this, R.layout.spinner_item, nameArray));
488    }
489
490    void resizePreview(int width, int height) {
491        if (mPreviewHolder != null) {
492            int viewHeight = mPreviewView.getHeight();
493            int viewWidth = (int)(((double)width)/height * viewHeight);
494
495            mPreviewView.setLayoutParams(
496                new LayoutParams(viewWidth, viewHeight));
497        }
498
499    }
500}