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 android.content.Context; 20 import android.util.AttributeSet; 21 import android.view.LayoutInflater; 22 import android.view.Surface; 23 import android.view.View; 24 import android.widget.AdapterView; 25 import android.widget.AdapterView.OnItemSelectedListener; 26 import android.widget.ArrayAdapter; 27 import android.widget.Spinner; 28 import android.widget.ToggleButton; 29 30 import org.xmlpull.v1.XmlPullParser; 31 import org.xmlpull.v1.XmlPullParserException; 32 33 import java.io.IOException; 34 import java.util.List; 35 import java.util.Locale; 36 37 public class TargetControlPane extends ControlPane { 38 // XML attributes 39 40 /** Name of pane tag */ 41 private static final String PANE_NAME = "target_pane"; 42 43 /** Attribute: ID for pane (integer) */ 44 private static final String PANE_ID = "id"; 45 46 /** Attribute: Type of output (string), value must be one of OutputViewType.getXmlName() */ 47 private static final String OUTPUT_TYPE = "type"; 48 49 // End XML attributes 50 51 /** 52 * Available output view types 53 * 54 * <p>Adding new entries to this list requires updating createOutputView() as 55 * well.</p> 56 */ 57 private enum OutputViewType { 58 SURFACE_VIEW, 59 TEXTURE_VIEW, 60 IMAGE_READER, 61 RENDERSCRIPT, 62 MEDIA_RECORDER, 63 MEDIA_CODEC; 64 65 static String getXmlName(OutputViewType type) { 66 return type.toString().toLowerCase(Locale.US); 67 } 68 } 69 70 private static int mTargetPaneIdCounter = 0; 71 72 private int mPaneId; 73 74 private List<CameraControlPane> mCameraPanes; 75 76 private Spinner mCameraSpinner; 77 private ToggleButton mCameraConfigureToggle; 78 79 private Spinner mOutputSpinner; 80 81 private TargetSubPane mCurrentOutput; 82 83 private int mOrientation = 0; 84 85 /** 86 * Constructor for tooling only 87 */ 88 public TargetControlPane(Context context, AttributeSet attrs) { 89 super(context, attrs, null, null); 90 91 mPaneId = 0; 92 setUpUI(context); 93 } 94 95 public TargetControlPane(TestingCamera21 tc, AttributeSet attrs, StatusListener listener) { 96 super(tc, attrs, listener, tc.getPaneTracker()); 97 98 mPaneId = mTargetPaneIdCounter++; 99 setUpUI(tc); 100 101 } 102 103 public TargetControlPane(TestingCamera21 tc, XmlPullParser configParser, StatusListener listener) 104 throws XmlPullParserException, IOException { 105 super(tc, null, listener, tc.getPaneTracker()); 106 107 configParser.require(XmlPullParser.START_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME); 108 109 int paneId = getAttributeInt(configParser, PANE_ID, -1); 110 if (paneId == -1) { 111 mPaneId = mTargetPaneIdCounter++; 112 } else { 113 mPaneId = paneId; 114 if (mPaneId >= mTargetPaneIdCounter) { 115 mTargetPaneIdCounter = mPaneId + 1; 116 } 117 } 118 119 configParser.next(); 120 configParser.require(XmlPullParser.END_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME); 121 122 setUpUI(tc); 123 } 124 125 /** 126 * Get this target's Surface aimed at the given camera pane. If no target 127 * for that camera is defined by this pane, returns null. 128 * 129 * @param paneName ID of the camera pane to return a Surface for 130 * @return a Surface to configure for the camera device, or null if none 131 * available 132 */ 133 public Surface getTargetSurfaceForCameraPane(String paneName) { 134 if (paneName == null || mCurrentOutput == null) return null; 135 136 boolean isMyTarget = 137 paneName.equals(mCameraSpinner.getSelectedItem()) && 138 mCameraConfigureToggle.isChecked(); 139 return isMyTarget ? mCurrentOutput.getOutputSurface() : null; 140 } 141 142 public void notifyPaneEvent(ControlPane sourcePane, PaneTracker.PaneEvent event) { 143 switch (event) { 144 case NEW_CAMERA_SELECTED: 145 if (mCameraPanes.size() > 0 146 && sourcePane == mCameraPanes.get(mCameraSpinner.getSelectedItemPosition())) { 147 if (mCurrentOutput != null) { 148 mCurrentOutput.setTargetCameraPane((CameraControlPane) sourcePane); 149 } 150 } 151 break; 152 default: 153 super.notifyPaneEvent(sourcePane, event); 154 } 155 } 156 157 public void onOrientationChange(int orientation) { 158 mOrientation = orientation; 159 if (mCurrentOutput != null) { 160 mCurrentOutput.setUiOrientation(mOrientation); 161 } 162 } 163 164 private void setUpUI(Context context) { 165 166 String paneTitle = 167 String.format(Locale.US, "%s %d", 168 context.getResources().getString(R.string.target_pane_title), mPaneId); 169 this.setName(paneTitle); 170 171 LayoutInflater inflater = 172 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 173 174 inflater.inflate(R.layout.target_pane, this); 175 176 mCameraSpinner = (Spinner) findViewById(R.id.target_pane_camera_spinner); 177 mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener); 178 mCameraConfigureToggle = (ToggleButton) findViewById(R.id.target_pane_configure_toggle); 179 mCameraConfigureToggle.setChecked(true); 180 181 mOutputSpinner = (Spinner) findViewById(R.id.target_pane_output_spinner); 182 mOutputSpinner.setOnItemSelectedListener(mOutputSpinnerListener); 183 184 OutputViewType[] outputTypes = OutputViewType.values(); 185 String[] outputSpinnerItems = new String[outputTypes.length]; 186 187 for (int i = 0; i < outputTypes.length; i++) { 188 outputSpinnerItems[i] = outputTypes[i].toString(); 189 } 190 mOutputSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item, 191 outputSpinnerItems)); 192 193 mPaneTracker.addPaneListener(new CameraPanesListener()); 194 mCameraPanes = mPaneTracker.getPanes(CameraControlPane.class); 195 updateCameraPaneList(); 196 197 } 198 199 private class CameraPanesListener extends PaneTracker.PaneSetChangedListener<CameraControlPane> { 200 public CameraPanesListener() { 201 super(CameraControlPane.class); 202 } 203 204 @Override 205 public void onPaneAdded(ControlPane pane) { 206 mCameraPanes.add((CameraControlPane) pane); 207 updateCameraPaneList(); 208 } 209 210 @Override 211 public void onPaneRemoved(ControlPane pane) { 212 mCameraPanes.remove((CameraControlPane) pane); 213 updateCameraPaneList(); 214 } 215 } 216 217 private OnItemSelectedListener mCameraSpinnerListener = new OnItemSelectedListener() { 218 @Override 219 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 220 updateSubPaneCamera(); 221 } 222 223 @Override 224 public void onNothingSelected(AdapterView<?> arg0) { 225 226 } 227 }; 228 229 private OnItemSelectedListener mOutputSpinnerListener = new OnItemSelectedListener() { 230 @Override 231 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 232 if (mCurrentOutput != null) { 233 TargetControlPane.this.removeView(mCurrentOutput); 234 } 235 OutputViewType outputType = 236 OutputViewType.valueOf((String) mOutputSpinner.getSelectedItem()); 237 mCurrentOutput = createOutputView(outputType); 238 if (mCurrentOutput != null) { 239 TargetControlPane.this.addView(mCurrentOutput); 240 mCurrentOutput.setUiOrientation(mOrientation); 241 updateSubPaneCamera(); 242 } 243 } 244 245 @Override 246 public void onNothingSelected(AdapterView<?> arg0) { 247 if (mCurrentOutput != null) { 248 TargetControlPane.this.removeView(mCurrentOutput); 249 mCurrentOutput = null; 250 } 251 } 252 }; 253 254 private void updateCameraPaneList() { 255 String currentSelection = (String) mCameraSpinner.getSelectedItem(); 256 int newSelectionIndex = 0; 257 String[] cameraSpinnerItems = new String[mCameraPanes.size()]; 258 for (int i = 0; i < cameraSpinnerItems.length; i++) { 259 cameraSpinnerItems[i] = mCameraPanes.get(i).getPaneName(); 260 if (cameraSpinnerItems[i].equals(currentSelection)) { 261 newSelectionIndex = i; 262 } 263 } 264 mCameraSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item, 265 cameraSpinnerItems)); 266 mCameraSpinner.setSelection(newSelectionIndex); 267 } 268 269 private void updateSubPaneCamera() { 270 if (mCameraPanes.size() > 0 && mCurrentOutput != null) { 271 mCurrentOutput.setTargetCameraPane(mCameraPanes.get(mCameraSpinner 272 .getSelectedItemPosition())); 273 } 274 } 275 276 private TargetSubPane createOutputView(OutputViewType type) { 277 TargetSubPane newPane = null; 278 switch (type) { 279 case IMAGE_READER: 280 newPane = new ImageReaderSubPane(getContext(), null); 281 break; 282 case MEDIA_CODEC: 283 case MEDIA_RECORDER: 284 case RENDERSCRIPT: 285 TLog.e("No implementation yet for view type %s", type); 286 break; 287 case SURFACE_VIEW: 288 newPane = new SurfaceViewSubPane(getContext(), null); 289 break; 290 case TEXTURE_VIEW: 291 newPane = new TextureViewSubPane(getContext(), null); 292 break; 293 } 294 return newPane; 295 } 296 } 297