Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko;
8 import android.app.Notification;
9 import android.app.PendingIntent;
10 import android.text.TextUtils;
11 import android.util.Log;
13 import java.util.LinkedList;
14 import java.util.concurrent.ConcurrentHashMap;
16 /**
17 * Client for posting notifications through a NotificationHandler.
18 */
19 public abstract class NotificationClient {
20 private static final String LOGTAG = "GeckoNotificationClient";
22 private volatile NotificationHandler mHandler;
23 private boolean mReady;
24 private final LinkedList<Runnable> mTaskQueue = new LinkedList<Runnable>();
25 private final ConcurrentHashMap<Integer, UpdateRunnable> mUpdatesMap =
26 new ConcurrentHashMap<Integer, UpdateRunnable>();
28 /**
29 * Runnable that is reused between update notifications.
30 *
31 * Updates happen frequently, so reusing Runnables prevents frequent dynamic allocation.
32 */
33 private class UpdateRunnable implements Runnable {
34 private long mProgress;
35 private long mProgressMax;
36 private String mAlertText;
37 final private int mNotificationID;
39 public UpdateRunnable(int notificationID) {
40 mNotificationID = notificationID;
41 }
43 public synchronized boolean updateProgress(long progress, long progressMax, String alertText) {
44 if (progress == mProgress
45 && mProgressMax == progressMax
46 && TextUtils.equals(mAlertText, alertText)) {
47 return false;
48 }
50 mProgress = progress;
51 mProgressMax = progressMax;
52 mAlertText = alertText;
53 return true;
54 }
56 @Override
57 public void run() {
58 long progress;
59 long progressMax;
60 String alertText;
62 synchronized (this) {
63 progress = mProgress;
64 progressMax = mProgressMax;
65 alertText = mAlertText;
66 }
68 mHandler.update(mNotificationID, progress, progressMax, alertText);
69 }
70 };
72 /**
73 * Adds a notification.
74 *
75 * @see NotificationHandler#add(int, String, String, String, PendingIntent, PendingIntent)
76 */
77 public synchronized void add(final int notificationID, final String aImageUrl,
78 final String aAlertTitle, final String aAlertText, final PendingIntent contentIntent) {
79 mTaskQueue.add(new Runnable() {
80 @Override
81 public void run() {
82 mHandler.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
83 }
84 });
85 notify();
87 if (!mReady) {
88 bind();
89 }
90 }
92 /**
93 * Adds a notification.
94 *
95 * @see NotificationHandler#add(int, Notification)
96 */
97 public synchronized void add(final int notificationID, final Notification notification) {
98 mTaskQueue.add(new Runnable() {
99 @Override
100 public void run() {
101 mHandler.add(notificationID, notification);
102 }
103 });
104 notify();
106 if (!mReady) {
107 bind();
108 }
109 }
111 /**
112 * Updates a notification.
113 *
114 * @see NotificationHandler#update(int, long, long, String)
115 */
116 public void update(final int notificationID, final long aProgress, final long aProgressMax,
117 final String aAlertText) {
118 UpdateRunnable runnable = mUpdatesMap.get(notificationID);
120 if (runnable == null) {
121 runnable = new UpdateRunnable(notificationID);
122 mUpdatesMap.put(notificationID, runnable);
123 }
125 // If we've already posted an update with these values, there's no
126 // need to do it again.
127 if (!runnable.updateProgress(aProgress, aProgressMax, aAlertText)) {
128 return;
129 }
131 synchronized (this) {
132 if (mReady) {
133 mTaskQueue.add(runnable);
134 notify();
135 }
136 }
137 }
139 /**
140 * Removes a notification.
141 *
142 * @see NotificationHandler#remove(int)
143 */
144 public synchronized void remove(final int notificationID) {
145 if (!mReady) {
146 return;
147 }
149 mTaskQueue.add(new Runnable() {
150 @Override
151 public void run() {
152 mHandler.remove(notificationID);
153 mUpdatesMap.remove(notificationID);
154 }
155 });
156 notify();
157 }
159 /**
160 * Determines whether a notification is showing progress.
161 *
162 * @see NotificationHandler#isProgressStyle(int)
163 */
164 public boolean isOngoing(int notificationID) {
165 final NotificationHandler handler = mHandler;
166 return handler != null && handler.isOngoing(notificationID);
167 }
169 protected void bind() {
170 mReady = true;
171 }
173 protected void unbind() {
174 mReady = false;
175 mUpdatesMap.clear();
176 }
178 protected void connectHandler(NotificationHandler handler) {
179 mHandler = handler;
180 new Thread(new NotificationRunnable()).start();
181 }
183 private class NotificationRunnable implements Runnable {
184 @Override
185 public void run() {
186 Runnable r;
187 try {
188 while (true) {
189 // Synchronize polls to prevent tasks from being added to the queue
190 // during the isDone check.
191 synchronized (NotificationClient.this) {
192 r = mTaskQueue.poll();
193 while (r == null) {
194 if (mHandler.isDone()) {
195 unbind();
196 return;
197 }
198 NotificationClient.this.wait();
199 r = mTaskQueue.poll();
200 }
201 }
202 r.run();
203 }
204 } catch (InterruptedException e) {
205 Log.e(LOGTAG, "Notification task queue processing interrupted", e);
206 }
207 }
208 }
209 }