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