зеркало из https://github.com/mozilla/gecko-dev.git
Bug 850693 - Create a NotificationHandler for each Fennec instance. r=kats
--HG-- rename : mobile/android/base/NotificationServiceClient.java => mobile/android/base/NotificationClient.java rename : mobile/android/base/NotificationService.java => mobile/android/base/NotificationHandler.java extra : rebase_source : 861bb500d9f1cd7cbc98a0c3628ddf7894e1e976
This commit is contained in:
Родитель
67a657cb5c
Коммит
f880b41bf8
|
@ -0,0 +1,25 @@
|
|||
/* -*- 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.content.Context;
|
||||
|
||||
/**
|
||||
* Client for posting notifications in the application.
|
||||
*/
|
||||
public class AppNotificationClient extends NotificationClient {
|
||||
private final Context mContext;
|
||||
|
||||
public AppNotificationClient(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind() {
|
||||
super.bind();
|
||||
connectHandler(new NotificationHandler(mContext));
|
||||
}
|
||||
}
|
|
@ -1634,6 +1634,13 @@ abstract public class BrowserApp extends GeckoApp
|
|||
}).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NotificationClient makeNotificationClient() {
|
||||
// The service is local to Fennec, so we can use it to keep
|
||||
// Fennec alive during downloads.
|
||||
return new ServiceNotificationClient(getApplicationContext());
|
||||
}
|
||||
|
||||
private void resetFeedbackLaunchCount() {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
|
|
|
@ -1457,6 +1457,8 @@ abstract public class GeckoApp
|
|||
editor.commit();
|
||||
}
|
||||
});
|
||||
|
||||
GeckoAppShell.setNotificationClient(makeNotificationClient());
|
||||
}
|
||||
|
||||
protected void initializeChrome(String uri, boolean isExternalURL) {
|
||||
|
@ -2636,4 +2638,10 @@ abstract public class GeckoApp
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected NotificationClient makeNotificationClient() {
|
||||
// Don't use a notification service; we may be killed in the background
|
||||
// during downloads.
|
||||
return new AppNotificationClient(getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ public class GeckoAppShell
|
|||
private static boolean mLocationHighAccuracy = false;
|
||||
|
||||
static ActivityHandlerHelper sActivityHelper = new ActivityHandlerHelper();
|
||||
static NotificationServiceClient sNotificationClient;
|
||||
static NotificationClient sNotificationClient;
|
||||
|
||||
/* The Android-side API: API methods that Android calls */
|
||||
|
||||
|
@ -1170,11 +1170,11 @@ public class GeckoAppShell
|
|||
}});
|
||||
}
|
||||
|
||||
public static void setNotificationClient(NotificationServiceClient client) {
|
||||
public static void setNotificationClient(NotificationClient client) {
|
||||
if (sNotificationClient == null) {
|
||||
sNotificationClient = client;
|
||||
} else {
|
||||
Log.w(LOGTAG, "Notification client already set");
|
||||
Log.d(LOGTAG, "Notification client already set");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ public class GeckoApplication extends Application {
|
|||
GeckoBatteryManager.getInstance().start();
|
||||
GeckoNetworkManager.getInstance().init(getApplicationContext());
|
||||
MemoryMonitor.getInstance().init(getApplicationContext());
|
||||
GeckoAppShell.setNotificationClient(new NotificationServiceClient(getApplicationContext()));
|
||||
|
||||
mInited = true;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ FENNEC_JAVA_FILES = \
|
|||
AndroidImportPreference.java \
|
||||
AnimatorProxy.java \
|
||||
AnimatedHeightLayout.java \
|
||||
AppNotificationClient.java \
|
||||
AwesomeBar.java \
|
||||
AwesomebarResultHandler.java \
|
||||
AwesomeBarTabs.java \
|
||||
|
@ -119,8 +120,9 @@ FENNEC_JAVA_FILES = \
|
|||
MenuPanel.java \
|
||||
MenuPopup.java \
|
||||
MultiChoicePreference.java \
|
||||
NotificationClient.java \
|
||||
NotificationHandler.java \
|
||||
NotificationService.java \
|
||||
NotificationServiceClient.java \
|
||||
NSSBridge.java \
|
||||
CustomEditText.java \
|
||||
OnInterceptTouchListener.java \
|
||||
|
@ -137,6 +139,7 @@ FENNEC_JAVA_FILES = \
|
|||
ReaderModeUtils.java \
|
||||
RemoteTabs.java \
|
||||
RobocopAPI.java \
|
||||
ServiceNotificationClient.java \
|
||||
SessionParser.java \
|
||||
SetupScreen.java \
|
||||
ShapedButton.java \
|
||||
|
|
|
@ -9,32 +9,21 @@ import java.util.LinkedList;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Client for posting notifications through the NotificationService.
|
||||
* Client for posting notifications through a NotificationHandler.
|
||||
*/
|
||||
public class NotificationServiceClient {
|
||||
private static final String LOGTAG = "GeckoNotificationServiceClient";
|
||||
public abstract class NotificationClient {
|
||||
private static final String LOGTAG = "GeckoNotificationClient";
|
||||
|
||||
private volatile NotificationService mService;
|
||||
private final ServiceConnection mConnection = new NotificationServiceConnection();
|
||||
private boolean mBound;
|
||||
private final Context mContext;
|
||||
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>();
|
||||
|
||||
public NotificationServiceClient(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable that is reused between update notifications.
|
||||
*
|
||||
|
@ -75,26 +64,26 @@ public class NotificationServiceClient {
|
|||
alertText = mAlertText;
|
||||
}
|
||||
|
||||
mService.update(mNotificationID, progress, progressMax, alertText);
|
||||
mHandler.update(mNotificationID, progress, progressMax, alertText);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a notification.
|
||||
*
|
||||
* @see NotificationService#add(int, String, String, String, PendingIntent, PendingIntent)
|
||||
* @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() {
|
||||
mService.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
|
||||
mHandler.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
|
||||
}
|
||||
});
|
||||
notify();
|
||||
|
||||
if (!mBound) {
|
||||
if (!mReady) {
|
||||
bind();
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +91,7 @@ public class NotificationServiceClient {
|
|||
/**
|
||||
* Updates a notification.
|
||||
*
|
||||
* @see NotificationService#update(int, long, long, String)
|
||||
* @see NotificationHandler#update(int, long, long, String)
|
||||
*/
|
||||
public void update(final int notificationID, final long aProgress, final long aProgressMax,
|
||||
final String aAlertText) {
|
||||
|
@ -120,7 +109,7 @@ public class NotificationServiceClient {
|
|||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (mBound) {
|
||||
if (mReady) {
|
||||
mTaskQueue.add(runnable);
|
||||
notify();
|
||||
}
|
||||
|
@ -130,17 +119,17 @@ public class NotificationServiceClient {
|
|||
/**
|
||||
* Removes a notification.
|
||||
*
|
||||
* @see NotificationService#remove(int)
|
||||
* @see NotificationHandler#remove(int)
|
||||
*/
|
||||
public synchronized void remove(final int notificationID) {
|
||||
if (!mBound) {
|
||||
if (!mReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTaskQueue.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mService.remove(notificationID);
|
||||
mHandler.remove(notificationID);
|
||||
mUpdatesMap.remove(notificationID);
|
||||
}
|
||||
});
|
||||
|
@ -150,47 +139,28 @@ public class NotificationServiceClient {
|
|||
/**
|
||||
* Determines whether a notification is showing progress.
|
||||
*
|
||||
* @see NotificationService#isProgressStyle(int)
|
||||
* @see NotificationHandler#isProgressStyle(int)
|
||||
*/
|
||||
public boolean isProgressStyle(int notificationID) {
|
||||
final NotificationService service = mService;
|
||||
return service != null && service.isProgressStyle(notificationID);
|
||||
final NotificationHandler handler = mHandler;
|
||||
return handler != null && handler.isProgressStyle(notificationID);
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
mBound = true;
|
||||
final Intent intent = new Intent(mContext, NotificationService.class);
|
||||
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||
protected void bind() {
|
||||
mReady = true;
|
||||
}
|
||||
|
||||
private void unbind() {
|
||||
if (mBound) {
|
||||
mBound = false;
|
||||
mContext.unbindService(mConnection);
|
||||
mUpdatesMap.clear();
|
||||
}
|
||||
protected void unbind() {
|
||||
mReady = false;
|
||||
mUpdatesMap.clear();
|
||||
}
|
||||
|
||||
class NotificationServiceConnection implements ServiceConnection, Runnable {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
final NotificationService.NotificationBinder binder =
|
||||
(NotificationService.NotificationBinder) service;
|
||||
mService = binder.getService();
|
||||
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected -- that is, its process crashed.
|
||||
// Because it is running in our same process, we should never
|
||||
// see this happen, and the correctness of this class relies on
|
||||
// this never happening.
|
||||
Log.e(LOGTAG, "Notification service disconnected", new Exception());
|
||||
}
|
||||
protected void connectHandler(NotificationHandler handler) {
|
||||
mHandler = handler;
|
||||
new Thread(new NotificationRunnable()).start();
|
||||
}
|
||||
|
||||
private class NotificationRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
Runnable r;
|
||||
|
@ -198,20 +168,14 @@ public class NotificationServiceClient {
|
|||
while (true) {
|
||||
// Synchronize polls to prevent tasks from being added to the queue
|
||||
// during the isDone check.
|
||||
synchronized (NotificationServiceClient.this) {
|
||||
synchronized (NotificationClient.this) {
|
||||
r = mTaskQueue.poll();
|
||||
while (r == null) {
|
||||
if (mService.isDone()) {
|
||||
// If there are no more tasks and no notifications being
|
||||
// displayed, the service is disconnected. Unfortunately,
|
||||
// since completed download notifications are shown by
|
||||
// removing the progress notification and creating a new
|
||||
// static one, this will cause the service to be unbound
|
||||
// and immediately rebound when a download completes.
|
||||
if (mHandler.isDone()) {
|
||||
unbind();
|
||||
return;
|
||||
}
|
||||
NotificationServiceClient.this.wait();
|
||||
NotificationClient.this.wait();
|
||||
r = mTaskQueue.poll();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* -*- 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 java.lang.reflect.Field;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
public class NotificationHandler {
|
||||
private final ConcurrentHashMap<Integer, AlertNotification>
|
||||
mAlertNotifications = new ConcurrentHashMap<Integer, AlertNotification>();
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Notification associated with this service's foreground state.
|
||||
*
|
||||
* {@link android.app.Service#startForeground(int, android.app.Notification)}
|
||||
* associates the foreground with exactly one notification from the service.
|
||||
* To keep Fennec alive during downloads (and to make sure it can be killed
|
||||
* once downloads are complete), we make sure that the foreground is always
|
||||
* associated with an active progress notification if and only if at least
|
||||
* one download is in progress.
|
||||
*/
|
||||
private AlertNotification mForegroundNotification;
|
||||
|
||||
public NotificationHandler(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notification.
|
||||
*
|
||||
* @param notificationID the unique ID of the notification
|
||||
* @param aImageUrl URL of the image to use
|
||||
* @param aAlertTitle title of the notification
|
||||
* @param aAlertText text of the notification
|
||||
* @param contentIntent Intent used when the notification is clicked
|
||||
* @param clearIntent Intent used when the notification is removed
|
||||
*/
|
||||
public void add(int notificationID, String aImageUrl, String aAlertTitle,
|
||||
String aAlertText, PendingIntent contentIntent) {
|
||||
// Remove the old notification with the same ID, if any
|
||||
remove(notificationID);
|
||||
|
||||
int icon = R.drawable.ic_status_logo;
|
||||
|
||||
Uri imageUri = Uri.parse(aImageUrl);
|
||||
final String scheme = imageUri.getScheme();
|
||||
if ("drawable".equals(scheme)) {
|
||||
String resource = imageUri.getSchemeSpecificPart();
|
||||
resource = resource.substring(resource.lastIndexOf('/') + 1);
|
||||
try {
|
||||
final Class<R.drawable> drawableClass = R.drawable.class;
|
||||
final Field f = drawableClass.getField(resource);
|
||||
icon = f.getInt(null);
|
||||
} catch (final Exception e) {} // just means the resource doesn't exist
|
||||
imageUri = null;
|
||||
}
|
||||
|
||||
final AlertNotification notification = new AlertNotification(mContext, notificationID,
|
||||
icon, aAlertTitle, aAlertText, System.currentTimeMillis(), imageUri);
|
||||
|
||||
notification.setLatestEventInfo(mContext, aAlertTitle, aAlertText, contentIntent);
|
||||
|
||||
notification.show();
|
||||
mAlertNotifications.put(notification.getId(), notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a notification.
|
||||
*
|
||||
* @param notificationID ID of existing notification
|
||||
* @param aProgress progress of item being updated
|
||||
* @param aProgressMax max progress of item being updated
|
||||
* @param aAlertText text of the notification
|
||||
*/
|
||||
public void update(int notificationID, long aProgress, long aProgressMax, String aAlertText) {
|
||||
final AlertNotification notification = mAlertNotifications.get(notificationID);
|
||||
if (notification == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notification.updateProgress(aAlertText, aProgress, aProgressMax);
|
||||
|
||||
if (mForegroundNotification == null && notification.isProgressStyle()) {
|
||||
setForegroundNotification(notification);
|
||||
}
|
||||
|
||||
// Hide the notification at 100%
|
||||
if (aProgress == aProgressMax) {
|
||||
remove(notificationID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a notification.
|
||||
*
|
||||
* @param notificationID ID of existing notification
|
||||
*/
|
||||
public void remove(int notificationID) {
|
||||
final AlertNotification notification = mAlertNotifications.remove(notificationID);
|
||||
if (notification != null) {
|
||||
updateForegroundNotification(notification);
|
||||
notification.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the service is done.
|
||||
*
|
||||
* The service is considered finished when all notifications have been
|
||||
* removed.
|
||||
*
|
||||
* @return whether all notifications have been removed
|
||||
*/
|
||||
public boolean isDone() {
|
||||
return mAlertNotifications.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a notification is showing progress.
|
||||
*
|
||||
* @param notificationID the notification to check
|
||||
* @return whether the notification is progress style
|
||||
*/
|
||||
public boolean isProgressStyle(int notificationID) {
|
||||
final AlertNotification notification = mAlertNotifications.get(notificationID);
|
||||
return notification != null && notification.isProgressStyle();
|
||||
}
|
||||
|
||||
protected void setForegroundNotification(AlertNotification notification) {
|
||||
mForegroundNotification = notification;
|
||||
}
|
||||
|
||||
private void updateForegroundNotification(AlertNotification oldNotification) {
|
||||
if (mForegroundNotification == oldNotification) {
|
||||
// If we're removing the notification associated with the
|
||||
// foreground, we need to pick another active notification to act
|
||||
// as the foreground notification.
|
||||
AlertNotification foregroundNotification = null;
|
||||
for (final AlertNotification notification : mAlertNotifications.values()) {
|
||||
if (notification.isProgressStyle()) {
|
||||
foregroundNotification = notification;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setForegroundNotification(foregroundNotification);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,33 +5,25 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class NotificationService extends Service {
|
||||
private final IBinder mBinder = new NotificationBinder();
|
||||
private final NotificationHandler mHandler = new NotificationHandler(this) {
|
||||
@Override
|
||||
protected void setForegroundNotification(AlertNotification notification) {
|
||||
super.setForegroundNotification(notification);
|
||||
|
||||
private final ConcurrentHashMap<Integer, AlertNotification>
|
||||
mAlertNotifications = new ConcurrentHashMap<Integer, AlertNotification>();
|
||||
|
||||
/**
|
||||
* Notification associated with this service's foreground state.
|
||||
*
|
||||
* {@link android.app.Service#startForeground(int, android.app.Notification)}
|
||||
* associates the foreground with exactly one notification from the service.
|
||||
* To keep Fennec alive during downloads (and to make sure it can be killed
|
||||
* once downloads are complete), we make sure that the foreground is always
|
||||
* associated with an active progress notification if and only if at least
|
||||
* one download is in progress.
|
||||
*/
|
||||
private AlertNotification mForegroundNotification;
|
||||
if (notification == null) {
|
||||
stopForeground(true);
|
||||
} else {
|
||||
startForeground(notification.getId(), notification);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public class NotificationBinder extends Binder {
|
||||
NotificationService getService() {
|
||||
|
@ -45,130 +37,7 @@ public class NotificationService extends Service {
|
|||
return mBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notification.
|
||||
*
|
||||
* @param notificationID the unique ID of the notification
|
||||
* @param aImageUrl URL of the image to use
|
||||
* @param aAlertTitle title of the notification
|
||||
* @param aAlertText text of the notification
|
||||
* @param contentIntent Intent used when the notification is clicked
|
||||
* @param clearIntent Intent used when the notification is removed
|
||||
*/
|
||||
public void add(int notificationID, String aImageUrl, String aAlertTitle,
|
||||
String aAlertText, PendingIntent contentIntent) {
|
||||
// Remove the old notification with the same ID, if any
|
||||
remove(notificationID);
|
||||
|
||||
int icon = R.drawable.ic_status_logo;
|
||||
|
||||
Uri imageUri = Uri.parse(aImageUrl);
|
||||
final String scheme = imageUri.getScheme();
|
||||
if ("drawable".equals(scheme)) {
|
||||
String resource = imageUri.getSchemeSpecificPart();
|
||||
resource = resource.substring(resource.lastIndexOf('/') + 1);
|
||||
try {
|
||||
final Class<R.drawable> drawableClass = R.drawable.class;
|
||||
final Field f = drawableClass.getField(resource);
|
||||
icon = f.getInt(null);
|
||||
} catch (final Exception e) {} // just means the resource doesn't exist
|
||||
imageUri = null;
|
||||
}
|
||||
|
||||
final AlertNotification notification = new AlertNotification(this, notificationID,
|
||||
icon, aAlertTitle, aAlertText, System.currentTimeMillis(), imageUri);
|
||||
|
||||
notification.setLatestEventInfo(this, aAlertTitle, aAlertText, contentIntent);
|
||||
|
||||
notification.show();
|
||||
mAlertNotifications.put(notification.getId(), notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a notification.
|
||||
*
|
||||
* @param notificationID ID of existing notification
|
||||
* @param aProgress progress of item being updated
|
||||
* @param aProgressMax max progress of item being updated
|
||||
* @param aAlertText text of the notification
|
||||
*/
|
||||
public void update(int notificationID, long aProgress, long aProgressMax, String aAlertText) {
|
||||
final AlertNotification notification = mAlertNotifications.get(notificationID);
|
||||
if (notification == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notification.updateProgress(aAlertText, aProgress, aProgressMax);
|
||||
|
||||
if (mForegroundNotification == null && notification.isProgressStyle()) {
|
||||
setForegroundNotification(notification);
|
||||
}
|
||||
|
||||
// Hide the notification at 100%
|
||||
if (aProgress == aProgressMax) {
|
||||
remove(notificationID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a notification.
|
||||
*
|
||||
* @param notificationID ID of existing notification
|
||||
*/
|
||||
public void remove(int notificationID) {
|
||||
final AlertNotification notification = mAlertNotifications.remove(notificationID);
|
||||
if (notification != null) {
|
||||
updateForegroundNotification(notification);
|
||||
notification.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the service is done.
|
||||
*
|
||||
* The service is considered finished when all notifications have been
|
||||
* removed.
|
||||
*
|
||||
* @return whether all notifications have been removed
|
||||
*/
|
||||
public boolean isDone() {
|
||||
return mAlertNotifications.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a notification is showing progress.
|
||||
*
|
||||
* @param notificationID the notification to check
|
||||
* @return whether the notification is progress style
|
||||
*/
|
||||
public boolean isProgressStyle(int notificationID) {
|
||||
final AlertNotification notification = mAlertNotifications.get(notificationID);
|
||||
return notification != null && notification.isProgressStyle();
|
||||
}
|
||||
|
||||
private void setForegroundNotification(AlertNotification notification) {
|
||||
mForegroundNotification = notification;
|
||||
if (notification == null) {
|
||||
stopForeground(true);
|
||||
} else {
|
||||
startForeground(notification.getId(), notification);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateForegroundNotification(AlertNotification oldNotification) {
|
||||
if (mForegroundNotification == oldNotification) {
|
||||
// If we're removing the notification associated with the
|
||||
// foreground, we need to pick another active notification to act
|
||||
// as the foreground notification.
|
||||
AlertNotification foregroundNotification = null;
|
||||
for (final AlertNotification notification : mAlertNotifications.values()) {
|
||||
if (notification.isProgressStyle()) {
|
||||
foregroundNotification = notification;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setForegroundNotification(foregroundNotification);
|
||||
}
|
||||
public NotificationHandler getNotificationHandler() {
|
||||
return mHandler;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- 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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Client for posting notifications through the NotificationService.
|
||||
*/
|
||||
public class ServiceNotificationClient extends NotificationClient {
|
||||
private static final String LOGTAG = "GeckoServiceNotificationClient";
|
||||
|
||||
private final ServiceConnection mConnection = new NotificationServiceConnection();
|
||||
private boolean mBound;
|
||||
private final Context mContext;
|
||||
|
||||
public ServiceNotificationClient(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind() {
|
||||
super.bind();
|
||||
final Intent intent = new Intent(mContext, NotificationService.class);
|
||||
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unbind() {
|
||||
// If there are no more tasks and no notifications being
|
||||
// displayed, the service is disconnected. Unfortunately,
|
||||
// since completed download notifications are shown by
|
||||
// removing the progress notification and creating a new
|
||||
// static one, this will cause the service to be unbound
|
||||
// and immediately rebound when a download completes.
|
||||
super.unbind();
|
||||
|
||||
if (mBound) {
|
||||
mBound = false;
|
||||
mContext.unbindService(mConnection);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
final NotificationService.NotificationBinder binder =
|
||||
(NotificationService.NotificationBinder) service;
|
||||
connectHandler(binder.getService().getNotificationHandler());
|
||||
mBound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected -- that is, its process crashed.
|
||||
// Because it is running in our same process, we should never
|
||||
// see this happen, and the correctness of this class relies on
|
||||
// this never happening.
|
||||
Log.e(LOGTAG, "Notification service disconnected", new Exception());
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче