Home | History | Annotate | Download | only in testingcamera2
      1 /*
      2  * Copyright (C) 2014 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 
     17 package com.android.testingcamera2;
     18 
     19 import java.util.ArrayList;
     20 import java.util.HashSet;
     21 import java.util.LinkedList;
     22 import java.util.List;
     23 import java.util.Locale;
     24 import java.util.Set;
     25 
     26 import android.content.Context;
     27 import android.util.AttributeSet;
     28 import android.view.LayoutInflater;
     29 import android.view.Surface;
     30 import android.view.View;
     31 import android.widget.AdapterView;
     32 import android.widget.AdapterView.OnItemSelectedListener;
     33 import android.widget.ArrayAdapter;
     34 import android.widget.Button;
     35 import android.widget.CompoundButton;
     36 import android.widget.Spinner;
     37 import android.widget.TextView;
     38 import android.widget.ToggleButton;
     39 import android.hardware.camera2.CameraAccessException;
     40 import android.hardware.camera2.CameraCaptureSession;
     41 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
     42 import android.hardware.camera2.CameraCharacteristics;
     43 import android.hardware.camera2.CameraDevice;
     44 import android.hardware.camera2.CameraManager;
     45 import android.hardware.camera2.CaptureRequest;
     46 import android.hardware.camera2.CaptureResult;
     47 import android.hardware.camera2.CaptureFailure;
     48 import android.hardware.camera2.TotalCaptureResult;
     49 
     50 import org.xmlpull.v1.XmlPullParser;
     51 import org.xmlpull.v1.XmlPullParserException;
     52 
     53 import com.android.testingcamera2.PaneTracker.PaneEvent;
     54 
     55 import java.io.IOException;
     56 
     57 /**
     58  *
     59  * Basic control pane block for the control list
     60  *
     61  */
     62 public class CameraControlPane extends ControlPane {
     63 
     64     // XML attributes
     65 
     66     /** Name of pane tag */
     67     private static final String PANE_NAME = "camera_pane";
     68 
     69     /** Attribute: ID for pane (integer) */
     70     private static final String PANE_ID = "id";
     71     /** Attribute: ID for camera to select (String) */
     72     private static final String CAMERA_ID = "camera_id";
     73 
     74     // End XML attributes
     75 
     76     private static final int MAX_CACHED_RESULTS = 100;
     77 
     78     private static int mCameraPaneIdCounter = 0;
     79 
     80     /**
     81      * These correspond to the callbacks from
     82      * android.hardware.camera2.CameraDevice.StateCallback, plus UNAVAILABLE for
     83      * when there's not a valid camera selected.
     84      */
     85     private enum CameraState {
     86         UNAVAILABLE,
     87         CLOSED,
     88         OPENED,
     89         DISCONNECTED,
     90         ERROR
     91     }
     92 
     93     /**
     94      * These correspond to the callbacks from {@link CameraCaptureSession.StateCallback}, plus
     95      * {@code CONFIGURING} for before a session is returned and {@code NONE} for when there
     96      * is no session created.
     97      */
     98     private enum SessionState {
     99         NONE,
    100         CONFIGURED,
    101         CONFIGURE_FAILED,
    102         READY,
    103         ACTIVE,
    104         CLOSED
    105     }
    106 
    107     private enum CameraCall {
    108         NONE,
    109         CONFIGURE
    110     }
    111 
    112     private final int mPaneId;
    113 
    114     private CameraOps2 mCameraOps;
    115     private InfoDisplayer mInfoDisplayer;
    116 
    117     private Spinner mCameraSpinner;
    118     private ToggleButton mOpenButton;
    119     private Button mInfoButton;
    120     private TextView mStatusText;
    121     private Button mConfigureButton;
    122     private Button mStopButton;
    123     private Button mFlushButton;
    124 
    125     /**
    126      * All controls that should be enabled when there's a valid camera ID
    127      * selected
    128      */
    129     private final Set<View> mBaseControls = new HashSet<View>();
    130     /**
    131      * All controls that should be enabled when camera is at least in the OPEN
    132      * state
    133      */
    134     private final Set<View> mOpenControls = new HashSet<View>();
    135     /**
    136      * All controls that should be enabled when camera is at least in the IDLE
    137      * state
    138      */
    139     private final Set<View> mConfiguredControls = new HashSet<View>();
    140 
    141     private String[] mCameraIds;
    142     private String mCurrentCameraId;
    143 
    144     private CameraState mCameraState;
    145     private CameraDevice mCurrentCamera;
    146     private CameraCaptureSession mCurrentCaptureSession;
    147     private SessionState mSessionState = SessionState.NONE;
    148     private CameraCall mActiveCameraCall;
    149     private LinkedList<TotalCaptureResult> mRecentResults = new LinkedList<>();
    150 
    151     private List<Surface> mConfiguredSurfaces;
    152     private List<TargetControlPane> mConfiguredTargetPanes;
    153 
    154     /**
    155      * Constructor for tooling only
    156      */
    157     public CameraControlPane(Context context, AttributeSet attrs) {
    158         super(context, attrs, null, null);
    159 
    160         mPaneId = 0;
    161         setUpUI(context);
    162     }
    163 
    164     public CameraControlPane(TestingCamera21 tc, AttributeSet attrs, StatusListener listener) {
    165 
    166         super(tc, attrs, listener, tc.getPaneTracker());
    167 
    168         mPaneId = mCameraPaneIdCounter++;
    169         setUpUI(tc);
    170         initializeCameras(tc);
    171 
    172         if (mCameraIds != null) {
    173             switchToCamera(mCameraIds[0]);
    174         }
    175     }
    176 
    177     public CameraControlPane(TestingCamera21 tc, XmlPullParser configParser, StatusListener listener)
    178             throws XmlPullParserException, IOException {
    179         super(tc, null, listener, tc.getPaneTracker());
    180 
    181         configParser.require(XmlPullParser.START_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME);
    182 
    183         int paneId = getAttributeInt(configParser, PANE_ID, -1);
    184         if (paneId == -1) {
    185             mPaneId = mCameraPaneIdCounter++;
    186         } else {
    187             mPaneId = paneId;
    188             if (mPaneId >= mCameraPaneIdCounter) {
    189                 mCameraPaneIdCounter = mPaneId + 1;
    190             }
    191         }
    192 
    193         String cameraId = getAttributeString(configParser, CAMERA_ID, null);
    194 
    195         configParser.next();
    196         configParser.require(XmlPullParser.END_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME);
    197 
    198         setUpUI(tc);
    199         initializeCameras(tc);
    200 
    201         boolean gotCamera = false;
    202         if (mCameraIds != null && cameraId != null) {
    203             for (int i = 0; i < mCameraIds.length; i++) {
    204                 if (cameraId.equals(mCameraIds[i])) {
    205                     switchToCamera(mCameraIds[i]);
    206                     mCameraSpinner.setSelection(i);
    207                     gotCamera = true;
    208                 }
    209             }
    210         }
    211 
    212         if (!gotCamera && mCameraIds != null) {
    213             switchToCamera(mCameraIds[0]);
    214         }
    215     }
    216 
    217     @Override
    218     public void remove() {
    219         closeCurrentCamera();
    220         super.remove();
    221     }
    222 
    223     /**
    224      * Get list of target panes that are currently actively configured for this
    225      * camera
    226      */
    227     public List<TargetControlPane> getCurrentConfiguredTargets() {
    228         return mConfiguredTargetPanes;
    229     }
    230 
    231     /**
    232      * Interface to be implemented by an application service for displaying a
    233      * camera's information.
    234      */
    235     public interface InfoDisplayer {
    236         public void showCameraInfo(String cameraId);
    237     }
    238 
    239     public CameraCharacteristics getCharacteristics() {
    240         if (mCurrentCameraId != null) {
    241             return mCameraOps.getCameraInfo(mCurrentCameraId);
    242         }
    243         return null;
    244     }
    245 
    246     public CaptureRequest.Builder getRequestBuilder(int template) {
    247         CaptureRequest.Builder request = null;
    248         if (mCurrentCamera != null) {
    249             try {
    250                 request = mCurrentCamera.createCaptureRequest(template);
    251                 // Workaround for b/15748139
    252                 request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
    253                         CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
    254             } catch (CameraAccessException e) {
    255                 TLog.e("Unable to build request for camera %s with template %d.", e,
    256                         mCurrentCameraId, template);
    257             }
    258         }
    259         return request;
    260     }
    261 
    262     /**
    263      * Send single capture to camera device.
    264      *
    265      * @param request
    266      * @return true if capture sent successfully
    267      */
    268     public boolean capture(CaptureRequest request) {
    269         if (mCurrentCaptureSession != null) {
    270             try {
    271                 mCurrentCaptureSession.capture(request, mResultListener, null);
    272                 return true;
    273             } catch (CameraAccessException e) {
    274                 TLog.e("Unable to capture for camera %s.", e, mCurrentCameraId);
    275             }
    276         }
    277         return false;
    278     }
    279 
    280     public boolean repeat(CaptureRequest request) {
    281         if (mCurrentCaptureSession != null) {
    282             try {
    283                 mCurrentCaptureSession.setRepeatingRequest(request, mResultListener, null);
    284                 return true;
    285             } catch (CameraAccessException e) {
    286                 TLog.e("Unable to set repeating request for camera %s.", e, mCurrentCameraId);
    287             }
    288         }
    289         return false;
    290     }
    291 
    292     public TotalCaptureResult getResultAt(long timestamp) {
    293         for (TotalCaptureResult result : mRecentResults) {
    294             long resultTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
    295             if (resultTimestamp == timestamp) return result;
    296             if (resultTimestamp > timestamp) return null;
    297         }
    298         return null;
    299     }
    300 
    301     public void prepareSurface(Surface target) {
    302         if (mCurrentCaptureSession != null) {
    303             try {
    304                 TLog.i("Preparing Surface " + target);
    305                 mCurrentCaptureSession.prepare(target);
    306             } catch (CameraAccessException e) {
    307                 TLog.e("Unable to prepare surface for camera %s.", e, mCurrentCameraId);
    308             } catch (IllegalArgumentException e) {
    309                 TLog.e("Bad Surface passed to prepare", e);
    310             }
    311         }
    312     }
    313 
    314     private CaptureCallback mResultListener = new CaptureCallback() {
    315         @Override
    316         public void onCaptureStarted(CameraCaptureSession session,
    317                 CaptureRequest request, long timestamp, long frameNumber) {
    318         }
    319 
    320         @Override
    321         public void onCaptureProgressed(CameraCaptureSession session,
    322                 CaptureRequest request, CaptureResult partialResult) {
    323         }
    324 
    325         @Override
    326         public void onCaptureCompleted(
    327                 CameraCaptureSession session,
    328                 CaptureRequest request,
    329                 TotalCaptureResult result) {
    330             mRecentResults.add(result);
    331             if (mRecentResults.size() > MAX_CACHED_RESULTS) {
    332                 mRecentResults.remove();
    333             }
    334         }
    335 
    336         @Override
    337         public void onCaptureFailed(CameraCaptureSession session,
    338                 CaptureRequest request, CaptureFailure failure) {
    339             TLog.e("Capture failed for request " + request +
    340                     " on frame  " + failure.getFrameNumber() + ": Reason " + failure.getReason() +
    341                     ". Images captured: " + failure.wasImageCaptured());
    342         }
    343 
    344         @Override
    345         public void onCaptureSequenceCompleted(CameraCaptureSession session,
    346                 int sequenceId, long frameNumber) {
    347         }
    348 
    349         @Override
    350         public void onCaptureSequenceAborted(CameraCaptureSession session,
    351                 int sequenceId) {
    352         }
    353 
    354         @Override
    355         public void onCaptureBufferLost(CameraCaptureSession session,
    356                 CaptureRequest request, Surface target, long frameNumber) {
    357             TLog.e("Lost buffer for Surface " + target + " for request " + request +
    358                     " on frame " + frameNumber);
    359         }
    360 
    361     };
    362 
    363     private void setUpUI(Context context) {
    364         String paneName =
    365                 String.format(Locale.US, "%s %c",
    366                         context.getResources().getString(R.string.camera_pane_title),
    367                         (char) ('A' + mPaneId));
    368         this.setName(paneName);
    369 
    370         LayoutInflater inflater =
    371                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    372 
    373         inflater.inflate(R.layout.camera_pane, this);
    374 
    375         mCameraSpinner = (Spinner) findViewById(R.id.camera_pane_camera_spinner);
    376         mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
    377 
    378         mOpenButton = (ToggleButton) findViewById(R.id.camera_pane_open_button);
    379         mOpenButton.setOnCheckedChangeListener(mOpenButtonListener);
    380         mBaseControls.add(mOpenButton);
    381 
    382         mInfoButton = (Button) findViewById(R.id.camera_pane_info_button);
    383         mInfoButton.setOnClickListener(mInfoButtonListener);
    384         mBaseControls.add(mInfoButton);
    385 
    386         mStatusText = (TextView) findViewById(R.id.camera_pane_status_text);
    387 
    388         mConfigureButton = (Button) findViewById(R.id.camera_pane_configure_button);
    389         mConfigureButton.setOnClickListener(mConfigureButtonListener);
    390         mOpenControls.add(mConfigureButton);
    391 
    392         mStopButton = (Button) findViewById(R.id.camera_pane_stop_button);
    393         mStopButton.setOnClickListener(mStopButtonListener);
    394         mConfiguredControls.add(mStopButton);
    395         mFlushButton = (Button) findViewById(R.id.camera_pane_flush_button);
    396         mFlushButton.setOnClickListener(mFlushButtonListener);
    397         mConfiguredControls.add(mFlushButton);
    398     }
    399 
    400     private void initializeCameras(TestingCamera21 tc) {
    401         mCameraOps = tc.getCameraOps();
    402         mInfoDisplayer = tc;
    403 
    404         updateCameraList();
    405     }
    406 
    407     private void updateCameraList() {
    408         mCameraIds = null;
    409         try {
    410             mCameraIds = mCameraOps.getCamerasAndListen(mCameraAvailabilityCallback);
    411             String[] cameraSpinnerItems = new String[mCameraIds.length];
    412             for (int i = 0; i < mCameraIds.length; i++) {
    413                 cameraSpinnerItems[i] = String.format("Camera %s", mCameraIds[i]);
    414             }
    415             mCameraSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item,
    416                     cameraSpinnerItems));
    417 
    418         } catch (CameraAccessException e) {
    419             TLog.e("Exception trying to get list of cameras: " + e);
    420         }
    421     }
    422 
    423     private final CompoundButton.OnCheckedChangeListener mOpenButtonListener =
    424             new CompoundButton.OnCheckedChangeListener() {
    425                 @Override
    426                 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    427                     if (isChecked) {
    428                         // Open camera
    429                         mCurrentCamera = null;
    430                         mCameraOps.openCamera(mCurrentCameraId, mCameraListener);
    431                     } else {
    432                         // Close camera
    433                         closeCurrentCamera();
    434                     }
    435                 }
    436             };
    437 
    438     private final OnClickListener mInfoButtonListener = new OnClickListener() {
    439         @Override
    440         public void onClick(View v) {
    441             mInfoDisplayer.showCameraInfo(mCurrentCameraId);
    442         }
    443     };
    444 
    445     private final OnClickListener mStopButtonListener = new OnClickListener() {
    446         @Override
    447         public void onClick(View v) {
    448             if (mCurrentCaptureSession != null) {
    449                 try {
    450                     mCurrentCaptureSession.stopRepeating();
    451                 } catch (CameraAccessException e) {
    452                     TLog.e("Unable to stop repeating request for camera %s.", e, mCurrentCameraId);
    453                 }
    454             }
    455         }
    456     };
    457 
    458     private final OnClickListener mFlushButtonListener = new OnClickListener() {
    459         @Override
    460         public void onClick(View v) {
    461             if (mCurrentCaptureSession != null) {
    462                 try {
    463                     mCurrentCaptureSession.abortCaptures();
    464                 } catch (CameraAccessException e) {
    465                     TLog.e("Unable to flush camera %s.", e, mCurrentCameraId);
    466                 }
    467             }
    468         }
    469     };
    470 
    471     private final OnClickListener mConfigureButtonListener = new OnClickListener() {
    472         @Override
    473         public void onClick(View v) {
    474             List<Surface> targetSurfaces = new ArrayList<Surface>();
    475             List<TargetControlPane> targetPanes = new ArrayList<TargetControlPane>();
    476             for (TargetControlPane targetPane : mPaneTracker.getPanes(TargetControlPane.class)) {
    477                 Surface target = targetPane.getTargetSurfaceForCameraPane(getPaneName());
    478                 if (target != null) {
    479                     targetSurfaces.add(target);
    480                     targetPanes.add(targetPane);
    481                 }
    482             }
    483             try {
    484                 TLog.i("Configuring camera %s with %d surfaces", mCurrentCamera.getId(),
    485                         targetSurfaces.size());
    486                 mActiveCameraCall = CameraCall.CONFIGURE;
    487                 if (targetSurfaces.size() > 0) {
    488                     mCurrentCamera.createCaptureSession(targetSurfaces, mSessionListener, /*handler*/null);
    489                 } else if (mCurrentCaptureSession != null) {
    490                     mCurrentCaptureSession.close();
    491                     mCurrentCaptureSession = null;
    492                 }
    493                 mConfiguredSurfaces = targetSurfaces;
    494                 mConfiguredTargetPanes = targetPanes;
    495             } catch (CameraAccessException e) {
    496                 mActiveCameraCall = CameraCall.NONE;
    497                 TLog.e("Unable to configure camera %s.", e, mCurrentCamera.getId());
    498             } catch (IllegalArgumentException e) {
    499                 mActiveCameraCall = CameraCall.NONE;
    500                 TLog.e("Unable to configure camera %s.", e, mCurrentCamera.getId());
    501             } catch (IllegalStateException e) {
    502                 mActiveCameraCall = CameraCall.NONE;
    503                 TLog.e("Unable to configure camera %s.", e, mCurrentCamera.getId());
    504             }
    505         }
    506     };
    507 
    508     private final CameraCaptureSession.StateCallback mSessionListener =
    509             new CameraCaptureSession.StateCallback() {
    510 
    511         @Override
    512         public void onConfigured(CameraCaptureSession session) {
    513             mCurrentCaptureSession = session;
    514             TLog.i("Configuration completed for camera %s.", mCurrentCamera.getId());
    515 
    516             setSessionState(SessionState.CONFIGURED);
    517         }
    518 
    519         @Override
    520         public void onConfigureFailed(CameraCaptureSession session) {
    521             mActiveCameraCall = CameraCall.NONE;
    522             TLog.e("Configuration failed for camera %s.", mCurrentCamera.getId());
    523 
    524             setSessionState(SessionState.CONFIGURE_FAILED);
    525         }
    526 
    527         @Override
    528         public void onReady(CameraCaptureSession session) {
    529             setSessionState(SessionState.READY);
    530         }
    531 
    532         /**
    533          * This method is called when the session starts actively processing capture requests.
    534          *
    535          * <p>If capture requests are submitted prior to {@link #onConfigured} being called,
    536          * then the session will start processing those requests immediately after the callback,
    537          * and this method will be immediately called after {@link #onConfigured}.
    538          *
    539          * <p>If the session runs out of capture requests to process and calls {@link #onReady},
    540          * then this callback will be invoked again once new requests are submitted for capture.</p>
    541          */
    542         @Override
    543         public void onActive(CameraCaptureSession session) {
    544             setSessionState(SessionState.ACTIVE);
    545         }
    546 
    547         /**
    548          * This method is called when the session is closed.
    549          *
    550          * <p>A session is closed when a new session is created by the parent camera device,
    551          * or when the parent camera device is closed (either by the user closing the device,
    552          * or due to a camera device disconnection or fatal error).</p>
    553          *
    554          * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
    555          * any repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
    556          * However, any in-progress capture requests submitted to the session will be completed
    557          * as normal.</p>
    558          */
    559         @Override
    560         public void onClosed(CameraCaptureSession session) {
    561             // Ignore closes if the session has been replaced
    562             if (mCurrentCaptureSession != null && session != mCurrentCaptureSession) {
    563                 return;
    564             }
    565             setSessionState(SessionState.CLOSED);
    566         }
    567 
    568         @Override
    569         public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
    570             TLog.i("Surface preparation complete for Surface " + surface);
    571         }
    572 
    573     };
    574 
    575     private final CameraDevice.StateCallback mCameraListener = new CameraDevice.StateCallback() {
    576         @Override
    577         public void onClosed(CameraDevice camera) {
    578             // Don't change state on close, tracked by callers of close()
    579             mOpenButton.setChecked(false);
    580         }
    581 
    582         @Override
    583         public void onDisconnected(CameraDevice camera) {
    584             setCameraState(CameraState.DISCONNECTED);
    585         }
    586 
    587         @Override
    588         public void onError(CameraDevice camera, int error) {
    589             setCameraState(CameraState.ERROR);
    590         }
    591 
    592         @Override
    593         public void onOpened(CameraDevice camera) {
    594             mCurrentCamera = camera;
    595             setCameraState(CameraState.OPENED);
    596         }
    597     };
    598 
    599     private void switchToCamera(String newCameraId) {
    600         closeCurrentCamera();
    601 
    602         mCurrentCameraId = newCameraId;
    603 
    604         if (mCurrentCameraId == null) {
    605             setCameraState(CameraState.UNAVAILABLE);
    606         } else {
    607             setCameraState(CameraState.CLOSED);
    608         }
    609 
    610         mPaneTracker.notifyOtherPanes(this, PaneTracker.PaneEvent.NEW_CAMERA_SELECTED);
    611     }
    612 
    613     private void closeCurrentCamera() {
    614         if (mCurrentCamera != null) {
    615             mCurrentCamera.close();
    616             mCurrentCamera = null;
    617             setCameraState(CameraState.CLOSED);
    618         }
    619     }
    620 
    621     private void setSessionState(SessionState newState) {
    622         mSessionState = newState;
    623         mStatusText.setText("S." + mSessionState.toString());
    624 
    625         switch (mSessionState) {
    626             case CONFIGURE_FAILED:
    627                 mActiveCameraCall = CameraCall.NONE;
    628                 // fall-through
    629             case CLOSED:
    630                 enableBaseControls(true);
    631                 enableOpenControls(true);
    632                 enableConfiguredControls(false);
    633                 mConfiguredTargetPanes = null;
    634                 break;
    635             case NONE:
    636                 enableBaseControls(true);
    637                 enableOpenControls(true);
    638                 enableConfiguredControls(false);
    639                 mConfiguredTargetPanes = null;
    640                 break;
    641             case CONFIGURED:
    642                 if (mActiveCameraCall != CameraCall.CONFIGURE) {
    643                     throw new AssertionError();
    644                 }
    645                 mPaneTracker.notifyOtherPanes(this, PaneEvent.CAMERA_CONFIGURED);
    646                 mActiveCameraCall = CameraCall.NONE;
    647                 // fall-through
    648             case READY:
    649             case ACTIVE:
    650                 enableBaseControls(true);
    651                 enableOpenControls(true);
    652                 enableConfiguredControls(true);
    653                 break;
    654             default:
    655                 throw new AssertionError("Unhandled case " + mSessionState);
    656         }
    657     }
    658 
    659     private void setCameraState(CameraState newState) {
    660         mCameraState = newState;
    661         mStatusText.setText("C." + mCameraState.toString());
    662         switch (mCameraState) {
    663             case UNAVAILABLE:
    664                 enableBaseControls(false);
    665                 enableOpenControls(false);
    666                 enableConfiguredControls(false);
    667                 mConfiguredTargetPanes = null;
    668                 break;
    669             case CLOSED:
    670             case DISCONNECTED:
    671             case ERROR:
    672                 enableBaseControls(true);
    673                 enableOpenControls(false);
    674                 enableConfiguredControls(false);
    675                 mConfiguredTargetPanes = null;
    676                 break;
    677             case OPENED:
    678                 enableBaseControls(true);
    679                 enableOpenControls(true);
    680                 enableConfiguredControls(false);
    681                 mConfiguredTargetPanes = null;
    682                 break;
    683         }
    684     }
    685 
    686     private void enableBaseControls(boolean enabled) {
    687         for (View v : mBaseControls) {
    688             v.setEnabled(enabled);
    689         }
    690         if (!enabled) {
    691             mOpenButton.setChecked(false);
    692         }
    693     }
    694 
    695     private void enableOpenControls(boolean enabled) {
    696         for (View v : mOpenControls) {
    697             v.setEnabled(enabled);
    698         }
    699     }
    700 
    701     private void enableConfiguredControls(boolean enabled) {
    702         for (View v : mConfiguredControls) {
    703             v.setEnabled(enabled);
    704         }
    705     }
    706 
    707     private final CameraManager.AvailabilityCallback mCameraAvailabilityCallback =
    708             new CameraManager.AvailabilityCallback() {
    709         @Override
    710         public void onCameraAvailable(String cameraId) {
    711             // TODO: Update camera list in an intelligent fashion
    712             // (can't just call updateCameraList or the selected camera may change)
    713         }
    714 
    715         @Override
    716         public void onCameraUnavailable(String cameraId) {
    717             // TODO: Update camera list in an intelligent fashion
    718             // (can't just call updateCameraList or the selected camera may change)
    719         }
    720     };
    721 
    722     private final OnItemSelectedListener mCameraSpinnerListener = new OnItemSelectedListener() {
    723         @Override
    724         public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    725             String newCameraId = mCameraIds[pos];
    726             if (newCameraId != mCurrentCameraId) {
    727                 switchToCamera(newCameraId);
    728             }
    729         }
    730 
    731         @Override
    732         public void onNothingSelected(AdapterView<?> parent) {
    733             switchToCamera(null);
    734         }
    735     };
    736 }
    737