mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1202767 - Bring Android notifications into Gaia r=fabrice
This commit is contained in:
parent
c74601b86c
commit
e25884cddc
@ -9,6 +9,9 @@ JAVAFILES := \
|
||||
src/main/java/org/mozilla/b2gdroid/Launcher.java \
|
||||
src/main/java/org/mozilla/b2gdroid/ScreenStateObserver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/SettingsMapper.java \
|
||||
src/main/java/org/mozilla/b2gdroid/GeckoEventReceiver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/NotificationObserver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/RemoteGeckoEventProxy.java \
|
||||
$(NULL)
|
||||
|
||||
# The GeckoView consuming APK depends on the GeckoView JAR files. There are two
|
||||
|
@ -121,6 +121,15 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="org.mozilla.b2gdroid.NotificationObserver"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
|
||||
android:process="org.mozilla.b2gdroid.NotificationObserver">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:exported="false"
|
||||
android:name="org.mozilla.gecko.updater.UpdateService"
|
||||
|
@ -0,0 +1,60 @@
|
||||
/* vim: set ts=4 sw=4 tw=80 et :
|
||||
* 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.b2gdroid;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
|
||||
/**
|
||||
* A generic 'GeckoEvent' Intent receiver.
|
||||
*
|
||||
* A Context running in a different process to the main Context may wish to
|
||||
* send a GeckoEvent. Using GeckoAppShell.sendEventToGecko from other
|
||||
* processes will silently fail. Instead, broadcast an Intent containing the
|
||||
* GeckoEvent data and the GeckoEventReceiver registered with the primary
|
||||
* Context will forward the Event to Gecko.
|
||||
*/
|
||||
public class GeckoEventReceiver extends BroadcastReceiver {
|
||||
private static final String LOGTAG = "B2GDroid:GeckoEventReceiver";
|
||||
|
||||
private static final String SEND_GECKO_EVENT = AppConstants.ANDROID_PACKAGE_NAME + ".SEND_GECKO_EVENT";
|
||||
|
||||
private static final String EXTRA_SUBJECT = "SUBJECT";
|
||||
private static final String EXTRA_DATA = "DATA";
|
||||
|
||||
public static Intent createBroadcastEventIntent(String subject, String data) {
|
||||
Intent intent = new Intent(SEND_GECKO_EVENT);
|
||||
intent.putExtra(EXTRA_SUBJECT, subject);
|
||||
intent.putExtra(EXTRA_DATA, data);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public void registerWithContext(Context aContext) {
|
||||
aContext.registerReceiver(this, new IntentFilter(SEND_GECKO_EVENT));
|
||||
}
|
||||
|
||||
public void destroy(Context aContext) {
|
||||
aContext.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context aContext, Intent aIntent) {
|
||||
String geckoEventSubject = aIntent.getStringExtra(EXTRA_SUBJECT);
|
||||
String geckoEventJSON = aIntent.getStringExtra(EXTRA_DATA);
|
||||
|
||||
Log.i(LOGTAG, "onReceive(" + geckoEventSubject + ")");
|
||||
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent(geckoEventSubject, geckoEventJSON);
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
@ -6,13 +6,8 @@ package org.mozilla.b2gdroid;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.KeyguardManager.KeyguardLock;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
@ -33,6 +28,7 @@ import org.mozilla.gecko.GeckoBatteryManager;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.IntentHelper;
|
||||
import org.mozilla.gecko.AppNotificationClient;
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
@ -48,6 +44,8 @@ public class Launcher extends FragmentActivity
|
||||
private ScreenStateObserver mScreenStateObserver;
|
||||
private Apps mApps;
|
||||
private SettingsMapper mSettings;
|
||||
private GeckoEventReceiver mGeckoEventReceiver;
|
||||
private RemoteGeckoEventProxy mGeckoEventProxy;
|
||||
|
||||
private static final long kHomeRepeat = 2;
|
||||
private static final long kHomeDelay = 500; // delay in ms to tap kHomeRepeat times.
|
||||
@ -95,6 +93,7 @@ public class Launcher extends FragmentActivity
|
||||
/** Initializes Gecko APIs */
|
||||
private void initGecko() {
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
GeckoAppShell.setNotificationClient(new AppNotificationClient(this));
|
||||
|
||||
GeckoBatteryManager.getInstance().start(this);
|
||||
mContactService = new ContactService(EventDispatcher.getInstance(), this);
|
||||
@ -122,6 +121,11 @@ public class Launcher extends FragmentActivity
|
||||
|
||||
initGecko();
|
||||
|
||||
mGeckoEventProxy = new RemoteGeckoEventProxy(this);
|
||||
|
||||
mGeckoEventReceiver = new GeckoEventReceiver();
|
||||
mGeckoEventReceiver.registerWithContext(this);
|
||||
|
||||
GeckoAppShell.setGeckoInterface(new GeckoInterface(this));
|
||||
|
||||
UpdateServiceHelper.registerForUpdates(this);
|
||||
@ -129,6 +133,12 @@ public class Launcher extends FragmentActivity
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this,
|
||||
"Launcher:Ready");
|
||||
|
||||
// Register the RemoteGeckoEventProxy with the Notification Opened
|
||||
// event, Notifications are handled in a different process as a
|
||||
// service, so we need to forward them to the remote service
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(mGeckoEventProxy,
|
||||
"Android:NotificationOpened");
|
||||
|
||||
setContentView(R.layout.launcher);
|
||||
|
||||
mHomeCount = 0;
|
||||
@ -141,6 +151,7 @@ public class Launcher extends FragmentActivity
|
||||
super.onResume();
|
||||
if (GeckoThread.isRunning()) {
|
||||
hideSplashScreen();
|
||||
NotificationObserver.registerForNativeNotifications(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,6 +160,10 @@ public class Launcher extends FragmentActivity
|
||||
Log.w(LOGTAG, "onDestroy");
|
||||
super.onDestroy();
|
||||
IntentHelper.destroy();
|
||||
|
||||
mGeckoEventReceiver.destroy(this);
|
||||
mGeckoEventReceiver = null;
|
||||
|
||||
mScreenStateObserver.destroy(this);
|
||||
mScreenStateObserver = null;
|
||||
|
||||
|
@ -0,0 +1,227 @@
|
||||
/* vim: set ts=4 sw=4 tw=78 et :
|
||||
* 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.b2gdroid;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.NotificationListenerService.RankingMap;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
public class NotificationObserver extends NotificationListenerService
|
||||
implements GeckoEventListener {
|
||||
|
||||
private static final String LOGTAG = "B2GDroid:NotificationObserver";
|
||||
|
||||
private static final String ACTION_REGISTER_FOR_NOTIFICATIONS = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_REGISTER_FOR_NOTIFICATIONS";
|
||||
|
||||
// Map a unique identifier consisting of the notification's
|
||||
// tag and id. If the notification does not have a tag, the notification
|
||||
// is keyed on the package name and id to attempt to namespace it
|
||||
private Map<String, StatusBarNotification> mCurrentNotifications = new HashMap<String, StatusBarNotification>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.i(LOGTAG, "onCreate()");
|
||||
super.onCreate();
|
||||
|
||||
RemoteGeckoEventProxy.registerRemoteGeckoThreadListener(this, this, "Android:NotificationOpened", "Android:NotificationClosed");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification aNotification, RankingMap aRanking) {
|
||||
Log.i(LOGTAG, "onNotificationPosted(aNotification, aRanking)");
|
||||
this.onNotificationPosted(aNotification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification aNotification) {
|
||||
Log.i(LOGTAG, "onNotificationPosted(aNotification, aRanking)");
|
||||
|
||||
Notification notification = aNotification.getNotification();
|
||||
|
||||
CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
|
||||
CharSequence extraText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
|
||||
|
||||
String notificationKey = getNotificationKey(aNotification);
|
||||
|
||||
JSONObject notificationData = new JSONObject();
|
||||
JSONObject behaviors = new JSONObject();
|
||||
|
||||
try {
|
||||
notificationData.put("_action", "post");
|
||||
notificationData.put("id", notificationKey);
|
||||
notificationData.put("title", title.toString());
|
||||
notificationData.put("text", extraText.toString());
|
||||
notificationData.put("manifestURL", "android://" + aNotification.getPackageName().toLowerCase() + "/manifest.webapp");
|
||||
notificationData.put("icon", getBase64Icon(aNotification));
|
||||
notificationData.put("timestamp", aNotification.getPostTime());
|
||||
|
||||
if ((Notification.FLAG_ONGOING_EVENT & notification.flags) != 0) {
|
||||
notificationData.put("noNotify", true);
|
||||
}
|
||||
|
||||
behaviors.put("vibrationPattern", Arrays.asList(notification.vibrate));
|
||||
|
||||
if ((Notification.FLAG_NO_CLEAR & notification.flags) != 0) {
|
||||
behaviors.put("noclear", true);
|
||||
}
|
||||
|
||||
notificationData.put("mozbehavior", behaviors);
|
||||
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error building android notification message " + ex.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentNotifications.put(notificationKey, aNotification);
|
||||
sendBroadcast(GeckoEventReceiver.createBroadcastEventIntent("Android:Notification", notificationData.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationRemoved(StatusBarNotification aNotification, RankingMap aRanking) {
|
||||
Log.i(LOGTAG, "onNotifciationRemoved(aNotification, aRanking)");
|
||||
onNotificationRemoved(aNotification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationRemoved(StatusBarNotification aNotification) {
|
||||
Log.i(LOGTAG, "onNotificationRemoved(aNotification)");
|
||||
|
||||
String notificationKey = getNotificationKey(aNotification);
|
||||
removeNotificationFromGaia(notificationKey);
|
||||
}
|
||||
|
||||
private void removeNotificationFromGaia(String aNotificationKey) {
|
||||
Log.i(LOGTAG, "removeNotificationFromGaia(aNotificationKey=" + aNotificationKey + ")");
|
||||
JSONObject notificationData = new JSONObject();
|
||||
|
||||
mCurrentNotifications.remove(aNotificationKey);
|
||||
|
||||
try {
|
||||
notificationData.put("_action", "remove");
|
||||
notificationData.put("id", aNotificationKey);
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(LOGTAG, "Error building android notification message " + ex.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
sendBroadcast(GeckoEventReceiver.createBroadcastEventIntent("Android:Notification", notificationData.toString()));
|
||||
}
|
||||
|
||||
private String getNotificationKey(StatusBarNotification aNotification) {
|
||||
// API Level 20 has a 'getKey' method, however for backwards we
|
||||
// implement this getNotificationKey
|
||||
String notificationTag = aNotification.getTag();
|
||||
if (notificationTag == null) {
|
||||
notificationTag = aNotification.getPackageName();
|
||||
}
|
||||
|
||||
return notificationTag + "#" + Integer.toString(aNotification.getId());
|
||||
}
|
||||
|
||||
private String getBase64Icon(StatusBarNotification aStatusBarNotification) {
|
||||
Notification notification = aStatusBarNotification.getNotification();
|
||||
|
||||
PackageManager manager = getPackageManager();
|
||||
Resources notifyingPackageResources;
|
||||
Bitmap bitmap = null;
|
||||
|
||||
try {
|
||||
notifyingPackageResources = manager.getResourcesForApplication(aStatusBarNotification.getPackageName());
|
||||
int resourceId = notification.icon;
|
||||
bitmap = BitmapFactory.decodeResource(notifyingPackageResources, resourceId);
|
||||
} catch(NameNotFoundException ex) {
|
||||
Log.i(LOGTAG, "Could not find package name: " + ex.getMessage());
|
||||
}
|
||||
|
||||
if (bitmap == null) {
|
||||
// Try and get the icon directly from the notification
|
||||
if (notification.largeIcon != null) {
|
||||
bitmap = notification.largeIcon;
|
||||
} else {
|
||||
Log.i(LOGTAG, "No image found for notification");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
|
||||
return "data:image/png;base64," + Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
/* GeckoEventListener */
|
||||
public void handleMessage(String aEvent, JSONObject message) {
|
||||
Log.i(LOGTAG, "handleMessage(aEvent=" + aEvent + ")");
|
||||
|
||||
String notificationId;
|
||||
try {
|
||||
notificationId = message.getString("id");
|
||||
} catch(Exception ex) {
|
||||
Log.i(LOGTAG, "Malformed Android:NotificationOpened JSON - missing ID");
|
||||
return;
|
||||
}
|
||||
|
||||
StatusBarNotification sbNotification = mCurrentNotifications.get(notificationId);
|
||||
|
||||
if (sbNotification == null) {
|
||||
Log.w(LOGTAG, "No notification found for ID: " + notificationId);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("Android:NotificationOpened".equals(aEvent)) {
|
||||
PendingIntent contentIntent = sbNotification.getNotification().contentIntent;
|
||||
|
||||
try {
|
||||
contentIntent.send();
|
||||
} catch(Exception ex) {
|
||||
Log.i(LOGTAG, "Intent was cancelled");
|
||||
}
|
||||
|
||||
removeNotificationFromGaia(notificationId);
|
||||
return;
|
||||
} else if("Android:NotificationClose".equals(aEvent)) {
|
||||
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
notificationManager.cancel(sbNotification.getTag(), sbNotification.getId());
|
||||
mCurrentNotifications.remove(notificationId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerForNativeNotifications(Context context) {
|
||||
Intent intent = createNotificationIntent(context, ACTION_REGISTER_FOR_NOTIFICATIONS);
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
private static Intent createNotificationIntent(Context context, String action) {
|
||||
return new Intent(action, /* URI */ null, context, NotificationObserver.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/* vim: set ts=4 sw=4 tw=78 et :
|
||||
* 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.b2gdroid;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.Context;
|
||||
import android.content.BroadcastReceiver;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
|
||||
/**
|
||||
* Proxy GeckoEvents from the Java UI Thread to other processes owned by this
|
||||
* app - deferring processing to those processes.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* // In remote process `context` given GeckoEventListener `listener`
|
||||
* RemoteGeckoEventProxy
|
||||
* .registerRemoteGeckoThreadListener(context, listener, "Message:Message");
|
||||
*
|
||||
* // In UI Thread (Main Activity `context`)
|
||||
* RemoteGeckoEventProxy mGeckoEventProxy = new RemoteGeckoEventProxy(context);
|
||||
* EventDispatcher
|
||||
* .getInstance()
|
||||
* .registerGeckoThreadListener(mGeckoEventProxy, "Message:Message");
|
||||
*/
|
||||
class RemoteGeckoEventProxy implements GeckoEventListener {
|
||||
|
||||
private static final String LOGTAG = "RemoteGeckoEventProxy";
|
||||
|
||||
private static final String RECEIVE_GECKO_EVENT = AppConstants.ANDROID_PACKAGE_NAME + ".RECEIVE_GECKO_EVENT";
|
||||
|
||||
private static final String EXTRA_DATA = "DATA";
|
||||
private static final String EXTRA_SUBJECT = "SUBJECT";
|
||||
|
||||
private Context mSenderContext;
|
||||
|
||||
public static void registerRemoteGeckoThreadListener(Context aContext, GeckoEventListener mListener, String... aMessages) {
|
||||
for (String message : aMessages) {
|
||||
IntentFilter intentFilter = new IntentFilter(RECEIVE_GECKO_EVENT + "." + message);
|
||||
aContext.registerReceiver(new GeckoEventProxyReceiver(mListener), intentFilter);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteGeckoEventProxy(Context aSenderContext) {
|
||||
mSenderContext = aSenderContext;
|
||||
}
|
||||
|
||||
public void handleMessage(String aEvent, JSONObject aMessage) {
|
||||
Intent broadcastIntent = createBroadcastIntent(aEvent, aMessage.toString());
|
||||
mSenderContext.sendBroadcast(broadcastIntent);
|
||||
}
|
||||
|
||||
private static Intent createBroadcastIntent(String aEvent, String aMessage) {
|
||||
Intent intent = new Intent(RECEIVE_GECKO_EVENT + "." + aEvent);
|
||||
intent.putExtra(EXTRA_SUBJECT, aEvent);
|
||||
intent.putExtra(EXTRA_DATA, aMessage);
|
||||
return intent;
|
||||
}
|
||||
|
||||
static class GeckoEventProxyReceiver extends BroadcastReceiver {
|
||||
|
||||
private GeckoEventListener mGeckoEventListener;
|
||||
|
||||
public GeckoEventProxyReceiver(GeckoEventListener aEventListener) {
|
||||
mGeckoEventListener = aEventListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context aContext, Intent aIntent) {
|
||||
String geckoEventSubject = aIntent.getStringExtra(EXTRA_SUBJECT);
|
||||
String geckoEventJSON = aIntent.getStringExtra(EXTRA_DATA);
|
||||
|
||||
// Unwrap the message data and invoke the GeckoEventListener
|
||||
JSONObject obj;
|
||||
try {
|
||||
obj = new JSONObject(geckoEventJSON);
|
||||
obj = obj.getJSONObject("value");
|
||||
} catch(Exception ex) {
|
||||
Log.wtf(RemoteGeckoEventProxy.LOGTAG, "Could not decode JSON payload for message: " + geckoEventSubject);
|
||||
return;
|
||||
}
|
||||
|
||||
mGeckoEventListener.handleMessage(geckoEventSubject, obj);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,11 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/SystemAppProxy.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "settings",
|
||||
"@mozilla.org/settingsService;1",
|
||||
@ -36,8 +41,11 @@ this.MessagesBridge = {
|
||||
Services.obs.addObserver(this.onAndroidMessage, "Android:Launcher", false);
|
||||
Services.obs.addObserver(this.onAndroidSetting, "Android:Setting", false);
|
||||
Services.obs.addObserver(this.onSettingChange, "mozsettings-changed", false);
|
||||
Services.obs.addObserver(this.onAndroidNotification, "Android:Notification", false);
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
|
||||
SystemAppProxy.addEventListener("mozContentNotificationEvent", this);
|
||||
|
||||
// Send a request to get the device's IMEI.
|
||||
Messaging.sendRequestForResult({ type: "Android:GetIMEI" })
|
||||
.then(aData => {
|
||||
@ -47,6 +55,38 @@ this.MessagesBridge = {
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
let detail = evt.detail;
|
||||
|
||||
switch(detail.type) {
|
||||
case "desktop-notification-click":
|
||||
debug("Sending Android:NotificationOpened");
|
||||
Messaging.sendRequest({ type: "Android:NotificationOpened", value: { id: detail.id }});
|
||||
break;
|
||||
case "desktop-notification-close":
|
||||
// On receipt of a notification close, send the id to the Android layer
|
||||
// so it can be removed from the Android NotificationManager.
|
||||
debug("Sending Android:NotificationClosed");
|
||||
Messaging.sendRequest({ type: "Android:NotificationClosed", value: { id: detail.id }});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onAndroidNotification: function(aSubject, aTopic, aData) {
|
||||
let data = JSON.parse(aData);
|
||||
debug("Got android notification: " + data._action);
|
||||
switch(data._action) {
|
||||
case "post":
|
||||
debug("showNotification(id=" + data.id + ")");
|
||||
showNotification(data);
|
||||
break;
|
||||
case "remove":
|
||||
debug("removeNotification(id=" + data.id + ")");
|
||||
removeNotification(data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onAndroidMessage: function(aSubject, aTopic, aData) {
|
||||
let data = JSON.parse(aData);
|
||||
debug(`Got Android:Launcher message ${data.action}`);
|
||||
@ -122,9 +162,50 @@ this.MessagesBridge = {
|
||||
Services.obs.removeObserver(this.onAndroidMessage, "Android:Launcher");
|
||||
Services.obs.removeObserver(this.onAndroidSetting, "Android:Setting");
|
||||
Services.obs.removeObserver(this.onSettingChange, "mozsettings-changed");
|
||||
Services.obs.removeObserver(this.onAndroidNotification, "Android:Notification");
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeNotification(aDetail) {
|
||||
// Remove the notification
|
||||
SystemAppProxy._sendCustomEvent("mozChromeNotificationEvent", {
|
||||
type: "desktop-notification-close",
|
||||
id: aDetail.id
|
||||
});
|
||||
}
|
||||
|
||||
function showNotification(aDetail) {
|
||||
const manifestURL = aDetail.manifestURL;
|
||||
|
||||
function send(appName) {
|
||||
aDetail.type = "desktop-notification";
|
||||
aDetail.appName = appName;
|
||||
|
||||
SystemAppProxy._sendCustomEvent("mozChromeNotificationEvent", aDetail);
|
||||
}
|
||||
|
||||
if (!manifestURL|| !manifestURL.length) {
|
||||
send(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a manifest URL, get the app title from the manifest
|
||||
// to prevent spoofing.
|
||||
appsService.getManifestFor(manifestURL).then((manifest) => {
|
||||
let app = appsService.getAppByManifestURL(manifestURL);
|
||||
|
||||
// Sometimes an android notification may be created by a service rather
|
||||
// than an app. In this case, don't use the appName.
|
||||
if (!app) {
|
||||
send(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let helper = new ManifestHelper(manifest, app.origin, manifestURL);
|
||||
send(helper.name);
|
||||
});
|
||||
}
|
||||
|
||||
this.MessagesBridge.init();
|
||||
|
Loading…
Reference in New Issue
Block a user