Initial commit
[fonbot.git] / lib / android / support / v4 / content / LocalBroadcastManager.java
1 /*
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
17 package android.support.v4.content;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Set;
22
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.net.Uri;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.util.Log;
31
32 /**
33 * Helper to register for and send broadcasts of Intents to local objects
34 * within your process. This is has a number of advantages over sending
35 * global broadcasts with {@link android.content.Context#sendBroadcast}:
36 * <ul>
37 * <li> You know that the data you are broadcasting won't leave your app, so
38 * don't need to worry about leaking private data.
39 * <li> It is not possible for other applications to send these broadcasts to
40 * your app, so you don't need to worry about having security holes they can
41 * exploit.
42 * <li> It is more efficient than sending a global broadcast through the
43 * system.
44 * </ul>
45 */
46 public class LocalBroadcastManager {
47 private static class ReceiverRecord {
48 final IntentFilter filter;
49 final BroadcastReceiver receiver;
50 boolean broadcasting;
51
52 ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
53 filter = _filter;
54 receiver = _receiver;
55 }
56
57 @Override
58 public String toString() {
59 StringBuilder builder = new StringBuilder(128);
60 builder.append("Receiver{");
61 builder.append(receiver);
62 builder.append(" filter=");
63 builder.append(filter);
64 builder.append("}");
65 return builder.toString();
66 }
67 }
68
69 private static class BroadcastRecord {
70 final Intent intent;
71 final ArrayList<ReceiverRecord> receivers;
72
73 BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
74 intent = _intent;
75 receivers = _receivers;
76 }
77 }
78
79 private static final String TAG = "LocalBroadcastManager";
80 private static final boolean DEBUG = false;
81
82 private final Context mAppContext;
83
84 private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
85 = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
86 private final HashMap<String, ArrayList<ReceiverRecord>> mActions
87 = new HashMap<String, ArrayList<ReceiverRecord>>();
88
89 private final ArrayList<BroadcastRecord> mPendingBroadcasts
90 = new ArrayList<BroadcastRecord>();
91
92 static final int MSG_EXEC_PENDING_BROADCASTS = 1;
93
94 private final Handler mHandler;
95
96 private static final Object mLock = new Object();
97 private static LocalBroadcastManager mInstance;
98
99 public static LocalBroadcastManager getInstance(Context context) {
100 synchronized (mLock) {
101 if (mInstance == null) {
102 mInstance = new LocalBroadcastManager(context.getApplicationContext());
103 }
104 return mInstance;
105 }
106 }
107
108 private LocalBroadcastManager(Context context) {
109 mAppContext = context;
110 mHandler = new Handler(context.getMainLooper()) {
111
112 @Override
113 public void handleMessage(Message msg) {
114 switch (msg.what) {
115 case MSG_EXEC_PENDING_BROADCASTS:
116 executePendingBroadcasts();
117 break;
118 default:
119 super.handleMessage(msg);
120 }
121 }
122 };
123 }
124
125 /**
126 * Register a receive for any local broadcasts that match the given IntentFilter.
127 *
128 * @param receiver The BroadcastReceiver to handle the broadcast.
129 * @param filter Selects the Intent broadcasts to be received.
130 *
131 * @see #unregisterReceiver
132 */
133 public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
134 synchronized (mReceivers) {
135 ReceiverRecord entry = new ReceiverRecord(filter, receiver);
136 ArrayList<IntentFilter> filters = mReceivers.get(receiver);
137 if (filters == null) {
138 filters = new ArrayList<IntentFilter>(1);
139 mReceivers.put(receiver, filters);
140 }
141 filters.add(filter);
142 for (int i=0; i<filter.countActions(); i++) {
143 String action = filter.getAction(i);
144 ArrayList<ReceiverRecord> entries = mActions.get(action);
145 if (entries == null) {
146 entries = new ArrayList<ReceiverRecord>(1);
147 mActions.put(action, entries);
148 }
149 entries.add(entry);
150 }
151 }
152 }
153
154 /**
155 * Unregister a previously registered BroadcastReceiver. <em>All</em>
156 * filters that have been registered for this BroadcastReceiver will be
157 * removed.
158 *
159 * @param receiver The BroadcastReceiver to unregister.
160 *
161 * @see #registerReceiver
162 */
163 public void unregisterReceiver(BroadcastReceiver receiver) {
164 synchronized (mReceivers) {
165 ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
166 if (filters == null) {
167 return;
168 }
169 for (int i=0; i<filters.size(); i++) {
170 IntentFilter filter = filters.get(i);
171 for (int j=0; j<filter.countActions(); j++) {
172 String action = filter.getAction(j);
173 ArrayList<ReceiverRecord> receivers = mActions.get(action);
174 if (receivers != null) {
175 for (int k=0; k<receivers.size(); k++) {
176 if (receivers.get(k).receiver == receiver) {
177 receivers.remove(k);
178 k--;
179 }
180 }
181 if (receivers.size() <= 0) {
182 mActions.remove(action);
183 }
184 }
185 }
186 }
187 }
188 }
189
190 /**
191 * Broadcast the given intent to all interested BroadcastReceivers. This
192 * call is asynchronous; it returns immediately, and you will continue
193 * executing while the receivers are run.
194 *
195 * @param intent The Intent to broadcast; all receivers matching this
196 * Intent will receive the broadcast.
197 *
198 * @see #registerReceiver
199 */
200 public boolean sendBroadcast(Intent intent) {
201 synchronized (mReceivers) {
202 final String action = intent.getAction();
203 final String type = intent.resolveTypeIfNeeded(
204 mAppContext.getContentResolver());
205 final Uri data = intent.getData();
206 final String scheme = intent.getScheme();
207 final Set<String> categories = intent.getCategories();
208
209 final boolean debug = DEBUG ||
210 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
211 if (debug) Log.v(
212 TAG, "Resolving type " + type + " scheme " + scheme
213 + " of intent " + intent);
214
215 ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
216 if (entries != null) {
217 if (debug) Log.v(TAG, "Action list: " + entries);
218
219 ArrayList<ReceiverRecord> receivers = null;
220 for (int i=0; i<entries.size(); i++) {
221 ReceiverRecord receiver = entries.get(i);
222 if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
223
224 if (receiver.broadcasting) {
225 if (debug) {
226 Log.v(TAG, " Filter's target already added");
227 }
228 continue;
229 }
230
231 int match = receiver.filter.match(action, type, scheme, data,
232 categories, "LocalBroadcastManager");
233 if (match >= 0) {
234 if (debug) Log.v(TAG, " Filter matched! match=0x" +
235 Integer.toHexString(match));
236 if (receivers == null) {
237 receivers = new ArrayList<ReceiverRecord>();
238 }
239 receivers.add(receiver);
240 receiver.broadcasting = true;
241 } else {
242 if (debug) {
243 String reason;
244 switch (match) {
245 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
246 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
247 case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
248 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
249 default: reason = "unknown reason"; break;
250 }
251 Log.v(TAG, " Filter did not match: " + reason);
252 }
253 }
254 }
255
256 if (receivers != null) {
257 for (int i=0; i<receivers.size(); i++) {
258 receivers.get(i).broadcasting = false;
259 }
260 mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
261 if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
262 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
263 }
264 return true;
265 }
266 }
267 }
268 return false;
269 }
270
271 /**
272 * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
273 * the Intent this function will block and immediately dispatch them before
274 * returning.
275 */
276 public void sendBroadcastSync(Intent intent) {
277 if (sendBroadcast(intent)) {
278 executePendingBroadcasts();
279 }
280 }
281
282 private void executePendingBroadcasts() {
283 while (true) {
284 BroadcastRecord[] brs = null;
285 synchronized (mReceivers) {
286 final int N = mPendingBroadcasts.size();
287 if (N <= 0) {
288 return;
289 }
290 brs = new BroadcastRecord[N];
291 mPendingBroadcasts.toArray(brs);
292 mPendingBroadcasts.clear();
293 }
294 for (int i=0; i<brs.length; i++) {
295 BroadcastRecord br = brs[i];
296 for (int j=0; j<br.receivers.size(); j++) {
297 br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
298 }
299 }
300 }
301 }
302 }
This page took 0.030632 seconds and 4 git commands to generate.