Merge "Use Application#registerLifecycleEvents() instead of a Fragment for Beam."
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <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.
|
||||
* <p>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<Activity, NfcActivityState> mNfcState; // contents protected by this
|
||||
final NfcEvent mDefaultEvent; // can re-use one NfcEvent because it just contains adapter
|
||||
final NfcEvent mDefaultEvent; // cached NfcEvent (its currently always the same)
|
||||
|
||||
// All objects in the lists are protected by this
|
||||
final List<NfcApplicationState> mApps; // Application(s) that have NFC state. Usually one
|
||||
final List<NfcActivityState> mActivities; // Activities that have NFC state
|
||||
|
||||
/**
|
||||
* NFC State associated with an {@link Application}.
|
||||
*/
|
||||
class NfcApplicationState {
|
||||
int refCount = 0;
|
||||
final Application app;
|
||||
public NfcApplicationState(Application app) {
|
||||
this.app = app;
|
||||
}
|
||||
public void register() {
|
||||
refCount++;
|
||||
if (refCount == 1) {
|
||||
this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
|
||||
}
|
||||
}
|
||||
public void unregister() {
|
||||
refCount--;
|
||||
if (refCount == 0) {
|
||||
this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this);
|
||||
} else if (refCount < 0) {
|
||||
Log.e(TAG, "-ve refcount for " + app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NfcApplicationState findAppState(Application app) {
|
||||
for (NfcApplicationState appState : mApps) {
|
||||
if (appState.app == app) {
|
||||
return appState;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void registerApplication(Application app) {
|
||||
NfcApplicationState appState = findAppState(app);
|
||||
if (appState == null) {
|
||||
appState = new NfcApplicationState(app);
|
||||
mApps.add(appState);
|
||||
}
|
||||
appState.register();
|
||||
}
|
||||
|
||||
void unregisterApplication(Application app) {
|
||||
NfcApplicationState appState = findAppState(app);
|
||||
if (appState == null) {
|
||||
Log.e(TAG, "app was not registered " + app);
|
||||
return;
|
||||
}
|
||||
appState.unregister();
|
||||
}
|
||||
|
||||
/**
|
||||
* NFC state associated with an {@link Activity}
|
||||
*/
|
||||
class NfcActivityState {
|
||||
boolean resumed = false; // is the activity resumed
|
||||
NdefMessage ndefMessage;
|
||||
NfcAdapter.CreateNdefMessageCallback ndefMessageCallback;
|
||||
NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback;
|
||||
boolean resumed = false;
|
||||
Activity activity;
|
||||
NdefMessage ndefMessage = null; // static NDEF message
|
||||
NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
|
||||
NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
|
||||
public NfcActivityState(Activity activity) {
|
||||
if (activity.getWindow().isDestroyed()) {
|
||||
throw new IllegalStateException("activity is already destroyed");
|
||||
}
|
||||
this.activity = activity;
|
||||
registerApplication(activity.getApplication());
|
||||
}
|
||||
public void destroy() {
|
||||
unregisterApplication(activity.getApplication());
|
||||
resumed = false;
|
||||
activity = null;
|
||||
ndefMessage = null;
|
||||
ndefMessageCallback = null;
|
||||
onNdefPushCompleteCallback = null;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder("[").append(resumed).append(" ");
|
||||
StringBuilder s = new StringBuilder("[").append(" ");
|
||||
s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
|
||||
s.append(onNdefPushCompleteCallback).append("]");
|
||||
return s.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public NfcActivityManager(NfcAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
mNfcState = new WeakHashMap<Activity, NfcActivityState>();
|
||||
mDefaultEvent = new NfcEvent(mAdapter);
|
||||
/** find activity state from mActivities */
|
||||
synchronized NfcActivityState findActivityState(Activity activity) {
|
||||
for (NfcActivityState state : mActivities) {
|
||||
if (state.activity == activity) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onDestroy hook from fragment attached to activity
|
||||
*/
|
||||
public void onDestroy(Activity activity) {
|
||||
mNfcState.remove(activity);
|
||||
}
|
||||
|
||||
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);
|
||||
/** find or create activity state from mActivities */
|
||||
synchronized NfcActivityState getActivityState(Activity activity) {
|
||||
NfcActivityState state = findActivityState(activity);
|
||||
if (state == null) {
|
||||
state = new NfcActivityState(activity);
|
||||
mActivities.add(state);
|
||||
}
|
||||
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) {
|
||||
NfcFragment.remove(activity);
|
||||
mNfcState.remove(activity);
|
||||
synchronized NfcActivityState findResumedActivityState() {
|
||||
for (NfcActivityState state : mActivities) {
|
||||
if (state.resumed) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized void destroyActivityState(Activity activity) {
|
||||
NfcActivityState activityState = findActivityState(activity);
|
||||
if (activityState != null) {
|
||||
activityState.destroy();
|
||||
mActivities.remove(activityState);
|
||||
}
|
||||
}
|
||||
|
||||
public NfcActivityManager(NfcAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
mActivities = new LinkedList<NfcActivityState>();
|
||||
mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app
|
||||
mDefaultEvent = new NfcEvent(mAdapter);
|
||||
}
|
||||
|
||||
public void setNdefPushMessage(Activity activity, NdefMessage message) {
|
||||
boolean isResumed;
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = getActivityState(activity);
|
||||
state.ndefMessage = message;
|
||||
isResumed = state.resumed;
|
||||
}
|
||||
if (isResumed) {
|
||||
requestNfcServiceCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNdefPushMessageCallback(Activity activity,
|
||||
NfcAdapter.CreateNdefMessageCallback callback) {
|
||||
boolean isResumed;
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = getActivityState(activity);
|
||||
state.ndefMessageCallback = callback;
|
||||
isResumed = state.resumed;
|
||||
}
|
||||
if (isResumed) {
|
||||
requestNfcServiceCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnNdefPushCompleteCallback(Activity activity,
|
||||
NfcAdapter.OnNdefPushCompleteCallback callback) {
|
||||
boolean isResumed;
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = getActivityState(activity);
|
||||
state.onNdefPushCompleteCallback = callback;
|
||||
isResumed = state.resumed;
|
||||
}
|
||||
if (isResumed) {
|
||||
requestNfcServiceCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register NfcActivityState with the NFC service.
|
||||
* Request or unrequest NFC service callbacks for NDEF push.
|
||||
* Makes IPC call - do not hold lock.
|
||||
* TODO: Do not do IPC on every onPause/onResume
|
||||
*/
|
||||
synchronized void updateNfcService(NfcActivityState state) {
|
||||
boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
|
||||
state.onNdefPushCompleteCallback != null;
|
||||
|
||||
void requestNfcServiceCallback(boolean request) {
|
||||
try {
|
||||
NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
|
||||
state.resumed && serviceCallbackNeeded ? this : null);
|
||||
NfcAdapter.sService.setNdefPushCallback(request ? this : null);
|
||||
} catch (RemoteException e) {
|
||||
mAdapter.attemptDeadServiceRecovery(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from NFC service
|
||||
*/
|
||||
/** Callback from NFC service, usually on binder thread */
|
||||
@Override
|
||||
public NdefMessage createMessage() {
|
||||
NfcAdapter.CreateNdefMessageCallback callback = null;
|
||||
NfcAdapter.CreateNdefMessageCallback callback;
|
||||
NdefMessage message;
|
||||
synchronized (NfcActivityManager.this) {
|
||||
for (NfcActivityState state : mNfcState.values()) {
|
||||
if (state.resumed) {
|
||||
callback = state.ndefMessageCallback;
|
||||
}
|
||||
}
|
||||
NfcActivityState state = findResumedActivityState();
|
||||
if (state == null) return null;
|
||||
|
||||
callback = state.ndefMessageCallback;
|
||||
message = state.ndefMessage;
|
||||
}
|
||||
|
||||
// drop lock before making callback
|
||||
// Make callback without lock
|
||||
if (callback != null) {
|
||||
return callback.createNdefMessage(mDefaultEvent);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from NFC service
|
||||
*/
|
||||
/** Callback from NFC service, usually on binder thread */
|
||||
@Override
|
||||
public void onNdefPushComplete() {
|
||||
NfcAdapter.OnNdefPushCompleteCallback callback = null;
|
||||
NfcAdapter.OnNdefPushCompleteCallback callback;
|
||||
synchronized (NfcActivityManager.this) {
|
||||
for (NfcActivityState state : mNfcState.values()) {
|
||||
if (state.resumed) {
|
||||
callback = state.onNdefPushCompleteCallback;
|
||||
}
|
||||
}
|
||||
NfcActivityState state = findResumedActivityState();
|
||||
if (state == null) return;
|
||||
|
||||
callback = state.onNdefPushCompleteCallback;
|
||||
}
|
||||
|
||||
// drop lock before making callback
|
||||
// Make callback without lock
|
||||
if (callback != null) {
|
||||
callback.onNdefPushComplete(mDefaultEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) { /* NO-OP */ }
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = findActivityState(activity);
|
||||
if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
|
||||
if (state == null) return;
|
||||
state.resumed = true;
|
||||
}
|
||||
requestNfcServiceCallback(true);
|
||||
}
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = findActivityState(activity);
|
||||
if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
|
||||
if (state == null) return;
|
||||
state.resumed = false;
|
||||
}
|
||||
requestNfcServiceCallback(false);
|
||||
}
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) { /* NO-OP */ }
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
|
||||
|
||||
/** Callback from Activity life-cycle, on main thread */
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
synchronized (NfcActivityManager.this) {
|
||||
NfcActivityState state = findActivityState(activity);
|
||||
if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
|
||||
if (state != null) {
|
||||
// release all associated references
|
||||
destroyActivityState(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,109 +556,230 @@ public final class NfcAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link NdefMessage} to push over NFC during the specified activities.
|
||||
* Set a static {@link NdefMessage} to send using Android Beam (TM).
|
||||
*
|
||||
* <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>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Pass a null NDEF message to disable foreground NDEF push in the
|
||||
* specified activities.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>At least one activity must be specified, and usually only one is necessary.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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:
|
||||
* <pre>
|
||||
* 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);
|
||||
* }
|
||||
* </pre>
|
||||
* 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.
|
||||
*
|
||||
* <p>If your Activity wants to dynamically generate an NDEF message,
|
||||
* then set a callback using {@link #setNdefPushMessageCallback} instead
|
||||
* of a static message.
|
||||
*
|
||||
* <p class="note">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}.
|
||||
*
|
||||
* <p class="note">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).
|
||||
*
|
||||
* <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>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Pass a null callback to disable the callback in the
|
||||
* specified activities.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>At least one activity must be specified, and usually only one is necessary.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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:
|
||||
* <pre>
|
||||
* 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);
|
||||
* }
|
||||
* </pre>
|
||||
* 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.
|
||||
*
|
||||
* <p class="note">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}.
|
||||
*
|
||||
* <p class="note">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).
|
||||
*
|
||||
* <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>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.
|
||||
*
|
||||
* <p>One or more activities must be specified.
|
||||
* <p>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:
|
||||
* <pre>
|
||||
* 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);
|
||||
* }
|
||||
* </pre>
|
||||
* 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.
|
||||
*
|
||||
* <p class="note">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}.
|
||||
*
|
||||
* <p class="note">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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user