Add an API demo showcasing the new rotation vector sensor

Change-Id: I3f00560b0340db31779259aa98efc174b0b645cd
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 6e39538..e9acbb4 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -671,6 +671,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".os.RotationVectorDemo" android:label="OS/Rotation Vector"  android:screenOrientation="nosensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".os.SmsMessagingDemo" android:label="OS/SMS Messaging">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/RotationVectorDemo.java b/samples/ApiDemos/src/com/example/android/apis/os/RotationVectorDemo.java
new file mode 100644
index 0000000..7417809
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/RotationVectorDemo.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.os;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import android.app.Activity;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+
+/**
+ * Wrapper activity demonstrating the use of the new
+ * {@link SensorEvent#values rotation vector sensor}
+ * ({@link Sensor#TYPE_ROTATION_VECTOR TYPE_ROTATION_VECTOR}).
+ * 
+ * @see Sensor
+ * @see SensorEvent
+ * @see SensorManager
+ * 
+ */
+public class RotationVectorDemo extends Activity {
+    private GLSurfaceView mGLSurfaceView;
+    private SensorManager mSensorManager;
+    private MyRenderer mRenderer;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Get an instance of the SensorManager
+        mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mRenderer = new MyRenderer();
+        mGLSurfaceView = new GLSurfaceView(this);
+        mGLSurfaceView.setRenderer(mRenderer);
+        setContentView(mGLSurfaceView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mRenderer.start();
+        mGLSurfaceView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mRenderer.stop();
+        mGLSurfaceView.onPause();
+    }
+
+
+    class MyRenderer implements GLSurfaceView.Renderer, SensorEventListener {
+        private Cube mCube;
+        private Sensor mRotationVectorSensor;
+        private final float[] mRotationMatrix = new float[16];
+
+        public MyRenderer() {
+            // find the rotation-vector sensor
+            mRotationVectorSensor = mSensorManager.getDefaultSensor(
+                    Sensor.TYPE_ROTATION_VECTOR);
+
+            mCube = new Cube();
+            // initialize the rotation matrix to identity
+            mRotationMatrix[ 0] = 1;
+            mRotationMatrix[ 4] = 1;
+            mRotationMatrix[ 8] = 1;
+            mRotationMatrix[12] = 1;
+        }
+
+        public void start() {
+            // enable our sensor when the activity is resumed, ask for
+            // 10 ms updates.
+            mSensorManager.registerListener(this, mRotationVectorSensor, 10000);
+        }
+
+        public void stop() {
+            // make sure to turn our sensor off when the activity is paused
+            mSensorManager.unregisterListener(this);
+        }
+
+        public void onSensorChanged(SensorEvent event) {
+            // we received a sensor event. it is a good practice to check
+            // that we received the proper event
+            if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
+                // convert the rotation-vector to a 4x4 matrix. the matrix
+                // is interpreted by Open GL as the inverse of the
+                // rotation-vector, which is what we want.
+                SensorManager.getRotationMatrixFromVector(
+                        mRotationMatrix , event.values);
+            }
+        }
+
+        public void onDrawFrame(GL10 gl) {
+            // clear screen
+            gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+
+            // set-up modelview matrix
+            gl.glMatrixMode(GL10.GL_MODELVIEW);
+            gl.glLoadIdentity();
+            gl.glTranslatef(0, 0, -3.0f);
+            gl.glMultMatrixf(mRotationMatrix, 0);
+
+            // draw our object
+            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
+
+            mCube.draw(gl);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            // set view-port
+            gl.glViewport(0, 0, width, height);
+            // set projection matrix
+            float ratio = (float) width / height;
+            gl.glMatrixMode(GL10.GL_PROJECTION);
+            gl.glLoadIdentity();
+            gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            // dither is enabled by default, we don't need it
+            gl.glDisable(GL10.GL_DITHER);
+            // clear screen in white
+            gl.glClearColor(1,1,1,1);
+        }
+
+        class Cube {
+            // initialize our cube
+            private FloatBuffer mVertexBuffer;
+            private FloatBuffer mColorBuffer;
+            private ByteBuffer  mIndexBuffer;
+
+            public Cube() {
+                final float vertices[] = {
+                        -1, -1, -1,		 1, -1, -1,
+                         1,  1, -1,	    -1,  1, -1,
+                        -1, -1,  1,      1, -1,  1,
+                         1,  1,  1,     -1,  1,  1,
+                };
+
+                final float colors[] = {
+                        0,  0,  0,  1,  1,  0,  0,  1,
+                        1,  1,  0,  1,  0,  1,  0,  1,
+                        0,  0,  1,  1,  1,  0,  1,  1,
+                        1,  1,  1,  1,  0,  1,  1,  1,
+                };
+
+                final byte indices[] = {
+                        0, 4, 5,    0, 5, 1,
+                        1, 5, 6,    1, 6, 2,
+                        2, 6, 7,    2, 7, 3,
+                        3, 7, 4,    3, 4, 0,
+                        4, 7, 6,    4, 6, 5,
+                        3, 0, 1,    3, 1, 2
+                };
+
+                ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
+                vbb.order(ByteOrder.nativeOrder());
+                mVertexBuffer = vbb.asFloatBuffer();
+                mVertexBuffer.put(vertices);
+                mVertexBuffer.position(0);
+
+                ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
+                cbb.order(ByteOrder.nativeOrder());
+                mColorBuffer = cbb.asFloatBuffer();
+                mColorBuffer.put(colors);
+                mColorBuffer.position(0);
+
+                mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
+                mIndexBuffer.put(indices);
+                mIndexBuffer.position(0);
+            }
+
+            public void draw(GL10 gl) {
+                gl.glEnable(GL10.GL_CULL_FACE);
+                gl.glFrontFace(GL10.GL_CW);
+                gl.glShadeModel(GL10.GL_SMOOTH);
+                gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
+                gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
+                gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
+            }            
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+    }
+}