Bug 1304145 - 2. Change GeckoAppShell's notification interface; r=nalexander

Instead of using NotificationClient directly from GeckoAppShell, add a
NotificationListener interface, which NotificationClient would
implement. This isolates NotificationClient (and the notification
package) from GeckoAppShell and lets us move the notification package to
Fennec. It also makes a cleaner interface for GeckoView consumers to
implement notification support.
This commit is contained in:
Jim Chen 2016-09-22 16:38:19 -04:00
Родитель a513529b7c
Коммит ae3538f775
8 изменённых файлов: 99 добавлений и 101 удалений

Просмотреть файл

@ -1141,11 +1141,10 @@ public abstract class GeckoApp
// When that's fixed, `this` can change to
// `(GeckoApplication) getApplication()` here.
GeckoAppShell.setContextGetter(this);
GeckoAppShell.setApplicationContext(getApplicationContext());
GeckoAppShell.setGeckoInterface(this);
// We need to set the notification client before launching Gecko, since Gecko could start
// sending notifications immediately after startup, which we don't want to lose/crash on.
GeckoAppShell.setNotificationClient(makeNotificationClient());
GeckoAppShell.setNotificationListener(makeNotificationClient());
// Tell Stumbler to register a local broadcast listener to listen for preference intents.
// We do this via intents since we can't easily access Stumbler directly,
@ -2394,7 +2393,8 @@ public abstract class GeckoApp
// even if Gecko is running but it was restarted since the notification
// was created, the notification won't be handled (bug 849653).
if (GeckoThread.isRunning()) {
GeckoAppShell.handleNotification(action, alertName, alertCookie);
((NotificationClient) GeckoAppShell.getNotificationListener()).onNotificationClick(
alertName);
}
}

Просмотреть файл

@ -158,6 +158,7 @@ public class GeckoApplication extends Application
mRefWatcher = LeakCanary.install(this);
final Context context = getApplicationContext();
GeckoAppShell.setApplicationContext(context);
HardwareUtils.init(context);
Clipboard.init(context);
FilePicker.init(context);

Просмотреть файл

@ -269,6 +269,7 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
'gfx/ViewTransform.java',
'InputConnectionListener.java',
'InputMethods.java',
'NotificationListener.java',
'notifications/AppNotificationClient.java',
'notifications/NotificationClient.java',
'notifications/NotificationHandler.java',

Просмотреть файл

