/* * 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.WeakHashMap; /** * 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 WeakHashMap<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 WeakHashMap<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); } } /** * 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); } 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); } } /** * 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); } } }