summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xAndroidManifest.xml7
-rw-r--r--res/layout/screenshot.xml2
-rwxr-xr-xres/values/strings.xml2
-rwxr-xr-xsrc/com/android/nfc/NdefP2pManager.java91
-rw-r--r--src/com/android/nfc/P2pAnimationActivity.java (renamed from src/com/android/nfc/ScreenshotWindowAnimator.java)475
-rwxr-xr-xsrc/com/android/nfc/ndefpush/NdefPushServer.java6
-rw-r--r--src/com/android/nfc/snep/SnepServer.java6
7 files changed, 277 insertions, 312 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 410fced..11d4edc 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -41,6 +41,13 @@
android:excludeFromRecents="true"
android:multiprocess="true"
/>
+ <activity android:name="com.android.nfc.P2pAnimationActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:excludeFromRecents="true"
+ android:launchMode="singleInstance"
+ android:hardwareAccelerated="true"
+ android:configChanges="orientation"
+ />
</application>
</manifest>
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index 2a6e242..3442b8b 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -18,7 +18,7 @@
android:layout_height="match_parent"
android:background="#FF000000"
>
- <TextView android:id="@+id/touch"
+ <TextView android:id="@+id/calltoaction"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/touch"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4d71b99..4aa9885 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -29,5 +29,5 @@
<!-- Content description of the NFC enabled notification icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_nfc_enabled">NFC enabled.</string>
- <string name="touch">Tap to share!</string>
+ <string name="touch">Rotate to share!</string>
</resources>
diff --git a/src/com/android/nfc/NdefP2pManager.java b/src/com/android/nfc/NdefP2pManager.java
index 3ba1cd2..78221e7 100755
--- a/src/com/android/nfc/NdefP2pManager.java
+++ b/src/com/android/nfc/NdefP2pManager.java
@@ -24,8 +24,8 @@ import com.android.nfc.snep.SnepServer;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
-import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -43,14 +43,12 @@ import android.provider.ContactsContract.Profile;
import android.util.Log;
import android.view.OrientationEventListener;
-import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
-public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimator.Callback {
+public class NdefP2pManager implements Handler.Callback, P2pAnimationActivity.Callback {
// TODO dynamically assign SAP values
static final int NDEFPUSH_SAP = 0x10;
@@ -65,11 +63,10 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
static final int MSG_ENABLE = 2;
static final int MSG_DISABLE = 3;
- static final long[] mVibPattern = {0, 100, 100, 150, 100, 250, 100, 10000};
+ static final long[] mVibPattern = {0, 100, 10000};
static final String TAG = "P2PManager";
static final boolean DBG = true;
- final ScreenshotWindowAnimator mScreenshot;
final NdefPushServer mNdefPushServer;
final SnepServer mDefaultSnepServer;
final ActivityManager mActivityManager;
@@ -87,6 +84,13 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
boolean mSendStarted;
boolean mIsP2pEnabled;
+ // Variables below may be read/written from both the UI thread and the
+ // thread calling onOrientationChanged. Synchronized on this.
+ boolean mNeedStartOrientation;
+
+ // Variables below are modified only on the thread calling onOrientationChanged
+ int mStartOrientation;
+
final static Uri mProfileUri = Profile.CONTENT_VCARD_URI.buildUpon().
appendQueryParameter(Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, "true").
build();
@@ -105,7 +109,6 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = context.getPackageManager();
mContext = context;
- mScreenshot = new ScreenshotWindowAnimator(context, this);
mListener = listener;
mAnimating = false;
mHandler = new Handler(this);
@@ -118,11 +121,20 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
mOrientationListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int orientation) {
- if (mAnimating && (orientation > 50 && orientation < 310)) {
- onConfirmSend();
+ synchronized (this) {
+ if (mNeedStartOrientation) {
+ mStartOrientation = orientation;
+ mNeedStartOrientation = false;
+ }
+ }
+ int diff = ((((mStartOrientation - orientation) % 360) + 540) % 360) - 180;
+ if (mAnimating && Math.abs(diff) > 35) {
+ onSendConfirmed();
+ // TODO give visual clue
}
}
};
+
}
/**
@@ -177,15 +189,18 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
public void onLlcpActivated() {
if (DBG) Log.d(TAG, "LLCP connection up and running");
+ mVibrator.vibrate(mVibPattern, -1);
+ mSendState = STATE_WAITING;
+ mReceiveState = STATE_WAITING;
+
if (!mIsP2pEnabled) {
if (DBG) Log.d(TAG, "P2P disabled, ignoring");
return;
}
- mOrientationListener.enable();
- mVibrator.vibrate(mVibPattern, 6);
- mSendState = STATE_WAITING;
- mReceiveState = STATE_WAITING;
+ // If the LLCP link came up again, remove any pending timeout
+ // messages that will cancel the animation - we may still
+ // complete it now.
mHandler.removeMessages(MSG_TIMEOUT);
NdefMessage foregroundMsg;
@@ -229,14 +244,24 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
// If an animation is still running, we don't restart it to avoid
// nasty effects if at the edge of RF field.
if (foregroundMsg != null && !mAnimating) {
- mScreenshot.stop();
- mScreenshot.start();
+ // Start listening for orientation changes
+ synchronized (NdefP2pManager.this) {
+ mNeedStartOrientation = true;
+ mOrientationListener.enable();
+ }
+ mPushMsg = foregroundMsg;
+ // Create a screenshot
+ P2pAnimationActivity.makeScreenshot(mContext);
+ P2pAnimationActivity.setCallback(this);
+ // Start the animation activity
+ Intent animIntent = new Intent();
+ animIntent.setClass(mContext, P2pAnimationActivity.class);
+ animIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(animIntent);
mAnimating = true;
mSendStarted = false;
mListener.onP2pBegin();
}
-
- mPushMsg = foregroundMsg;
}
/**
@@ -244,8 +269,6 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
*/
public void onLlcpDeactivated() {
if (DBG) Log.d(TAG, "LLCP deactivated.");
-
- mOrientationListener.disable();
mVibrator.cancel();
if (mPushTask != null) {
mPushTask.cancel(true);
@@ -380,20 +403,20 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
if (mSendStarted && sendSuccess) {
if (mAnimating) {
- mScreenshot.finishWithSendReceive();
+ P2pAnimationActivity.finishWithSend();
}
mListener.onP2pEnd();
- }
- else if (mSendStarted && !sendSuccess) {
+ } else if (receiveSuccess){
if (mAnimating) {
- mScreenshot.finishWithFailure();
+ P2pAnimationActivity.finishWithReceive();
}
- mListener.onP2pError();
+ mListener.onP2pEnd();
} else {
if (mAnimating) {
- mScreenshot.finishWithFailure();
+ P2pAnimationActivity.finishWithFailure();
}
mListener.onP2pError();
+
}
// Remove all queued messages - until we start again
mHandler.removeMessages(MSG_TIMEOUT);
@@ -401,6 +424,7 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
mVibrator.cancel();
mAnimating = false;
+ mOrientationListener.disable();
}
@Override
@@ -408,6 +432,7 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
switch (msg.what) {
case MSG_RECEIVE_SUCCESS:
mReceiveState = STATE_SUCCESS;
+ finish(mSendState == STATE_SUCCESS, mReceiveState == STATE_SUCCESS);
break;
case MSG_TIMEOUT:
finish(mSendState == STATE_SUCCESS, mReceiveState == STATE_SUCCESS);
@@ -417,23 +442,27 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
break;
}
mIsP2pEnabled = false;
- mDefaultSnepServer.stop();
- mNdefPushServer.stop();
+ // Don't disable our p2p servers here - we still want to be able
+ // to receive data
break;
case MSG_ENABLE:
if (mIsP2pEnabled) {
break;
}
+ if (!mDefaultSnepServer.isRunning()) {
+ mDefaultSnepServer.start();
+ }
+ if (!mNdefPushServer.isRunning()) {
+ mNdefPushServer.start();
+ }
mIsP2pEnabled = true;
- mDefaultSnepServer.start();
- mNdefPushServer.start();
break;
}
return true;
}
@Override
- public void onConfirmSend() {
+ public void onSendConfirmed() {
if (!mSendStarted) {
// Start sending
if (mPushTask != null) {
@@ -442,6 +471,8 @@ public class NdefP2pManager implements Handler.Callback, ScreenshotWindowAnimato
mSendStarted = true;
mPushTask = new PushTask(mPushMsg);
mPushTask.execute();
+
+ mVibrator.vibrate(mVibPattern, -1);
}
}
diff --git a/src/com/android/nfc/ScreenshotWindowAnimator.java b/src/com/android/nfc/P2pAnimationActivity.java
index d61aa0a..853357f 100644
--- a/src/com/android/nfc/ScreenshotWindowAnimator.java
+++ b/src/com/android/nfc/P2pAnimationActivity.java
@@ -24,67 +24,54 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Activity;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
+import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
-/**
- * TODO:
- * - Performance when over gl surfaces? Ie. Gallery
- * - what do we say in the Toast? Which icon do we get if the user uses another
- * type of gallery?
- */
-public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdateListener,
- View.OnTouchListener {
- private static final String TAG = "ScreenshotWindowAnimator";
-
+public class P2pAnimationActivity extends Activity implements Handler.Callback,
+ AnimatorUpdateListener, View.OnTouchListener {
private static final float INITIAL_SCREENSHOT_SCALE = 0.7f;
private static final float FINAL_SCREENSHOT_SCALE = 0.0f;
- private static final int MSG_START_ANIMATION = 1;
- private static final int MSG_START_SEND_ANIMATION = 2;
- private static final int MSG_START_SEND_RECV_ANIMATION = 3;
- private static final int MSG_START_FAIL_ANIMATION = 4;
- private static final int MSG_STOP_ANIMATIONS = 5;
+ private static final int MSG_RESULT_FAILURE = 4;
+ private static final int MSG_RESULT_SEND = 5;
+ private static final int MSG_RESULT_RECEIVE = 6;
+
private static final int RESULT_WAITING = 0;
private static final int RESULT_FAILURE = 1;
private static final int RESULT_SEND = 2;
- private static final int RESULT_SEND_RECV = 3;
+ private static final int RESULT_RECV = 3;
private static int mResult;
Context mContext;
LayoutInflater mLayoutInflater;
- WindowManager mWindowManager;
- WindowManager.LayoutParams mWindowLayoutParams;
- Display mDisplay;
- DisplayMetrics mDisplayMetrics;
- Matrix mDisplayMatrix;
- Bitmap mScreenBitmap;
View mScreenshotLayout;
ImageView mScreenshotView;
ImageView mClonedView;
+ TextView mShareText;
+
int mScreenshotWidth;
StartAnimationListener mStartListener;
@@ -94,20 +81,16 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
ValueAnimator mStartAnimator;
// Send only animation
- AnimatorSet mSendRecvAnimatorSet;
+ AnimatorSet mSendAnimatorSet;
ValueAnimator mScaleDownAnimator;
ValueAnimator mScaleUpAnimator;
- // Send/receive animation
- ValueAnimator mFadeToBlackAnimator;
+ // Receive animation
+ ValueAnimator mReceiveAnimator;
// Failure animation
- AnimatorSet mFailureAnimatorSet;
- ValueAnimator mCenterToLeftAnimator;
- ValueAnimator mLeftToRightAnimator;
- ValueAnimator mRightToCenterAnimator;
-
ValueAnimator mFailureAnimator;
+
// Down interpolators
DecelerateInterpolator mScaleDownInterpolator;
DecelerateInterpolator mAlphaDownInterpolator;
@@ -122,19 +105,27 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
// These are all read/written on the UI thread, so no
// need to synchronize these.
// TODO state var could clean this up a lot.
- boolean mAttached = false;
boolean mWaitingForResult = true;
boolean mStartAnimDone = false;
boolean mEndRequested = false;
+ static Bitmap mScreenBitmap;
+ static Callback mCallback;
+ static Handler mHandler;
+
+ // These are initialized by calls to the static method createScreenshot()
+ // and are synchronized on P2pAnimationActivity.class
+ static Display mDisplay;
+ static DisplayMetrics mDisplayMetrics;
+ static Matrix mDisplayMatrix;
+ static WindowManager mWindowManager;
+
- final Handler mHandler;
- final Callback mCallback;
/* Interface to be used whenever the user confirms
* the send action.
*/
interface Callback {
- public void onConfirmSend();
+ public void onSendConfirmed();
}
class StartAnimationListener extends AnimatorListenerAdapter {
@@ -144,10 +135,6 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
if (mEndRequested) {
// Ended on request, don't start follow-up anim
// and get rid of the view
- if (mAttached) {
- mWindowManager.removeView(mScreenshotLayout);
- mAttached = false;
- }
} else {
mStartAnimDone = true;
if (!mWaitingForResult) { // Result already in
@@ -160,23 +147,20 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
class EndAnimationListener extends AnimatorListenerAdapter {
@Override
public void onAnimationEnd(Animator animation) {
- if (mAttached) {
- mWindowManager.removeView(mScreenshotLayout);
- mAttached = false;
- }
+ finish();
}
}
void playEndAnimation(int result) {
switch (result) {
case RESULT_SEND:
- mHandler.sendEmptyMessage(MSG_START_SEND_ANIMATION);
+ mSendAnimatorSet.start();
break;
- case RESULT_SEND_RECV:
- mHandler.sendEmptyMessage(MSG_START_SEND_RECV_ANIMATION);
+ case RESULT_RECV:
+ mReceiveAnimator.start();
break;
case RESULT_FAILURE:
- mHandler.sendEmptyMessage(MSG_START_FAIL_ANIMATION);
+ mFailureAnimator.start();
break;
}
}
@@ -195,42 +179,25 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
return anim;
}
+
void createAnimators() {
mStartListener = new StartAnimationListener();
mEndListener = new EndAnimationListener();
mStartAnimator = getFloatAnimation(500, this, mStartListener);
- mFadeToBlackAnimator = getFloatAnimation(500, this, mEndListener);
mScaleDownAnimator = getFloatAnimation(500, this, null);
mScaleUpAnimator = getFloatAnimation(500, this, null);
// Combine the two in a set
- mSendRecvAnimatorSet = new AnimatorSet();
+ mSendAnimatorSet = new AnimatorSet();
List<Animator> animList = new ArrayList<Animator>();
animList.add(mScaleDownAnimator);
animList.add(mScaleUpAnimator);
- mSendRecvAnimatorSet.playSequentially(animList);
- mSendRecvAnimatorSet.addListener(mEndListener);
-
- mCenterToLeftAnimator = getFloatAnimation(80, this, null);
- mLeftToRightAnimator = getFloatAnimation(80, this, null);
- mLeftToRightAnimator.setRepeatCount(4);
- mLeftToRightAnimator.setRepeatMode(ValueAnimator.REVERSE);
- mRightToCenterAnimator = getFloatAnimation(80, this, null);
- mFailureAnimator = getFloatAnimation(500, this, null);
-
- // Combine them into a set
- mFailureAnimatorSet = new AnimatorSet();
- animList.clear();
-
- /*
- animList.add(mCenterToLeftAnimator);
- animList.add(mLeftToRightAnimator);
- animList.add(mRightToCenterAnimator);
- */
- animList.add(mFailureAnimator);
- mFailureAnimatorSet.playSequentially(animList);
- mFailureAnimatorSet.addListener(mEndListener);
+ mSendAnimatorSet.playSequentially(animList);
+ mSendAnimatorSet.addListener(mEndListener);
+
+ mFailureAnimator = getFloatAnimation(500, this, mEndListener);
+ mReceiveAnimator = getFloatAnimation(200, this, mEndListener);
mScaleDownInterpolator = new DecelerateInterpolator(1.5f);
mAlphaDownInterpolator = new DecelerateInterpolator(1f);
@@ -242,206 +209,116 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
}
- /**
- * @param context everything needs a context :(
- */
- public ScreenshotWindowAnimator(Context context, Callback callback) {
- mContext = context;
- mCallback = callback;
- mHandler = new Handler(this);
- mLayoutInflater = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- createAnimators();
+ mHandler = new Handler(this);
// Inflate the screenshot layout
- mDisplayMetrics = new DisplayMetrics();
- mDisplayMatrix = new Matrix();
+ mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
-
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
mClonedView = (ImageView) mScreenshotLayout.findViewById(R.id.clone);
- mScreenshotLayout.setFocusable(true);
- /*
- * this doesn't work on the TYPE_SYSTEM_OVERLAY layer
- * mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
- *
- * @Override public boolean onTouch(View v, MotionEvent event) { //
- * Intercept and ignore all touch events return true; } });
- */
-
- // Setup the window that we are going to use
- // TODO Figure out how to do OnTouch using TYPE_SYSTEM_OVERLAY
- // and re-add TYPE_SYSTEM_OVERLAY to layout params below.
- mWindowLayoutParams =
- new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- 0,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM
- | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
- PixelFormat.OPAQUE);
- mWindowLayoutParams.token = new Binder();
- mWindowLayoutParams.setTitle("ScreenshotAnimation");
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = mWindowManager.getDefaultDisplay();
-
+ mShareText = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
mScreenshotView.setOnTouchListener(this);
mClonedView.setOnTouchListener(this);
+ setContentView(mScreenshotLayout);
-
+ createAnimators();
}
- /**
- * @return the current display rotation in degrees
- */
- private float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 90f;
- case Surface.ROTATION_180:
- return 180f;
- case Surface.ROTATION_270:
- return 270f;
- }
- return 0f;
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Do nothing to ignore orientation changes
}
- /**
- * Takes a screenshot of the current display and shows an animation.
- *
- * Must be called from the UI thread.
- */
- public void start() {
- // We need to orient the screenshot correctly (and the Surface api seems to
- // take screenshots
- // only in the natural orientation of the device :!)
-
- // Make sure any existing animations are ended
- endAnimations();
-
- mDisplay.getRealMetrics(mDisplayMetrics);
- float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
- float degrees = getDegreesForRotation(mDisplay.getRotation());
- boolean requiresRotation = (degrees > 0);
- if (requiresRotation) {
- // Get the dimensions of the device in its native orientation
- mDisplayMatrix.reset();
- mDisplayMatrix.preRotate(-degrees);
- mDisplayMatrix.mapPoints(dims);
- dims[0] = Math.abs(dims[0]);
- dims[1] = Math.abs(dims[1]);
- }
+ @Override
+ protected void onResume() {
+ super.onResume();
- mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
- // Bail if we couldn't take the screenshot
- if (mScreenBitmap == null) {
- Log.e(TAG, "couldn't get screenshot");
- return;
- }
+ if (mScreenBitmap != null) {
+ mClonedView.setImageBitmap(mScreenBitmap);
+ mClonedView.setVisibility(View.GONE);
+ mScreenshotView.setImageBitmap(mScreenBitmap);
+ mScreenshotWidth = mScreenBitmap.getWidth();
- if (requiresRotation) {
- // Rotate the screenshot to the current orientation
- Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(ss);
- c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
- c.rotate(360f - degrees);
- c.translate(-dims[0] / 2, -dims[1] / 2);
- c.drawBitmap(mScreenBitmap, 0, 0, null);
-
- mScreenBitmap = ss;
+ startAnimating();
}
-
- // The clone is hidden at the beginning
- mClonedView.setImageBitmap(mScreenBitmap);
- mClonedView.setVisibility(View.GONE);
-
- // Start the post-screenshot animation
-
- mScreenshotView.setImageBitmap(mScreenBitmap);
-
- mScreenshotLayout.requestFocus();
-
- mScreenshotWidth = mScreenBitmap.getWidth();
-
- // At this point no anims are running, no need to sync these
- mResult = RESULT_WAITING;
- mWaitingForResult = true;
- mStartAnimDone = false;
- mEndRequested = false;
-
- // Add the view for the animation
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-
- mAttached = true;
- mHandler.sendEmptyMessage(MSG_START_ANIMATION);
}
- private void endAnimations() {
- mEndRequested = true;
+ @Override
+ protected void onPause() {
+ super.onPause();
if (mStartAnimator != null) {
mStartAnimator.end();
}
- if (mSendRecvAnimatorSet != null) {
- mSendRecvAnimatorSet.end();
+ if (mSendAnimatorSet != null) {
+ mSendAnimatorSet.end();
}
- if (mFadeToBlackAnimator != null) {
- mFadeToBlackAnimator.end();
+ if (mReceiveAnimator != null) {
+ mReceiveAnimator.end();
}
- if (mFailureAnimatorSet != null) {
- mFailureAnimatorSet.end();
- }
-
- if (mAttached) {
- mWindowManager.removeView(mScreenshotLayout);
- mAttached = false;
+ if (mFailureAnimator != null) {
+ mFailureAnimator.end();
}
}
- private void postResult(int result) {
- mResult = result;
- mWaitingForResult = false;
- if (mStartAnimDone) {
- playEndAnimation(mResult);
- } // else end animation will play when the start anim is done
+ /**
+ * Takes a screenshot of the current display and shows an animation.
+ *
+ * Must be called from the UI thread.
+ */
+ public void startAnimating() {
+ // At this point no anims are running, no need to sync these
+ mResult = RESULT_WAITING;
+ mWaitingForResult = true;
+ mStartAnimDone = false;
+ mEndRequested = false;
+
+ mStartAnimator.start();
}
/**
* Finalizes the running animation with a failure animation.
- * Must be called from the UI thread.
*/
- public void finishWithFailure() {
- postResult(RESULT_FAILURE);
- }
+ public static void finishWithFailure() {
+ if (mHandler != null) {
+ mHandler.sendEmptyMessage(MSG_RESULT_FAILURE);
+ }
+ }
/**
- * Finalizes the running animation with the send/recv animation.
- * Must be called from the UI thread.
+ * Finalizes the running animation with the send animation.
*/
- public void finishWithSendReceive() {
- postResult(RESULT_SEND_RECV);
+ public static void finishWithSend() {
+ if (mHandler != null) {
+ mHandler.sendEmptyMessage(MSG_RESULT_SEND);
+ }
}
/**
- * Finalizes the running animation with the send-only animation.
- * Must be called from the UI thread.
+ * Finalizes the running animation with the received animation.
*/
- public void finishWithSend() {
- postResult(RESULT_SEND);
+ public static void finishWithReceive() {
+ if (mHandler != null) {
+ mHandler.sendEmptyMessage(MSG_RESULT_RECEIVE);
+ }
}
/**
- * Stops the currently playing animation.
- *
- * Must be called from the UI thread.
+ * Creates and sets the screenshot to be animated.
+ * Must be called on the UI thread.
+ * @param screenshot to be animated
*/
- public void stop() {
- endAnimations();
+ public static void makeScreenshot(Context context) {
+ mScreenBitmap = createScreenshot(context);
+ }
+
+ public static void setCallback(Callback callback) {
+ mCallback = callback;
}
private void onStartAnimationUpdate(ValueAnimator animation) {
@@ -488,27 +365,6 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
mScreenshotView.setAlpha(alpha);
}
- private void onCenterToLeftUpdate(ValueAnimator animation) {
- // scale the clone down to zero.
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mScaleDownInterpolator.getInterpolation(t);
-
- mScreenshotView.setX(scale * -mScreenshotWidth / 4);
- }
-
- private void onLeftToRightUpdate(ValueAnimator animation) {
- float t = ((Float) animation.getAnimatedValue()).floatValue();
-
- mScreenshotView.setX(-mScreenshotWidth / 4 +
- t * mScreenshotWidth / 2);
- }
-
- private void onRightToCenterUpdate(ValueAnimator animation) {
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mScaleDownInterpolator.getInterpolation(t);
- mScreenshotView.setX((1 - scale) * mScreenshotWidth / 4);
- }
-
private void onFailureUpdate(ValueAnimator animation) {
// Scale back from initial scale to normal scale
float t = ((Float) animation.getAnimatedValue()).floatValue();
@@ -521,15 +377,13 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
}
- private void onFadeToBlackUpdate(ValueAnimator animation) {
+ private void onReceiveUpdate(ValueAnimator animation) {
float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mScaleDownInterpolator.getInterpolation(t);
- float scaleT = INITIAL_SCREENSHOT_SCALE - (INITIAL_SCREENSHOT_SCALE * scale);
+ float offset = mScaleDownInterpolator.getInterpolation(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
+ mScreenshotView.setX(offset * mScreenshotWidth);
+ mShareText.setX(offset * mScreenshotWidth);
}
-
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (animation == mStartAnimator) {
@@ -540,49 +394,110 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
onSuccessUpUpdate(animation);
} else if (animation == mFailureAnimator) {
onFailureUpdate(animation);
- } else if (animation == mFadeToBlackAnimator) {
- onFadeToBlackUpdate(animation);
- } else if (animation == mCenterToLeftAnimator) {
- onCenterToLeftUpdate(animation);
- } else if (animation == mLeftToRightAnimator) {
- onLeftToRightUpdate(animation);
- } else if (animation == mRightToCenterAnimator) {
- onRightToCenterUpdate(animation);
} else if (animation == mFailureAnimator) {
onFailureUpdate(animation);
+ } else if (animation == mReceiveAnimator) {
+ onReceiveUpdate(animation);
}
}
@Override
public boolean handleMessage(Message msg) {
+ mWaitingForResult = false;
switch (msg.what) {
- case MSG_START_ANIMATION: {
- mStartAnimator.start();
+ case MSG_RESULT_FAILURE: {
+ mResult = RESULT_FAILURE;
break;
}
- case MSG_START_SEND_ANIMATION: {
- mSendRecvAnimatorSet.start();
+ case MSG_RESULT_SEND: {
+ mResult = RESULT_SEND;
break;
}
- case MSG_START_SEND_RECV_ANIMATION: {
- mFadeToBlackAnimator.start();
- break;
- }
- case MSG_START_FAIL_ANIMATION: {
- mFailureAnimatorSet.start();
- break;
- }
- case MSG_STOP_ANIMATIONS: {
- endAnimations();
+ case MSG_RESULT_RECEIVE: {
+ mResult = RESULT_RECV;
break;
}
}
+
+ if (mStartAnimDone) {
+ playEndAnimation(mResult);
+ }
return true;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
- mCallback.onConfirmSend();
+ if (mCallback != null) {
+ mCallback.onSendConfirmed();
+ }
return true;
}
+
+ /**
+ * @return the current display rotation in degrees
+ */
+ static float getDegreesForRotation(int value) {
+ switch (value) {
+ case Surface.ROTATION_90:
+ return 90f;
+ case Surface.ROTATION_180:
+ return 180f;
+ case Surface.ROTATION_270:
+ return 270f;
+ }
+ return 0f;
+ }
+
+ /**
+ * Returns a screenshot of the current display contents.
+ * @param context Context.
+ * @return
+ */
+ static Bitmap createScreenshot(Context context) {
+ synchronized(P2pAnimationActivity.class) {
+ if (mDisplay == null) {
+ // First time, init statics
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMatrix = new Matrix();
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWindowManager.getDefaultDisplay();
+ }
+ }
+ // We need to orient the screenshot correctly (and the Surface api seems to
+ // take screenshots only in the natural orientation of the device :!)
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
+ float degrees = getDegreesForRotation(mDisplay.getRotation());
+ boolean requiresRotation = (degrees > 0);
+ if (requiresRotation) {
+ // Get the dimensions of the device in its native orientation
+ mDisplayMatrix.reset();
+ mDisplayMatrix.preRotate(-degrees);
+ mDisplayMatrix.mapPoints(dims);
+ dims[0] = Math.abs(dims[0]);
+ dims[1] = Math.abs(dims[1]);
+ }
+
+ Bitmap bitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
+ // Bail if we couldn't take the screenshot
+ if (bitmap == null) {
+ return null;
+ }
+
+ if (requiresRotation) {
+ // Rotate the screenshot to the current orientation
+ Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(ss);
+ c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
+ c.rotate(360f - degrees);
+ c.translate(-dims[0] / 2, -dims[1] / 2);
+ c.drawBitmap(bitmap, 0, 0, null);
+
+ bitmap = ss;
+ }
+
+ return bitmap;
+ }
+
}
diff --git a/src/com/android/nfc/ndefpush/NdefPushServer.java b/src/com/android/nfc/ndefpush/NdefPushServer.java
index 6a76cc2..1548fe2 100755
--- a/src/com/android/nfc/ndefpush/NdefPushServer.java
+++ b/src/com/android/nfc/ndefpush/NdefPushServer.java
@@ -193,4 +193,10 @@ public class NdefPushServer {
}
}
}
+
+ public boolean isRunning() {
+ synchronized (this) {
+ return (mServerThread != null);
+ }
+ }
}
diff --git a/src/com/android/nfc/snep/SnepServer.java b/src/com/android/nfc/snep/SnepServer.java
index 84bb673..0b2d709 100644
--- a/src/com/android/nfc/snep/SnepServer.java
+++ b/src/com/android/nfc/snep/SnepServer.java
@@ -266,4 +266,10 @@ public final class SnepServer {
}
}
}
+
+ public boolean isRunning() {
+ synchronized (SnepServer.this) {
+ return mServerRunning;
+ }
+ }
}