diff options
-rwxr-xr-x | AndroidManifest.xml | 7 | ||||
-rw-r--r-- | res/layout/screenshot.xml | 2 | ||||
-rwxr-xr-x | res/values/strings.xml | 2 | ||||
-rwxr-xr-x | src/com/android/nfc/NdefP2pManager.java | 91 | ||||
-rw-r--r-- | src/com/android/nfc/P2pAnimationActivity.java (renamed from src/com/android/nfc/ScreenshotWindowAnimator.java) | 475 | ||||
-rwxr-xr-x | src/com/android/nfc/ndefpush/NdefPushServer.java | 6 | ||||
-rw-r--r-- | src/com/android/nfc/snep/SnepServer.java | 6 |
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; + } + } } |