summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xAndroidManifest.xml1
-rw-r--r--res/layout/screenshot.xml8
-rwxr-xr-xres/values/strings.xml2
-rwxr-xr-xsrc/com/android/nfc/NdefP2pManager.java94
-rw-r--r--src/com/android/nfc/ScreenshotWindowAnimator.java52
5 files changed, 113 insertions, 44 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fa21e41..410fced 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<permission android:name="com.android.nfc.permission.NFCEE_ADMIN"
android:label="@string/permlab_nfcAdmin"
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index dc34ba3..2a6e242 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -18,6 +18,14 @@
android:layout_height="match_parent"
android:background="#FF000000"
>
+ <TextView android:id="@+id/touch"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/touch"
+ android:paddingTop="20dip"
+ android:textSize="50px"
+ android:gravity="center"
+ />
<ImageView android:id="@+id/clone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 248669c..4d71b99 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>
</resources>
diff --git a/src/com/android/nfc/NdefP2pManager.java b/src/com/android/nfc/NdefP2pManager.java
index 888dbe2..18cf3fe 100755
--- a/src/com/android/nfc/NdefP2pManager.java
+++ b/src/com/android/nfc/NdefP2pManager.java
@@ -37,16 +37,19 @@ import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.os.Vibrator;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Profile;
import android.util.Log;
+import android.view.OrientationEventListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
-public class NdefP2pManager implements Handler.Callback {
+public class NdefP2pManager implements Handler.Callback,
+ ScreenshotWindowAnimator.Callback {
// TODO dynamically assign SAP values
static final int NDEFPUSH_SAP = 0x10;
@@ -59,6 +62,7 @@ public class NdefP2pManager implements Handler.Callback {
static final int MSG_RECEIVE_SUCCESS = 0;
static final int MSG_TIMEOUT = 1;
+ static final long[] mVibPattern = {0, 100, 100, 150, 100, 250, 100, 10000};
static final String TAG = "P2PManager";
static final boolean DBG = true;
@@ -70,33 +74,52 @@ public class NdefP2pManager implements Handler.Callback {
final Context mContext;
final P2pStatusListener mListener;
+ final Vibrator mVibrator;
+
// Used only from the UI thread
PushTask mPushTask;
Handler mHandler;
int mSendState;
int mReceiveState;
boolean mAnimating;
+ boolean mSendStarted;
final static Uri mProfileUri = Profile.CONTENT_VCARD_URI.buildUpon().
appendQueryParameter(Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, "true").
build();
+ NdefMessage mPushMsg;
+
/** Locked on NdefP2pManager.class */
NdefMessage mForegroundMsg;
INdefPushCallback mCallback;
+ OrientationEventListener mOrientationListener;
+
public NdefP2pManager(Context context, P2pStatusListener listener) {
mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback);
mDefaultSnepServer = new SnepServer(mDefaultSnepCallback);
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = context.getPackageManager();
mContext = context;
- mScreenshot = new ScreenshotWindowAnimator(context);
+ mScreenshot = new ScreenshotWindowAnimator(context, this);
mListener = listener;
mAnimating = false;
mHandler = new Handler(this);
mSendState = STATE_WAITING;
mReceiveState = STATE_WAITING;
+ mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ mSendStarted = false;
+
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ if (mAnimating && (orientation > 50 && orientation < 310)) {
+ onConfirmSend();
+ }
+ }
+ };
+
}
public void enableNdefServer() {
@@ -156,8 +179,11 @@ public class NdefP2pManager implements Handler.Callback {
void llcpActivated() {
if (DBG) Log.d(TAG, "LLCP connection up and running");
+ mOrientationListener.enable();
+ mVibrator.vibrate(mVibPattern, 6);
mSendState = STATE_WAITING;
mReceiveState = STATE_WAITING;
+ mHandler.removeMessages(MSG_TIMEOUT);
NdefMessage foregroundMsg;
synchronized (this) {
@@ -197,29 +223,30 @@ public class NdefP2pManager implements Handler.Callback {
}
}
- if (mPushTask != null) {
- mPushTask.cancel(true);
- }
- mPushTask = new PushTask(foregroundMsg);
-
- // Start rendering the animation if we have something to send,
- // otherwise just sound.
- if (foregroundMsg != null) {
+ // 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();
mAnimating = true;
+ mSendStarted = false;
+ mListener.onP2pBegin();
}
- mListener.onP2pBegin();
- mPushTask.execute();
+
+ mPushMsg = foregroundMsg;
}
void llcpDeactivated() {
if (DBG) Log.d(TAG, "LLCP deactivated.");
+ mOrientationListener.disable();
+ mVibrator.cancel();
if (mPushTask != null) {
mPushTask.cancel(true);
mPushTask = null;
}
-
- finish(mSendState == STATE_SUCCESS, mReceiveState == STATE_SUCCESS);
+ // Don't cancel the animation immediately - retain it
+ // up to one second.
+ mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, 1000);
}
/**
@@ -229,11 +256,7 @@ public class NdefP2pManager implements Handler.Callback {
*/
void onSendComplete(boolean success) {
mSendState = success ? STATE_SUCCESS : STATE_FAILURE;
- if (mReceiveState != STATE_WAITING) {
- finish(success, mReceiveState == STATE_SUCCESS);
- } else {
- mHandler.sendEmptyMessageDelayed(MSG_TIMEOUT, RECEIVE_TIMEOUT_MS);
- }
+ finish(success, mReceiveState == STATE_SUCCESS);
}
/**
@@ -346,24 +369,18 @@ public class NdefP2pManager implements Handler.Callback {
* Must be called on the UI thread.
*/
private void finish(boolean sendSuccess, boolean receiveSuccess) {
- if (sendSuccess && receiveSuccess) {
+
+ if (mSendStarted && sendSuccess) {
if (mAnimating) {
mScreenshot.finishWithSendReceive();
}
mListener.onP2pEnd();
- } else if (sendSuccess) {
- if (mAnimating) {
- mScreenshot.finishWithSend();
- }
- mListener.onP2pEnd();
- } else if (receiveSuccess) {
- // TODO tricky case - sending failed, but we did receive something
- // see if we can come up with a different animation for that.
+ }
+ else if (mSendStarted && !sendSuccess) {
if (mAnimating) {
mScreenshot.finishWithFailure();
}
- mListener.onP2pEnd();
-
+ mListener.onP2pError();
} else {
if (mAnimating) {
mScreenshot.finishWithFailure();
@@ -374,6 +391,7 @@ public class NdefP2pManager implements Handler.Callback {
mHandler.removeMessages(MSG_TIMEOUT);
mHandler.removeMessages(MSG_RECEIVE_SUCCESS);
+ mVibrator.cancel();
mAnimating = false;
}
@@ -382,9 +400,6 @@ public class NdefP2pManager implements Handler.Callback {
switch (msg.what) {
case MSG_RECEIVE_SUCCESS:
mReceiveState = STATE_SUCCESS;
- if (mSendState != STATE_WAITING) {
- finish(mSendState == STATE_SUCCESS, true);
- } // else will wait for send to complete
break;
case MSG_TIMEOUT:
finish(mSendState == STATE_SUCCESS, mReceiveState == STATE_SUCCESS);
@@ -392,4 +407,17 @@ public class NdefP2pManager implements Handler.Callback {
}
return true;
}
+
+ @Override
+ public void onConfirmSend() {
+ if (!mSendStarted) {
+ // Start sending
+ if (mPushTask != null) {
+ mPushTask.cancel(true);
+ }
+ mSendStarted = true;
+ mPushTask = new PushTask(mPushMsg);
+ mPushTask.execute();
+ }
+ }
}
diff --git a/src/com/android/nfc/ScreenshotWindowAnimator.java b/src/com/android/nfc/ScreenshotWindowAnimator.java
index b8b123e..d61aa0a 100644
--- a/src/com/android/nfc/ScreenshotWindowAnimator.java
+++ b/src/com/android/nfc/ScreenshotWindowAnimator.java
@@ -36,6 +36,7 @@ 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;
@@ -53,7 +54,8 @@ import java.util.List;
* - 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 {
+public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdateListener,
+ View.OnTouchListener {
private static final String TAG = "ScreenshotWindowAnimator";
private static final float INITIAL_SCREENSHOT_SCALE = 0.7f;
@@ -125,7 +127,15 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
boolean mStartAnimDone = false;
boolean mEndRequested = false;
- Handler mHandler;
+ final Handler mHandler;
+ final Callback mCallback;
+
+ /* Interface to be used whenever the user confirms
+ * the send action.
+ */
+ interface Callback {
+ public void onConfirmSend();
+ }
class StartAnimationListener extends AnimatorListenerAdapter {
@Override
@@ -212,9 +222,12 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
// 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);
@@ -232,8 +245,9 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
/**
* @param context everything needs a context :(
*/
- public ScreenshotWindowAnimator(Context context) {
+ public ScreenshotWindowAnimator(Context context, Callback callback) {
mContext = context;
+ mCallback = callback;
mHandler = new Handler(this);
mLayoutInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -257,10 +271,12 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
*/
// 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,
- WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ 0,
WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM
@@ -271,6 +287,12 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
+
+ mScreenshotView.setOnTouchListener(this);
+ mClonedView.setOnTouchListener(this);
+
+
+
}
/**
@@ -297,6 +319,10 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
// 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());
@@ -342,9 +368,6 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
mScreenshotWidth = mScreenBitmap.getWidth();
- // Make sure any existing animations are ended
- endAnimations();
-
// At this point no anims are running, no need to sync these
mResult = RESULT_WAITING;
mWaitingForResult = true;
@@ -373,6 +396,11 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
if (mFailureAnimatorSet != null) {
mFailureAnimatorSet.end();
}
+
+ if (mAttached) {
+ mWindowManager.removeView(mScreenshotLayout);
+ mAttached = false;
+ }
}
private void postResult(int result) {
@@ -413,7 +441,7 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
* Must be called from the UI thread.
*/
public void stop() {
- mHandler.sendEmptyMessage(MSG_STOP_ANIMATIONS);
+ endAnimations();
}
private void onStartAnimationUpdate(ValueAnimator animation) {
@@ -443,8 +471,6 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
float scaleT = INITIAL_SCREENSHOT_SCALE - (scale *
(INITIAL_SCREENSHOT_SCALE - FINAL_SCREENSHOT_SCALE));
- float cloneAlpha = mAlphaDownInterpolator.getInterpolation(t) * 0.4f;
-
mClonedView.setScaleX(scaleT);
mClonedView.setScaleY(scaleT);
}
@@ -553,4 +579,10 @@ public class ScreenshotWindowAnimator implements Handler.Callback, AnimatorUpdat
}
return true;
}
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mCallback.onConfirmSend();
+ return true;
+ }
}