mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-27 19:09:47 +00:00
213 lines
6.4 KiB
Java
213 lines
6.4 KiB
Java
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package org.mozilla.gecko;
|
|
|
|
import android.app.Notification;
|
|
import android.app.PendingIntent;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
/**
|
|
* Client for posting notifications through a NotificationHandler.
|
|
*/
|
|
public abstract class NotificationClient {
|
|
private static final String LOGTAG = "GeckoNotificationClient";
|
|
|
|
private volatile NotificationHandler mHandler;
|
|
private boolean mReady;
|
|
private final LinkedList<Runnable> mTaskQueue = new LinkedList<Runnable>();
|
|
private final ConcurrentHashMap<Integer, UpdateRunnable> mUpdatesMap =
|
|
new ConcurrentHashMap<Integer, UpdateRunnable>();
|
|
|
|
/**
|
|
* Runnable that is reused between update notifications.
|
|
*
|
|
* Updates happen frequently, so reusing Runnables prevents frequent dynamic allocation.
|
|
*/
|
|
private class UpdateRunnable implements Runnable {
|
|
private long mProgress;
|
|
private long mProgressMax;
|
|
private String mAlertText;
|
|
final private int mNotificationID;
|
|
|
|
public UpdateRunnable(int notificationID) {
|
|
mNotificationID = notificationID;
|
|
}
|
|
|
|
public synchronized boolean updateProgress(long progress, long progressMax, String alertText) {
|
|
if (progress == mProgress
|
|
&& mProgressMax == progressMax
|
|
&& TextUtils.equals(mAlertText, alertText)) {
|
|
return false;
|
|
}
|
|
|
|
mProgress = progress;
|
|
mProgressMax = progressMax;
|
|
mAlertText = alertText;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
long progress;
|
|
long progressMax;
|
|
String alertText;
|
|
|
|
synchronized (this) {
|
|
progress = mProgress;
|
|
progressMax = mProgressMax;
|
|
alertText = mAlertText;
|
|
}
|
|
|
|
mHandler.update(mNotificationID, progress, progressMax, alertText);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds a notification.
|
|
*
|
|
* @see NotificationHandler#add(int, String, String, String, PendingIntent, PendingIntent)
|
|
*/
|
|
public synchronized void add(final int notificationID, final String aImageUrl,
|
|
final String aAlertTitle, final String aAlertText, final PendingIntent contentIntent) {
|
|
mTaskQueue.add(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mHandler.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
|
|
}
|
|
});
|
|
notify();
|
|
|
|
if (!mReady) {
|
|
bind();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a notification.
|
|
*
|
|
* @see NotificationHandler#add(int, Notification)
|
|
*/
|
|
public synchronized void add(final int notificationID, final Notification notification) {
|
|
mTaskQueue.add(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mHandler.add(notificationID, notification);
|
|
}
|
|
});
|
|
notify();
|
|
|
|
if (!mReady) {
|
|
bind();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates a notification.
|
|
*
|
|
* @see NotificationHandler#update(int, long, long, String)
|
|
*/
|
|
public void update(final int notificationID, final long aProgress, final long aProgressMax,
|
|
final String aAlertText) {
|
|
UpdateRunnable runnable = mUpdatesMap.get(notificationID);
|
|
|
|
if (runnable == null) {
|
|
runnable = new UpdateRunnable(notificationID);
|
|
mUpdatesMap.put(notificationID, runnable);
|
|
}
|
|
|
|
// If we've already posted an update with these values, there's no
|
|
// need to do it again.
|
|
if (!runnable.updateProgress(aProgress, aProgressMax, aAlertText)) {
|
|
return;
|
|
}
|
|
|
|
synchronized (this) {
|
|
if (mReady) {
|
|
mTaskQueue.add(runnable);
|
|
notify();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a notification.
|
|
*
|
|
* @see NotificationHandler#remove(int)
|
|
*/
|
|
public synchronized void remove(final int notificationID) {
|
|
mTaskQueue.add(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mHandler.remove(notificationID);
|
|
mUpdatesMap.remove(notificationID);
|
|
}
|
|
});
|
|
|
|
// If mReady == false, we haven't added any notifications yet. That can happen if Fennec is being
|
|
// started in response to clicking a notification. Call bind() to ensure the task we posted above is run.
|
|
if (!mReady) {
|
|
bind();
|
|
}
|
|
|
|
notify();
|
|
}
|
|
|
|
/**
|
|
* Determines whether a notification is showing progress.
|
|
*
|
|
* @see NotificationHandler#isProgressStyle(int)
|
|
*/
|
|
public boolean isOngoing(int notificationID) {
|
|
final NotificationHandler handler = mHandler;
|
|
return handler != null && handler.isOngoing(notificationID);
|
|
}
|
|
|
|
protected void bind() {
|
|
mReady = true;
|
|
}
|
|
|
|
protected void unbind() {
|
|
mReady = false;
|
|
mUpdatesMap.clear();
|
|
}
|
|
|
|
protected void connectHandler(NotificationHandler handler) {
|
|
mHandler = handler;
|
|
new Thread(new NotificationRunnable()).start();
|
|
}
|
|
|
|
private class NotificationRunnable implements Runnable {
|
|
@Override
|
|
public void run() {
|
|
Runnable r;
|
|
try {
|
|
while (true) {
|
|
// Synchronize polls to prevent tasks from being added to the queue
|
|
// during the isDone check.
|
|
synchronized (NotificationClient.this) {
|
|
r = mTaskQueue.poll();
|
|
while (r == null) {
|
|
if (mHandler.isDone()) {
|
|
unbind();
|
|
return;
|
|
}
|
|
NotificationClient.this.wait();
|
|
r = mTaskQueue.poll();
|
|
}
|
|
}
|
|
r.run();
|
|
}
|
|
} catch (InterruptedException e) {
|
|
Log.e(LOGTAG, "Notification task queue processing interrupted", e);
|
|
}
|
|
}
|
|
}
|
|
}
|