diff options
author | Nick Pelly <npelly@google.com> | 2011-08-12 17:55:53 -0700 |
---|---|---|
committer | Nick Pelly <npelly@google.com> | 2011-08-12 18:14:13 -0700 |
commit | 8a558ba19be56e71e6c2015dea862d150e6d07cb (patch) | |
tree | 83e87d4bba1f6d5f282a54187a69abe9e91eae25 | |
parent | 024ce7511607e332742ce7bc8eec65c5bcb674da (diff) | |
download | packages_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.png | bin | 5494 -> 0 bytes | |||
-rw-r--r-- | res/drawable-hdpi/ic_rotate_arrow_top.png | bin | 7230 -> 0 bytes | |||
-rw-r--r-- | res/drawable-mdpi/ic_rotate_arrow.png | bin | 5494 -> 0 bytes | |||
-rw-r--r-- | res/drawable-mdpi/ic_rotate_arrow_top.png | bin | 7230 -> 0 bytes | |||
-rw-r--r-- | res/layout/screenshot.xml | 26 | ||||
-rwxr-xr-x | res/values/strings.xml | 2 | ||||
-rw-r--r-- | src/com/android/nfc/P2pAnimationActivity.java | 10 | ||||
-rw-r--r-- | src/com/android/nfc/P2pEventManager.java | 155 | ||||
-rwxr-xr-x | src/com/android/nfc/P2pLinkManager.java | 20 |
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 Binary files differdeleted file mode 100644 index 0907772..0000000 --- a/res/drawable-hdpi/ic_rotate_arrow.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_rotate_arrow_top.png b/res/drawable-hdpi/ic_rotate_arrow_top.png Binary files differdeleted file mode 100644 index 966d409..0000000 --- a/res/drawable-hdpi/ic_rotate_arrow_top.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_rotate_arrow.png b/res/drawable-mdpi/ic_rotate_arrow.png Binary files differdeleted file mode 100644 index 0907772..0000000 --- a/res/drawable-mdpi/ic_rotate_arrow.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_rotate_arrow_top.png b/res/drawable-mdpi/ic_rotate_arrow_top.png Binary files differdeleted file mode 100644 index 966d409..0000000 --- a/res/drawable-mdpi/ic_rotate_arrow_top.png +++ /dev/null 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(); |