summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2011-08-12 17:55:53 -0700
committerNick Pelly <npelly@google.com>2011-08-12 18:14:13 -0700
commit8a558ba19be56e71e6c2015dea862d150e6d07cb (patch)
tree83e87d4bba1f6d5f282a54187a69abe9e91eae25
parent024ce7511607e332742ce7bc8eec65c5bcb674da (diff)
downloadpackages_apps_nfc-8a558ba19be56e71e6c2015dea862d150e6d07cb.zip
packages_apps_nfc-8a558ba19be56e71e6c2015dea862d150e6d07cb.tar.gz
packages_apps_nfc-8a558ba19be56e71e6c2015dea862d150e6d07cb.tar.bz2
Implement Tap-to-Share and Tilt-to-Share (DropShare).
o Text says "Tap to Share", but tilting (the phone on top sends) also works. o Move text to top. Remove arrows. o First Accelerometer value takes ~700ms :( o Only call onP2pSendComplete on success. If the send failed, we retry quietly. This improves perceived reliability. o Reduce debounce from 1000ms to 750ms. TODO o Never do orientation change while in the animation activity o Get rid of black transition when entering/leaivng activity o Figure out why it takes 700ms to get first accelerometer value o Improve launch time of animation activity (sometimes it takes 3+ seconds) o Consider going back to a window overlay? o Make it snappy snappy snappy Change-Id: Idbc8e4bb744051b7b69d234a51a3ce69e23b699f
-rw-r--r--res/drawable-hdpi/ic_rotate_arrow.pngbin5494 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_rotate_arrow_top.pngbin7230 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_rotate_arrow.pngbin5494 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_rotate_arrow_top.pngbin7230 -> 0 bytes
-rw-r--r--res/layout/screenshot.xml26
-rwxr-xr-xres/values/strings.xml2
-rw-r--r--src/com/android/nfc/P2pAnimationActivity.java10
-rw-r--r--src/com/android/nfc/P2pEventManager.java155
-rwxr-xr-xsrc/com/android/nfc/P2pLinkManager.java20
9 files changed, 110 insertions, 103 deletions
diff --git a/res/drawable-hdpi/ic_rotate_arrow.png b/res/drawable-hdpi/ic_rotate_arrow.png
deleted file mode 100644
index 0907772..0000000
--- a/res/drawable-hdpi/ic_rotate_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_rotate_arrow_top.png b/res/drawable-hdpi/ic_rotate_arrow_top.png
deleted file mode 100644
index 966d409..0000000
--- a/res/drawable-hdpi/ic_rotate_arrow_top.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_rotate_arrow.png b/res/drawable-mdpi/ic_rotate_arrow.png
deleted file mode 100644
index 0907772..0000000
--- a/res/drawable-mdpi/ic_rotate_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_rotate_arrow_top.png b/res/drawable-mdpi/ic_rotate_arrow_top.png
deleted file mode 100644
index 966d409..0000000
--- a/res/drawable-mdpi/ic_rotate_arrow_top.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index 81a4096..9f27b7f 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -27,35 +27,13 @@
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
>
- <ImageView android:id="@+id/rotatetop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:src="@drawable/ic_rotate_arrow_top"
- android:layout_alignParentRight="true"
- android:layout_marginTop="60dip"
- android:layout_marginRight="15dip"
- android:transformPivotX="0.0px"
- android:transformPivotY="100.0px"
- />
- <ImageView android:id="@+id/rotatebottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:src="@drawable/ic_rotate_arrow"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="60dip"
- android:layout_marginLeft="15dip"
- android:transformPivotX="100.0px"
- android:transformPivotY="0.0px"
- />
<TextView android:id="@+id/calltoaction"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/touch"
android:textSize="35px"
- android:layout_alignParentBottom="true"
- android:paddingBottom="30dip"
+ android:layout_alignParentTop="true"
+ android:paddingTop="30dip"
android:gravity="center"
/>
</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a1a7510..2f9f065 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">Rotate to share</string>
+ <string name="touch">Tap to share</string>
</resources>
diff --git a/src/com/android/nfc/P2pAnimationActivity.java b/src/com/android/nfc/P2pAnimationActivity.java
index f54fb0b..fe0edba 100644
--- a/src/com/android/nfc/P2pAnimationActivity.java
+++ b/src/com/android/nfc/P2pAnimationActivity.java
@@ -73,8 +73,6 @@ public class P2pAnimationActivity extends Activity implements Handler.Callback,
View mScreenshotLayout;
ImageView mScreenshotView;
ImageView mClonedView;
- ImageView mBottomArrow;
- ImageView mTopArrow;
ImageView mStars;
TextView mShareText;
@@ -139,8 +137,6 @@ public class P2pAnimationActivity extends Activity implements Handler.Callback,
}
void playEndAnimation() {
- mTopArrow.setVisibility(View.GONE);
- mBottomArrow.setVisibility(View.GONE);
mShareText.setVisibility(View.GONE);
mArrowStarsAnimator.cancel();
switch (mAnimationState) {
@@ -220,8 +216,6 @@ public class P2pAnimationActivity extends Activity implements Handler.Callback,
mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
- mBottomArrow = (ImageView) mScreenshotLayout.findViewById(R.id.rotatebottom);
- mTopArrow = (ImageView) mScreenshotLayout.findViewById(R.id.rotatetop);
mStars = (ImageView) mScreenshotLayout.findViewById(R.id.stars);
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
@@ -406,8 +400,6 @@ public class P2pAnimationActivity extends Activity implements Handler.Callback,
int rotation = (int) ((double)ARROW_START_ROTATION * (1.0 - offset) +
(double) ARROW_FINISH_ROTATION * offset);
- mBottomArrow.setRotation(rotation);
- mTopArrow.setRotation(rotation);
float scale = 1.0f + (0.5f * offset);
mStars.setScaleX(scale);
@@ -462,7 +454,7 @@ public class P2pAnimationActivity extends Activity implements Handler.Callback,
@Override
public boolean onTouch(View v, MotionEvent event) {
- if (sCallback != null) {
+ if (sCallback != null && P2pEventManager.TAP_ENABLED) {
sCallback.onP2pSendConfirmed();
}
return true;
diff --git a/src/com/android/nfc/P2pEventManager.java b/src/com/android/nfc/P2pEventManager.java
index 02f7d76..ba1f57c 100644
--- a/src/com/android/nfc/P2pEventManager.java
+++ b/src/com/android/nfc/P2pEventManager.java
@@ -22,11 +22,15 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Vibrator;
import android.provider.Settings;
-import android.view.OrientationEventListener;
+import android.util.Log;
import com.android.nfc3.R;
@@ -42,6 +46,9 @@ public class P2pEventManager implements P2pEventListener {
static final long[] VIBRATION_PATTERN = {0, 100, 10000};
+ public static final boolean TILT_ENABLED = true;
+ public static final boolean TAP_ENABLED = true;
+
final Context mContext;
final P2pEventListener.Callback mCallback;
final SharedPreferences mPrefs;
@@ -50,56 +57,83 @@ public class P2pEventManager implements P2pEventListener {
final int mErrorSound;
final SoundPool mSoundPool; // playback synchronized on this
final Vibrator mVibrator;
- final RotationDetector mRotationDetector;
+ final TiltDetector mTiltDetector;
final NotificationManager mNotificationManager;
- boolean mAnimating;
+ // only used on UI thread
boolean mPrefsFirstShare;
+ boolean mAnimating;
+
+ /** Detect if the screen is facing up or down */
+ class TiltDetector implements SensorEventListener {
+ /**
+ * Percent tilt required before triggering detection.
+ * 100 indicates the device must be exactly face-down.
+ */
+ static final int THRESHOLD_PERCENT = 75;
+ final SensorManager mSensorManager;
+ final Sensor mSensor;
- class RotationDetector extends OrientationEventListener {
- static final int THRESHOLD_DEGREES = 35;
- int mStartOrientation;
- boolean mRotateDetectionEnabled;
+ // Only used on UI thread
+ boolean mSensorEnabled;
+ boolean mTriggerEnabled;
+ float mLastValue;
- public RotationDetector(Context context) {
- super(context);
+ public TiltDetector(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+ mSensorEnabled = false;
+ mTriggerEnabled = false;
+ mLastValue = Float.MIN_VALUE;
}
- public void start() {
- synchronized (RotationDetector.this) {
- mRotateDetectionEnabled = true;
- mStartOrientation = -1;
- super.enable();
+ public void enable() {
+ if (mSensorEnabled) {
+ return;
}
+ mSensorEnabled = true;
+ mLastValue = Float.MIN_VALUE;
+ mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI);
}
- public void cancel() {
- synchronized (RotationDetector.this) {
- mRotateDetectionEnabled = false;
- super.disable();
+ public boolean enableTrigger() {
+ if (!mSensorEnabled || mTriggerEnabled) {
+ return false;
}
+ mTriggerEnabled = true;
+ return checkTrigger();
}
- @Override
- public void onOrientationChanged(int orientation) {
- synchronized (RotationDetector.this) {
- if (!mRotateDetectionEnabled) {
- return;
- }
- if (mStartOrientation < 0) {
- mStartOrientation = orientation;
- }
- int diff = Math.abs(mStartOrientation - orientation);
- if (diff > THRESHOLD_DEGREES && diff < (360-THRESHOLD_DEGREES)) {
- cancel();
- mVibrator.vibrate(VIBRATION_PATTERN, -1);
- mCallback.onP2pSendConfirmed();
- }
+ public void disable() {
+ if (!mSensorEnabled) {
+ return;
}
+ mSensorManager.unregisterListener(this, mSensor);
+ mSensorEnabled = false;
+ mTriggerEnabled = false;
}
- }
+ boolean checkTrigger() {
+ if (!mTriggerEnabled ||
+ 100.0 * mLastValue / SensorManager.GRAVITY_EARTH < THRESHOLD_PERCENT) {
+ return false;
+ }
+ disable();
+ mVibrator.vibrate(VIBRATION_PATTERN, -1);
+ mCallback.onP2pSendConfirmed();
+ return true;
+ }
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ // always called on UI thread
+ mLastValue = event.values[2];
+ if (DBG) Log.d(TAG, "z=" + mLastValue);
+ checkTrigger();
+ }
+ @Override
+ public void onAccuracyChanged(Sensor arg0, int arg1) { }
+ };
public P2pEventManager(Context context, P2pEventListener.Callback callback) {
mContext = context;
mCallback = callback;
- mRotationDetector = new RotationDetector(mContext);
+ mTiltDetector = new TiltDetector(mContext);
mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
mStartSound = mSoundPool.load(mContext, R.raw.start, 1);
mEndSound = mSoundPool.load(mContext, R.raw.end, 1);
@@ -119,52 +153,58 @@ public class P2pEventManager implements P2pEventListener {
mPrefsFirstShare = false;
}
+ P2pAnimationActivity.setCallback(mCallback);
mAnimating = false;
}
@Override
public void onP2pInRange() {
+ if (TILT_ENABLED) {
+ mTiltDetector.enable();
+ }
mVibrator.vibrate(VIBRATION_PATTERN, -1);
- playSound(mStartSound);
+ P2pAnimationActivity.makeScreenshot(mContext);
}
@Override
public void onP2pSendConfirmationRequested() {
- mRotationDetector.start();
- P2pAnimationActivity.makeScreenshot(mContext);
- P2pAnimationActivity.setCallback(mCallback);
+ if (TILT_ENABLED && mTiltDetector.enableTrigger()) {
+ return;
+ }
+
mAnimating = true;
// 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);
+ playSound(mStartSound);
}
@Override
- public void onP2pSendComplete(boolean result) {
- if (!result) {
- playSound(mErrorSound);
- } else {
- playSound(mEndSound);
- checkFirstShare();
- }
- finish(result, false);
+ public void onP2pSendComplete() {
+ playSound(mEndSound);
+ checkFirstShare();
+ finish(true, false);
}
@Override
public void onP2pReceiveComplete() {
- mRotationDetector.cancel();
+ if (TILT_ENABLED) {
+ mTiltDetector.disable();
+ }
finish(false, true);
}
@Override
public void onP2pOutOfRange() {
- mRotationDetector.cancel();
+ if (TILT_ENABLED) {
+ mTiltDetector.disable();
+ }
if (mAnimating) {
- finish(false, false);
playSound(mErrorSound);
}
+ finish(false, false);
}
/**
@@ -172,18 +212,15 @@ public class P2pEventManager implements P2pEventListener {
* Must be called on the UI thread.
*/
void finish(boolean sendSuccess, boolean receiveSuccess) {
+ if (!mAnimating) {
+ return;
+ }
if (sendSuccess) {
- if (mAnimating) {
- P2pAnimationActivity.finishWithSend();
- }
+ P2pAnimationActivity.finishWithSend();
} else if (receiveSuccess) {
- if (mAnimating) {
- P2pAnimationActivity.finishWithReceive();
- }
+ P2pAnimationActivity.finishWithReceive();
} else {
- if (mAnimating) {
- P2pAnimationActivity.finishWithFailure();
- }
+ P2pAnimationActivity.finishWithFailure();
}
mAnimating = false;
}
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index 2fbde95..c3f9fc6 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -65,9 +65,9 @@ interface P2pEventListener {
public void onP2pSendConfirmationRequested();
/**
- * Called when the send is complete, with the result.
+ * Called to indicate a send was successful.
*/
- public void onP2pSendComplete(boolean result);
+ public void onP2pSendComplete();
/**
* Called to indicate a receive was successful.
@@ -96,7 +96,7 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
// TODO dynamically assign SAP values
static final int NDEFPUSH_SAP = 0x10;
- static final int LINK_DEBOUNCE_MS = 1000;
+ static final int LINK_DEBOUNCE_MS = 750;
static final int STATE_WAITING = 0;
static final int STATE_SUCCESS = 1;
@@ -289,9 +289,9 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
}
}
- void onSendComplete(boolean result) {
+ void onSendComplete() {
// Make callbacks on UI thread
- mHandler.obtainMessage(MSG_SEND_COMPLETE, new Boolean(result)).sendToTarget();
+ mHandler.sendEmptyMessage(MSG_SEND_COMPLETE);
}
void sendNdefMessage() {
@@ -318,7 +318,6 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
synchronized (P2pLinkManager.this) {
if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
- onSendComplete(false);
return null;
}
m = mMessageToSend;
@@ -331,13 +330,15 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
Log.i(TAG, "Failed to connect over SNEP, trying NPP");
if (isCancelled()) {
- onSendComplete(false);
return null;
}
result = new NdefPushClient().push(m);
}
- onSendComplete(result);
+ if (DBG) Log.d(TAG, "SendTask result=" + result);
+ if (result) {
+ onSendComplete();
+ }
return null;
}
}
@@ -421,7 +422,6 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
}
break;
case MSG_SEND_COMPLETE:
- boolean result = (Boolean) msg.obj;
synchronized (P2pLinkManager.this) {
mSendTask = null;
@@ -430,7 +430,7 @@ public class P2pLinkManager implements Handler.Callback, P2pEventListener.Callba
}
mSendState = SEND_STATE_NOTHING_TO_SEND;
if (DBG) Log.d(TAG, "onP2pSendComplete()");
- mEventListener.onP2pSendComplete(result);
+ mEventListener.onP2pSendComplete();
if (mCallbackNdef != null) {
try {
mCallbackNdef.onMessagePushed();