blob: 9cd335b135c7ee867de8244a7656855ecc91034a [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.mojo.notifications;
import android.app.ActivityManager;
import android.app.NotificationManager;
import android.content.Context;
import android.util.SparseArray;
import org.chromium.mojo.application.ShellHelper;
import org.chromium.mojo.intent.IntentReceiverManager;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojom.mojo.Shell;
import org.chromium.mojom.notifications.Notification;
import org.chromium.mojom.notifications.NotificationClient;
import org.chromium.mojom.notifications.NotificationData;
import org.chromium.mojom.notifications.NotificationService;
import java.util.List;
/**
* Android implementation of Notifications.
*/
class NotificationServiceImpl implements NotificationService,
NotificationBuilder.NotificationActionListener,
NotificationBuilder.NotificationReadyListener {
private final NotificationManager mNotificationManager;
private final SparseArray<NotificationImpl> mNotificationMap;
private final SparseArray<NotificationClient> mNotificationClientMap;
/**
* This tag is used when notifying and cancelling notifications to ensure the notifications
* being notified and cancelled are unique to this notification service instance.
*/
private final String mNotificationManagerTag;
private final NotificationBuilder mNotificationBuilder;
private final ActivityManager mActivityManager;
private int mNextNotificationId;
private ActivityManager.AppTask mAppTask;
NotificationServiceImpl(
Context context, Core core, Shell shell, int notificationIconResourceId) {
mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationMap = new SparseArray<NotificationImpl>();
mNotificationClientMap = new SparseArray<NotificationClient>();
mNotificationManagerTag = toString();
IntentReceiverManager intentReceiverManager = ShellHelper.connectToService(
core, shell, "mojo:intent_receiver", IntentReceiverManager.MANAGER);
mNotificationBuilder = new NotificationBuilder(
context, intentReceiverManager, notificationIconResourceId, this, this);
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = mActivityManager.getAppTasks();
// Associate the service instance with the current top task of the shell
// application. All notifications created by this instance will be associated with
// this task too, and the task will be foregrounded when any of the notifications
// is selected.
mAppTask = tasks.isEmpty() ? null : tasks.get(0);
mNextNotificationId = 1;
}
// NotificationService implementation.
@Override
public void close() {}
@Override
public void onConnectionError(MojoException e) {}
@Override
public void post(NotificationData notificationData, NotificationClient notificationClient,
org.chromium.mojo.bindings.InterfaceRequest<Notification> request) {
final int newNotificationId = getNewNotificationId();
mNotificationClientMap.put(newNotificationId, notificationClient);
NotificationImpl notification = new NotificationImpl(this, newNotificationId);
mNotificationMap.put(newNotificationId, notification);
Notification.MANAGER.bind(notification, request);
postOrUpdateNotification(newNotificationId, notificationData);
}
// NotificationBuilder.NotificationActionListener implementation.
@Override
public void onNotificationSelected(int notificationId) {
NotificationClient client = mNotificationClientMap.get(notificationId);
if (client != null) {
// If the selected task is not active anymore, choose the most recent one and associate
// this service with it.
if (mAppTask == null || mAppTask.getTaskInfo().id == -1) {
List<ActivityManager.AppTask> tasks = mActivityManager.getAppTasks();
mAppTask = tasks.isEmpty() ? null : tasks.get(0);
}
if (mAppTask != null) {
mAppTask.moveToFront();
}
client.onSelected();
}
// Since autoCancel is set to true (@see NotificationBuilder#build(int, NotificationData)),
// the notification no longer exists at this point. Clean it up.
cleanUpNotification(notificationId);
}
@Override
public void onNotificationDismissed(int notificationId) {
NotificationClient client = mNotificationClientMap.get(notificationId);
if (client != null) {
client.onDismissed();
}
// Since the notification was dismissed it can no longer be interacted with. Clean it up.
cleanUpNotification(notificationId);
}
// NotificationBuilder.NotificationReadyListener implementation.
@Override
public void onNotificationReady(int notificationId, android.app.Notification notification) {
// Only allow notification of ids we know about. This will filter out situations where a
// cancel quickly follows a post or update before the notification is finished being
// created.
if (mNotificationMap.get(notificationId) != null) {
mNotificationManager.notify(mNotificationManagerTag, notificationId, notification);
}
}
void cancel(int notificationId) {
mNotificationManager.cancel(mNotificationManagerTag, notificationId);
// Since we're cancelling the notification, the notification can no longer be interacted
// with. Clean it up.
cleanUpNotification(notificationId);
}
/**
* Posts a notification to the notification manager once built, or updates that notification if
* it already exists.
*/
void postOrUpdateNotification(int notificationId, NotificationData notificationData) {
mNotificationBuilder.build(notificationId, notificationData);
}
private int getNewNotificationId() {
return mNextNotificationId++;
}
private void cleanUpNotification(int notificationId) {
NotificationClient client = mNotificationClientMap.get(notificationId);
if (client != null) {
client.close();
}
mNotificationClientMap.remove(notificationId);
NotificationImpl notification = mNotificationMap.get(notificationId);
if (notification != null) {
notification.invalidate();
}
mNotificationMap.remove(notificationId);
}
}