/*
* 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.
*
*
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.
*
* @hide
*/
public final class NfcActivityManager extends INdefPushCallback.Stub {
static final String TAG = NfcAdapter.TAG;
static final Boolean DBG = false;
final NfcAdapter mAdapter;
final WeakHashMap 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();
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);
}
}
}