summaryrefslogtreecommitdiffstats
path: root/core/java/android/nfc
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2011-08-26 17:31:02 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-08-26 17:31:02 -0700
commite432de8493bae1f6017731d6668a0fada334aa8d (patch)
tree7a6ff4bc822fa714efe22d3190e1f4c19fc9f11d /core/java/android/nfc
parent3ffb889746b881575d6ce4e33ef1835d71fb31ef (diff)
parentc84c89a6cacaf16c1ba41f57cc1aecdb150e85f9 (diff)
downloadframeworks_base-e432de8493bae1f6017731d6668a0fada334aa8d.zip
frameworks_base-e432de8493bae1f6017731d6668a0fada334aa8d.tar.gz
frameworks_base-e432de8493bae1f6017731d6668a0fada334aa8d.tar.bz2
Merge "Improve NDEF push API"
Diffstat (limited to 'core/java/android/nfc')
-rw-r--r--core/java/android/nfc/INdefPushCallback.aidl4
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl21
-rw-r--r--core/java/android/nfc/NfcActivityManager.java217
-rw-r--r--core/java/android/nfc/NfcAdapter.java449
-rw-r--r--core/java/android/nfc/NfcEvent.java44
-rw-r--r--core/java/android/nfc/NfcFragment.java83
-rw-r--r--core/java/android/nfc/NfcManager.java2
7 files changed, 609 insertions, 211 deletions
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index 80ba2ed..e60a5b0 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -23,6 +23,6 @@ import android.nfc.NdefMessage;
*/
interface INdefPushCallback
{
- NdefMessage onConnect();
- void onMessagePushed();
+ NdefMessage createMessage();
+ void onNdefPushComplete();
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 4fc248f..016af58 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -23,8 +23,8 @@ import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
import android.nfc.INdefPushCallback;
-import android.nfc.INfcTag;
import android.nfc.INfcAdapterExtras;
+import android.nfc.INfcTag;
/**
* @hide
@@ -34,19 +34,14 @@ interface INfcAdapter
INfcTag getNfcTagInterface();
INfcAdapterExtras getNfcAdapterExtrasInterface();
- // NfcAdapter-class related methods
int getState();
- void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent,
- in IntentFilter[] filters, in TechListParcel techLists);
- void disableForegroundDispatch(in ComponentName activity);
- void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg);
- void enableForegroundNdefPushWithCallback(in ComponentName activity, in INdefPushCallback callback);
- void disableForegroundNdefPush(in ComponentName activity);
-
- // Non-public methods
boolean disable();
boolean enable();
- boolean enableZeroClick();
- boolean disableZeroClick();
- boolean isZeroClickEnabled();
+ boolean enableNdefPush();
+ boolean disableNdefPush();
+ boolean isNdefPushEnabled();
+
+ void setForegroundDispatch(in PendingIntent intent,
+ in IntentFilter[] filters, in TechListParcel techLists);
+ void setForegroundNdefPush(in NdefMessage msg, in INdefPushCallback callback);
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
new file mode 100644
index 0000000..3cc6820
--- /dev/null
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -0,0 +1,217 @@
+/*
+ * 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 android.nfc;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Manages NFC API's that are coupled to the life-cycle of an Activity.
+ *
+ * <p>Uses a fragment to hook into onPause() and onResume() of the host
+ * activities.
+ *
+ * <p>Ideally all of this management would be done in the NFC Service,
+ * but right now it is much easier to do it in the application process.
+ *
+ * @hide
+ */
+public final class NfcActivityManager extends INdefPushCallback.Stub {
+ static final String TAG = NfcAdapter.TAG;
+ static final Boolean DBG = false;
+
+ final NfcAdapter mAdapter;
+ final HashMap<Activity, NfcActivityState> mNfcState; // contents protected by this
+ final NfcEvent mDefaultEvent; // can re-use one NfcEvent because it just contains adapter
+
+ /**
+ * NFC state associated with an {@link Activity}
+ */
+ class NfcActivityState {
+ boolean resumed = false; // is the activity resumed
+ NdefMessage ndefMessage;
+ NfcAdapter.CreateNdefMessageCallback ndefMessageCallback;
+ NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback;
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder("[").append(resumed).append(" ");
+ s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
+ s.append(onNdefPushCompleteCallback).append("]");
+ return s.toString();
+ }
+ }
+
+ public NfcActivityManager(NfcAdapter adapter) {
+ mAdapter = adapter;
+ mNfcState = new HashMap<Activity, NfcActivityState>();
+ mDefaultEvent = new NfcEvent(mAdapter);
+ }
+
+ /**
+ * onResume hook from fragment attached to activity
+ */
+ public synchronized void onResume(Activity activity) {
+ NfcActivityState state = mNfcState.get(activity);
+ if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
+ if (state != null) {
+ state.resumed = true;
+ updateNfcService(state);
+ }
+ }
+
+ /**
+ * onPause hook from fragment attached to activity
+ */
+ public synchronized void onPause(Activity activity) {
+ NfcActivityState state = mNfcState.get(activity);
+ if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
+ if (state != null) {
+ state.resumed = false;
+ updateNfcService(state);
+ }
+ }
+
+ public synchronized void setNdefPushMessage(Activity activity, NdefMessage message) {
+ NfcActivityState state = getOrCreateState(activity, message != null);
+ if (state == null || state.ndefMessage == message) {
+ return; // nothing more to do;
+ }
+ state.ndefMessage = message;
+ if (message == null) {
+ maybeRemoveState(activity, state);
+ }
+ if (state.resumed) {
+ updateNfcService(state);
+ }
+ }
+
+ public synchronized void setNdefPushMessageCallback(Activity activity,
+ NfcAdapter.CreateNdefMessageCallback callback) {
+ NfcActivityState state = getOrCreateState(activity, callback != null);
+ if (state == null || state.ndefMessageCallback == callback) {
+ return; // nothing more to do;
+ }
+ state.ndefMessageCallback = callback;
+ if (callback == null) {
+ maybeRemoveState(activity, state);
+ }
+ if (state.resumed) {
+ updateNfcService(state);
+ }
+ }
+
+ public synchronized void setOnNdefPushCompleteCallback(Activity activity,
+ NfcAdapter.OnNdefPushCompleteCallback callback) {
+ NfcActivityState state = getOrCreateState(activity, callback != null);
+ if (state == null || state.onNdefPushCompleteCallback == callback) {
+ return; // nothing more to do;
+ }
+ state.onNdefPushCompleteCallback = callback;
+ if (callback == null) {
+ maybeRemoveState(activity, state);
+ }
+ if (state.resumed) {
+ updateNfcService(state);
+ }
+ }
+
+ /**
+ * Get the NfcActivityState for the specified Activity.
+ * If create is true, then create it if it doesn't already exist,
+ * and ensure the NFC fragment is attached to the activity.
+ */
+ synchronized NfcActivityState getOrCreateState(Activity activity, boolean create) {
+ if (DBG) Log.d(TAG, "getOrCreateState " + activity + " " + create);
+ NfcActivityState state = mNfcState.get(activity);
+ if (state == null && create) {
+ state = new NfcActivityState();
+ mNfcState.put(activity, state);
+ NfcFragment.attach(activity);
+ }
+ return state;
+ }
+
+ /**
+ * If the NfcActivityState is empty then remove it, and
+ * detach it from the Activity.
+ */
+ synchronized void maybeRemoveState(Activity activity, NfcActivityState state) {
+ if (state.ndefMessage == null && state.ndefMessageCallback == null &&
+ state.onNdefPushCompleteCallback == null) {
+ mNfcState.remove(activity);
+ }
+ }
+
+ /**
+ * Register NfcActivityState with the NFC service.
+ */
+ synchronized void updateNfcService(NfcActivityState state) {
+ boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
+ state.onNdefPushCompleteCallback != null;
+
+ try {
+ NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
+ state.resumed && serviceCallbackNeeded ? this : null);
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
+ /**
+ * Callback from NFC service
+ */
+ @Override
+ public NdefMessage createMessage() {
+ NfcAdapter.CreateNdefMessageCallback callback = null;
+ synchronized (NfcActivityManager.this) {
+ for (NfcActivityState state : mNfcState.values()) {
+ if (state.resumed) {
+ callback = state.ndefMessageCallback;
+ }
+ }
+ }
+
+ // drop lock before making callback
+ if (callback != null) {
+ return callback.createNdefMessage(mDefaultEvent);
+ }
+ return null;
+ }
+
+ /**
+ * Callback from NFC service
+ */
+ @Override
+ public void onNdefPushComplete() {
+ NfcAdapter.OnNdefPushCompleteCallback callback = null;
+ synchronized (NfcActivityManager.this) {
+ for (NfcActivityState state : mNfcState.values()) {
+ if (state.resumed) {
+ callback = state.onNdefPushCompleteCallback;
+ }
+ }
+ }
+
+ // drop lock before making callback
+ if (callback != null) {
+ callback.onNdefPushComplete(mDefaultEvent);
+ }
+ }
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 6a904ae..d58b249 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -42,7 +42,7 @@ import android.util.Log;
* adapter for this Android device.
*/
public final class NfcAdapter {
- private static final String TAG = "NFC";
+ static final String TAG = "NFC";
/**
* Intent to start an activity when a tag with NDEF payload is discovered.
@@ -187,107 +187,68 @@ public final class NfcAdapter {
/** @hide */
public static final int STATE_TURNING_OFF = 4;
- /**
- * LLCP link status: The LLCP link is activated.
- * @hide
- */
- public static final int LLCP_LINK_STATE_ACTIVATED = 0;
-
- /**
- * LLCP link status: The LLCP link is deactivated.
- * @hide
- */
- public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
+ // Guarded by NfcAdapter.class
+ static boolean sIsInitialized = false;
- /**
- * Broadcast Action: the LLCP link state changed.
- * <p>
- * Always contains the extra field
- * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_LLCP_LINK_STATE_CHANGED =
- "android.nfc.action.LLCP_LINK_STATE_CHANGED";
+ // Final after first constructor, except for
+ // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
+ // recovery
+ static INfcAdapter sService;
+ static INfcTag sTagService;
/**
- * Used as int extra field in
- * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}.
- * <p>
- * It contains the new state of the LLCP link.
- * @hide
+ * NfcAdapter is currently a singleton, and does not require a context.
+ * However all the public API's are future-proofed to require a context.
+ * If we start using that then we'll need to keep a HashMap of
+ * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter
+ * is a singleton within each application context.
*/
- public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE";
+ static NfcAdapter sSingleton; // protected by NfcAdapter.class
- /**
- * Tag Reader Discovery mode
- * @hide
- */
- private static final int DISCOVERY_MODE_TAG_READER = 0;
+ final NfcActivityManager mNfcActivityManager;
/**
- * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
- * NFC-IP1 communication. Implementations should not assume that the
- * controller will end up behaving as an NFC-IP1 target or initiator and
- * should handle both cases, depending on the type of the remote peer type.
- * @hide
+ * @see {@link #setNdefPushMessageCallback}
*/
- private static final int DISCOVERY_MODE_NFCIP1 = 1;
-
- /**
- * Card Emulation mode Enables the manager to act as an NFC tag. Provided
- * that a Secure Element (an UICC for instance) is connected to the NFC
- * controller through its SWP interface, it can be exposed to the outside
- * NFC world and be addressed by external readers the same way they would
- * with a tag.
- * <p>
- * Which Secure Element is exposed is implementation-dependent.
- *
- * @hide
- */
- private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
+ public interface OnNdefPushCompleteCallback {
+ /**
+ * Called on successful NDEF push.
+ *
+ * <p>This callback is usually made on a binder thread (not the UI thread).
+ *
+ * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
+ * @see {@link #setNdefPushMessageCallback}
+ */
+ public void onNdefPushComplete(NfcEvent event);
+ }
/**
- * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This
+ * @see {@link #setCeateNdefMessageCallback}
*/
- public interface NdefPushCallback {
+ public interface CreateNdefMessageCallback {
/**
- * Called when a P2P connection is created.
+ * Called to provide a {@link NdefMessage} to push.
+ *
+ * <p>This callback is usually made on a binder thread (not the UI thread).
+ *
+ * <p>Called when this device is in range of another device
+ * that might support NDEF push. It allows the application to
+ * create the NDEF message only when it is required.
+ *
+ * <p>NDEF push cannot occur until this method returns, so do not
+ * block for too long.
+ *
+ * <p>The Android operating system will usually show a system UI
+ * on top of your activity during this time, so do not try to request
+ * input from the user to complete the callback, or provide custom NDEF
+ * push UI. The user probably will not see it.
+ *
+ * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
+ * @return NDEF message to push, or null to not provide a message
*/
- NdefMessage createMessage();
- /**
- * Called when the message is pushed.
- */
- void onMessagePushed();
+ public NdefMessage createNdefMessage(NfcEvent event);
}
- private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub {
- private NdefPushCallback mCallback;
-
- public NdefPushCallbackWrapper(NdefPushCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public NdefMessage onConnect() {
- return mCallback.createMessage();
- }
-
- @Override
- public void onMessagePushed() {
- mCallback.onMessagePushed();
- }
- }
-
- // Guarded by NfcAdapter.class
- private static boolean sIsInitialized = false;
-
- // Final after first constructor, except for
- // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
- // recovery
- private static INfcAdapter sService;
- private static INfcTag sTagService;
-
/**
* Helper to check if this device has FEATURE_NFC, but without using
* a context.
@@ -308,29 +269,36 @@ public final class NfcAdapter {
}
}
- private static synchronized INfcAdapter setupService() {
+ /**
+ * Returns the singleton, or throws if NFC is not available.
+ */
+ static synchronized NfcAdapter getSingleton() {
if (!sIsInitialized) {
sIsInitialized = true;
/* is this device meant to have NFC */
if (!hasNfcFeature()) {
Log.v(TAG, "this device does not have NFC support");
- return null;
+ throw new UnsupportedOperationException();
}
sService = getServiceInterface();
if (sService == null) {
Log.e(TAG, "could not retrieve NFC service");
- return null;
+ throw new UnsupportedOperationException();
}
try {
sTagService = sService.getNfcTagInterface();
} catch (RemoteException e) {
Log.e(TAG, "could not retrieve NFC Tag service");
- return null;
+ throw new UnsupportedOperationException();
}
+ sSingleton = new NfcAdapter();
}
- return sService;
+ if (sSingleton == null) {
+ throw new UnsupportedOperationException();
+ }
+ return sSingleton;
}
/** get handle to NFC service interface */
@@ -376,13 +344,14 @@ public final class NfcAdapter {
public static NfcAdapter getDefaultAdapter() {
Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
"NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
- return new NfcAdapter(null);
+ return getSingleton();
}
- /*package*/ NfcAdapter(Context context) {
- if (setupService() == null) {
- throw new UnsupportedOperationException();
- }
+ /**
+ * Does not currently need a context.
+ */
+ NfcAdapter() {
+ mNfcActivityManager = new NfcActivityManager(this);
}
/**
@@ -524,6 +493,87 @@ public final class NfcAdapter {
}
/**
+ * Set the {@link NdefMessage} to push over NFC during the specified activities.
+ *
+ * <p>This method may be called at any time, but the NDEF message is
+ * only made available for NDEF push when one of the specified activities
+ * is in resumed (foreground) state.
+ *
+ * <p>Only one NDEF message can be pushed by the currently resumed activity.
+ * If both {@link #setNdefPushMessage} and
+ * {@link #setNdefPushMessageCallback} are set then
+ * the callback will take priority.
+ *
+ * <p>Pass a null NDEF message to disable foreground NDEF push in the
+ * specified activities.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param message NDEF message to push over NFC, or null to disable
+ * @param activities one or more {@link Activity} to enable for NDEF push
+ */
+ public void setNdefPushMessage(NdefMessage message, Activity ... activities) {
+ if (activities.length == 0) {
+ throw new NullPointerException("Must specificy one or more activities");
+ }
+ for (Activity a : activities) {
+ mNfcActivityManager.setNdefPushMessage(a, message);
+ }
+ }
+
+ /**
+ * Set the callback to create a {@link NdefMessage} to push over NFC.
+ *
+ * <p>This method may be called at any time, but this callback is
+ * only made if one of the specified activities
+ * is in resumed (foreground) state.
+ *
+ * <p>Only one NDEF message can be pushed by the currently resumed activity.
+ * If both {@link #setNdefPushMessage} and
+ * {@link #setNdefPushMessageCallback} are set then
+ * the callback will take priority.
+ *
+ * <p>Pass a null callback to disable the callback in the
+ * specified activities.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param callback callback, or null to disable
+ * @param activities one or more {@link Activity} to enable for NDEF push
+ */
+ public void setNdefPushMessageCallback(CreateNdefMessageCallback callback,
+ Activity ... activities) {
+ if (activities.length == 0) {
+ throw new NullPointerException("Must specificy one or more activities");
+ }
+ for (Activity a : activities) {
+ mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+ }
+ }
+
+ /**
+ * Set the callback on a successful NDEF push over NFC.
+ *
+ * <p>This method may be called at any time, but NDEF push and this callback
+ * can only occur when one of the specified activities is in resumed
+ * (foreground) state.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param callback callback, or null to disable
+ * @param activities one or more {@link Activity} to enable the callback
+ */
+ public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
+ Activity ... activities) {
+ if (activities.length == 0) {
+ throw new NullPointerException("Must specificy one or more activities");
+ }
+ for (Activity a : activities) {
+ mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+ }
+ }
+
+ /**
* Enable foreground dispatch to the given Activity.
*
* <p>This will give give priority to the foreground activity when
@@ -562,7 +612,7 @@ public final class NfcAdapter {
throw new NullPointerException();
}
if (!activity.isResumed()) {
- throw new IllegalStateException("Foregorund dispatching can only be enabled " +
+ throw new IllegalStateException("Foreground dispatch can only be enabled " +
"when your activity is resumed");
}
try {
@@ -572,8 +622,7 @@ public final class NfcAdapter {
}
ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
mForegroundDispatchListener);
- sService.enableForegroundDispatch(activity.getComponentName(), intent, filters,
- parcel);
+ sService.setForegroundDispatch(intent, filters, parcel);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
}
@@ -608,9 +657,9 @@ public final class NfcAdapter {
void disableForegroundDispatchInternal(Activity activity, boolean force) {
try {
- sService.disableForegroundDispatch(activity.getComponentName());
+ sService.setForegroundDispatch(null, null, null);
if (!force && !activity.isResumed()) {
- throw new IllegalStateException("You must disable forgeground dispatching " +
+ throw new IllegalStateException("You must disable foreground dispatching " +
"while your activity is still resumed");
}
} catch (RemoteException e) {
@@ -619,78 +668,38 @@ public final class NfcAdapter {
}
/**
- * Enable NDEF message push over P2P while this Activity is in the foreground.
- *
- * <p>For this to function properly the other NFC device being scanned must
- * support the "com.android.npp" NDEF push protocol. Support for this
- * protocol is currently optional for Android NFC devices.
+ * Enable NDEF message push over NFC while this Activity is in the foreground.
*
- * <p>This method must be called from the main thread.
+ * <p>You must explicitly call this method every time the activity is
+ * resumed, and you must call {@link #disableForegroundNdefPush} before
+ * your activity completes {@link Activity#onPause}.
*
- * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled.
- * Only the foreground activity may receive tag discovered dispatches via
- * {@link #enableForegroundDispatch}.
+ * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
+ * instead: it automatically hooks into your activity life-cycle,
+ * so you do not need to call enable/disable in your onResume/onPause.
*
- * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
- *
- * @param activity the foreground Activity
- * @param msg a NDEF Message to push over P2P
- * @throws IllegalStateException if the Activity is not currently in the foreground
- * @throws OperationNotSupportedException if this Android device does not support NDEF push
- */
- public void enableForegroundNdefPush(Activity activity, NdefMessage msg) {
- if (activity == null || msg == null) {
- throw new NullPointerException();
- }
- if (!activity.isResumed()) {
- throw new IllegalStateException("Foregorund NDEF push can only be enabled " +
- "when your activity is resumed");
- }
- try {
- ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
- mForegroundNdefPushListener);
- sService.enableForegroundNdefPush(activity.getComponentName(), msg);
- } catch (RemoteException e) {
- attemptDeadServiceRecovery(e);
- }
- }
-
- /**
- * Enable NDEF message push over P2P while this Activity is in the foreground.
- *
- * <p>For this to function properly the other NFC device being scanned must
- * support the "com.android.npp" NDEF push protocol. Support for this
- * protocol is currently optional for Android NFC devices.
+ * <p>For NDEF push to function properly the other NFC device must
+ * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or
+ * Android's "com.android.npp" (Ndef Push Protocol). This was optional
+ * on Gingerbread level Android NFC devices, but SNEP is mandatory on
+ * Ice-Cream-Sandwich and beyond.
*
* <p>This method must be called from the main thread.
*
- * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled.
- * Only the foreground activity may receive tag discovered dispatches via
- * {@link #enableForegroundDispatch}.
- *
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
- * @param activity the foreground Activity
- * @param callback is called on when the P2P connection is established
- * @throws IllegalStateException if the Activity is not currently in the foreground
- * @throws OperationNotSupportedException if this Android device does not support NDEF push
+ * @param activity foreground activity
+ * @param message a NDEF Message to push over NFC
+ * @throws IllegalStateException if the activity is not currently in the foreground
+ * @deprecated use {@link #setNdefPushMessage} instead
*/
- public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) {
- if (activity == null || callback == null) {
+ @Deprecated
+ public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
+ if (activity == null || message == null) {
throw new NullPointerException();
}
- if (!activity.isResumed()) {
- throw new IllegalStateException("Foregorund NDEF push can only be enabled " +
- "when your activity is resumed");
- }
- try {
- ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
- mForegroundNdefPushListener);
- sService.enableForegroundNdefPushWithCallback(activity.getComponentName(),
- new NdefPushCallbackWrapper(callback));
- } catch (RemoteException e) {
- attemptDeadServiceRecovery(e);
- }
+ enforceResumed(activity);
+ mNfcActivityManager.setNdefPushMessage(activity, message);
}
/**
@@ -700,47 +709,91 @@ public final class NfcAdapter {
* must call this method before its {@link Activity#onPause} callback
* completes.
*
+ * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
+ * instead: it automatically hooks into your activity life-cycle,
+ * so you do not need to call enable/disable in your onResume/onPause.
+ *
* <p>This method must be called from the main thread.
*
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param activity the Foreground activity
* @throws IllegalStateException if the Activity has already been paused
- * @throws OperationNotSupportedException if this Android device does not support NDEF push
+ * @deprecated use {@link #setNdefPushMessage} instead
*/
public void disableForegroundNdefPush(Activity activity) {
- ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
- mForegroundNdefPushListener);
- disableForegroundNdefPushInternal(activity, false);
+ if (activity == null) {
+ throw new NullPointerException();
+ }
+ enforceResumed(activity);
+ mNfcActivityManager.setNdefPushMessage(activity, null);
+ mNfcActivityManager.setNdefPushMessageCallback(activity, null);
+ mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
}
- OnActivityPausedListener mForegroundNdefPushListener = new OnActivityPausedListener() {
+ /**
+ * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
+ * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback}
+ * @hide
+ */
+ @Deprecated
+ public interface NdefPushCallback {
+ /**
+ * @deprecated use {@link CreateNdefMessageCallback} instead
+ */
+ @Deprecated
+ NdefMessage createMessage();
+ /**
+ * @deprecated use{@link OnNdefPushCompleteCallback} instead
+ */
+ @Deprecated
+ void onMessagePushed();
+ }
+
+ /**
+ * TODO: Remove this
+ * Converts new callbacks to old callbacks.
+ */
+ static final class LegacyCallbackWrapper implements CreateNdefMessageCallback,
+ OnNdefPushCompleteCallback {
+ final NdefPushCallback mLegacyCallback;
+ LegacyCallbackWrapper(NdefPushCallback legacyCallback) {
+ mLegacyCallback = legacyCallback;
+ }
@Override
- public void onPaused(Activity activity) {
- disableForegroundNdefPushInternal(activity, true);
+ public void onNdefPushComplete(NfcEvent event) {
+ mLegacyCallback.onMessagePushed();
}
- };
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ return mLegacyCallback.createMessage();
+ }
+ }
- void disableForegroundNdefPushInternal(Activity activity, boolean force) {
- try {
- sService.disableForegroundNdefPush(activity.getComponentName());
- if (!force && !activity.isResumed()) {
- throw new IllegalStateException("You must disable forgeground NDEF push " +
- "while your activity is still resumed");
- }
- } catch (RemoteException e) {
- attemptDeadServiceRecovery(e);
+ /**
+ * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
+ * @deprecated use {@link #setNdefPushMessageCallback} instead
+ * @hide
+ */
+ @Deprecated
+ public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) {
+ if (activity == null || callback == null) {
+ throw new NullPointerException();
}
+ enforceResumed(activity);
+ LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback);
+ mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper);
+ mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper);
}
/**
- * Enable zero-click sharing.
- *
+ * Enable NDEF Push feature.
+ * <p>This API is for the Settings application.
* @hide
*/
- public boolean enableZeroClick() {
+ public boolean enableNdefPush() {
try {
- return sService.enableZeroClick();
+ return sService.enableNdefPush();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -748,13 +801,13 @@ public final class NfcAdapter {
}
/**
- * Disable zero-click sharing.
- *
+ * Disable NDEF Push feature.
+ * <p>This API is for the Settings application.
* @hide
*/
- public boolean disableZeroClick() {
+ public boolean disableNdefPush() {
try {
- return sService.disableZeroClick();
+ return sService.disableNdefPush();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -762,20 +815,20 @@ public final class NfcAdapter {
}
/**
- * Return true if zero-click sharing feature is enabled.
+ * Return true if NDEF Push feature is enabled.
* <p>This function can return true even if NFC is currently turned-off.
- * This indicates that zero-click is not currently active, but it has
+ * This indicates that NDEF Push is not currently active, but it has
* been requested by the user and will be active as soon as NFC is turned
* on.
- * <p>If you want to check if zero-click sharing is currently active, use
- * <code>{@link #isEnabled()} && {@link #isZeroClickEnabled()}</code>
+ * <p>If you want to check if NDEF PUsh sharing is currently active, use
+ * <code>{@link #isEnabled()} && {@link #isNdefPushEnabled()}</code>
*
- * @return true if zero-click sharing is enabled
+ * @return true if NDEF Push feature is enabled
* @hide
*/
- public boolean isZeroClickEnabled() {
+ public boolean isNdefPushEnabled() {
try {
- return sService.isZeroClickEnabled();
+ return sService.isNdefPushEnabled();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -793,4 +846,10 @@ public final class NfcAdapter {
return null;
}
}
+
+ void enforceResumed(Activity activity) {
+ if (!activity.isResumed()) {
+ throw new IllegalStateException("API cannot be called while activity is paused");
+ }
+ }
}
diff --git a/core/java/android/nfc/NfcEvent.java b/core/java/android/nfc/NfcEvent.java
new file mode 100644
index 0000000..b00d8b7
--- /dev/null
+++ b/core/java/android/nfc/NfcEvent.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.nfc;
+
+/**
+ * Wraps information associated with any NFC event.
+ *
+ * <p>Immutable object, with direct access to the (final) fields.
+ *
+ * <p>An {@link NfcEvent} object is usually included in callbacks from
+ * {@link NfcAdapter}. Check the documentation of the callback to see
+ * which fields may be set.
+ *
+ * <p>This wrapper object is used (instead of parameters
+ * in the callback) because it allows new fields to be added without breaking
+ * API compatibility.
+ *
+ * @see {@link NfcAdapter.OnNdefPushCompleteCallback#onNdefPushComplete}
+ * @see {@link NfcAdapter.CreateNdefMessageCallback#createNdefMessage}
+ */
+public final class NfcEvent {
+ /**
+ * The {@link NfcAdapter} associated with the NFC event.
+ */
+ public final NfcAdapter nfcAdapter;
+
+ NfcEvent(NfcAdapter nfcAdapter) {
+ this.nfcAdapter = nfcAdapter;
+ }
+}
diff --git a/core/java/android/nfc/NfcFragment.java b/core/java/android/nfc/NfcFragment.java
new file mode 100644
index 0000000..c48859b
--- /dev/null
+++ b/core/java/android/nfc/NfcFragment.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.nfc;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+
+/**
+ * Used by {@link NfcActivityManager} to attach to activity life-cycle.
+ * @hide
+ */
+public final class NfcFragment extends Fragment {
+ static final String FRAGMENT_TAG = "android.nfc.NfcFragment";
+
+ // only used on UI thread
+ static boolean sIsInitialized = false;
+ static NfcActivityManager sNfcActivityManager;
+
+ /**
+ * Attach NfcFragment to an activity (if not already attached).
+ */
+ public static void attach(Activity activity) {
+ FragmentManager manager = activity.getFragmentManager();
+ if (manager.findFragmentByTag(FRAGMENT_TAG) == null) {
+ manager.beginTransaction().add(new NfcFragment(), FRAGMENT_TAG).commit();
+ }
+ }
+
+ /**
+ * Remove NfcFragment from activity.
+ */
+ public static void remove(Activity activity) {
+ FragmentManager manager = activity.getFragmentManager();
+ Fragment fragment = manager.findFragmentByTag(FRAGMENT_TAG);
+ if (fragment != null) {
+ manager.beginTransaction().remove(fragment).commit();
+ }
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ if (!sIsInitialized) {
+ sIsInitialized = true;
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(
+ activity.getApplicationContext());
+ if (adapter != null) {
+ sNfcActivityManager = adapter.mNfcActivityManager;
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (sNfcActivityManager != null) {
+ sNfcActivityManager.onResume(getActivity());
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (sNfcActivityManager != null) {
+ sNfcActivityManager.onPause(getActivity());
+ }
+ }
+}
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 5fa6483..300ab45 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -40,7 +40,7 @@ public final class NfcManager {
public NfcManager(Context context) {
NfcAdapter adapter;
try {
- adapter = new NfcAdapter(context);
+ adapter = NfcAdapter.getSingleton();
} catch (UnsupportedOperationException e) {
adapter = null;
}