summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/drawable-hdpi/howto_sharetap.pngbin0 -> 7792 bytes
-rw-r--r--res/layout/holding_it_wrong.xml32
-rw-r--r--res/layout/screenshot.xml20
-rw-r--r--res/raw/start.oggbin18515 -> 23068 bytes
-rw-r--r--src/com/android/nfc/DeviceHost.java4
-rw-r--r--src/com/android/nfc/HoldingItWrongUi.java62
-rw-r--r--src/com/android/nfc/P2pAnimationActivity.java525
-rw-r--r--src/com/android/nfc/P2pEventManager.java169
-rw-r--r--src/com/android/nfc/SendUi.java232
9 files changed, 397 insertions, 647 deletions
diff --git a/res/drawable-hdpi/howto_sharetap.png b/res/drawable-hdpi/howto_sharetap.png
new file mode 100644
index 0000000..b78329d
--- /dev/null
+++ b/res/drawable-hdpi/howto_sharetap.png
Binary files differ
diff --git a/res/layout/holding_it_wrong.xml b/res/layout/holding_it_wrong.xml
new file mode 100644
index 0000000..4cd86ad
--- /dev/null
+++ b/res/layout/holding_it_wrong.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/padding"
+ android:orientation="vertical"
+ android:gravity="center"
+>
+
+ <ImageView android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="15px"
+ android:src="@drawable/howto_sharetap"
+ android:paddingTop="10px"
+ />
+
+</LinearLayout>
+
+
diff --git a/res/layout/screenshot.xml b/res/layout/screenshot.xml
index 9f27b7f..e29aff4 100644
--- a/res/layout/screenshot.xml
+++ b/res/layout/screenshot.xml
@@ -24,29 +24,9 @@
android:adjustViewBounds="true"
android:src="@drawable/p2p_bg"
/>
- <RelativeLayout android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
- <TextView android:id="@+id/calltoaction"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/touch"
- android:textSize="35px"
- android:layout_alignParentTop="true"
- android:paddingTop="30dip"
- android:gravity="center"
- />
- </RelativeLayout>
-
- <ImageView android:id="@+id/clone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- />
<ImageView android:id="@+id/screenshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
/>
-
</FrameLayout>
diff --git a/res/raw/start.ogg b/res/raw/start.ogg
index baa38ba..e08e155 100644
--- a/res/raw/start.ogg
+++ b/res/raw/start.ogg
Binary files differ
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index d1e7064..d8192b7 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -16,10 +16,6 @@
package com.android.nfc;
-import com.android.nfc.nxp.NativeLlcpConnectionlessSocket;
-import com.android.nfc.nxp.NativeLlcpServiceSocket;
-import com.android.nfc.nxp.NativeLlcpSocket;
-
import android.nfc.NdefMessage;
import android.os.Bundle;
diff --git a/src/com/android/nfc/HoldingItWrongUi.java b/src/com/android/nfc/HoldingItWrongUi.java
new file mode 100644
index 0000000..e915662
--- /dev/null
+++ b/src/com/android/nfc/HoldingItWrongUi.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.nfc3.R;
+
+public class HoldingItWrongUi implements DialogInterface.OnDismissListener {
+
+ AlertDialog mDialog;
+
+ /** Must call from UI thread */
+ public void show(Context context) {
+ if (mDialog != null) {
+ return;
+ }
+
+ View v = View.inflate(context, R.layout.holding_it_wrong, null);
+
+ AlertDialog.Builder b = new AlertDialog.Builder(context);
+ b.setCancelable(false);
+ b.setView(v);
+
+ AlertDialog d = b.create();
+ d.setOnDismissListener(this);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
+ mDialog = d;
+ }
+
+ /** Must call from UI thread */
+ public void dismiss() {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mDialog = null;
+ }
+}
diff --git a/src/com/android/nfc/P2pAnimationActivity.java b/src/com/android/nfc/P2pAnimationActivity.java
deleted file mode 100644
index 6bc3fb9..0000000
--- a/src/com/android/nfc/P2pAnimationActivity.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc;
-
-import com.android.nfc3.R;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-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.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.View;
-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;
-
-public class P2pAnimationActivity extends Activity implements Handler.Callback,
- AnimatorUpdateListener, View.OnTouchListener {
- private static final float INITIAL_SCREENSHOT_SCALE = 0.6f;
- private static final float FINAL_SCREENSHOT_SCALE = 0.0f;
-
- private static final int MSG_RESULT_FAILURE = 1;
- private static final int MSG_RESULT_SEND = 2;
- private static final int MSG_RESULT_RECEIVE = 3;
-
- private static final int STATE_WAITING = 0;
- private static final int STATE_START_ANIM_DONE = 1;
- private static final int STATE_SEND_SUCCESS = 2;
- private static final int STATE_RECEIVE_SUCCESS = 3;
- private static final int STATE_FAILURE = 4;
-
- private static final int DURATION_MS = 1400;
-
- Context mContext;
- LayoutInflater mLayoutInflater;
- View mScreenshotLayout;
- ImageView mScreenshotView;
- ImageView mClonedView;
- ImageView mStars;
- TextView mShareText;
-
- int mScreenshotWidth;
-
- StartAnimationListener mStartListener;
- EndAnimationListener mEndListener;
-
- // Start animator, always played
- AnimatorSet mStartAnimatorSet;
- ValueAnimator mStartAnimator;
- ValueAnimator mArrowStarsAnimator;
-
- // Send only animation
- AnimatorSet mSendAnimatorSet;
- ValueAnimator mScaleDownAnimator;
- ValueAnimator mScaleUpAnimator;
-
- // Receive animation
- ValueAnimator mReceiveAnimator;
-
- // Failure animation
- ValueAnimator mFailureAnimator;
-
- DecelerateInterpolator mDecelerateInterpolator;
- AccelerateInterpolator mAccelerateInterpolator;
-
- // Variables below are all read/written on the UI thread, so no
- // need to synchronize these.
- static int mAnimationState;
- static Bitmap sScreenBitmap;
- static P2pEventListener.Callback sCallback;
-
- // sHandler is initialized in onCreate() and can be read from
- // multiple threads - synchronized on P2pAnimationActivity.class
- static Handler sHandler;
-
- // 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;
-
- class StartAnimationListener extends AnimatorListenerAdapter {
- @Override
- // Note that this will be called on the UI thread!
- public void onAnimationEnd(Animator animation) {
- if (mAnimationState != STATE_WAITING) { // Result already in
- playEndAnimation();
- } else {
- mAnimationState = STATE_START_ANIM_DONE;
- }
- }
- }
-
- class EndAnimationListener extends AnimatorListenerAdapter {
- @Override
- public void onAnimationEnd(Animator animation) {
- finish();
- overridePendingTransition(0, 0);
- }
- }
-
- void playEndAnimation() {
- mShareText.setVisibility(View.GONE);
- mArrowStarsAnimator.cancel();
- switch (mAnimationState) {
- case STATE_SEND_SUCCESS:
- mSendAnimatorSet.start();
- break;
- case STATE_RECEIVE_SUCCESS:
- mReceiveAnimator.start();
- break;
- default:
- mFailureAnimator.start();
- break;
- }
- }
-
- ValueAnimator getFloatAnimation(int duration, AnimatorUpdateListener updateListener,
- AnimatorListener listener) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setInterpolator(null);
- anim.setDuration(duration);
- if (updateListener != null) {
- anim.addUpdateListener(updateListener);
- }
- if (listener != null) {
- anim.addListener(listener);
- }
-
- return anim;
- }
-
- void createAnimators() {
- mStartListener = new StartAnimationListener();
- mEndListener = new EndAnimationListener();
-
-
- mStartAnimator = getFloatAnimation(500, this, mStartListener);
- mArrowStarsAnimator = getFloatAnimation(DURATION_MS, this, null);
- mArrowStarsAnimator.setRepeatCount(ValueAnimator.INFINITE);
-
- mStartAnimatorSet = new AnimatorSet();
- List<Animator> animList = new ArrayList<Animator>();
- animList.add(mStartAnimator);
- animList.add(mArrowStarsAnimator);
- mStartAnimatorSet.playSequentially(animList);
-
-
- mScaleDownAnimator = getFloatAnimation(500, this, null);
- mScaleUpAnimator = getFloatAnimation(500, this, null);
- // Combine the two in a set
- mSendAnimatorSet = new AnimatorSet();
- animList = new ArrayList<Animator>();
- animList.add(mScaleDownAnimator);
- animList.add(mScaleUpAnimator);
- mSendAnimatorSet.playSequentially(animList);
- mSendAnimatorSet.addListener(mEndListener);
-
- mFailureAnimator = getFloatAnimation(500, this, mEndListener);
- mReceiveAnimator = getFloatAnimation(200, this, mEndListener);
-
- mDecelerateInterpolator = new DecelerateInterpolator(1.5f);
- mAccelerateInterpolator = new AccelerateInterpolator(1.5f);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- synchronized(P2pAnimationActivity.class) {
- // Note that we will always need to allocate a new handler,
- // since the handler is bound a specific activity instance
- // which will be destroyed and recreated
- sHandler = new Handler(this);
- }
-
- // Inflate the screenshot layout
- mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
-
- mStars = (ImageView) mScreenshotLayout.findViewById(R.id.stars);
-
- mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
- mScreenshotView.setOnTouchListener(this);
-
- mClonedView = (ImageView) mScreenshotLayout.findViewById(R.id.clone);
- mClonedView.setOnTouchListener(this);
-
- mShareText = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
-
- setContentView(mScreenshotLayout);
-
- createAnimators();
-
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // Do nothing to ignore orientation changes
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- // Lock rotation
- final int orientation = getResources().getConfiguration().orientation;
- setRequestedOrientation(orientation);
-
- if (sScreenBitmap != null) {
- mClonedView.setImageBitmap(sScreenBitmap);
- mClonedView.setVisibility(View.GONE);
- mScreenshotView.setImageBitmap(sScreenBitmap);
- mScreenshotWidth = sScreenBitmap.getWidth();
-
- startAnimating();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- mStars.setVisibility(View.GONE);
- if (mStartAnimator != null) {
- mStartAnimator.end();
- }
- if (mSendAnimatorSet != null) {
- mSendAnimatorSet.end();
- }
- if (mReceiveAnimator != null) {
- mReceiveAnimator.end();
- }
- if (mFailureAnimator != null) {
- mFailureAnimator.end();
- }
- if (mStartAnimatorSet != null) {
- mStartAnimatorSet.end();
- }
- }
-
- /**
- * 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
- mAnimationState = STATE_WAITING;
- mStartAnimatorSet.start();
- }
-
- /**
- * Finalizes the running animation with a failure animation.
- */
- public static void finishWithFailure() {
- synchronized (P2pAnimationActivity.class) {
- if (sHandler != null) {
- sHandler.sendEmptyMessage(MSG_RESULT_FAILURE);
- }
- }
- }
-
- /**
- * Finalizes the running animation with the send animation.
- */
- public static void finishWithSend() {
- synchronized (P2pAnimationActivity.class) {
- if (sHandler != null) {
- sHandler.sendEmptyMessage(MSG_RESULT_SEND);
- }
- }
- }
-
- /**
- * Finalizes the running animation with the received animation.
- */
- public static void finishWithReceive() {
- synchronized (P2pAnimationActivity.class) {
- if (sHandler != null) {
- sHandler.sendEmptyMessage(MSG_RESULT_RECEIVE);
- }
- }
- }
-
- /**
- * Creates and sets the screenshot to be animated.
- * Must be called on the UI thread.
- * @param screenshot to be animated
- */
- public static void makeScreenshot(Context context) {
- sScreenBitmap = createScreenshot(context);
- }
-
- public static void setCallback(P2pEventListener.Callback callback) {
- sCallback = callback;
- }
-
- private void onStartAnimationUpdate(ValueAnimator animation) {
- // Just scale the screenshot down
- float t = ((Float) animation.getAnimatedValue()).floatValue();
-
- float scale = mDecelerateInterpolator.getInterpolation(t);
- float scaleT = INITIAL_SCREENSHOT_SCALE + (1f - scale) *
- (1 - INITIAL_SCREENSHOT_SCALE);
-
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- }
-
- private void onSuccessCloneAnimationUpdate(ValueAnimator animation) {
- // Clone the screenshot
- if (mClonedView.getVisibility() != View.VISIBLE) {
- mClonedView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0.5f);
- }
-
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mDecelerateInterpolator.getInterpolation(t);
- float scaleT = INITIAL_SCREENSHOT_SCALE - (scale *
- (INITIAL_SCREENSHOT_SCALE - FINAL_SCREENSHOT_SCALE));
-
- mClonedView.setScaleX(scaleT);
- mClonedView.setScaleY(scaleT);
- }
-
- private void onSuccessUpUpdate(ValueAnimator animation) {
- // Scale the screenshot all the way back to the front,
- // scale the clone down to zero.
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mDecelerateInterpolator.getInterpolation(t);
- float scaleT = INITIAL_SCREENSHOT_SCALE +
- (scale * (1.0f - INITIAL_SCREENSHOT_SCALE));
- float alpha = 0.5f + (0.5f * mAccelerateInterpolator.getInterpolation(t));
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setAlpha(alpha);
- }
-
- private void onFailureUpdate(ValueAnimator animation) {
- // Scale back from initial scale to normal scale
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float scale = mDecelerateInterpolator.getInterpolation(t);
- float scaleT = INITIAL_SCREENSHOT_SCALE + (scale *
- (1.0f - INITIAL_SCREENSHOT_SCALE));
-
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
-
- }
-
- private void onReceiveUpdate(ValueAnimator animation) {
- float t = ((Float) animation.getAnimatedValue()).floatValue();
- float offset = mDecelerateInterpolator.getInterpolation(t);
-
- mScreenshotView.setX(offset * mScreenshotWidth);
- mShareText.setX(offset * mScreenshotWidth);
- }
-
- private void onArrowStarsAnimationUpdate(ValueAnimator animation) {
-
- float t = ((Float) animation.getAnimatedValue()).floatValue();
-
- float scale = 1.0f + (0.5f * t);
- mStars.setScaleX(scale);
- mStars.setScaleY(scale);
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (animation == mStartAnimator) {
- onStartAnimationUpdate(animation);
- } else if (animation == mArrowStarsAnimator) {
- onArrowStarsAnimationUpdate(animation);
- } else if (animation == mScaleDownAnimator) {
- onSuccessCloneAnimationUpdate(animation);
- } else if (animation == mScaleUpAnimator) {
- onSuccessUpUpdate(animation);
- } else if (animation == mFailureAnimator) {
- onFailureUpdate(animation);
- } else if (animation == mReceiveAnimator) {
- onReceiveUpdate(animation);
- }
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- int oldState = mAnimationState;
- switch (msg.what) {
- case MSG_RESULT_FAILURE: {
- mAnimationState = STATE_FAILURE;
- break;
- }
- case MSG_RESULT_SEND: {
- mAnimationState = STATE_SEND_SUCCESS;
- break;
- }
- case MSG_RESULT_RECEIVE: {
- mAnimationState = STATE_RECEIVE_SUCCESS;
- break;
- }
- }
-
- if (oldState == STATE_START_ANIM_DONE) {
- // Start animaton was already completed, play
- // ending animation now.
- playEndAnimation();
- } else {
- // The ending animation will be played whenever the
- // start animation is finished.
- }
- return true;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (sCallback != null && P2pEventManager.TAP_ENABLED) {
- sCallback.onP2pSendConfirmed();
- }
- 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/P2pEventManager.java b/src/com/android/nfc/P2pEventManager.java
index 05fc234..66d8a92 100644
--- a/src/com/android/nfc/P2pEventManager.java
+++ b/src/com/android/nfc/P2pEventManager.java
@@ -37,7 +37,7 @@ import com.android.nfc3.R;
/**
* Manages vibration, sound and animation for P2P events.
*/
-public class P2pEventManager implements P2pEventListener {
+public class P2pEventManager implements P2pEventListener, SendUi.Callback {
static final String TAG = "NfcP2pEventManager";
static final boolean DBG = true;
@@ -46,9 +46,6 @@ 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;
@@ -59,10 +56,12 @@ public class P2pEventManager implements P2pEventListener {
final Vibrator mVibrator;
final TiltDetector mTiltDetector;
final NotificationManager mNotificationManager;
+ final HoldingItWrongUi mHoldingItWrongUi;
+ final SendUi mSendUi;
// only used on UI thread
boolean mPrefsFirstShare;
- boolean mAnimating;
+ boolean mSending;
/** Detect if the screen is facing up or down */
class TiltDetector implements SensorEventListener {
@@ -76,15 +75,12 @@ public class P2pEventManager implements P2pEventListener {
// Only used on UI thread
boolean mSensorEnabled;
- boolean mTriggerEnabled;
float mLastValue;
public TiltDetector(Context context) {
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorEnabled = false;
- mTriggerEnabled = false;
- mLastValue = Float.MIN_VALUE;
}
public void enable() {
if (mSensorEnabled) {
@@ -94,37 +90,33 @@ public class P2pEventManager implements P2pEventListener {
mLastValue = Float.MIN_VALUE;
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI);
}
- public boolean enableTrigger() {
- if (!mSensorEnabled || mTriggerEnabled) {
- return false;
- }
- mTriggerEnabled = true;
- return checkTrigger();
- }
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();
+ if (!mSensorEnabled) {
+ return;
+ }
+ final float z = event.values[2];
+ final boolean triggered = 100.0 * z / SensorManager.GRAVITY_EARTH > THRESHOLD_PERCENT;
+ //TODO: apply a low pass filter so we get something closer to real gravity
+ if (DBG) Log.d(TAG, "z=" + z + (triggered ? " TRIGGERED" : ""));
+ if (mLastValue == Float.MIN_VALUE && !triggered) {
+ // Received first value, and you're holding it wrong
+ mHoldingItWrongUi.show(mContext);
+ }
+ mLastValue = z;
+ if (triggered) {
+ disable();
+ onSendConfirmed();
+ }
+ return;
}
@Override
public void onAccuracyChanged(Sensor arg0, int arg1) { }
@@ -153,105 +145,86 @@ public class P2pEventManager implements P2pEventListener {
mPrefsFirstShare = false;
}
- P2pAnimationActivity.setCallback(mCallback);
- mAnimating = false;
+ mSending = false;
+ mHoldingItWrongUi = new HoldingItWrongUi();
+ mSendUi = new SendUi(context, this);
}
@Override
public void onP2pInRange() {
- if (TILT_ENABLED) {
- mTiltDetector.enable();
- }
- mVibrator.vibrate(VIBRATION_PATTERN, -1);
- P2pAnimationActivity.makeScreenshot(mContext);
+ mSendUi.takeScreenshot();
}
@Override
public void onP2pSendConfirmationRequested() {
- 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);
+ mTiltDetector.enable();
}
@Override
public void onP2pSendComplete() {
- playSound(mEndSound);
checkFirstShare();
- finish(true, false);
+ playSound(mEndSound);
+ mVibrator.vibrate(VIBRATION_PATTERN, -1);
+ mSendUi.showPostSend();
+ mSending = false;
}
@Override
public void onP2pReceiveComplete() {
- if (TILT_ENABLED) {
- mTiltDetector.disable();
- }
- finish(false, true);
+ mHoldingItWrongUi.dismiss();
+ mTiltDetector.disable();
+ mVibrator.vibrate(VIBRATION_PATTERN, -1);
+ playSound(mEndSound);
}
@Override
public void onP2pOutOfRange() {
- if (TILT_ENABLED) {
- mTiltDetector.disable();
- }
- if (mAnimating) {
+ mHoldingItWrongUi.dismiss();
+ mTiltDetector.disable();
+ if (mSending) {
playSound(mErrorSound);
+ mSendUi.dismiss();
+ mSending = false;
}
- finish(false, false);
+ mSendUi.releaseScreenshot();
}
- /**
- * Finish up the animation, if running.
- * Must be called on the UI thread.
- */
- void finish(boolean sendSuccess, boolean receiveSuccess) {
- if (!mAnimating) {
- return;
- }
- if (sendSuccess) {
- P2pAnimationActivity.finishWithSend();
- } else if (receiveSuccess) {
- P2pAnimationActivity.finishWithReceive();
- } else {
- P2pAnimationActivity.finishWithFailure();
- }
- mAnimating = false;
+ void onSendConfirmed() {
+ mVibrator.vibrate(VIBRATION_PATTERN, -1);
+ playSound(mStartSound);
+ mHoldingItWrongUi.dismiss();
+ mSending = true;
+ mSendUi.showPreSend();
}
- /** If first time, display up a notification */
- void checkFirstShare() {
- synchronized (this) {
- if (mPrefsFirstShare) {
- mPrefsFirstShare = false;
- SharedPreferences.Editor editor = mPrefs.edit();
- editor.putBoolean(PREF_FIRST_SHARE, false);
- editor.apply();
+ @Override
+ public void onPreFinished() {
+ mCallback.onP2pSendConfirmed();
+ }
- Intent intent = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- Notification notification = new Notification.Builder(mContext)
- .setContentTitle(mContext.getString(R.string.first_share_title))
- .setContentText(mContext.getString(R.string.first_share_text))
- .setContentIntent(pi)
- .setSmallIcon(R.drawable.stat_sys_nfc)
- .setAutoCancel(true)
- .getNotification();
- mNotificationManager.notify(NOTIFICATION_FIRST_SHARE, notification);
- }
+ /** If first time, display a notification */
+ void checkFirstShare() {
+ if (mPrefsFirstShare) {
+ mPrefsFirstShare = false;
+ SharedPreferences.Editor editor = mPrefs.edit();
+ editor.putBoolean(PREF_FIRST_SHARE, false);
+ editor.apply();
+
+ Intent intent = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ Notification notification = new Notification.Builder(mContext)
+ .setContentTitle(mContext.getString(R.string.first_share_title))
+ .setContentText(mContext.getString(R.string.first_share_text))
+ .setContentIntent(pi)
+ .setSmallIcon(R.drawable.stat_sys_nfc)
+ .setAutoCancel(true)
+ .getNotification();
+ mNotificationManager.notify(NOTIFICATION_FIRST_SHARE, notification);
}
}
void playSound(int sound) {
- synchronized (this) {
- mSoundPool.play(sound, 1.0f, 1.0f, 0, 0, 1.0f);
- }
+ mSoundPool.play(sound, 1.0f, 1.0f, 0, 0, 1.0f);
}
}
diff --git a/src/com/android/nfc/SendUi.java b/src/com/android/nfc/SendUi.java
new file mode 100644
index 0000000..a9acb96
--- /dev/null
+++ b/src/com/android/nfc/SendUi.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nfc;
+
+import com.android.nfc3.R;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.os.Binder;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+/**
+ * All methods must be called on UI thread
+ */
+public class SendUi implements Animator.AnimatorListener {
+
+ static final float[] PRE_SCREENSHOT_SCALE = {1.0f, 0.6f};
+ static final int PRE_DURATION_MS = 50;
+
+ static final float[] POST_SCREENSHOT_SCALE = {0.6f, 0.0f};
+ static final int POST_DURATION_MS = 200;
+
+ // all members are only used on UI thread
+ final WindowManager mWindowManager;
+ final Context mContext;
+ final Display mDisplay;
+ final DisplayMetrics mDisplayMetrics;
+ final Matrix mDisplayMatrix;
+ final WindowManager.LayoutParams mWindowLayoutParams;
+ final LayoutInflater mLayoutInflater;
+ final View mScreenshotLayout;
+ final ImageView mScreenshotView;
+ final Callback mCallback;
+ final ObjectAnimator mPreAnimator;
+ final ObjectAnimator mPostAnimator;
+
+ Bitmap mScreenshotBitmap;
+ boolean mAttached;
+
+ interface Callback {
+ public void onPreFinished();
+ }
+
+ public SendUi(Context context, Callback callback) {
+ mContext = context;
+ mCallback = callback;
+
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMatrix = new Matrix();
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWindowManager.getDefaultDisplay();
+
+ mLayoutInflater = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
+ mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
+ mScreenshotLayout.setFocusable(true);
+
+ 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
+ | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ PixelFormat.OPAQUE);
+ mWindowLayoutParams.token = new Binder();
+
+ PropertyValuesHolder preX = PropertyValuesHolder.ofFloat("scaleX", PRE_SCREENSHOT_SCALE);
+ PropertyValuesHolder preY = PropertyValuesHolder.ofFloat("scaleY", PRE_SCREENSHOT_SCALE);
+ mPreAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, preX, preY);
+ mPreAnimator.setInterpolator(null); // linear
+ mPreAnimator.setDuration(PRE_DURATION_MS);
+ mPreAnimator.addListener(this);
+
+ PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", POST_SCREENSHOT_SCALE);
+ PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", POST_SCREENSHOT_SCALE);
+ mPostAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, postY);
+ mPostAnimator.setInterpolator(null); // linear
+ mPostAnimator.setDuration(POST_DURATION_MS);
+ mPostAnimator.addListener(this);
+
+ mAttached = false;
+ }
+
+ public void takeScreenshot() {
+ mScreenshotBitmap = createScreenshot();
+ }
+
+ /** Show pre-send animation, calls onPreFinished() when complete */
+ public void showPreSend() {
+ if (mScreenshotBitmap == null || mAttached) {
+ return;
+ }
+ mScreenshotView.setImageBitmap(mScreenshotBitmap);
+ mScreenshotLayout.requestFocus();
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ mAttached = true;
+ mPreAnimator.start();
+
+ //TODO: Lock rotation
+// final int orientation = getResources().getConfiguration().orientation;
+// setRequestedOrientation(orientation);
+ }
+
+ /** Show post-send animation */
+ public void showPostSend() {
+ if (!mAttached) {
+ return;
+ }
+ mPostAnimator.start();
+ }
+
+ public void dismiss() {
+ if (!mAttached) {
+ return;
+ }
+ mPreAnimator.cancel();
+ mPostAnimator.cancel();
+ mWindowManager.removeView(mScreenshotLayout);
+ mAttached = false;
+ releaseScreenshot();
+ }
+
+ public void releaseScreenshot() {
+ mScreenshotBitmap = null;
+ }
+
+ /**
+ * @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
+ */
+ Bitmap createScreenshot() {
+ // 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;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) { }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (animation == mPreAnimator) {
+ mCallback.onPreFinished();
+ } else if (animation == mPostAnimator) {
+ dismiss();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) { }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) { }
+}