Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c84c89a6 authored by Nick Pelly's avatar Nick Pelly
Browse files

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
# ************************************************
+15 −6
Original line number Diff line number Diff line
@@ -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 {
+2 −2
Original line number Diff line number Diff line
@@ -23,6 +23,6 @@ import android.nfc.NdefMessage;
 */
interface INdefPushCallback
{
    NdefMessage onConnect();
    void onMessagePushed();
    NdefMessage createMessage();
    void onNdefPushComplete();
}
+8 −13
Original line number Diff line number Diff line
@@ -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);
}
+217 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
Loading