summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/nfc/FireflyRenderThread.java394
-rw-r--r--src/com/android/nfc/FireflyRenderer.java424
-rw-r--r--src/com/android/nfc/P2pEventManager.java2
-rw-r--r--src/com/android/nfc/SendUi.java248
4 files changed, 582 insertions, 486 deletions
diff --git a/src/com/android/nfc/FireflyRenderThread.java b/src/com/android/nfc/FireflyRenderThread.java
deleted file mode 100644
index 5019ffb..0000000
--- a/src/com/android/nfc/FireflyRenderThread.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2011 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.android.nfc;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.SurfaceTexture;
-import android.opengl.GLUtils;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import javax.microedition.khronos.opengles.GL10;
-
-public class FireflyRenderThread extends Thread {
- private static final String LOG_TAG = "NfcFireflyThread";
-
- SurfaceTexture mSurface;
-
- EGL10 mEgl;
- EGLDisplay mEglDisplay;
- EGLConfig mEglConfig;
- EGLContext mEglContext;
- EGLSurface mEglSurface;
- GL10 mGL;
-
- static final int NUM_FIREFLIES = 200;
-
- static final float NEAR_CLIPPING_PLANE = 50f;
- static final float FAR_CLIPPING_PLANE = 100f;
-
- static final int[] sEglConfig = {
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 0,
- EGL10.EGL_DEPTH_SIZE, 0,
- EGL10.EGL_STENCIL_SIZE, 0,
- EGL10.EGL_NONE
- };
-
- // Vertices for drawing a 32x32 rect
- static final float mVertices[] = {
- 0.0f, 0.0f, 0.0f, // 0, Top Left
- 0.0f, 32.0f, 0.0f, // 1, Bottom Left
- 32.0f, 32.0f, 0.0f, // 2, Bottom Right
- 32.0f, 0.0f, 0.0f, // 3, Top Right
- };
-
- // Mapping coordinates for the texture
- static final float mTextCoords[] = {
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- 1.0f, 1.0f,
- 0.0f, 1.0f
- };
-
- // Connecting order (draws a square)
- static final short[] mIndices = { 0, 1, 2, 0, 2, 3 };
-
- // Buffer holding the vertices
- FloatBuffer mVertexBuffer;
-
- // Buffer holding the indices
- ShortBuffer mIndexBuffer;
-
- // Buffer holding the texture mapping coordinates
- FloatBuffer mTextureBuffer;
-
- // Holding the handle to the texture
- int mTextureId;
-
- final Context mContext;
- final int mDisplayWidth;
- final int mDisplayHeight;
-
- Firefly[] mFireflies;
- long mStartTime;
-
- // Read/written by multiple threads
- volatile boolean mFinished;
- volatile boolean mFadeOut;
-
- public FireflyRenderThread(Context context, SurfaceTexture surface, int width, int height) {
- mSurface = surface;
- mContext = context;
- mDisplayWidth = width;
- mDisplayHeight = height;
- mFinished = false;
- }
-
- public void finish() {
- mFinished = true;
- }
-
- public void fadeOut() {
- mFadeOut = true;
- }
-
- void initShapes() {
- // First, build the vertex, texture and index buffers
- ByteBuffer vbb = ByteBuffer.allocateDirect(mVertices.length * 4); // Float => 4 bytes
- vbb.order(ByteOrder.nativeOrder());
- mVertexBuffer = vbb.asFloatBuffer();
- mVertexBuffer.put(mVertices);
- mVertexBuffer.position(0);
-
- ByteBuffer ibb = ByteBuffer.allocateDirect(mIndices.length * 2); // Short => 2 bytes
- ibb.order(ByteOrder.nativeOrder());
- mIndexBuffer = ibb.asShortBuffer();
- mIndexBuffer.put(mIndices);
- mIndexBuffer.position(0);
-
- ByteBuffer tbb = ByteBuffer.allocateDirect(mTextCoords.length * 4);
- tbb.order(ByteOrder.nativeOrder());
- mTextureBuffer = tbb.asFloatBuffer();
- mTextureBuffer.put(mTextCoords);
- mTextureBuffer.position(0);
-
- mFadeOut = false;
-
- mFireflies = new Firefly[NUM_FIREFLIES];
- for (int i = 0; i < NUM_FIREFLIES; i++) {
- mFireflies[i] = new Firefly();
- }
- loadStarTexture();
- }
-
- void loadStarTexture() {
- int[] textureIds = new int[1];
- mGL.glGenTextures(1, textureIds, 0);
- mTextureId = textureIds[0];
-
- InputStream in = null;
- try {
- // Remember that both texture dimensions must be a power of 2!
- in = mContext.getAssets().open("star.png");
-
- Bitmap bitmap = BitmapFactory.decodeStream(in);
- mGL.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId);
-
- mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
- mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
-
- GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
-
- bitmap.recycle();
-
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException opening assets.");
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) { }
- }
- }
- }
-
- @Override
- public void run() {
- if (!initGL()) {
- finishGL();
- return;
- }
-
- mGL.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-
- initShapes();
-
- mGL.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
-
- // make adjustments for screen ratio
- mGL.glMatrixMode(GL10.GL_PROJECTION);
- mGL.glLoadIdentity();
- mGL.glFrustumf(-mDisplayWidth, mDisplayWidth, mDisplayHeight, -mDisplayHeight, NEAR_CLIPPING_PLANE, FAR_CLIPPING_PLANE);
-
- // Switch back to modelview
- mGL.glMatrixMode(GL10.GL_MODELVIEW);
- mGL.glLoadIdentity();
-
- mGL.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
- mGL.glDepthMask(true);
-
- // Reset firefly models
- for (Firefly firefly : mFireflies) {
- firefly.reset();
- }
-
- mStartTime = System.currentTimeMillis();
-
- while (!mFinished) {
- long timeElapsedMs = System.currentTimeMillis() - mStartTime;
- mStartTime = System.currentTimeMillis();
-
- checkCurrent();
-
- mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
- mGL.glLoadIdentity();
-
- mGL.glEnable(GL10.GL_TEXTURE_2D);
- mGL.glEnable(GL10.GL_BLEND);
- mGL.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
-
- for (Firefly firefly : mFireflies) {
- firefly.updatePositionAndScale(timeElapsedMs);
- firefly.draw();
- }
-
- if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
- Log.e(LOG_TAG, "Could not swap buffers");
- mFinished = true;
- }
-
- long elapsed = System.currentTimeMillis() - mStartTime;
- try {
- Thread.sleep(Math.max(30 - elapsed, 0));
- } catch (InterruptedException e) {
-
- }
- }
- finishGL();
- }
-
- boolean initGL() {
- // Initialize openGL engine
- mEgl = (EGL10) EGLContext.getEGL();
-
- mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
- if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
- Log.e(LOG_TAG, "eglGetDisplay failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- return false;
- }
-
- int[] version = new int[2];
- if (!mEgl.eglInitialize(mEglDisplay, version)) {
- Log.e(LOG_TAG, "eglInitialize failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- return false;
- }
-
- mEglConfig = chooseEglConfig();
- if (mEglConfig == null) {
- Log.e(LOG_TAG, "eglConfig not initialized.");
- return false;
- }
-
- mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null);
-
- mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
-
- if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
- int error = mEgl.eglGetError();
- Log.e(LOG_TAG,"createWindowSurface returned error");
- return false;
- }
-
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- Log.e(LOG_TAG, "eglMakeCurrent failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- return false;
- }
-
- mGL = (GL10) mEglContext.getGL();
-
- return true;
- }
-
- private void finishGL() {
- if (mEgl == null || mEglDisplay == null) {
- // Nothing to free
- return;
- }
- // Unbind the current surface and context from the display
- mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_CONTEXT);
- if (mEglContext != null) {
- mEgl.eglDestroyContext(mEglDisplay, mEglContext);
- }
- if (mEglSurface != null) {
- mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
- }
- }
-
- private void checkCurrent() {
- if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
- !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new RuntimeException("eglMakeCurrent failed "
- + GLUtils.getEGLErrorString(mEgl.eglGetError()));
- }
- }
- }
-
- private EGLConfig chooseEglConfig() {
- int[] configsCount = new int[1];
- EGLConfig[] configs = new EGLConfig[1];
- if (!mEgl.eglChooseConfig(mEglDisplay, sEglConfig, configs, 1, configsCount)) {
- throw new IllegalArgumentException("eglChooseConfig failed " +
- GLUtils.getEGLErrorString(mEgl.eglGetError()));
- } else if (configsCount[0] > 0) {
- return configs[0];
- }
- return null;
- }
-
- public class Firefly {
- static final float TEXTURE_HEIGHT = 30f; // TODO use measurement of texture size
- static final float SPEED = .5f;
-
- float mX; // between -mDisplayHeight and mDisplayHeight
- float mY; // between -mDisplayWidth and mDisplayWidth
- float mZ; // between 0.0 (near) and 1.0 (far)
- float mZ0;
- float mT;
- float mScale;
- float mAlpha;
-
- public Firefly() {
- reset();
- }
-
- void reset() {
- mX = (float) (Math.random() * mDisplayWidth) * 4 - 2 * mDisplayWidth;
- mY = (float) (Math.random() * mDisplayHeight) * 4 - 2 * mDisplayHeight;
- mZ0 = mZ = (float) (Math.random()) * 2 - 1;
- mT = 0f;
- mScale = 1.5f;
- mAlpha = 0f;
- }
-
- public void updatePositionAndScale(long timeElapsedMs) {
- mT += timeElapsedMs;
- mZ = mZ0 + mT/1000f * SPEED;
- mAlpha = 1f-mZ;
- if(mZ > 1.0) reset();
- }
-
- public void draw() {
- mGL.glLoadIdentity();
-
- // Counter clockwise winding
- mGL.glFrontFace(GL10.GL_CCW);
-
- mGL.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- mGL.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
-
- mGL.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
- mGL.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);
-
- mGL.glTranslatef(mX, mY, -NEAR_CLIPPING_PLANE-mZ*(FAR_CLIPPING_PLANE-NEAR_CLIPPING_PLANE));
- mGL.glColor4f(1, 1, 1, mAlpha);
-
- // scale around center
- mGL.glTranslatef(TEXTURE_HEIGHT/2, TEXTURE_HEIGHT/2, 0);
- mGL.glScalef(mScale, mScale, 0);
- mGL.glTranslatef(-TEXTURE_HEIGHT/2, -TEXTURE_HEIGHT/2, 0);
-
- mGL.glDrawElements(GL10.GL_TRIANGLES, mIndices.length, GL10.GL_UNSIGNED_SHORT,
- mIndexBuffer);
-
- mGL.glColor4f(1, 1, 1, 1);
- mGL.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
- mGL.glDisableClientState(GL10.GL_VERTEX_ARRAY);
- }
- }
-}
diff --git a/src/com/android/nfc/FireflyRenderer.java b/src/com/android/nfc/FireflyRenderer.java
new file mode 100644
index 0000000..4ce58b4
--- /dev/null
+++ b/src/com/android/nfc/FireflyRenderer.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2011 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.android.nfc;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL10;
+
+public class FireflyRenderer {
+ private static final String LOG_TAG = "NfcFireflyThread";
+
+ static final int NUM_FIREFLIES = 200;
+
+ static final float NEAR_CLIPPING_PLANE = 50f;
+ static final float FAR_CLIPPING_PLANE = 100f;
+
+ // All final variables below only need to be allocated once
+ // and can be reused between subsequent Beams
+ static final int[] sEglConfig = {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_DEPTH_SIZE, 0,
+ EGL10.EGL_STENCIL_SIZE, 0,
+ EGL10.EGL_NONE
+ };
+
+ // Vertices for drawing a 32x32 rect
+ static final float mVertices[] = {
+ 0.0f, 0.0f, 0.0f, // 0, Top Left
+ 0.0f, 32.0f, 0.0f, // 1, Bottom Left
+ 32.0f, 32.0f, 0.0f, // 2, Bottom Right
+ 32.0f, 0.0f, 0.0f, // 3, Top Right
+ };
+
+ // Mapping coordinates for the texture
+ static final float mTextCoords[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f,
+ 0.0f, 1.0f
+ };
+
+ // Connecting order (draws a square)
+ static final short[] mIndices = { 0, 1, 2, 0, 2, 3 };
+
+ final Context mContext;
+
+ // Buffer holding the vertices
+ final FloatBuffer mVertexBuffer;
+
+ // Buffer holding the indices
+ final ShortBuffer mIndexBuffer;
+
+ // Buffer holding the texture mapping coordinates
+ final FloatBuffer mTextureBuffer;
+
+ final Firefly[] mFireflies;
+
+ FireflyRenderThread mFireflyRenderThread;
+
+ // The surface to render the flies on, including width and height
+ SurfaceTexture mSurface;
+ int mDisplayWidth;
+ int mDisplayHeight;
+
+ public FireflyRenderer(Context context) {
+ mContext = context;
+
+ // First, build the vertex, texture and index buffers
+ ByteBuffer vbb = ByteBuffer.allocateDirect(mVertices.length * 4); // Float => 4 bytes
+ vbb.order(ByteOrder.nativeOrder());
+ mVertexBuffer = vbb.asFloatBuffer();
+ mVertexBuffer.put(mVertices);
+ mVertexBuffer.position(0);
+
+ ByteBuffer ibb = ByteBuffer.allocateDirect(mIndices.length * 2); // Short => 2 bytes
+ ibb.order(ByteOrder.nativeOrder());
+ mIndexBuffer = ibb.asShortBuffer();
+ mIndexBuffer.put(mIndices);
+ mIndexBuffer.position(0);
+
+ ByteBuffer tbb = ByteBuffer.allocateDirect(mTextCoords.length * 4);
+ tbb.order(ByteOrder.nativeOrder());
+ mTextureBuffer = tbb.asFloatBuffer();
+ mTextureBuffer.put(mTextCoords);
+ mTextureBuffer.position(0);
+
+ mFireflies = new Firefly[NUM_FIREFLIES];
+ for (int i = 0; i < NUM_FIREFLIES; i++) {
+ mFireflies[i] = new Firefly();
+ }
+ }
+
+ /**
+ * Starts rendering fireflies on the given surface.
+ * Must be called from the UI-thread.
+ */
+ public void start(SurfaceTexture surface, int width, int height) {
+ mSurface = surface;
+ mDisplayWidth = width;
+ mDisplayHeight = height;
+
+ mFireflyRenderThread = new FireflyRenderThread();
+ mFireflyRenderThread.start();
+ }
+
+ /**
+ * Stops rendering fireflies.
+ * Must be called from the UI-thread.
+ */
+ public void stop() {
+ if (mFireflyRenderThread != null) {
+ mFireflyRenderThread.finish();
+ try {
+ mFireflyRenderThread.join();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Couldn't wait for FireflyRenderThread.");
+ }
+ mFireflyRenderThread = null;
+ }
+ }
+
+ private class FireflyRenderThread extends Thread {
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+ EGLSurface mEglSurface;
+ GL10 mGL;
+
+ // Holding the handle to the texture
+ int mTextureId;
+
+ // Read/written by multiple threads
+ volatile boolean mFinished;
+
+ @Override
+ public void run() {
+ if (!initGL()) {
+ Log.e(LOG_TAG, "Failed to initialize OpenGL.");
+ return;
+ }
+ loadStarTexture();
+
+ mGL.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ mGL.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
+
+ // make adjustments for screen ratio
+ mGL.glMatrixMode(GL10.GL_PROJECTION);
+ mGL.glLoadIdentity();
+ mGL.glFrustumf(-mDisplayWidth, mDisplayWidth, mDisplayHeight, -mDisplayHeight, NEAR_CLIPPING_PLANE, FAR_CLIPPING_PLANE);
+
+ // Switch back to modelview
+ mGL.glMatrixMode(GL10.GL_MODELVIEW);
+ mGL.glLoadIdentity();
+
+ mGL.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
+ mGL.glDepthMask(true);
+
+
+ for (Firefly firefly : mFireflies) {
+ firefly.reset();
+ }
+
+ for (int i = 0; i < 3; i++) {
+ // Call eglSwapBuffers 3 times - this will allocate the necessary
+ // buffers, and make sure the animation looks smooth from the start.
+ if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ Log.e(LOG_TAG, "Could not swap buffers");
+ mFinished = true;
+ }
+ }
+
+ long startTime = System.currentTimeMillis();
+
+ while (!mFinished) {
+ long timeElapsedMs = System.currentTimeMillis() - startTime;
+ startTime = System.currentTimeMillis();
+
+ checkCurrent();
+
+ mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
+ mGL.glLoadIdentity();
+
+ mGL.glEnable(GL10.GL_TEXTURE_2D);
+ mGL.glEnable(GL10.GL_BLEND);
+ mGL.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
+
+ for (Firefly firefly : mFireflies) {
+ firefly.updatePositionAndScale(timeElapsedMs);
+ firefly.draw(mGL);
+ }
+
+ if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ Log.e(LOG_TAG, "Could not swap buffers");
+ mFinished = true;
+ }
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ try {
+ Thread.sleep(Math.max(30 - elapsed, 0));
+ } catch (InterruptedException e) {
+
+ }
+ }
+ finishGL();
+ }
+
+ public void finish() {
+ mFinished = true;
+ }
+
+ void loadStarTexture() {
+ int[] textureIds = new int[1];
+ mGL.glGenTextures(1, textureIds, 0);
+ mTextureId = textureIds[0];
+
+ InputStream in = null;
+ try {
+ // Remember that both texture dimensions must be a power of 2!
+ in = mContext.getAssets().open("star.png");
+
+ Bitmap bitmap = BitmapFactory.decodeStream(in);
+ mGL.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId);
+
+ mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
+ mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
+
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
+
+ bitmap.recycle();
+
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "IOException opening assets.");
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+
+ private void checkCurrent() {
+ if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
+ !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed "
+ + GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+ }
+ }
+
+ boolean initGL() {
+ // Initialize openGL engine
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ Log.e(LOG_TAG, "eglGetDisplay failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ return false;
+ }
+
+ int[] version = new int[2];
+ if (!mEgl.eglInitialize(mEglDisplay, version)) {
+ Log.e(LOG_TAG, "eglInitialize failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ return false;
+ }
+
+ mEglConfig = chooseEglConfig();
+ if (mEglConfig == null) {
+ Log.e(LOG_TAG, "eglConfig not initialized.");
+ return false;
+ }
+
+ mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null);
+
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ Log.e(LOG_TAG,"createWindowSurface returned error " + Integer.toString(error));
+ return false;
+ }
+
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ Log.e(LOG_TAG, "eglMakeCurrent failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ return false;
+ }
+
+ mGL = (GL10) mEglContext.getGL();
+
+ return true;
+ }
+
+ private void finishGL() {
+ if (mEgl == null || mEglDisplay == null) {
+ // Nothing to free
+ return;
+ }
+ // Unbind the current surface and context from the display
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+
+ if (mEglSurface != null) {
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+
+ if (mEglContext != null) {
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ }
+
+ private EGLConfig chooseEglConfig() {
+ int[] configsCount = new int[1];
+ EGLConfig[] configs = new EGLConfig[1];
+ if (!mEgl.eglChooseConfig(mEglDisplay, sEglConfig, configs, 1, configsCount)) {
+ throw new IllegalArgumentException("eglChooseConfig failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ } else if (configsCount[0] > 0) {
+ return configs[0];
+ }
+ return null;
+ }
+ }
+
+ private class Firefly {
+ static final float TEXTURE_HEIGHT = 30f; // TODO use measurement of texture size
+ static final float SPEED = .5f;
+
+ float mX; // between -mDisplayHeight and mDisplayHeight
+ float mY; // between -mDisplayWidth and mDisplayWidth
+ float mZ; // between 0.0 (near) and 1.0 (far)
+ float mZ0;
+ float mT;
+ float mScale;
+ float mAlpha;
+
+ public Firefly() {
+ }
+
+ void reset() {
+ mX = (float) (Math.random() * mDisplayWidth) * 4 - 2 * mDisplayWidth;
+ mY = (float) (Math.random() * mDisplayHeight) * 4 - 2 * mDisplayHeight;
+ mZ0 = mZ = (float) (Math.random()) * 2 - 1;
+ mT = 0f;
+ mScale = 1.5f;
+ mAlpha = 0f;
+ }
+
+ public void updatePositionAndScale(long timeElapsedMs) {
+ mT += timeElapsedMs;
+ mZ = mZ0 + mT/1000f * SPEED;
+ mAlpha = 1f-mZ;
+ if(mZ > 1.0) reset();
+ }
+
+ public void draw(GL10 gl) {
+ gl.glLoadIdentity();
+
+ // Counter clockwise winding
+ gl.glFrontFace(GL10.GL_CCW);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+
+ gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
+ gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);
+
+ gl.glTranslatef(mX, mY, -NEAR_CLIPPING_PLANE-mZ*(FAR_CLIPPING_PLANE-NEAR_CLIPPING_PLANE));
+ gl.glColor4f(1, 1, 1, mAlpha);
+
+ // scale around center
+ gl.glTranslatef(TEXTURE_HEIGHT/2, TEXTURE_HEIGHT/2, 0);
+ gl.glScalef(mScale, mScale, 0);
+ gl.glTranslatef(-TEXTURE_HEIGHT/2, -TEXTURE_HEIGHT/2, 0);
+
+ gl.glDrawElements(GL10.GL_TRIANGLES, mIndices.length, GL10.GL_UNSIGNED_SHORT,
+ mIndexBuffer);
+
+ gl.glColor4f(1, 1, 1, 1);
+ gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+ }
+ }
+}
diff --git a/src/com/android/nfc/P2pEventManager.java b/src/com/android/nfc/P2pEventManager.java
index fdeede5..4a57deb 100644
--- a/src/com/android/nfc/P2pEventManager.java
+++ b/src/com/android/nfc/P2pEventManager.java
@@ -81,7 +81,7 @@ public class P2pEventManager implements P2pEventListener, SendUi.Callback {
public void onP2pSendComplete() {
mNfcService.playSound(NfcService.SOUND_END);
mVibrator.vibrate(VIBRATION_PATTERN, -1);
- mSendUi.showPostSend();
+ mSendUi.finish(SendUi.FINISH_SEND_SUCCESS);
mSending = false;
mNdefSent = true;
}
diff --git a/src/com/android/nfc/SendUi.java b/src/com/android/nfc/SendUi.java
index 6fc24d5..ae54e0a 100644
--- a/src/com/android/nfc/SendUi.java
+++ b/src/com/android/nfc/SendUi.java
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.animation.TimeAnimator;
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
@@ -32,7 +33,6 @@ import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.os.Binder;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -42,26 +42,43 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
/**
- * All methods must be called on UI thread
+ * This class is responsible for handling the UI animation
+ * around Android Beam. The animation consists of the following
+ * animators:
+ *
+ * mPreAnimator: scales the screenshot down to INTERMEDIATE_SCALE
+ * mSlowSendAnimator: scales the screenshot down to 0.2f (used as a "send in progress" animation)
+ * mFastSendAnimator: quickly scales the screenshot down to 0.0f (used for send success)
+ * mFadeInAnimator: fades the current activity back in (used after mFastSendAnimator completes)
+ * mScaleUpAnimator: scales the screenshot back up to full screen (used for failure or receiving)
+ * mHintAnimator: Slowly turns up the alpha of the "Touch to Beam" hint
+ *
+ * Possible sequences are:
+ *
+ * mPreAnimator => mSlowSendAnimator => mFastSendAnimator => mFadeInAnimator (send success)
+ * mPreAnimator => mSlowSendAnimator => mScaleUpAnimator (send failure)
+ * mPreAnimator => mScaleUpAnimator (p2p link broken, or data received)
+ *
+ * Note that mFastSendAnimator and mFadeInAnimator are combined in a set, as they
+ * are an atomic animation that cannot be interrupted.
+ *
+ * All methods of this class must be called on the UI thread
*/
public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
- TextureView.SurfaceTextureListener {
- private static final String LOG_TAG = "SendUI";
-
+ TimeAnimator.TimeListener, TextureView.SurfaceTextureListener {
static final float INTERMEDIATE_SCALE = 0.6f;
static final float[] PRE_SCREENSHOT_SCALE = {1.0f, INTERMEDIATE_SCALE};
static final int PRE_DURATION_MS = 350;
- static final float[] CLONE_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.2f};
+ static final float[] SEND_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.2f};
static final int SLOW_SEND_DURATION_MS = 8000; // Stretch out sending over 8s
- static final int FAST_CLONE_DURATION_MS = 350;
+ static final int FAST_SEND_DURATION_MS = 350;
static final float[] SCALE_UP_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 1.0f};
static final int SCALE_UP_DURATION_MS = 300;
@@ -76,7 +93,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
static final int TEXT_HINT_ALPHA_START_DELAY_MS = 300;
static final int FINISH_SCALE_UP = 0;
- static final int FINISH_SLIDE_OUT = 1;
+ static final int FINISH_SEND_SUCCESS = 1;
// all members are only used on UI thread
final WindowManager mWindowManager;
@@ -92,20 +109,47 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
final TextureView mTextureView;
final TextView mTextHint;
final Callback mCallback;
+
+ // The mFrameCounter animation is purely used to count down a certain
+ // number of (vsync'd) frames. This is needed because the first 3
+ // times the animation internally calls eglSwapBuffers(), large buffers
+ // are allocated by the graphics drivers. This causes the animation
+ // to look janky. So on platforms where we can use hardware acceleration,
+ // the animation order is:
+ // Wait for hw surface => start frame counter => start pre-animation after 3 frames
+ // For platforms where no hw acceleration can be used, the pre-animation
+ // is started immediately.
+ final TimeAnimator mFrameCounterAnimator;
+
final ObjectAnimator mPreAnimator;
final ObjectAnimator mSlowSendAnimator;
- final ObjectAnimator mFastCloneAnimator;
+ final ObjectAnimator mFastSendAnimator;
final ObjectAnimator mFadeInAnimator;
final ObjectAnimator mHintAnimator;
+ final ObjectAnimator mScaleUpAnimator;
final AnimatorSet mSuccessAnimatorSet;
+
+ // Besides animating the screenshot, the Beam UI also renders
+ // fireflies on platforms where we can do hardware-acceleration.
+ // Firefly rendering is only started once the initial
+ // "pre-animation" has scaled down the screenshot, to avoid
+ // that animation becoming janky. Likewise, the fireflies are
+ // stopped in their tracks as soon as we finish the animation,
+ // to make the finishing animation smooth.
final boolean mHardwareAccelerated;
+ final FireflyRenderer mFireflyRenderer;
Bitmap mScreenshotBitmap;
- ObjectAnimator mSlideoutAnimator;
- ObjectAnimator mScaleUpAnimator;
- FireflyRenderThread mFireflyRenderThread;
boolean mAttached;
+ boolean mSending;
+
+ int mRenderedFrames;
+
+ // Used for holding the surface
+ SurfaceTexture mSurface;
+ int mSurfaceWidth;
+ int mSurfaceHeight;
interface Callback {
public void onSendConfirmed();
@@ -125,6 +169,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
mLayoutInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
+
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
mScreenshotLayout.setFocusable(true);
@@ -150,6 +195,9 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
PixelFormat.OPAQUE);
mWindowLayoutParams.token = new Binder();
+ mFrameCounterAnimator = new TimeAnimator();
+ mFrameCounterAnimator.setTimeListener(this);
+
PropertyValuesHolder preX = PropertyValuesHolder.ofFloat("scaleX", PRE_SCREENSHOT_SCALE);
PropertyValuesHolder preY = PropertyValuesHolder.ofFloat("scaleY", PRE_SCREENSHOT_SCALE);
mPreAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, preX, preY);
@@ -157,8 +205,8 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
mPreAnimator.setDuration(PRE_DURATION_MS);
mPreAnimator.addListener(this);
- PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", CLONE_SCREENSHOT_SCALE);
- PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", CLONE_SCREENSHOT_SCALE);
+ PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", SEND_SCREENSHOT_SCALE);
+ PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", SEND_SCREENSHOT_SCALE);
PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha",
new float[]{1.0f, 0.0f});
@@ -166,11 +214,11 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
mSlowSendAnimator.setInterpolator(new DecelerateInterpolator());
mSlowSendAnimator.setDuration(SLOW_SEND_DURATION_MS);
- mFastCloneAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX,
+ mFastSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX,
postY, alphaDown);
- mFastCloneAnimator.setInterpolator(new DecelerateInterpolator());
- mFastCloneAnimator.setDuration(FAST_CLONE_DURATION_MS);
- mFastCloneAnimator.addListener(this);
+ mFastSendAnimator.setInterpolator(new DecelerateInterpolator());
+ mFastSendAnimator.setDuration(FAST_SEND_DURATION_MS);
+ mFastSendAnimator.addListener(this);
PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX", SCALE_UP_SCREENSHOT_SCALE);
PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY", SCALE_UP_SCREENSHOT_SCALE);
@@ -194,8 +242,13 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
mHintAnimator.setStartDelay(TEXT_HINT_ALPHA_START_DELAY_MS);
mSuccessAnimatorSet = new AnimatorSet();
- mSuccessAnimatorSet.playSequentially(mFastCloneAnimator, mFadeInAnimator);
+ mSuccessAnimatorSet.playSequentially(mFastSendAnimator, mFadeInAnimator);
+ if (mHardwareAccelerated) {
+ mFireflyRenderer = new FireflyRenderer(context);
+ } else {
+ mFireflyRenderer = null;
+ }
mAttached = false;
}
@@ -253,92 +306,85 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
// Disable statusbar pull-down
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
+ mSending = false;
mAttached = true;
- mPreAnimator.start();
+
+ if (!mHardwareAccelerated) {
+ mPreAnimator.start();
+ } // else, we will start the animation once we get the hardware surface
}
/** Show starting send animation */
public void showStartSend() {
- if (!mAttached) {
- return;
- }
- mSlowSendAnimator.start();
- }
-
- /** Show post-send animation */
- public void showPostSend() {
- if (!mAttached) {
- return;
- }
-
- mSlowSendAnimator.cancel();
- mTextHint.setVisibility(View.GONE);
-
+ if (!mAttached) return;
+ // Update the starting scale - touchscreen-mashers may trigger
+ // this before the pre-animation completes.
float currentScale = mScreenshotView.getScaleX();
-
- // Modify the fast clone parameters to match the current scale
PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX",
new float[] {currentScale, 0.0f});
PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
new float[] {currentScale, 0.0f});
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",
- new float[] {1.0f, 0.0f});
- mFastCloneAnimator.setValues(postX, postY, alpha);
-
- // Modify the fadeIn parameters to match the current scale
- PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha",
- new float[] {0.0f, 1.0f});
- mFadeInAnimator.setValues(fadeIn);
- if (mFireflyRenderThread != null) {
- mFireflyRenderThread.fadeOut();
- }
-
- mSuccessAnimatorSet.start();
+ mSlowSendAnimator.setValues(postX, postY);
+ mSlowSendAnimator.start();
}
/** Return to initial state */
public void finish(int finishMode) {
- if (!mAttached) {
- return;
+ if (!mAttached) return;
+
+ // Stop rendering the fireflies
+ if (mFireflyRenderer != null) {
+ mFireflyRenderer.stop();
}
+
mTextHint.setVisibility(View.GONE);
- if (finishMode == FINISH_SLIDE_OUT) {
- PropertyValuesHolder slideX = PropertyValuesHolder.ofFloat("translationX",
- new float[]{0.0f, mScreenshotView.getWidth()});
- mSlideoutAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, slideX);
- mSlideoutAnimator.setInterpolator(new AccelerateInterpolator());
- mSlideoutAnimator.setDuration(SLIDE_OUT_DURATION_MS);
- mSlideoutAnimator.addListener(this);
- mSlideoutAnimator.start();
- } else {
- float currentScale = mScreenshotView.getScaleX();
- float currentAlpha = mScreenshotView.getAlpha();
+
+ float currentScale = mScreenshotView.getScaleX();
+ float currentAlpha = mScreenshotView.getAlpha();
+
+ if (finishMode == FINISH_SCALE_UP) {
PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
new float[] {currentScale, 1.0f});
PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
new float[] {currentScale, 1.0f});
PropertyValuesHolder scaleUpAlpha = PropertyValuesHolder.ofFloat("alpha",
new float[] {currentAlpha, 1.0f});
- mScaleUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, scaleUpX, scaleUpY, scaleUpAlpha);
- mScaleUpAnimator.setInterpolator(new DecelerateInterpolator());
- mScaleUpAnimator.setDuration(SCALE_UP_DURATION_MS);
- mScaleUpAnimator.addListener(this);
+ mScaleUpAnimator.setValues(scaleUpX, scaleUpY, scaleUpAlpha);
+
mScaleUpAnimator.start();
+ } else if (finishMode == FINISH_SEND_SUCCESS){
+ // Modify the fast send parameters to match the current scale
+ PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX",
+ new float[] {currentScale, 0.0f});
+ PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
+ new float[] {currentScale, 0.0f});
+ PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",
+ new float[] {1.0f, 0.0f});
+ mFastSendAnimator.setValues(postX, postY, alpha);
+
+ // Reset the fadeIn parameters to start from alpha 1
+ PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha",
+ new float[] {0.0f, 1.0f});
+ mFadeInAnimator.setValues(fadeIn);
+
+ mSlowSendAnimator.cancel();
+ mSuccessAnimatorSet.start();
}
}
public void dismiss() {
- if (!mAttached) {
- return;
- }
+ if (!mAttached) return;
+
// Immediately set to false, to prevent .cancel() calls
// below from immediately calling into dismiss() again.
mAttached = false;
+ mSurface = null;
+ mFrameCounterAnimator.cancel();
mPreAnimator.cancel();
mSlowSendAnimator.cancel();
- mFastCloneAnimator.cancel();
+ mFastSendAnimator.cancel();
mSuccessAnimatorSet.cancel();
mScaleUpAnimator.cancel();
mWindowManager.removeView(mScreenshotLayout);
@@ -403,7 +449,6 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
return null;
}
-
if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
@@ -444,13 +489,18 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
@Override
public void onAnimationEnd(Animator animation) {
if (animation == mScaleUpAnimator || animation == mSuccessAnimatorSet ||
- animation == mSlideoutAnimator || animation == mFadeInAnimator) {
+ animation == mFadeInAnimator) {
+ // These all indicate the end of the animation
dismiss();
- } else if (animation == mFastCloneAnimator) {
- // After cloning is done and we've faded out, reset the scale to 1
+ } else if (animation == mFastSendAnimator) {
+ // After sending is done and we've faded out, reset the scale to 1
// so we can fade it back in.
mScreenshotView.setScaleX(1.0f);
mScreenshotView.setScaleY(1.0f);
+ } else if (animation == mPreAnimator) {
+ if (mHardwareAccelerated && mAttached && !mSending) {
+ mFireflyRenderer.start(mSurface, mSurfaceWidth, mSurfaceHeight);
+ }
}
}
@@ -461,23 +511,46 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
public void onAnimationRepeat(Animator animation) { }
@Override
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ // This gets called on animation vsync
+ if (++mRenderedFrames < 4) {
+ // For the first 3 frames, call invalidate(); this calls eglSwapBuffers
+ // on the surface, which will allocate large buffers the first three calls
+ // as Android uses triple buffering.
+ mScreenshotLayout.invalidate();
+ } else {
+ // Buffers should be allocated, start the real animation
+ mFrameCounterAnimator.cancel();
+ mPreAnimator.start();
+ }
+ }
+
+ @Override
public boolean onTouch(View v, MotionEvent event) {
if (!mAttached) {
return false;
}
+ mSending = true;
// Ignore future touches
mScreenshotView.setOnTouchListener(null);
- mPreAnimator.end();
+ // Cancel any ongoing animations
+ mFrameCounterAnimator.cancel();
+ mPreAnimator.cancel();
+
mCallback.onSendConfirmed();
return true;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- if (mHardwareAccelerated) {
- mFireflyRenderThread = new FireflyRenderThread(mContext, surface, width, height);
- mFireflyRenderThread.start();
+ if (mHardwareAccelerated && !mSending) {
+ mRenderedFrames = 0;
+
+ mFrameCounterAnimator.start();
+ mSurface = surface;
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
}
}
@@ -488,19 +561,12 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- if (mFireflyRenderThread != null) {
- mFireflyRenderThread.finish();
- try {
- mFireflyRenderThread.join();
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Couldn't wait for FireflyRenderThread.");
- }
- mFireflyRenderThread = null;
- }
+ mSurface = null;
+
return true;
}
@Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- }
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
+
}