summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartijn Coenen <maco@google.com>2011-09-02 10:20:04 -0700
committerMartijn Coenen <maco@google.com>2011-09-02 18:18:32 -0700
commit217edd31f93c3917a8bb02420366b88d27c431c2 (patch)
treefc9da9cee9d987e33bdd9d2f383d995b57707840
parentf7966d1dadff2d78f01bbdb134e8252b53343179 (diff)
downloadpackages_apps_nfc-217edd31f93c3917a8bb02420366b88d27c431c2.zip
packages_apps_nfc-217edd31f93c3917a8bb02420366b88d27c431c2.tar.gz
packages_apps_nfc-217edd31f93c3917a8bb02420366b88d27c431c2.tar.bz2
Render fireflies with OpenGL.
- Show a static (scaling in) background in case we're not hw-accelerated. - Modify the cloning animation a bit. Change-Id: I808238fb515c2c8d7832a4ab8bc70a37e1198de5
-rw-r--r--assets/star.pngbin0 -> 978 bytes
-rw-r--r--res/drawable-hdpi/back.pngbin0 -> 32598 bytes
-rw-r--r--res/layout/screenshot.xml16
-rw-r--r--src/com/android/nfc/FireflyRenderThread.java415
-rw-r--r--src/com/android/nfc/SendUi.java118
5 files changed, 528 insertions, 21 deletions
diff --git a/assets/star.png b/assets/star.png
new file mode 100644
index 0000000..77b9ca9
--- /dev/null
+++ b/assets/star.png
Binary files differ
diff --git a/res/drawable-hdpi/back.png b/res/drawable-hdpi/back.png
new file mode 100644
index 0000000..dcad41d
--- /dev/null
+++ b/res/drawable-hdpi/back.png
Binary files differ
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index 180d045..9e62af7 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -18,6 +18,18 @@
android:layout_height="match_parent"
android:background="#FF000000"
>
+ <TextureView android:id="@+id/fireflies"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <ImageView android:id="@+id/back"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:adjustViewBounds="true"
+ android:src="@drawable/back"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
>
@@ -34,12 +46,12 @@
android:textColor="?android:attr/textColorPrimary"
/>
</RelativeLayout>
- <ImageView android:id="@+id/screenshot"
+ <ImageView android:id="@+id/clone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
/>
- <ImageView android:id="@+id/clone"
+ <ImageView android:id="@+id/screenshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
diff --git a/src/com/android/nfc/FireflyRenderThread.java b/src/com/android/nfc/FireflyRenderThread.java
new file mode 100644
index 0000000..13b9a12
--- /dev/null
+++ b/src/com/android/nfc/FireflyRenderThread.java
@@ -0,0 +1,415 @@
+/*
+ * 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 = 100;
+ static final int PIXELS_PER_SECOND = 50; // Speed of fireflies
+
+ 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.glOrthof(0, mDisplayWidth, mDisplayHeight, 0, -1, 1);
+
+ // Switch back to modelview
+ mGL.glMatrixMode(GL10.GL_MODELVIEW);
+ mGL.glLoadIdentity();
+
+ // Reset firefly models
+ for (Firefly firefly : mFireflies) {
+ firefly.reset();
+ }
+
+ 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 int STEADY_INTERVAL_MS = 1250; // Time fireflies continue in same direction
+ static final float MIN_SCALE = 0.5f; // The minimum amount fireflies are scaled to
+
+ float mX;
+ float mY;
+ float mAngle;
+ float mScale;
+ float mAlpha;
+ boolean mScalingDown;
+ int mTimeRemaining;
+
+ public Firefly() {
+ reset();
+ }
+
+ void reset() {
+ float randomVal = (float) Math.random();
+
+ mX = randomVal * mDisplayWidth;
+ mY = (float) (Math.random() * mDisplayHeight);
+ mAngle = randomVal * 360f;
+ mScalingDown = (randomVal > 0.5f) ? true : false;
+ mTimeRemaining = (int) (randomVal * STEADY_INTERVAL_MS);
+ mAlpha = 1;
+ }
+
+ public void updatePositionAndScale(long timeElapsedMs) {
+ if (mFadeOut) {
+ // Diminish alpha and return
+ if (mAlpha > 0) {
+ mAlpha -= timeElapsedMs * 0.003;
+ }
+ return;
+ }
+ mX += Math.cos(Math.toRadians(mAngle)) * timeElapsedMs / 1000 * PIXELS_PER_SECOND;
+ mY += Math.sin(Math.toRadians(mAngle)) * timeElapsedMs / 1000 * PIXELS_PER_SECOND;
+
+ mX %= mDisplayWidth;
+ if (mX < 0) {
+ mX += mDisplayWidth;
+ }
+ mY %= mDisplayHeight;
+ if (mY < 0) {
+ mY += mDisplayHeight;
+ }
+
+ if (mScalingDown) {
+ mScale = 1.0f - (((float)mTimeRemaining / STEADY_INTERVAL_MS) * MIN_SCALE);
+ } else {
+ mScale = MIN_SCALE + (((float)mTimeRemaining / STEADY_INTERVAL_MS) * MIN_SCALE);
+ }
+
+ mTimeRemaining -= timeElapsedMs;
+ if (mTimeRemaining < 0) {
+ // Update our angle, reverse our scaling
+ mAngle = (float) (Math.random() * 360f);
+ mScalingDown = !mScalingDown;
+ mTimeRemaining = STEADY_INTERVAL_MS;
+ }
+ }
+
+ 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, 0);
+ mGL.glScalef(mScale, mScale, 0);
+ mGL.glColor4f(1, 1, 1, mAlpha);
+
+ 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/SendUi.java b/src/com/android/nfc/SendUi.java
index 39b3758..0bb8916 100644
--- a/src/com/android/nfc/SendUi.java
+++ b/src/com/android/nfc/SendUi.java
@@ -31,6 +31,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
import android.os.Binder;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -38,6 +39,7 @@ import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -50,7 +52,8 @@ import android.widget.TextView;
/**
* All methods must be called on UI thread
*/
-public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
+public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
+ TextureView.SurfaceTextureListener {
private static final String LOG_TAG = "SendUI";
static final float INTERMEDIATE_SCALE = 0.6f;
@@ -59,7 +62,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
static final int PRE_DURATION_MS = 300;
static final float[] CLONE_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.0f};
- static final int SLOW_CLONE_DURATION_MS = 3000; // Stretch out sending over 3s
+ static final int SLOW_SEND_DURATION_MS = 3000; // Stretch out sending over 3s
static final int FAST_CLONE_DURATION_MS = 200;
static final float[] SCALE_UP_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 1.0f};
@@ -70,6 +73,9 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
static final float[] TEXT_HINT_ALPHA_RANGE = {0.0f, 1.0f};
static final int TEXT_HINT_ALPHA_DURATION_MS = 500;
+ static final float[] BACKGROUND_SCALE_RANGE = {1.0f, 2.0f};
+ static final int BACKGROUND_SCALE_DURATION_MS = 5000;
+
static final int FINISH_SCALE_UP = 0;
static final int FINISH_SLIDE_OUT = 1;
@@ -85,18 +91,22 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
final View mScreenshotLayout;
final ImageView mScreenshotView;
final ImageView mCloneView;
+ final ImageView mBackgroundImage;
+ final TextureView mTextureView;
final TextView mTextHint;
final Callback mCallback;
final ObjectAnimator mPreAnimator;
- final ObjectAnimator mSlowCloneAnimator;
+ final ObjectAnimator mSlowSendAnimator;
final ObjectAnimator mFastCloneAnimator;
final ObjectAnimator mScaleUpAnimator;
final ObjectAnimator mHintAnimator;
final AnimatorSet mSuccessAnimatorSet;
+ final ObjectAnimator mBackgroundAnimator;
final boolean mHardwareAccelerated;
Bitmap mScreenshotBitmap;
ObjectAnimator mSlideoutAnimator;
+ FireflyRenderThread mFireflyRenderThread;
boolean mAttached;
@@ -125,6 +135,10 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
mTextHint = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
+ mTextureView = (TextureView) mScreenshotLayout.findViewById(R.id.fireflies);
+ mTextureView.setSurfaceTextureListener(this);
+
+ mBackgroundImage = (ImageView) mScreenshotLayout.findViewById(R.id.back);
// We're only allowed to use hardware acceleration if
// isHighEndGfx() returns true - otherwise, we're too limited
// on resources to do it.
@@ -132,6 +146,11 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
int hwAccelerationFlags = mHardwareAccelerated ?
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED : 0;
+ if (!mHardwareAccelerated) {
+ // Only show background in case we're not hw-accelerated
+ mBackgroundImage.setVisibility(View.VISIBLE);
+ }
+
mWindowLayoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
@@ -151,9 +170,9 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", CLONE_SCREENSHOT_SCALE);
PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", CLONE_SCREENSHOT_SCALE);
- mSlowCloneAnimator = ObjectAnimator.ofPropertyValuesHolder(mCloneView, postX, postY);
- mSlowCloneAnimator.setInterpolator(null); // linear
- mSlowCloneAnimator.setDuration(SLOW_CLONE_DURATION_MS);
+ mSlowSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, postY);
+ mSlowSendAnimator.setInterpolator(null); // linear
+ mSlowSendAnimator.setDuration(SLOW_SEND_DURATION_MS);
mFastCloneAnimator = ObjectAnimator.ofPropertyValuesHolder(mCloneView, postX, postY);
mFastCloneAnimator.setInterpolator(null); // linear
@@ -174,6 +193,12 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
mSuccessAnimatorSet = new AnimatorSet();
mSuccessAnimatorSet.playSequentially(mFastCloneAnimator, mScaleUpAnimator);
+ scaleUpX = PropertyValuesHolder.ofFloat("scaleX", BACKGROUND_SCALE_RANGE);
+ scaleUpY = PropertyValuesHolder.ofFloat("scaleY", BACKGROUND_SCALE_RANGE);
+ mBackgroundAnimator = ObjectAnimator.ofPropertyValuesHolder(mBackgroundImage, scaleUpX, scaleUpY);
+ mBackgroundAnimator.setInterpolator(new DecelerateInterpolator(2.0f));
+ mBackgroundAnimator.setDuration(BACKGROUND_SCALE_DURATION_MS);
+
mAttached = false;
}
@@ -204,7 +229,6 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
mCloneView.setScaleX(INTERMEDIATE_SCALE);
mCloneView.setScaleY(INTERMEDIATE_SCALE);
-
mTextHint.setVisibility(showHint ? View.VISIBLE : View.GONE);
mTextHint.setAlpha(1.0f);
@@ -231,12 +255,23 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
break;
}
+ // Reset scale up parameters
+ PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
+ SCALE_UP_SCREENSHOT_SCALE);
+ PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
+ SCALE_UP_SCREENSHOT_SCALE);
+ mScaleUpAnimator.setValues(scaleUpX, scaleUpY);
+
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
// Disable statusbar pull-down
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
mAttached = true;
mPreAnimator.start();
+
+ if (!mHardwareAccelerated) {
+ mBackgroundAnimator.start();
+ }
}
/** Show starting send animation */
@@ -244,11 +279,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
if (!mAttached) {
return;
}
- mScreenshotView.setAlpha(0.7f);
- mCloneView.setScaleX(INTERMEDIATE_SCALE);
- mCloneView.setScaleY(INTERMEDIATE_SCALE);
- mCloneView.setVisibility(View.VISIBLE);
- mSlowCloneAnimator.start();
+ mSlowSendAnimator.start();
}
/** Show post-send animation */
@@ -257,15 +288,33 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
return;
}
- mSlowCloneAnimator.cancel();
+ mSlowSendAnimator.cancel();
mTextHint.setVisibility(View.GONE);
- // Modify the fast clone parameters to match the current scale
- float currentScale = mCloneView.getScaleX();
- currentScale = mCloneView.getScaleX();
- PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", new float[] {currentScale, 0.0f});
- PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", new float[] {currentScale, 0.0f});
+ float currentScale = mScreenshotView.getScaleX();
+ mScreenshotView.setAlpha(0.7f);
+
+ // Make the clone visible for scaling to the background
+ mCloneView.setScaleX(currentScale);
+ mCloneView.setScaleY(currentScale);
+ mCloneView.setVisibility(View.VISIBLE);
+
+ // 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});
mFastCloneAnimator.setValues(postX, postY);
+ // Modify the scale up parameters to match the current scale
+ PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
+ new float[] {currentScale, 1.0f});
+ PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
+ new float[] {currentScale, 1.0f});
+ mScaleUpAnimator.setValues(scaleUpX, scaleUpY);
+
+ if (mFireflyRenderThread != null) {
+ mFireflyRenderThread.fadeOut();
+ }
mSuccessAnimatorSet.start();
}
@@ -294,7 +343,7 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
return;
}
mPreAnimator.cancel();
- mSlowCloneAnimator.cancel();
+ mSlowSendAnimator.cancel();
mFastCloneAnimator.cancel();
mSuccessAnimatorSet.cancel();
mScaleUpAnimator.cancel();
@@ -397,4 +446,35 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener {
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();
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ // Since we've disabled orientation changes, we can safely ignore this
+ }
+
+ @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;
+ }
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
}