diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/nfc/FireflyRenderThread.java | 394 | ||||
-rw-r--r-- | src/com/android/nfc/FireflyRenderer.java | 424 | ||||
-rw-r--r-- | src/com/android/nfc/P2pEventManager.java | 2 | ||||
-rw-r--r-- | src/com/android/nfc/SendUi.java | 248 |
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) { } + } |