diff options
-rw-r--r-- | CleanSpec.mk | 1 | ||||
-rw-r--r-- | api/current.txt | 21 | ||||
-rw-r--r-- | core/java/android/nfc/INdefPushCallback.aidl | 4 | ||||
-rw-r--r-- | core/java/android/nfc/INfcAdapter.aidl | 21 | ||||
-rw-r--r-- | core/java/android/nfc/NfcActivityManager.java | 217 | ||||
-rw-r--r-- | core/java/android/nfc/NfcAdapter.java | 449 | ||||
-rw-r--r-- | core/java/android/nfc/NfcEvent.java | 44 | ||||
-rw-r--r-- | core/java/android/nfc/NfcFragment.java | 83 | ||||
-rw-r--r-- | core/java/android/nfc/NfcManager.java | 2 |
9 files changed, 625 insertions, 217 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 0dba18b..e6c4183 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -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 # ************************************************ diff --git a/api/current.txt b/api/current.txt index bfe1f85..c8cebce 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { 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; } |