diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 10da9ef1df97a..ddd00a4368b82 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -42,7 +42,7 @@ interface INfcAdapter void setForegroundDispatch(in PendingIntent intent, in IntentFilter[] filters, in TechListParcel techLists); - void setForegroundNdefPush(in NdefMessage msg, in INdefPushCallback callback); + void setNdefPushCallback(in INdefPushCallback callback); void dispatch(in Tag tag); diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index 5fe58e902f826..2c730564fb7bb 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -17,210 +17,303 @@ package android.nfc; import android.app.Activity; +import android.app.Application; +import android.os.Bundle; import android.os.RemoteException; import android.util.Log; -import java.util.WeakHashMap; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; /** * Manages NFC API's that are coupled to the life-cycle of an Activity. * - *
Uses a fragment to hook into onPause() and onResume() of the host - * activities. - * - *
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. + *
Uses {@link Application#registerActivityLifecycleCallbacks} to hook
+ * into activity life-cycle events such as onPause() and onResume().
*
* @hide
*/
-public final class NfcActivityManager extends INdefPushCallback.Stub {
+public final class NfcActivityManager extends INdefPushCallback.Stub
+ implements Application.ActivityLifecycleCallbacks {
static final String TAG = NfcAdapter.TAG;
static final Boolean DBG = false;
final NfcAdapter mAdapter;
- final WeakHashMap 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.
+ * This method may be called at any time before {@link Activity#onDestroy},
+ * but the NDEF message is only made available for NDEF push when the
+ * specified activity(s) are in resumed (foreground) state. The recommended
+ * approach is to call this method during your Activity's
+ * {@link Activity#onCreate} - see sample
+ * code below. This method does not immediately perform any I/O or blocking work,
+ * so is safe to call on your main thread.
*
* Only one NDEF message can be pushed by the currently resumed activity.
* If both {@link #setNdefPushMessage} and
- * {@link #setNdefPushMessageCallback} are set then
+ * {@link #setNdefPushMessageCallback} are set, then
* the callback will take priority.
*
- * Pass a null NDEF message to disable foreground NDEF push in the
- * specified activities.
+ * If neither {@link #setNdefPushMessage} or
+ * {@link #setNdefPushMessageCallback} have been called for your activity, then
+ * the Android OS may choose to send a default NDEF message on your behalf,
+ * such as a URI for your application.
*
- * At least one activity must be specified, and usually only one is necessary.
+ * If {@link #setNdefPushMessage} is called with a null NDEF message,
+ * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+ * then NDEF push will be completely disabled for the specified activity(s).
+ * This also disables any default NDEF message the Android OS would have
+ * otherwise sent on your behalf.
+ *
+ * The API allows for multiple activities to be specified at a time,
+ * but it is strongly recommended to just register one at a time,
+ * and to do so during the activity's {@link Activity#onCreate}. For example:
+ * If your Activity wants to dynamically generate an NDEF message,
+ * then set a callback using {@link #setNdefPushMessageCallback} instead
+ * of a static message.
+ *
+ * Do not pass in an Activity that has already been through
+ * {@link Activity#onDestroy}. This is guaranteed if you call this API
+ * during {@link Activity#onCreate}.
*
* Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param message NDEF message to push over NFC, or null to disable
- * @param activity an activity in which NDEF push should be enabled to share the provided
- * NDEF message
- * @param activities optional additional activities that should also enable NDEF push with
- * the provided NDEF message
+ * @param activity activity for which the NDEF message will be pushed
+ * @param activities optional additional activities, however we strongly recommend
+ * to only register one at a time, and to do so in that activity's
+ * {@link Activity#onCreate}
*/
public void setNdefPushMessage(NdefMessage message, Activity activity,
Activity ... activities) {
- if (activity == null) {
- throw new NullPointerException("activity cannot be null");
- }
- mNfcActivityManager.setNdefPushMessage(activity, message);
- for (Activity a : activities) {
- if (a == null) {
- throw new NullPointerException("activities cannot contain null");
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessage(activity, message);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setNdefPushMessage(a, message);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
}
- mNfcActivityManager.setNdefPushMessage(a, message);
}
}
/**
- * Set the callback to create a {@link NdefMessage} to push over NFC.
+ * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM).
*
- * 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.
+ * This method may be called at any time before {@link Activity#onDestroy},
+ * but the NDEF message callback can only occur when the
+ * specified activity(s) are in resumed (foreground) state. The recommended
+ * approach is to call this method during your Activity's
+ * {@link Activity#onCreate} - see sample
+ * code below. This method does not immediately perform any I/O or blocking work,
+ * so is safe to call on your main thread.
*
* Only one NDEF message can be pushed by the currently resumed activity.
* If both {@link #setNdefPushMessage} and
- * {@link #setNdefPushMessageCallback} are set then
+ * {@link #setNdefPushMessageCallback} are set, then
* the callback will take priority.
*
- * Pass a null callback to disable the callback in the
- * specified activities.
+ * If neither {@link #setNdefPushMessage} or
+ * {@link #setNdefPushMessageCallback} have been called for your activity, then
+ * the Android OS may choose to send a default NDEF message on your behalf,
+ * such as a URI for your application.
*
- * At least one activity must be specified, and usually only one is necessary.
+ * If {@link #setNdefPushMessage} is called with a null NDEF message,
+ * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+ * then NDEF push will be completely disabled for the specified activity(s).
+ * This also disables any default NDEF message the Android OS would have
+ * otherwise sent on your behalf.
+ *
+ * The API allows for multiple activities to be specified at a time,
+ * but it is strongly recommended to just register one at a time,
+ * and to do so during the activity's {@link Activity#onCreate}. For example:
+ * Do not pass in an Activity that has already been through
+ * {@link Activity#onDestroy}. This is guaranteed if you call this API
+ * during {@link Activity#onCreate}.
*
* Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param callback callback, or null to disable
- * @param activity an activity in which NDEF push should be enabled to share an NDEF message
- * that's retrieved from the provided callback
- * @param activities optional additional activities that should also enable NDEF push using
- * the provided callback
+ * @param activity activity for which the NDEF message will be pushed
+ * @param activities optional additional activities, however we strongly recommend
+ * to only register one at a time, and to do so in that activity's
+ * {@link Activity#onCreate}
*/
public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
Activity ... activities) {
- if (activity == null) {
- throw new NullPointerException("activity cannot be null");
- }
- mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
- for (Activity a : activities) {
- if (a == null) {
- throw new NullPointerException("activities cannot contain null");
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
}
- mNfcActivityManager.setNdefPushMessageCallback(a, callback);
}
}
/**
- * Set the callback on a successful NDEF push over NFC.
+ * Set a callback on successful Android Beam (TM).
*
- * 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.
+ * This method may be called at any time before {@link Activity#onDestroy},
+ * but the callback can only occur when the
+ * specified activity(s) are in resumed (foreground) state. The recommended
+ * approach is to call this method during your Activity's
+ * {@link Activity#onCreate} - see sample
+ * code below. This method does not immediately perform any I/O or blocking work,
+ * so is safe to call on your main thread.
*
- * One or more activities must be specified.
+ * The API allows for multiple activities to be specified at a time,
+ * but it is strongly recommended to just register one at a time,
+ * and to do so during the activity's {@link Activity#onCreate}. For example:
+ * Do not pass in an Activity that has already been through
+ * {@link Activity#onDestroy}. This is guaranteed if you call this API
+ * during {@link Activity#onCreate}.
*
* Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param callback callback, or null to disable
- * @param activity an activity to enable the callback (at least one is required)
- * @param activities zero or more additional activities to enable to callback
+ * @param activity activity for which the NDEF message will be pushed
+ * @param activities optional additional activities, however we strongly recommend
+ * to only register one at a time, and to do so in that activity's
+ * {@link Activity#onCreate}
*/
public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
Activity activity, Activity ... activities) {
- if (activity == null) {
- throw new NullPointerException("activity cannot be null");
- }
- mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
- for (Activity a : activities) {
- if (a == null) {
- throw new NullPointerException("activities cannot contain null");
+ int targetSdkVersion = getSdkVersion();
+ try {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
+ for (Activity a : activities) {
+ if (a == null) {
+ throw new NullPointerException("activities cannot contain null");
+ }
+ mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+ }
+ } catch (IllegalStateException e) {
+ if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ // Less strict on old applications - just log the error
+ Log.e(TAG, "Cannot call API with Activity that has already " +
+ "been destroyed", e);
+ } else {
+ // Prevent new applications from making this mistake, re-throw
+ throw(e);
}
- mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
}
}
@@ -932,4 +1053,12 @@ public final class NfcAdapter {
throw new IllegalStateException("API cannot be called while activity is paused");
}
}
+
+ int getSdkVersion() {
+ if (mContext == null) {
+ return android.os.Build.VERSION_CODES.GINGERBREAD; // best guess
+ } else {
+ return mContext.getApplicationInfo().targetSdkVersion;
+ }
+ }
}
diff --git a/core/java/android/nfc/NfcFragment.java b/core/java/android/nfc/NfcFragment.java
deleted file mode 100644
index d6b15ad1bf5a4..0000000000000
--- a/core/java/android/nfc/NfcFragment.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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) {
- // We allow state loss at this point, because the state is only
- // lost when activity is being paused *AND* subsequently destroyed.
- // In that case, the app will setup foreground dispatch again anyway.
- manager.beginTransaction().remove(fragment).commitAllowingStateLoss();
- }
- }
-
- @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());
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (sNfcActivityManager != null) {
- sNfcActivityManager.onDestroy(getActivity());
- }
- }
-
-
-}
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ * if (nfcAdapter == null) return; // NFC not available on this device
+ * nfcAdapter.setNdefPushMessage(ndefMessage, this);
+ * }
+ *
+ * And that is it. Only one call per activity is necessary. The Android
+ * OS will automatically release its references to the NDEF message and the
+ * Activity object when it is destroyed if you follow this pattern.
+ *
+ *
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ * if (nfcAdapter == null) return; // NFC not available on this device
+ * nfcAdapter.setNdefPushMessageCallback(callback, this);
+ * }
+ *
+ * And that is it. Only one call per activity is necessary. The Android
+ * OS will automatically release its references to the callback and the
+ * Activity object when it is destroyed if you follow this pattern.
+ *
+ *
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ * if (nfcAdapter == null) return; // NFC not available on this device
+ * nfcAdapter.setOnNdefPushCompleteCallback(callback, this);
+ * }
+ *
+ * And that is it. Only one call per activity is necessary. The Android
+ * OS will automatically release its references to the callback and the
+ * Activity object when it is destroyed if you follow this pattern.
+ *
+ *