TestingCamera2: Add prepare button to target control pane

Triggers the CameraCaptureSession prepare call for the target
Surface.

Also fix reading past the end of the ByteBuffer for YUV_420_888
with semiplanar formats.

Change-Id: I529400a0ecd9acc00c584c77a70c34f55fcb67d1
diff --git a/apps/TestingCamera2/res/layout/target_pane.xml b/apps/TestingCamera2/res/layout/target_pane.xml
index 9c241a6..4efcc31 100644
--- a/apps/TestingCamera2/res/layout/target_pane.xml
+++ b/apps/TestingCamera2/res/layout/target_pane.xml
@@ -28,15 +28,21 @@
             android:id="@+id/target_pane_camera_spinner"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
+            android:layout_weight="2"
             android:prompt="@string/target_pane_camera_prompt" />
         <ToggleButton
             android:id="@+id/target_pane_configure_toggle"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
+            android:layout_weight="2"
             android:textOn="@string/target_pane_configure_button_on"
             android:textOff="@string/target_pane_configure_button_off" />
+        <Button
+            android:id="@+id/target_pane_prepare_button"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:text="@string/target_pane_prepare_button"
+            android:layout_weight="1" />
     </LinearLayout>
     <Spinner
         android:id="@+id/target_pane_output_spinner"
diff --git a/apps/TestingCamera2/res/values/strings.xml b/apps/TestingCamera2/res/values/strings.xml
index dcca6bb..5f749aa 100644
--- a/apps/TestingCamera2/res/values/strings.xml
+++ b/apps/TestingCamera2/res/values/strings.xml
@@ -59,6 +59,7 @@
     <string name="target_pane_output_prompt">Output</string>
     <string name="target_pane_configure_button_off">Not configured</string>
     <string name="target_pane_configure_button_on">Will be configured</string>
+    <string name="target_pane_prepare_button">Prep</string>
 
     <string name="target_subpane_texture_view_size_prompt">Size</string>
     <string name="target_subpane_surface_view_size_prompt">Size</string>
diff --git a/apps/TestingCamera2/src/com/android/testingcamera2/CameraControlPane.java b/apps/TestingCamera2/src/com/android/testingcamera2/CameraControlPane.java
index b220328..594d161 100644
--- a/apps/TestingCamera2/src/com/android/testingcamera2/CameraControlPane.java
+++ b/apps/TestingCamera2/src/com/android/testingcamera2/CameraControlPane.java
@@ -297,6 +297,19 @@
         return null;
     }
 
+    public void prepareSurface(Surface target) {
+        if (mCurrentCaptureSession != null) {
+            try {
+                TLog.i("Preparing Surface " + target);
+                mCurrentCaptureSession.prepare(target);
+            } catch (CameraAccessException e) {
+                TLog.e("Unable to prepare surface for camera %s.", e, mCurrentCameraId);
+            } catch (IllegalArgumentException e) {
+                TLog.e("Bad Surface passed to prepare", e);
+            }
+        }
+    }
+
     private CaptureCallback mResultListener = new CaptureCallback() {
         public void onCaptureCompleted(
                 CameraCaptureSession session,
@@ -513,6 +526,12 @@
             }
             setSessionState(SessionState.CLOSED);
         }