@ -33,7 +33,6 @@ import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.PanZoomController;
import org.mozilla.gecko.notifications.NotificationClient;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoRequest;
@ -182,13 +181,6 @@ public class GeckoAppShell
private static volatile boolean locationHighAccuracyEnabled;
// Accessed by NotificationHelper. This should be encapsulated.
/* package */ static NotificationClient notificationClient;
public static NotificationClient getNotificationClient() {
return notificationClient;
}
// See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
@ -467,7 +459,8 @@ public class GeckoAppShell
double altitude, float accuracy,
float bearing, float speed, long time);
private static class DefaultListeners implements SensorEventListener, LocationListener {
private static class DefaultListeners
implements SensorEventListener, LocationListener, NotificationListener {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@ -577,18 +570,39 @@ public class GeckoAppShell
public void onStatusChanged(String provider, int status, Bundle extras)
{
}
@Override // NotificationListener
public void showNotification(String name, String cookie, String host,
String title, String text, String imageUrl) {
// Default is to not show the notification, and immediate send close message.
GeckoAppShell.onNotificationClose(name);
}
@Override // NotificationListener
public void showPersistentNotification(String name, String cookie, String host,
String title, String text, String imageUrl,
String data) {
// Default is to not show the notification, and immediate send close message.
GeckoAppShell.onNotificationClose(name);
}
@Override // NotificationListener
public void closeNotification(String name) {
// Do nothing.
}
}
private static final DefaultListeners DEFAULT_LISTENERS = new DefaultListeners();
private static SensorEventListener sSensorListener = DEFAULT_LISTENERS;
private static LocationListener sLocationListener = DEFAULT_LISTENERS;
private static NotificationListener sNotificationListener = DEFAULT_LISTENERS;
public static SensorEventListener getSensorListener() {
return sSensorListener;
}
public static void setSensorListener(final SensorEventListener listener) {
sSensorListener = listener;
sSensorListener = (listener != null) ? listener : DEFAULT_LISTENERS;
}
public static LocationListener getLocationListener() {
@ -596,7 +610,15 @@ public class GeckoAppShell
}
public static void setLocationListener(final LocationListener listener) {
sLocationListener = listener;
sLocationListener = (listener != null) ? listener : DEFAULT_LISTENERS;
}
public static NotificationListener getNotificationListener() {
return sNotificationListener;
}
public static void setNotificationListener(final NotificationListener listener) {
sNotificationListener = (listener != null) ? listener : DEFAULT_LISTENERS;
}
@WrapForJNI(calledFrom = "gecko")
@ -921,97 +943,55 @@ public class GeckoAppShell
return geckoInterface.openUriExternal(targetURI, mimeType, packageName, className, action, title);
}
/**
* Only called from GeckoApp.
*/
public static void setNotificationClient(NotificationClient client) {
if (notificationClient == null) {
notificationClient = client;
} else {
Log.d(LOGTAG, "Notification client already set");
}
}
private static PendingIntent makePersistentNotificationIntent(int notificationID, String type,
String persistentData) {
final Uri.Builder b = new Uri.Builder();
final Uri u = b.scheme("notification-event")
.path(Integer.toString(notificationID))
.appendQueryParameter("type", type)
.build();
final Intent intent = GeckoService.getIntentToCreateServices(
getApplicationContext(), type, persistentData);
intent.setData(u);
return PendingIntent.getService(getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
@WrapForJNI(dispatchTo = "gecko")
private static native void notifyAlertListener(String name, String topic);
@WrapForJNI(calledFrom = "gecko")
private static void showAlertNotification(String imageUrl, String alertTitle, String alertText,
String alertCookie, String alertName, String host,
String persistentData) {
final int notificationID = alertName.hashCode();
final PendingIntent clickIntent, closeIntent;
if (persistentData != null) {
clickIntent = makePersistentNotificationIntent(
notificationID, "persistent-notification-click", persistentData);
closeIntent = makePersistentNotificationIntent(
notificationID, "persistent-notification-close", persistentData);
} else {
notifyAlertListener(alertName, "alertshow");
// The intent to launch when the user clicks the expanded notification
final Intent notificationIntent = new Intent(ACTION_ALERT_CALLBACK);
notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Put the strings into the intent as an URI
// "alert:?name=<alertName>&app=<appName>&cookie=<cookie>"
final Uri.Builder b = new Uri.Builder();
final Uri dataUri = b.scheme("alert")
.path(Integer.toString(notificationID))
.appendQueryParameter("name", alertName)
.appendQueryParameter("cookie", alertCookie)
.build();
notificationIntent.setData(dataUri);
clickIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
closeIntent = null;
/**
* Called by the NotificationListener to notify Gecko that a notification has been
* shown.
*/
public static void onNotificationShow(final String name) {
if (GeckoThread.isRunning()) {
notifyAlertListener(name, "alertshow");
}
}
notificationClient.add(notificationID, imageUrl, host, alertTitle,
alertText, clickIntent, closeIntent);
/**
* Called by the NotificationListener to notify Gecko that a previously shown
* notification has been closed.
*/
public static void onNotificationClose(final String name) {
if (GeckoThread.isRunning()) {
notifyAlertListener(name, "alertfinished");
}
}
/**
* Called by the NotificationListener to notify Gecko that a previously shown
* notification has been clicked on.
*/
public static void onNotificationClick(final String name) {
if (GeckoThread.isRunning()) {
notifyAlertListener(name, "alertclickcallback");
}
}
@WrapForJNI(calledFrom = "gecko")
private static void closeNotification(String alertName) {
notifyAlertListener(alertName, "alertfinished");
final int notificationID = alertName.hashCode();
notificationClient.remove(notificationID);
}
public static void handleNotification(String action, String alertName, String alertCookie) {
final int notificationID = alertName.hashCode();
if (ACTION_ALERT_CALLBACK.equals(action)) {
notifyAlertListener(alertName, "alertclickcallback");
if (notificationClient.isOngoing(notificationID)) {
// When clicked, keep the notification if it displays progress
return;
}
private static void showNotification(String name, String cookie, String title,
String text, String host, String imageUrl,
String persistentData) {
if (persistentData == null) {
getNotificationListener().showNotification(name, cookie, title, text, host, imageUrl);
return;
}
closeNotification(alertName);
getNotificationListener().showPersistentNotification(
name, cookie, title, text, host, imageUrl, persistentData);
}
@WrapForJNI(calledFrom = "gecko")
private static void closeNotification(String name) {
getNotificationListener().closeNotification(name);
}
@WrapForJNI(calledFrom = "gecko")

Просмотреть файл

@ -87,8 +87,7 @@ public class GeckoService extends Service {
@Override // Service
public void onCreate() {
GeckoAppShell.ensureCrashHandling();
GeckoAppShell.setApplicationContext(getApplicationContext());
GeckoAppShell.setNotificationClient(new ServiceNotificationClient(getApplicationContext()));
GeckoAppShell.setNotificationListener(new ServiceNotificationClient(getApplicationContext()));
GeckoThread.onResume();
super.onCreate();

Просмотреть файл

@ -0,0 +1,17 @@
/* -*- 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;
public interface NotificationListener
{
void showNotification(String name, String cookie, String title, String text,
String host, String imageUrl);
void showPersistentNotification(String name, String cookie, String title, String text,
String host, String imageUrl, String data);
void closeNotification(String name);
}

Просмотреть файл

@ -303,7 +303,7 @@ public final class NotificationHelper implements GeckoEventListener {
PendingIntent deletePendingIntent = buildNotificationPendingIntent(message, CLEARED_EVENT);
builder.setDeleteIntent(deletePendingIntent);
GeckoAppShell.getNotificationClient().add(id.hashCode(), builder.build());
((NotificationClient) GeckoAppShell.getNotificationListener()).add(id.hashCode(), builder.build());
boolean persistent = message.optBoolean(PERSISTENT_ATTR);
// We add only not persistent notifications to the list since we want to purge only
@ -344,7 +344,7 @@ public final class NotificationHelper implements GeckoEventListener {
}
private void closeNotification(String id, String handlerKey, String cookie) {
GeckoAppShell.getNotificationClient().remove(id.hashCode());
((NotificationClient) GeckoAppShell.getNotificationListener()).remove(id.hashCode());
sendNotificationWasClosed(id, handlerKey, cookie);
}

Просмотреть файл

@ -84,8 +84,8 @@ AndroidAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
sAlertInfoMap->Put(name, new AlertInfo{aAlertListener, cookie});
}
java::GeckoAppShell::ShowAlertNotification(
imageUrl, title, text, cookie, name, host,
java::GeckoAppShell::ShowNotification(
name, cookie, title, text, host, imageUrl,
!aPersistentData.IsEmpty() ? jni::StringParam(aPersistentData)
: jni::StringParam(nullptr));
return NS_OK;