Merge "Improve NDEF push API"

This commit is contained in:
Nick Pelly
2011-08-26 17:31:02 -07:00
committed by Android (Google) Code Review
9 changed files with 637 additions and 229 deletions

View File

@@ -107,6 +107,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************

View File

@@ -12413,13 +12413,15 @@ package android.nfc {
public final class NfcAdapter {
method public void disableForegroundDispatch(android.app.Activity);
method public void disableForegroundNdefPush(android.app.Activity);
method public deprecated void disableForegroundNdefPush(android.app.Activity);
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]);
method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NfcAdapter.NdefPushCallback);
method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method public static deprecated android.nfc.NfcAdapter getDefaultAdapter();
method public boolean isEnabled();
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity...);
method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity...);
method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity...);
field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
field public static final java.lang.String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
@@ -12428,9 +12430,16 @@ package android.nfc {
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
}
public static abstract interface NfcAdapter.NdefPushCallback {
method public abstract android.nfc.NdefMessage createMessage();
method public abstract void onMessagePushed();
public static abstract interface NfcAdapter.CreateNdefMessageCallback {
method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
}
public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
}
public final class NfcEvent {
field public final android.nfc.NfcAdapter nfcAdapter;
}
public final class NfcManager {

View File

@@ -23,6 +23,6 @@ import android.nfc.NdefMessage;
*/
interface INdefPushCallback
{
NdefMessage onConnect();
void onMessagePushed();
NdefMessage createMessage();
void onNdefPushComplete();
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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,106 +187,67 @@ 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;
/**
* 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";
/**
* 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
*/
public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE";
/**
* Tag Reader Discovery mode
* @hide
*/
private static final int DISCOVERY_MODE_TAG_READER = 0;
/**
* 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
*/
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;
/**
* Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This
*/
public interface NdefPushCallback {
/**
* Called when a P2P connection is created.
*/
NdefMessage createMessage();
/**
* Called when the message is pushed.
*/
void onMessagePushed();
}
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;
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;
static INfcAdapter sService;
static INfcTag sTagService;
/**
* 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.
*/
static NfcAdapter sSingleton; // protected by NfcAdapter.class
final NfcActivityManager mNfcActivityManager;
/**
* @see {@link #setNdefPushMessageCallback}
*/
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);
}
/**
* @see {@link #setCeateNdefMessageCallback}
*/
public interface CreateNdefMessageCallback {
/**
* 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
*/
public NdefMessage createNdefMessage(NfcEvent event);
}
/**
* Helper to check if this device has FEATURE_NFC, but without using
@@ -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);
}
/**
@@ -523,6 +492,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.
*
@@ -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.
* Enable NDEF message push over NFC 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>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>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>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 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
* @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, NdefMessage msg) {
if (activity == null || msg == 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.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>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
*/
public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) {
if (activity == null || callback == 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();
}
};
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);
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
return mLegacyCallback.createMessage();
}
}
/**
* Enable zero-click sharing.
*
* TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
* @deprecated use {@link #setNdefPushMessageCallback} instead
* @hide
*/
public boolean enableZeroClick() {
@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 NDEF Push feature.
* <p>This API is for the Settings application.
* @hide
*/
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");
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}