blob: da878d4f22280b56f28bcc55965763b941583551 [file] [log] [blame]
Nick Pellyc84c89a2011-08-22 22:27:11 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.nfc;
18
19import android.app.Activity;
20import android.os.RemoteException;
21import android.util.Log;
22
Martijn Coenen3433a8a2011-09-01 19:18:02 -070023import java.util.WeakHashMap;
Nick Pellyc84c89a2011-08-22 22:27:11 -070024
25/**
26 * Manages NFC API's that are coupled to the life-cycle of an Activity.
27 *
28 * <p>Uses a fragment to hook into onPause() and onResume() of the host
29 * activities.
30 *
31 * <p>Ideally all of this management would be done in the NFC Service,
32 * but right now it is much easier to do it in the application process.
33 *
34 * @hide
35 */
36public final class NfcActivityManager extends INdefPushCallback.Stub {
37 static final String TAG = NfcAdapter.TAG;
38 static final Boolean DBG = false;
39
40 final NfcAdapter mAdapter;
Martijn Coenen3433a8a2011-09-01 19:18:02 -070041 final WeakHashMap<Activity, NfcActivityState> mNfcState; // contents protected by this
Nick Pellyc84c89a2011-08-22 22:27:11 -070042 final NfcEvent mDefaultEvent; // can re-use one NfcEvent because it just contains adapter
43
44 /**
45 * NFC state associated with an {@link Activity}
46 */
47 class NfcActivityState {
48 boolean resumed = false; // is the activity resumed
49 NdefMessage ndefMessage;
50 NfcAdapter.CreateNdefMessageCallback ndefMessageCallback;
51 NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback;
52 @Override
53 public String toString() {
54 StringBuilder s = new StringBuilder("[").append(resumed).append(" ");
55 s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
56 s.append(onNdefPushCompleteCallback).append("]");
57 return s.toString();
58 }
59 }
60
61 public NfcActivityManager(NfcAdapter adapter) {
62 mAdapter = adapter;
Martijn Coenen3433a8a2011-09-01 19:18:02 -070063 mNfcState = new WeakHashMap<Activity, NfcActivityState>();
Nick Pellyc84c89a2011-08-22 22:27:11 -070064 mDefaultEvent = new NfcEvent(mAdapter);
65 }
66
67 /**
68 * onResume hook from fragment attached to activity
69 */
70 public synchronized void onResume(Activity activity) {
71 NfcActivityState state = mNfcState.get(activity);
72 if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
73 if (state != null) {
74 state.resumed = true;
75 updateNfcService(state);
76 }
77 }
78
79 /**
80 * onPause hook from fragment attached to activity
81 */
82 public synchronized void onPause(Activity activity) {
83 NfcActivityState state = mNfcState.get(activity);
84 if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
85 if (state != null) {
86 state.resumed = false;
87 updateNfcService(state);
88 }
89 }
90
Martijn Coenen3433a8a2011-09-01 19:18:02 -070091 /**
92 * onDestroy hook from fragment attached to activity
93 */
94 public void onDestroy(Activity activity) {
95 mNfcState.remove(activity);
96 }
97
Nick Pellyc84c89a2011-08-22 22:27:11 -070098 public synchronized void setNdefPushMessage(Activity activity, NdefMessage message) {
99 NfcActivityState state = getOrCreateState(activity, message != null);
100 if (state == null || state.ndefMessage == message) {
101 return; // nothing more to do;
102 }
103 state.ndefMessage = message;
104 if (message == null) {
105 maybeRemoveState(activity, state);
106 }
107 if (state.resumed) {
108 updateNfcService(state);
109 }
110 }
111
112 public synchronized void setNdefPushMessageCallback(Activity activity,
113 NfcAdapter.CreateNdefMessageCallback callback) {
114 NfcActivityState state = getOrCreateState(activity, callback != null);
115 if (state == null || state.ndefMessageCallback == callback) {
116 return; // nothing more to do;
117 }
118 state.ndefMessageCallback = callback;
119 if (callback == null) {
120 maybeRemoveState(activity, state);
121 }
122 if (state.resumed) {
123 updateNfcService(state);
124 }
125 }
126
127 public synchronized void setOnNdefPushCompleteCallback(Activity activity,
128 NfcAdapter.OnNdefPushCompleteCallback callback) {
129 NfcActivityState state = getOrCreateState(activity, callback != null);
130 if (state == null || state.onNdefPushCompleteCallback == callback) {
131 return; // nothing more to do;
132 }
133 state.onNdefPushCompleteCallback = callback;
134 if (callback == null) {
135 maybeRemoveState(activity, state);
136 }
137 if (state.resumed) {
138 updateNfcService(state);
139 }
140 }
141
142 /**
143 * Get the NfcActivityState for the specified Activity.
144 * If create is true, then create it if it doesn't already exist,
145 * and ensure the NFC fragment is attached to the activity.
146 */
147 synchronized NfcActivityState getOrCreateState(Activity activity, boolean create) {
148 if (DBG) Log.d(TAG, "getOrCreateState " + activity + " " + create);
149 NfcActivityState state = mNfcState.get(activity);
150 if (state == null && create) {
151 state = new NfcActivityState();
152 mNfcState.put(activity, state);
153 NfcFragment.attach(activity);
154 }
155 return state;
156 }
157
158 /**
159 * If the NfcActivityState is empty then remove it, and
160 * detach it from the Activity.
161 */
162 synchronized void maybeRemoveState(Activity activity, NfcActivityState state) {
163 if (state.ndefMessage == null && state.ndefMessageCallback == null &&
164 state.onNdefPushCompleteCallback == null) {
165 mNfcState.remove(activity);
166 }
167 }
168
169 /**
170 * Register NfcActivityState with the NFC service.
171 */
172 synchronized void updateNfcService(NfcActivityState state) {
173 boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
174 state.onNdefPushCompleteCallback != null;
175
176 try {
177 NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
178 state.resumed && serviceCallbackNeeded ? this : null);
179 } catch (RemoteException e) {
180 mAdapter.attemptDeadServiceRecovery(e);
181 }
182 }
183
184 /**
185 * Callback from NFC service
186 */
187 @Override
188 public NdefMessage createMessage() {
189 NfcAdapter.CreateNdefMessageCallback callback = null;
190 synchronized (NfcActivityManager.this) {
191 for (NfcActivityState state : mNfcState.values()) {
192 if (state.resumed) {
193 callback = state.ndefMessageCallback;
194 }
195 }
196 }
197
198 // drop lock before making callback
199 if (callback != null) {
200 return callback.createNdefMessage(mDefaultEvent);
201 }
202 return null;
203 }
204
205 /**
206 * Callback from NFC service
207 */
208 @Override
209 public void onNdefPushComplete() {
210 NfcAdapter.OnNdefPushCompleteCallback callback = null;
211 synchronized (NfcActivityManager.this) {
212 for (NfcActivityState state : mNfcState.values()) {
213 if (state.resumed) {
214 callback = state.onNdefPushCompleteCallback;
215 }
216 }
217 }
218
219 // drop lock before making callback
220 if (callback != null) {
221 callback.onNdefPushComplete(mDefaultEvent);
222 }
223 }
Martijn Coenen3433a8a2011-09-01 19:18:02 -0700224
Nick Pellyc84c89a2011-08-22 22:27:11 -0700225}