+
+        @Override
+        public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+            TLog.i("Surface preparation complete for Surface " + surface);
+        }
+
     };
 
     private final CameraDevice.StateCallback mCameraListener = new CameraDevice.StateCallback() {
diff --git a/apps/TestingCamera2/src/com/android/testingcamera2/ImageReaderSubPane.java b/apps/TestingCamera2/src/com/android/testingcamera2/ImageReaderSubPane.java
index 4b4d978..24233ca 100644
--- a/apps/TestingCamera2/src/com/android/testingcamera2/ImageReaderSubPane.java
+++ b/apps/TestingCamera2/src/com/android/testingcamera2/ImageReaderSubPane.java
@@ -345,8 +345,8 @@
                 int uPStride = img.getPlanes()[1].getPixelStride();
                 int vPStride = img.getPlanes()[2].getPixelStride();
                 byte[] row = new byte[mConfiguredSize.getWidth()];
-                byte[] uRow = new byte[mConfiguredSize.getWidth()/2*uPStride];
-                byte[] vRow = new byte[mConfiguredSize.getWidth()/2*vPStride];
+                byte[] uRow = new byte[(mConfiguredSize.getWidth()/2-1)*uPStride + 1];
+                byte[] vRow = new byte[(mConfiguredSize.getWidth()/2-1)*vPStride + 1];
                 int[] imgArray = new int[w * h];
                 for (int y = 0, j = 0, rowStart = 0, uRowStart = 0, vRowStart = 0; y < h;
                      y++, rowStart += stride*SCALE_FACTOR) {
@@ -669,7 +669,7 @@
                 }
             } else {
                 // Need to pack rows
-                byte[] row = new byte[colorW * colorPlane.getPixelStride()];
+                byte[] row = new byte[(colorW - 1) * colorPlane.getPixelStride() + 1];
                 byte[] packedRow = new byte[colorW];
                 ByteBuffer packedRowBuffer = ByteBuffer.wrap(packedRow);
                 for (int y = 0, rowStart = 0; y < colorH;
diff --git a/apps/TestingCamera2/src/com/android/testingcamera2/TargetControlPane.java b/apps/TestingCamera2/src/com/android/testingcamera2/TargetControlPane.java
index a8fe4f8..b2343f4 100644
--- a/apps/TestingCamera2/src/com/android/testingcamera2/TargetControlPane.java
+++ b/apps/TestingCamera2/src/com/android/testingcamera2/TargetControlPane.java
@@ -25,6 +25,7 @@
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.ArrayAdapter;
 import android.widget.Spinner;
+import android.widget.Button;
 import android.widget.ToggleButton;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -75,6 +76,7 @@
 
     private Spinner mCameraSpinner;
     private ToggleButton mCameraConfigureToggle;
+    private Button mPrepareButton;
 
     private Spinner mOutputSpinner;
 
@@ -136,6 +138,7 @@
         boolean isMyTarget =
                 paneName.equals(mCameraSpinner.getSelectedItem()) &&
                 mCameraConfigureToggle.isChecked();
+        mPrepareButton.setEnabled(isMyTarget);
         return isMyTarget ? mCurrentOutput.getOutputSurface() : null;
     }
 
@@ -177,6 +180,9 @@
         mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
         mCameraConfigureToggle = (ToggleButton) findViewById(R.id.target_pane_configure_toggle);
         mCameraConfigureToggle.setChecked(true);
+        mPrepareButton = (Button) findViewById(R.id.target_pane_prepare_button);
+        mPrepareButton.setEnabled(false);
+        mPrepareButton.setOnClickListener(mPrepareButtonListener);
 
         mOutputSpinner = (Spinner) findViewById(R.id.target_pane_output_spinner);
         mOutputSpinner.setOnItemSelectedListener(mOutputSpinnerListener);
@@ -226,9 +232,19 @@
         }
     };
 
+    private final OnClickListener mPrepareButtonListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            CameraControlPane currentCameraPane = mCameraPanes.get(
+                    mCameraSpinner.getSelectedItemPosition());
+            currentCameraPane.prepareSurface(mCurrentOutput.getOutputSurface());
+        }
+    };
+
     private OnItemSelectedListener mOutputSpinnerListener = new OnItemSelectedListener() {
         @Override
         public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+            mPrepareButton.setEnabled(false);
             if (mCurrentOutput != null) {
                 TargetControlPane.this.removeView(mCurrentOutput);
             }
@@ -244,6 +260,7 @@
 
         @Override
         public void onNothingSelected(AdapterView<?> arg0) {
+            mPrepareButton.setEnabled(false);
             if (mCurrentOutput != null) {
                 TargetControlPane.this.removeView(mCurrentOutput);
                 mCurrentOutput = null;