Merge "Add onSurfaceTextureDestroyed() callback."
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 755ecf5..bc1ad3c 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -56,14 +56,7 @@
  *          setContentView(mTextureView);
  *      }
  *
- *      protected void onDestroy() {
- *          super.onDestroy();
- *
- *          mCamera.stopPreview();
- *          mCamera.release();
- *      }
- *
- *      public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+ *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
  *          mCamera = Camera.open();
  *
  *          try {
@@ -77,6 +70,11 @@
  *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
  *          // Ignored, Camera does all the work for us
  *      }
+ *      
+ *      public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ *          mCamera.stopPreview();
+ *          mCamera.release();
+ *      }
  *  }
  * </pre>
  * 
@@ -155,6 +153,21 @@
         }
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (isHardwareAccelerated() && mLayer != null) {
+            if (mListener != null) {
+                mListener.onSurfaceTextureDestroyed(mSurface);
+            }
+
+            mLayer.destroy();            
+            mSurface = null;
+            mLayer = null;
+        }
+    }
+
     /**
      * The layer type of a TextureView is ignored since a TextureView is always
      * considered to act as a hardware layer. The optional paint supplied to this
@@ -217,6 +230,9 @@
         super.onSizeChanged(w, h, oldw, oldh);
         if (mSurface != null) {
             nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
+            if (mListener != null) {
+                mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
+            }
         }
     }
 
@@ -242,7 +258,7 @@
             mSurface.setOnFrameAvailableListener(mUpdateListener);
 
             if (mListener != null) {
-                mListener.onSurfaceTextureAvailable(mSurface);
+                mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
             }
         }
 
@@ -316,8 +332,10 @@
          * 
          * @param surface The surface returned by
          *                {@link android.view.TextureView#getSurfaceTexture()}
+         * @param width The width of the surface
+         * @param height The height of the surface
          */
-        public void onSurfaceTextureAvailable(SurfaceTexture surface);
+        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
 
         /**
          * Invoked when the {@link SurfaceTexture}'s buffers size changed.
@@ -328,6 +346,15 @@
          * @param height The new height of the surface
          */
         public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
+
+        /**
+         * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
+         * After this method is invoked, no rendering should happen inside the surface
+         * texture.
+         * 
+         * @param surface The surface about to be destroyed
+         */
+        public void onSurfaceTextureDestroyed(SurfaceTexture surface);
     }
 
     private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
index 7f97098..9bb5ba8 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -52,13 +52,7 @@
     }
 
     @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mRenderThread.finish();
-    }
-
-    @Override
-    public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
         mRenderThread = new RenderThread(surface);
         mRenderThread.start();
 
@@ -81,6 +75,16 @@
     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
     }
 
+    @Override
+    public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        mRenderThread.finish();
+        try {
+            mRenderThread.join();
+        } catch (InterruptedException e) {
+            Log.e(RenderThread.LOG_TAG, "Could not wait for render thread");
+        }
+    }
+
     private static class RenderThread extends Thread {
         private static final String LOG_TAG = "GLTextureView";
 
@@ -108,26 +112,23 @@
         public void run() {
             initGL();
 
-            float red = 0.0f;
+            float red = 1.0f;
             while (!mFinished) {
                 checkCurrent();
 
+                Log.d(LOG_TAG, "Rendering frame");
+
                 GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
-                int error = GLES20.glGetError();
-                if (error != GLES20.GL_NO_ERROR) {
-                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
-                }
+                checkGlError();
 
                 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
-                error = GLES20.glGetError();
-                if (error != GLES20.GL_NO_ERROR) {
-                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
-                }
+                checkGlError();
 
                 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
                     throw new RuntimeException("Cannot swap buffers");
                 }
-                
+                checkEglError();
+
                 try {
                     Thread.sleep(20);
                 } catch (InterruptedException e) {
@@ -141,6 +142,20 @@
             finishGL();
         }
 
+        private void checkEglError() {
+            int error = mEgl.eglGetError();
+            if (error != EGL10.EGL_SUCCESS) {
+                Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
+            }
+        }
+
+        private void checkGlError() {
+            int error = GLES20.glGetError();
+            if (error != GLES20.GL_NO_ERROR) {
+                Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+            }
+        }
+
         private void finishGL() {
             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 2feda57..fa2e39a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.test.hwui;
 
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -25,6 +26,7 @@
 import android.view.Gravity;
 import android.view.TextureView;
 import android.view.View;
+import android.widget.Button;
 import android.widget.FrameLayout;
 
 import java.io.IOException;
@@ -33,27 +35,44 @@
 public class TextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
     private Camera mCamera;
     private TextureView mTextureView;
+    private FrameLayout mContent;
+    private AnimatorSet mAnimatorSet;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mContent = new FrameLayout(this);
+
         mTextureView = new TextureView(this);
         mTextureView.setSurfaceTextureListener(this);
 
-        setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+        Button button = new Button(this);
+        button.setText("Remove/Add");
+        button.setOnClickListener(new View.OnClickListener() {
+            private boolean mAdded = true;
+
+            @Override
+            public void onClick(View v) {
+                if (mAdded) {
+                    mAnimatorSet.cancel();
+                    mContent.removeView(mTextureView);
+                } else {
+                    mContent.addView(mTextureView);
+                }
+                mAdded = !mAdded;
+            }
+        });
+
+        mContent.addView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+        mContent.addView(button, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+        setContentView(mContent);
     }
 
     @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        mCamera.stopPreview();
-        mCamera.release();
-    }
-
-    @Override
-    public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
         mCamera = Camera.open();
 
         try {
@@ -66,27 +85,35 @@
 
         mTextureView.setCameraDistance(5000);
 
-        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
-        animator.setRepeatMode(ObjectAnimator.REVERSE);
-        animator.setRepeatCount(ObjectAnimator.INFINITE);
-        animator.setDuration(4000);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+        ObjectAnimator rotationY = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+        rotationY.setRepeatMode(ObjectAnimator.REVERSE);
+        rotationY.setRepeatCount(ObjectAnimator.INFINITE);
+        rotationY.setDuration(4000);
+        rotationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 ((View) mTextureView.getParent()).invalidate();
             }
         });
-        animator.start();
 
-        animator = ObjectAnimator.ofFloat(mTextureView, "alpha", 1.0f, 0.0f);
-        animator.setRepeatMode(ObjectAnimator.REVERSE);
-        animator.setRepeatCount(ObjectAnimator.INFINITE);
-        animator.setDuration(4000);
-        animator.start();
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(mTextureView, "alpha", 1.0f, 0.0f);
+        alpha.setRepeatMode(ObjectAnimator.REVERSE);
+        alpha.setRepeatCount(ObjectAnimator.INFINITE);
+        alpha.setDuration(4000);
+
+        mAnimatorSet = new AnimatorSet();
+        mAnimatorSet.play(alpha).with(rotationY);
+        mAnimatorSet.start();
     }
 
     @Override
     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
         // Ignored, the Camera does all the work for us
     }
+
+    @Override
+    public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        mCamera.stopPreview();
+        mCamera.release();
+    }
 }