Commit c84c89a6 authored by Nick Pelly's avatar Nick Pelly

Improve NDEF push API

Introduce
  setNdefPushMessage()
  setNdefPushMessageCallback()
  setNdefPushCompleteCallback()

Deprecate public API
  enableForegroundNdefPush()
  disableForegroundNdefPush()

Hide & Deprecate staged (public but never released) API
  enableForegroundNdefPushCallback()

The new API's do not require the application to explicitly call
enable()/disable() in onPause()/onResume(), we use a Fragment behind
the scenes to manager this automatically.

NDEF Push can be disabled by using a null parameter, so each
enable()/disable() pair is collapsed to a single set() call.

Application code should now look something like:

    public void onCreate() {
        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
        if (adapter != null) {  // check that NFC is available on this device
            adapter.setNdefPushMessage(myNdefMessage, this);
        }
    }

And that's it - no need to explicitly hook into onPause() and onResume() events.

Also - introduce a generic NfcEvent class that is provided as a parameter on
all NFC callbacks. Right now it just provides the NfcAdapter, but using
the wrapper classes allows us to add more fields later without changing
the callback signature. (i'm thinking Bluetooth).

Change-Id: I371dcb026b535b8199225c1262eca64ce644458a
parent 359ef798
......@@ -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
# ************************************************
......@@ -12412,13 +12412,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";
......@@ -12427,9 +12429,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 {
......@@ -23,6 +23,6 @@ import android.nfc.NdefMessage;
*/
interface INdefPushCallback
{
NdefMessage onConnect();
void onMessagePushed();
NdefMessage createMessage();
void onNdefPushComplete();
}
......@@ -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);
}
/*
* 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);
}
}
}
This diff is collapsed.
/*
* 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;
}
}
/*
* 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());
}
}
}
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment