Merge "Make Beam more forgiving." into jb-mr2-dev
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index 24a3a71..2523e50 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -49,5 +49,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- />
+ />
+ <ImageView android:id="@+id/blacklayer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ff000000"
+ android:visibility="gone"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+ <View
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="0.25"
+ />
+ <TextView android:id="@+id/retrytext"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:background="@null"
+ android:textColor="@android:color/holo_blue_light"
+ />
+ <View
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="0.25"
+ />
+ </LinearLayout>
</FrameLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a4bda3b..b944937 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -27,6 +27,7 @@
<string name="cancel">Cancel</string>
<string name="beam_touch_to_view">Touch to view</string>
<string name="beam_handover_not_supported">The receiver\'s device doesn\'t support large file transfer via beam.</string>
+ <string name="beam_try_again">Bring devices together again</string>
<string name="connecting_headset">Connecting</string>
<string name="connected_headset">Connected</string>
diff --git a/src/com/android/nfc/P2pEventManager.java b/src/com/android/nfc/P2pEventManager.java
index 68f479b..65e428e 100644
--- a/src/com/android/nfc/P2pEventManager.java
+++ b/src/com/android/nfc/P2pEventManager.java
@@ -144,4 +144,17 @@
mSending = true;
}
+
+ @Override
+ public void onP2pSendDebounce() {
+ mNfcService.playSound(NfcService.SOUND_ERROR);
+ mSendUi.showSendHint();
+ }
+
+ @Override
+ public void onP2pResumeSend() {
+ mVibrator.vibrate(VIBRATION_PATTERN, -1);
+ mNfcService.playSound(NfcService.SOUND_START);
+ mSendUi.showStartSend();
+ }
}
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index 0b3fc73..b25b41d 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -76,6 +76,20 @@
public void onP2pSendComplete();
/**
+ *
+ * Called to indicate the link has broken while we were trying to send
+ * a message. We'll start a debounce timer for the user to get the devices
+ * back together. UI may show a hint to achieve that
+ */
+ public void onP2pSendDebounce();
+
+ /**
+ * Called to indicate a link has come back up after being temporarily
+ * broken, and sending is resuming
+ */
+ public void onP2pResumeSend();
+
+ /**
* Called to indicate the remote device does not support connection handover
*/
public void onP2pHandoverNotSupported();
@@ -124,8 +138,10 @@
static final int NDEFPUSH_SAP = 0x10;
static final int HANDOVER_SAP = 0x14;
- static final int LINK_DEBOUNCE_MS = 750;
-
+ static final int LINK_NOTHING_TO_SEND_DEBOUNCE_MS = 750;
+ static final int LINK_SEND_PENDING_DEBOUNCE_MS = 3000;
+ static final int LINK_SEND_CONFIRMED_DEBOUNCE_MS = 5000;
+ static final int LINK_SEND_COMPLETE_DEBOUNCE_MS = 250;
static final int MSG_DEBOUNCE_TIMEOUT = 1;
static final int MSG_RECEIVE_COMPLETE = 2;
static final int MSG_RECEIVE_HANDOVER = 3;
@@ -137,12 +153,13 @@
// values for mLinkState
static final int LINK_STATE_DOWN = 1;
static final int LINK_STATE_UP = 2;
- static final int LINK_STATE_DEBOUNCE =3;
+ static final int LINK_STATE_DEBOUNCE = 3;
// values for mSendState
static final int SEND_STATE_NOTHING_TO_SEND = 1;
static final int SEND_STATE_NEED_CONFIRMATION = 2;
static final int SEND_STATE_SENDING = 3;
+ static final int SEND_STATE_SEND_COMPLETE = 4;
// return values for doSnepProtocol
static final int SNEP_SUCCESS = 0;
@@ -263,7 +280,6 @@
if (mEchoServer != null) {
mEchoServer.onLlcpActivated();
}
-
switch (mLinkState) {
case LINK_STATE_DOWN:
mLinkState = LINK_STATE_UP;
@@ -442,7 +458,26 @@
case LINK_STATE_UP:
// Debounce
mLinkState = LINK_STATE_DEBOUNCE;
- mHandler.sendEmptyMessageDelayed(MSG_DEBOUNCE_TIMEOUT, LINK_DEBOUNCE_MS);
+ int debounceTimeout = 0;
+ switch (mSendState) {
+ case SEND_STATE_NOTHING_TO_SEND:
+ debounceTimeout = 0;
+ break;
+ case SEND_STATE_NEED_CONFIRMATION:
+ debounceTimeout = LINK_SEND_PENDING_DEBOUNCE_MS;
+ break;
+ case SEND_STATE_SENDING:
+ debounceTimeout = LINK_SEND_CONFIRMED_DEBOUNCE_MS;
+ break;
+ case SEND_STATE_SEND_COMPLETE:
+ debounceTimeout = LINK_SEND_COMPLETE_DEBOUNCE_MS;
+ break;
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_DEBOUNCE_TIMEOUT, debounceTimeout);
+ if (mSendState == SEND_STATE_SENDING) {
+ Log.e(TAG, "onP2pSendDebounce()");
+ mEventListener.onP2pSendDebounce();
+ }
cancelSendNdefMessage();
disconnectLlcpServices();
break;
@@ -502,6 +537,7 @@
mLlcpServicesConnected = true;
if (mSendState == SEND_STATE_SENDING) {
// Send was previously confirmed, we probably cycled through a debounce
+ mEventListener.onP2pResumeSend();
sendNdefMessage();
} else {
// User still needs to confirm, or we may have received something already.
@@ -840,7 +876,8 @@
if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
break;
}
- mSendState = SEND_STATE_NOTHING_TO_SEND;
+ mSendState = SEND_STATE_SEND_COMPLETE;
+ mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
if (DBG) Log.d(TAG, "onP2pSendComplete()");
mEventListener.onP2pSendComplete();
if (mCallbackNdef != null) {
@@ -924,6 +961,13 @@
mSendState = SEND_STATE_SENDING;
if (mLinkState == LINK_STATE_UP && mLlcpServicesConnected) {
sendNdefMessage();
+ } else if (mLinkState == LINK_STATE_DEBOUNCE) {
+ // Restart debounce timeout
+ mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_DEBOUNCE_TIMEOUT,
+ LINK_SEND_CONFIRMED_DEBOUNCE_MS);
+ // Tell user to tap devices again
+ mEventListener.onP2pSendDebounce();
}
}
}
diff --git a/src/com/android/nfc/SendUi.java b/src/com/android/nfc/SendUi.java
index 7839ed6..d153a62 100644
--- a/src/com/android/nfc/SendUi.java
+++ b/src/com/android/nfc/SendUi.java
@@ -43,6 +43,7 @@
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
@@ -90,6 +91,9 @@
static final int SLIDE_OUT_DURATION_MS = 300;
+ static final float[] BLACK_LAYER_ALPHA_DOWN_RANGE = {0.9f, 0.0f};
+ static final float[] BLACK_LAYER_ALPHA_UP_RANGE = {0.0f, 0.9f};
+
static final float[] TEXT_HINT_ALPHA_RANGE = {0.0f, 1.0f};
static final int TEXT_HINT_ALPHA_DURATION_MS = 500;
static final int TEXT_HINT_ALPHA_START_DELAY_MS = 300;
@@ -108,8 +112,10 @@
final StatusBarManager mStatusBarManager;
final View mScreenshotLayout;
final ImageView mScreenshotView;
+ final ImageView mBlackLayer;
final TextureView mTextureView;
final TextView mTextHint;
+ final TextView mTextRetry;
final Callback mCallback;
// The mFrameCounter animation is purely used to count down a certain
@@ -129,6 +135,8 @@
final ObjectAnimator mFadeInAnimator;
final ObjectAnimator mHintAnimator;
final ObjectAnimator mScaleUpAnimator;
+ final ObjectAnimator mAlphaDownAnimator;
+ final ObjectAnimator mAlphaUpAnimator;
final AnimatorSet mSuccessAnimatorSet;
// Besides animating the screenshot, the Beam UI also renders
@@ -177,7 +185,8 @@
mScreenshotLayout.setFocusable(true);
mTextHint = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
-
+ mTextRetry = (TextView) mScreenshotLayout.findViewById(R.id.retrytext);
+ mBlackLayer = (ImageView) mScreenshotLayout.findViewById(R.id.blacklayer);
mTextureView = (TextureView) mScreenshotLayout.findViewById(R.id.fireflies);
mTextureView.setSurfaceTextureListener(this);
@@ -245,6 +254,16 @@
mHintAnimator.setDuration(TEXT_HINT_ALPHA_DURATION_MS);
mHintAnimator.setStartDelay(TEXT_HINT_ALPHA_START_DELAY_MS);
+ alphaDown = PropertyValuesHolder.ofFloat("alpha", BLACK_LAYER_ALPHA_DOWN_RANGE);
+ mAlphaDownAnimator = ObjectAnimator.ofPropertyValuesHolder(mBlackLayer, alphaDown);
+ mAlphaDownAnimator.setInterpolator(new DecelerateInterpolator());
+ mAlphaDownAnimator.setDuration(400);
+
+ alphaUp = PropertyValuesHolder.ofFloat("alpha", BLACK_LAYER_ALPHA_UP_RANGE);
+ mAlphaUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mBlackLayer, alphaUp);
+ mAlphaUpAnimator.setInterpolator(new DecelerateInterpolator());
+ mAlphaUpAnimator.setDuration(200);
+
mSuccessAnimatorSet = new AnimatorSet();
mSuccessAnimatorSet.playSequentially(mFastSendAnimator, mFadeInAnimator);
@@ -271,6 +290,8 @@
if (mScreenshotBitmap == null || mAttached) {
return;
}
+ mBlackLayer.setVisibility(View.GONE);
+ mBlackLayer.setAlpha(0f);
mScreenshotView.setOnTouchListener(this);
mScreenshotView.setImageBitmap(mScreenshotBitmap);
mScreenshotView.setTranslationX(0f);
@@ -279,6 +300,7 @@
mScreenshotLayout.requestFocus();
+ mTextHint.setText(mContext.getResources().getString(R.string.touch));
mTextHint.setAlpha(0.0f);
mTextHint.setVisibility(View.VISIBLE);
mHintAnimator.start();
@@ -323,6 +345,7 @@
public void showStartSend() {
if (!mAttached) return;
+ mTextRetry.setVisibility(View.GONE);
// Update the starting scale - touchscreen-mashers may trigger
// this before the pre-animation completes.
float currentScale = mScreenshotView.getScaleX();
@@ -332,6 +355,14 @@
new float[] {currentScale, 0.0f});
mSlowSendAnimator.setValues(postX, postY);
+
+ float currentAlpha = mBlackLayer.getAlpha();
+ if (mBlackLayer.isShown() && currentAlpha > 0.0f) {
+ PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha",
+ new float[] {currentAlpha, 0.0f});
+ mAlphaDownAnimator.setValues(alphaDown);
+ mAlphaDownAnimator.start();
+ }
mSlowSendAnimator.start();
}
@@ -352,11 +383,13 @@
}
mTextHint.setVisibility(View.GONE);
+ mTextRetry.setVisibility(View.GONE);
float currentScale = mScreenshotView.getScaleX();
float currentAlpha = mScreenshotView.getAlpha();
if (finishMode == FINISH_SCALE_UP) {
+ mBlackLayer.setVisibility(View.GONE);
PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
new float[] {currentScale, 1.0f});
PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
@@ -373,7 +406,7 @@
PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
new float[] {currentScale, 0.0f});
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",
- new float[] {1.0f, 0.0f});
+ new float[] {currentAlpha, 0.0f});
mFastSendAnimator.setValues(postX, postY, alpha);
// Reset the fadeIn parameters to start from alpha 1
@@ -399,6 +432,8 @@
mFastSendAnimator.cancel();
mSuccessAnimatorSet.cancel();
mScaleUpAnimator.cancel();
+ mAlphaUpAnimator.cancel();
+ mAlphaDownAnimator.cancel();
mWindowManager.removeView(mScreenshotLayout);
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
releaseScreenshot();
@@ -595,4 +630,24 @@
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
+ public void showSendHint() {
+ if (mAlphaDownAnimator.isRunning()) {
+ mAlphaDownAnimator.cancel();
+ }
+ if (mSlowSendAnimator.isRunning()) {
+ mSlowSendAnimator.cancel();
+ }
+ mBlackLayer.setScaleX(mScreenshotView.getScaleX());
+ mBlackLayer.setScaleY(mScreenshotView.getScaleY());
+ mBlackLayer.setVisibility(View.VISIBLE);
+ mTextHint.setVisibility(View.GONE);
+
+ mTextRetry.setText(mContext.getResources().getString(R.string.beam_try_again));
+ mTextRetry.setVisibility(View.VISIBLE);
+
+ PropertyValuesHolder alphaUp = PropertyValuesHolder.ofFloat("alpha",
+ new float[] {mBlackLayer.getAlpha(), 0.9f});
+ mAlphaUpAnimator.setValues(alphaUp);
+ mAlphaUpAnimator.start();
+ }
}