[chrome-gnome-shell/feature/connector-update-check: 17/18] notifications: implemented native Gio.Notification support
- From: Yuri Konotopov <ykonotopov src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [chrome-gnome-shell/feature/connector-update-check: 17/18] notifications: implemented native Gio.Notification support
- Date: Sun, 18 Dec 2016 21:04:03 +0000 (UTC)
commit 4fc72424371c61292981ddb35445fcf07b12376d
Author: Yuri Konotopov <ykonotopov gnome org>
Date: Mon Dec 19 00:59:05 2016 +0400
notifications: implemented native Gio.Notification support
connector/chrome-gnome-shell.py | 317 +++++++++++++++++------
connector/org.gnome.ChromeGnomeShell.desktop | 6 +
connector/org.gnome.ChromeGnomeShell.service.in | 3 +
extension/extension.js | 134 ++++++----
extension/include/constants.js | 2 +
extension/include/gsc.js | 121 ++++++----
extension/include/notifications.js | 122 +++++++--
extension/include/sync.js | 38 +++-
extension/include/update.js | 64 ++++-
9 files changed, 588 insertions(+), 219 deletions(-)
---
diff --git a/connector/chrome-gnome-shell.py b/connector/chrome-gnome-shell.py
index 9e7815e..6d1924c 100755
--- a/connector/chrome-gnome-shell.py
+++ b/connector/chrome-gnome-shell.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
'''
GNOME Shell integration for Chrome
@@ -12,7 +13,7 @@
from __future__ import unicode_literals
from __future__ import print_function
-from gi.repository import GLib, Gio
+from gi.repository import GLib, Gio, GObject
import json
import os
import re
@@ -47,68 +48,147 @@ def logError(message):
class ChromeGNOMEShell(Gio.Application):
- def __init__(self):
+ def __init__(self, run_as_service):
Gio.Application.__init__(self,
- application_id='org.gnome.chrome-gnome-shell-%s' % os.getppid(),
- flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
+ application_id='org.gnome.ChromeGnomeShell',
+ flags=Gio.ApplicationFlags.IS_SERVICE if run_as_service
+ else Gio.ApplicationFlags.IS_LAUNCHER | Gio.ApplicationFlags.HANDLES_OPEN
+ )
self.shellAppearedId = None
self.shellSignalId = None
- self.proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
- Gio.DBusProxyFlags.NONE,
- None,
- 'org.gnome.Shell',
- '/org/gnome/Shell',
- 'org.gnome.Shell.Extensions',
- None)
# Set custom exception hook
sys.excepthook = self.default_exception_hook
+ self.register()
+
+ if not run_as_service:
+ self.shell_proxy = Gio.DBusProxy.new_sync(self.get_dbus_connection(),
+ Gio.DBusProxyFlags.NONE,
+ None,
+ 'org.gnome.Shell',
+ '/org/gnome/Shell',
+ 'org.gnome.Shell.Extensions',
+ None)
+
+ self.get_dbus_connection().signal_subscribe(
+ self.get_application_id(),
+ self.get_application_id(),
+ None,
+ "/org/gnome/ChromeGnomeShell",
+ None,
+ Gio.DBusSignalFlags.NONE,
+ self.on_dbus_signal,
+ None
+ )
- def default_exception_hook(self, type, value, tb):
- logError("Uncaught exception of type %s occured" % type)
- traceback.print_tb(tb)
- logError("Exception: %s" % value)
+ stdin = GLib.IOChannel.unix_new(sys.stdin.fileno())
+ stdin.set_encoding(None)
+ stdin.set_buffered(False)
- self.release()
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, self.on_input, None)
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.HUP, self.on_hup, None)
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.ERR, self.on_hup, None)
+ else:
+ self.add_simple_action("create-notification", self.on_create_notification, 'a{sv}')
+ self.add_simple_action("on-notification-clicked", self.on_notification_clicked, 's')
+ self.add_simple_action("on-notification-action", self.on_notification_action, '(si)')
+
+ GLib.timeout_add_seconds(5 * 60, self.on_service_timeout, None)
- def do_startup(self):
- debug('Startup')
- Gio.Application.do_startup(self)
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.on_sigint, None)
+ if not run_as_service or not self.get_is_remote():
+ self.hold()
- def do_shutdown(self):
- debug('Shutdown')
- Gio.Application.do_shutdown(self)
+ # Is there any way to hook this to shutdown?
+ def cleanup(self):
+ debug('Cleanup')
if self.shellAppearedId:
Gio.bus_unwatch_name(self.shellAppearedId)
if self.shellSignalId:
- self.proxy.disconnect(self.shellSignalId)
+ self.get_dbus_connection().signal_unsubscribe(self.shellSignalId)
+ def default_exception_hook(self, exception_type, value, tb):
+ logError("Uncaught exception of type %s occured" % exception_type)
+ traceback.print_tb(tb)
+ logError("Exception: %s" % value)
- def do_activate(self, app):
- debug('Activate')
- Gio.Application.do_activate(self)
+ self.release()
+ def add_simple_action(self, name, callback, parameter_type):
+ action = Gio.SimpleAction.new(
+ name,
+ GLib.VariantType.new(parameter_type) if parameter_type is not None else None
+ )
+ action.connect('activate', callback)
+ self.add_action(action)
def do_local_command_line(self, arguments):
- stdin = GLib.IOChannel.unix_new(sys.stdin.fileno())
- stdin.set_encoding(None)
- stdin.set_buffered(False)
-
- GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.on_sigint, None)
- GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, self.on_input, None)
- GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.HUP, self.on_hup, None)
- GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.ERR, self.on_hup, None)
-
- self.hold()
-
- return (True, None, 0)
+ if '--gapplication-service' in arguments:
+ arguments.remove('--gapplication-service')
+
+ return Gio.Application.do_local_command_line(self, arguments)
+
+ # Service events
+ def on_create_notification(self, object, request):
+ debug('On create notification')
+
+ request = request.unpack()
+
+ notification = Gio.Notification.new(request['title'])
+ notification.set_body(request['message'])
+ notification.set_priority(Gio.NotificationPriority.NORMAL)
+ notification.set_default_action_and_target(
+ "app.on-notification-clicked",
+ GLib.Variant.new_string(request['name'])
+ )
+
+ if 'buttons' in request:
+ for button_id, button in enumerate(request['buttons']):
+ notification.add_button_with_target(
+ button['title'],
+ "app.on-notification-action",
+ GLib.Variant.new_tuple(
+ GLib.Variant.new_string(request['name']),
+ GLib.Variant.new_int32(button_id)
+ )
+ )
+
+ self.send_notification(request['name'], notification)
+
+ def on_notification_action(self, notification, parameters):
+ debug('Notification %s action: %s' % parameters.unpack())
+
+ self.get_dbus_connection().emit_signal(
+ None,
+ self.get_dbus_object_path(),
+ self.get_application_id(),
+ "NotificationAction",
+ parameters
+ )
+
+ def on_notification_clicked(self, notification, notification_name):
+ debug('Notification %s clicked' % (notification_name))
+
+ self.get_dbus_connection().emit_signal(
+ None,
+ self.get_dbus_object_path(),
+ self.get_application_id(),
+ "NotificationClicked",
+ GLib.Variant.new_tuple(notification_name)
+ )
+
+ def on_service_timeout(self, data):
+ debug('On service timeout')
+ self.release()
+ return False
+ # Native messaging events
def on_input(self, source, condition, data):
debug('On input')
text_length_bytes = source.read(MESSAGE_LENGTH_SIZE)
@@ -132,29 +212,63 @@ class ChromeGNOMEShell(Gio.Application):
self.process_request(request)
+ return True
+
+ def on_dbus_signal(self, connection, sender_name, object_path, interface_name, signal_name, parameters,
user_data):
+ debug('Signal %s from %s' % (signal_name, interface_name))
- def on_shell_signal(self, d_bus_proxy, sender_name, signal_name, parameters):
- if signal_name == 'ExtensionStatusChanged':
- debug('Signal: to %s' % signal_name)
+ if interface_name == "org.gnome.Shell.Extensions" and signal_name == 'ExtensionStatusChanged':
self.send_message({'signal': signal_name, 'parameters': parameters.unpack()})
- debug('Signal: from %s' % signal_name)
+ elif interface_name == self.get_application_id():
+ if signal_name == 'NotificationAction':
+ notification_name, button_id = parameters.unpack()
+ self.send_message({
+ 'signal': "NotificationAction",
+ 'name': notification_name,
+ 'button_id': button_id
+ })
+ elif signal_name == 'NotificationClicked':
+ (notification_name,) = parameters.unpack()
+
+ self.send_message({
+ 'signal': "NotificationClicked",
+ 'name': notification_name
+ })
def on_shell_appeared(self, connection, name, name_owner):
debug('Signal: to %s' % name)
self.send_message({'signal': name})
debug('Signal: from %s' % name)
-
+ # General events
def on_hup(self, source, condition, data):
debug('On hup: %s' % str(condition))
self.release()
+ return False
def on_sigint(self, data):
debug('On sigint')
self.release()
+ return False
+
+ # Helpers
+ def dbus_call_response(self, method, parameters, resultProperty):
+ try:
+ result = self.shell_proxy.call_sync(method,
+ parameters,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ None)
+
+ self.send_message({'success': True, resultProperty: result.unpack()[0]})
+ except GLib.GError as e:
+ self.send_error(e.message)
+
+ def send_error(self, message):
+ self.send_message({'success': False, 'message': message})
# Helper function that sends a message to the webapp.
def send_message(self, response):
@@ -178,30 +292,51 @@ class ChromeGNOMEShell(Gio.Application):
logError('IOError occured: %s' % e.strerror)
sys.exit(1)
+ def get_variant(self, data, basic_type = False):
+ if isinstance(data, ("".__class__, u"".__class__)) or type(data) is int or basic_type:
+ if isinstance(data, ("".__class__, u"".__class__)):
+ return GLib.Variant.new_string(data)
+ elif type(data) is int:
+ return GLib.Variant.new_int32(data)
+ else:
+ raise Exception("Unknown basic data type: %s, %s" % (type(data), str(data)))
+ elif type(data) is list:
+ variant_builder = GLib.VariantBuilder.new(GLib.VariantType.new('av'))
- def send_error(self, message):
- self.send_message({'success': False, 'message': message})
+ for value in data:
+ variant_builder.add_value(GLib.Variant.new_variant(self.get_variant(value)))
+ return variant_builder.end()
- def dbus_call_response(self, method, parameters, resultProperty):
- try:
- result = self.proxy.call_sync(method,
- parameters,
- Gio.DBusCallFlags.NONE,
- -1,
- None)
+ elif type(data) is dict:
+ variant_builder = GLib.VariantBuilder.new(GLib.VariantType.new('a{sv}'))
- self.send_message({'success': True, resultProperty: result.unpack()[0]})
- except GLib.GError as e:
- self.send_error(e.message)
+ for key in data:
+ if data[key] is None:
+ continue
+
+ if sys.version < '3':
+ # noinspection PyUnresolvedReferences
+ key_string = unicode(key)
+ else:
+ key_string = str(key)
+ variant_builder.add_value(
+ GLib.Variant.new_dict_entry(
+ self.get_variant(key_string, True),
GLib.Variant.new_variant(self.get_variant(data[key]))
+ )
+ )
+
+ return variant_builder.end()
+ else:
+ raise Exception("Unknown data type: %s" % type(data))
def process_request(self, request):
debug('Execute: to %s' % request['execute'])
if request['execute'] == 'initialize':
settings = Gio.Settings.new(SHELL_SCHEMA)
- shellVersion = self.proxy.get_cached_property("ShellVersion")
+ shellVersion = self.shell_proxy.get_cached_property("ShellVersion")
if EXTENSION_DISABLE_VERSION_CHECK_KEY in settings.keys():
disableVersionCheck = settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY)
else:
@@ -214,25 +349,42 @@ class ChromeGNOMEShell(Gio.Application):
'connectorVersion': CONNECTOR_VERSION,
'shellVersion': shellVersion.unpack(),
'versionValidationEnabled': not disableVersionCheck
- }
+ },
+ 'supports': [
+ 'notifications',
+ 'update-check'
+ ]
}
)
elif request['execute'] == 'subscribeSignals':
if not self.shellAppearedId:
- self.shellAppearedId = Gio.bus_watch_name(Gio.BusType.SESSION,
- 'org.gnome.Shell',
- Gio.BusNameWatcherFlags.NONE,
- self.on_shell_appeared,
- None)
+ self.shellAppearedId = Gio.bus_watch_name_on_connection(
+ self.get_dbus_connection(),
+ 'org.gnome.Shell',
+ Gio.BusNameWatcherFlags.NONE,
+ self.on_shell_appeared,
+ None
+ )
if not self.shellSignalId:
- self.shellSignalId = self.proxy.connect('g-signal', self.on_shell_signal)
+ self.shellSignalId = self.get_dbus_connection().signal_subscribe(
+ "org.gnome.Shell",
+ "org.gnome.Shell.Extensions",
+ "ExtensionStatusChanged",
+ "/org/gnome/Shell",
+ None,
+ Gio.DBusSignalFlags.NONE,
+ self.on_dbus_signal,
+ None
+ )
elif request['execute'] == 'installExtension':
- self.dbus_call_response("InstallRemoteExtension",
- GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
- "status")
+ self.dbus_call_response(
+ "InstallRemoteExtension",
+ GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
+ "status"
+ )
elif request['execute'] == 'listExtensions':
self.dbus_call_response("ListExtensions", None, "extensions")
@@ -291,8 +443,20 @@ class ChromeGNOMEShell(Gio.Application):
self.check_update(update_url)
-
-
+ elif request['execute'] == 'createNotification':
+ Gio.DBusActionGroup.get(
+ app.get_dbus_connection(),
+ app.get_application_id(),
+ app.get_dbus_object_path()
+ ).activate_action('create-notification', self.get_variant({
+ 'name': request['name'],
+ 'title': request['options']['title'],
+ 'message': request['options']['message'],
+ 'buttons' : request['options']['buttons']
+ }))
+
+ elif request['execute'] == 'removeNotification':
+ self.withdraw_notification(request['name'])
debug('Execute: from %s' % request['execute'])
@@ -307,7 +471,7 @@ class ChromeGNOMEShell(Gio.Application):
if extensions:
http_request = {
- 'shell_version': self.proxy.get_cached_property("ShellVersion").unpack(),
+ 'shell_version': self.shell_proxy.get_cached_property("ShellVersion").unpack(),
'installed': {}
}
@@ -342,15 +506,22 @@ class ChromeGNOMEShell(Gio.Application):
requests.ConnectionError, requests.HTTPError, requests.Timeout,
requests.TooManyRedirects, requests.RequestException, ValueError
) as ex:
- logError('Unable to check extensions updates: %s' % (str(ex.message) if ('message' in ex)
else str(ex)))
- self.send_message({'success': False, 'message': str(ex.message) if ('message' in ex) else
str(ex)})
+ error_message = str(ex.message) if ('message' in ex) else str(ex)
+ logError('Unable to check extensions updates: %s' % error_message)
+
+ request_url = ex.response.url if ex.response is not None else ex.request.url
+ if request_url:
+ url_parameters = request_url.replace(update_url, "")
+ error_message = error_message.replace(url_parameters, "…")
+
+ self.send_message({'success': False, 'message': error_message})
if __name__ == '__main__':
debug('Main. Use CTRL+D to quit.')
+ app = ChromeGNOMEShell('--gapplication-service' in sys.argv)
- app = ChromeGNOMEShell()
- app.register()
app.run(sys.argv)
+ app.cleanup()
debug('Quit')
diff --git a/connector/org.gnome.ChromeGnomeShell.desktop b/connector/org.gnome.ChromeGnomeShell.desktop
new file mode 100644
index 0000000..658505f
--- /dev/null
+++ b/connector/org.gnome.ChromeGnomeShell.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+Encoding=UTF-8
+Name=GNOME Shell integration
+Comment=Provides integration with GNOME Shell and the corresponding extensions repository
https://extensions.gnome.org.
+DBusActivatable=true
diff --git a/connector/org.gnome.ChromeGnomeShell.service.in b/connector/org.gnome.ChromeGnomeShell.service.in
new file mode 100644
index 0000000..f22fec4
--- /dev/null
+++ b/connector/org.gnome.ChromeGnomeShell.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.ChromeGnomeShell
+Exec=${CMAKE_INSTALL_FULL_BINDIR}/chrome-gnome-shell --gapplication-service
diff --git a/extension/extension.js b/extension/extension.js
index 5c8d87b..ca781cc 100644
--- a/extension/extension.js
+++ b/extension/extension.js
@@ -74,78 +74,104 @@ var port = chrome.runtime.connectNative(NATIVE_HOST);
* Native host messaging events handler.
*/
port.onMessage.addListener(function (message) {
- if (message && message.signal && [SIGNAL_EXTENSION_CHANGED,
SIGNAL_SHELL_APPEARED].indexOf(message.signal) !== -1)
+ if (message && message.signal)
{
- /*
- * Skip duplicate events. This is happens eg when extension is installed.
- */
- if((new Date().getTime()) - lastPortMessage.date < 1000 && GSC.isSignalsEqual(message,
lastPortMessage.message))
+ if([SIGNAL_EXTENSION_CHANGED, SIGNAL_SHELL_APPEARED].indexOf(message.signal) !== -1)
{
- lastPortMessage.date = new Date().getTime();
- return;
- }
-
- /*
- * Send events to opened extensions.gnome.org tabs
- */
- chrome.tabs.query({
- url: EXTENSIONS_WEBSITE + '*'
- },
- function (tabs) {
- for (k in tabs)
+ /*
+ * Skip duplicate events. This is happens eg when extension is installed.
+ */
+ if ((new Date().getTime()) - lastPortMessage.date < 1000 &&
GSC.isSignalsEqual(message, lastPortMessage.message))
{
- chrome.tabs.sendMessage(tabs[k].id, message);
+ lastPortMessage.date = new Date().getTime();
+ return;
}
- });
- /*
- * Route message to Options page.
- */
- chrome.runtime.sendMessage(GS_CHROME_ID, message);
- if(message.signal === SIGNAL_EXTENSION_CHANGED)
- {
/*
- * GNOME Shell sends 2 events when extension is uninstalled:
- * "disabled" event and then "uninstalled" event.
- * Let's delay any "disabled" event and drop it if
- * "uninstalled" event received within 1,5 secs.
+ * Send events to opened extensions.gnome.org tabs
*/
- if(message.parameters[EXTENSION_CHANGED_STATE] === EXTENSION_STATE.DISABLED)
+ chrome.tabs.query({
+ url: EXTENSIONS_WEBSITE + '*'
+ },
+ function (tabs) {
+ for (k in tabs)
+ {
+ chrome.tabs.sendMessage(tabs[k].id, message);
+ }
+ });
+
+ /*
+ * Route message to Options page.
+ */
+ chrome.runtime.sendMessage(GS_CHROME_ID, message);
+ if (message.signal === SIGNAL_EXTENSION_CHANGED)
{
- disabledExtensionTimeout = setTimeout(function() {
+ /*
+ * GNOME Shell sends 2 events when extension is uninstalled:
+ * "disabled" event and then "uninstalled" event.
+ * Let's delay any "disabled" event and drop it if
+ * "uninstalled" event received within 1,5 secs.
+ */
+ if (message.parameters[EXTENSION_CHANGED_STATE] === EXTENSION_STATE.DISABLED)
+ {
+ disabledExtensionTimeout = setTimeout(function () {
+ disabledExtensionTimeout = null;
+ GSC.sync.onExtensionChanged(message);
+ }, 1500);
+ }
+ else if (
+ disabledExtensionTimeout &&
+ message.parameters[EXTENSION_CHANGED_STATE] ===
EXTENSION_STATE.UNINSTALLED &&
+ lastPortMessage.message.signal === SIGNAL_EXTENSION_CHANGED &&
+ lastPortMessage.message.parameters[EXTENSION_CHANGED_UUID] ===
message.parameters[EXTENSION_CHANGED_UUID] &&
+ lastPortMessage.message.parameters[EXTENSION_CHANGED_STATE] ===
EXTENSION_STATE.DISABLED
+ )
+ {
+ clearTimeout(disabledExtensionTimeout);
disabledExtensionTimeout = null;
GSC.sync.onExtensionChanged(message);
- }, 1500);
- }
- else if(
- disabledExtensionTimeout &&
- message.parameters[EXTENSION_CHANGED_STATE] === EXTENSION_STATE.UNINSTALLED &&
- lastPortMessage.message.signal === SIGNAL_EXTENSION_CHANGED &&
- lastPortMessage.message.parameters[EXTENSION_CHANGED_UUID] ===
message.parameters[EXTENSION_CHANGED_UUID] &&
- lastPortMessage.message.parameters[EXTENSION_CHANGED_STATE] ===
EXTENSION_STATE.DISABLED
- )
- {
- clearTimeout(disabledExtensionTimeout);
- disabledExtensionTimeout = null;
- GSC.sync.onExtensionChanged(message);
- }
- else
- {
- GSC.sync.onExtensionChanged(message);
+ }
+ else
+ {
+ GSC.sync.onExtensionChanged(message);
+ }
}
- }
- lastPortMessage = {
- message: message,
- date: new Date().getTime()
- };
+ lastPortMessage = {
+ message: message,
+ date: new Date().getTime()
+ };
+ }
+ else if([SIGNAL_NOTIFICATION_ACTION, SIGNAL_NOTIFICATION_CLICKED].indexOf(message.signal) !=
-1)
+ {
+ chrome.runtime.sendMessage(message);
+ }
}
});
/*
- * Subsctibe to GNOME Shell signals
+ * Subscribe to GNOME Shell signals
*/
port.postMessage({execute: 'subscribeSignals'});
+chrome.runtime.onMessage.addListener(
+ function (request, sender, sendResponse) {
+ if(
+ sender.id && sender.id === GS_CHROME_ID &&
+ request && request.execute)
+ {
+ switch (request.execute)
+ {
+ case 'createNotification':
+ port.postMessage(request);
+ break;
+ case 'removeNotification':
+ port.postMessage(request);
+ break;
+ }
+ }
+ }
+);
+
chrome.runtime.getPlatformInfo(function(info) {
if (PLATFORMS_WHITELIST.indexOf(info.os) !== -1)
{
diff --git a/extension/include/constants.js b/extension/include/constants.js
index 96ae2ab..1b14dd6 100644
--- a/extension/include/constants.js
+++ b/extension/include/constants.js
@@ -22,6 +22,8 @@ MESSAGE_NEXT_UPDATE_CHANGED = 'gs-next-update-changed';
MESSAGE_SYNC_FROM_REMOTE = 'gs-sync-from-remote';
SIGNAL_EXTENSION_CHANGED = 'ExtensionStatusChanged';
+SIGNAL_NOTIFICATION_ACTION = 'NotificationAction';
+SIGNAL_NOTIFICATION_CLICKED = 'NotificationClicked';
SIGNAL_SHELL_APPEARED = 'org.gnome.Shell';
EXTENSION_CHANGED_UUID = 0;
diff --git a/extension/include/gsc.js b/extension/include/gsc.js
index 8f71ae0..b5d64d7 100644
--- a/extension/include/gsc.js
+++ b/extension/include/gsc.js
@@ -23,59 +23,78 @@ GSC = (function() {
});
ready.catch(function() {});
+ var onInitialize = new Promise((resolve, reject) => {
+ sendNativeRequest({ execute: "initialize" }, (response) => {
+ if(response && response.success)
+ {
+ resolve(response);
+ }
+ else
+ {
+ reject(response);
+ }
+ });
+ });
+
+ function sendNativeRequest(request, sendResponse) {
+ ready.then(function () {
+ if (sendResponse)
+ {
+ chrome.runtime.sendNativeMessage(
+ NATIVE_HOST,
+ request,
+ function (response) {
+ if (response)
+ {
+ sendResponse(response);
+ }
+ else
+ {
+ var message = m('no_host_connector');
+ if (
+ chrome.runtime.lastError &&
+ chrome.runtime.lastError.message &&
+
chrome.runtime.lastError.message.indexOf("host not found") === -1
+ )
+ {
+ // Some error occured. Show to user
+ message = chrome.runtime.lastError.message;
+ }
+
+ sendResponse({
+ success: false,
+ message: message
+ });
+ }
+ }
+ );
+ }
+ else
+ {
+ chrome.runtime.sendNativeMessage(NATIVE_HOST, request);
+ }
+ }, function () {
+ if (sendResponse)
+ {
+ sendResponse({
+ success: false,
+ message: m('platform_not_supported')
+ });
+ }
+ });
+ }
+
+ function isSupported(feature, response) {
+ return response.supports && response.supports.indexOf(feature) !== -1;
+ }
+
return {
// https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines
isUUID: function(uuid) {
return uuid && uuid.match('^[-a-zA-Z0-9@._]+$');
},
- sendNativeRequest: function(request, sendResponse) {
- ready.then(function() {
- if(sendResponse)
- {
- chrome.runtime.sendNativeMessage(
- NATIVE_HOST,
- request,
- function (response) {
- if (response)
- {
- sendResponse(response);
- }
- else
- {
- var message = m('no_host_connector');
- if(
- chrome.runtime.lastError &&
- chrome.runtime.lastError.message &&
-
chrome.runtime.lastError.message.indexOf("host not found") === -1
- )
- {
- // Some error occured. Show to user
- message =
chrome.runtime.lastError.message;
- }
-
- sendResponse({
- success: false,
- message: message
- });
- }
- }
- );
- }
- else
- {
- chrome.runtime.sendNativeMessage(NATIVE_HOST, request);
- }
- }, function() {
- if(sendResponse)
- {
- sendResponse({
- success: false,
- message: m('platform_not_supported')
- });
- }
- });
- },
+ sendNativeRequest: sendNativeRequest,
isSignalsEqual: function(newSignal, oldSignal) {
if(!oldSignal || !newSignal)
@@ -106,6 +125,14 @@ GSC = (function() {
}
return true;
+ },
+
+ onInitialize: function() {
+ return onInitialize;
+ },
+
+ nativeNotificationsSupported: function (response) {
+ return isSupported('notifications', response);
}
};
})();
diff --git a/extension/include/notifications.js b/extension/include/notifications.js
index 2c12141..7913300 100644
--- a/extension/include/notifications.js
+++ b/extension/include/notifications.js
@@ -9,6 +9,47 @@
*/
GSC.notifications = (function($) {
+ var DEFAULT_NOTIFICATION_OPTIONS = {
+ type: chrome.notifications.TemplateType.BASIC,
+ iconUrl: 'icons/GnomeLogo-128.png',
+ title: m('gs_chrome'),
+ buttons: [
+ {title: m('close')}
+ ],
+ priority: 2,
+ isClickable: true,
+ requireInteraction: true
+ };
+
+ function remove_list(options) {
+ if(options.items)
+ {
+ var items = [];
+ for (k in options.items)
+ {
+ if (options.items.hasOwnProperty(k))
+ {
+ items.push(options.items[k].title + ' ' + options.items[k].message);
+ }
+ }
+
+ if(options.message && items)
+ {
+ options.message += "\n";
+ }
+
+ options.message += items.join("\n");
+
+ options.type = chrome.notifications.TemplateType.BASIC;
+ delete options.items;
+ }
+
+ return options;
+ }
+
+ /*
+ @Deprecated: remove browser notifications in version 9
+ */
var browser = (function() {
function init() {
chrome.runtime.onStartup.addListener(function() {
@@ -39,16 +80,7 @@ GSC.notifications = (function($) {
}, function (items) {
var notifications = items.notifications;
- notifications[name] = $.extend({
- type: chrome.notifications.TemplateType.BASIC,
- iconUrl: 'icons/GnomeLogo-128.png',
- title: 'GNOME Shell integration',
- buttons: [
- {title: m('close')}
- ],
- priority: 2,
- isClickable: true
- }, options);
+ notifications[name] = $.extend(DEFAULT_NOTIFICATION_OPTIONS, options);
_create(name, notifications[name], function (notificationId) {
chrome.storage.local.set({
@@ -67,18 +99,7 @@ GSC.notifications = (function($) {
delete options.buttons;
if(options.type === chrome.notifications.TemplateType.LIST)
{
- var items = [];
- for(k in options.items)
- {
- if(options.items.hasOwnProperty(k))
- {
- items.push(options.items[k].title + ' ' +
options.items[k].message);
- }
- }
- options.message += "\n" + items.join("\n");
-
- options.type = chrome.notifications.TemplateType.BASIC;
- delete options.items;
+ options = remove_list(options);
}
}
@@ -136,7 +157,30 @@ GSC.notifications = (function($) {
});
}
- init();
+ return {
+ create: create,
+ remove: remove,
+ init: init
+ };
+ })();
+
+ var native = (function() {
+ function create(name, options) {
+ options = remove_list(options);
+
+ chrome.runtime.sendMessage({
+ execute: 'createNotification',
+ name: name,
+ options: $.extend(DEFAULT_NOTIFICATION_OPTIONS, options)
+ });
+ }
+
+ function remove(notificationId) {
+ chrome.runtime.sendMessage({
+ execute: 'removeNotification',
+ name: notificationId
+ });
+ }
return {
create: create,
@@ -144,9 +188,37 @@ GSC.notifications = (function($) {
};
})();
+ GSC.onInitialize().then(response => {
+ if (!GSC.nativeNotificationsSupported(response))
+ {
+ browser.init();
+ }
+ });
return {
- create: browser.create,
- remove: browser.remove
+ create: function() {
+ GSC.onInitialize().then(response => {
+ if(GSC.nativeNotificationsSupported(response))
+ {
+ native.create.apply(this, arguments);
+ }
+ else
+ {
+ browser.create.apply(this, arguments);
+ }
+ });
+ },
+ remove: function() {
+ GSC.onInitialize().then(response => {
+ if(GSC.nativeNotificationsSupported(response))
+ {
+ native.remove.apply(this, arguments);
+ }
+ else
+ {
+ browser.remove.apply(this, arguments);
+ }
+ });
+ }
};
})(jQuery);
diff --git a/extension/include/sync.js b/extension/include/sync.js
index 80be629..95a1e1f 100644
--- a/extension/include/sync.js
+++ b/extension/include/sync.js
@@ -22,6 +22,15 @@ GSC.sync = (function($) {
return;
}
+ function onNotificationAction(notificationId, buttonIndex) {
+ if (notificationId !== NOTIFICATION_SYNC_FAILED)
+ {
+ return;
+ }
+
+ GSC.notifications.remove(notificationId);
+ }
+
onSyncFromRemote();
chrome.storage.onChanged.addListener(function(changes, areaName) {
if(areaName === 'sync' && changes.extensions)
@@ -42,11 +51,30 @@ GSC.sync = (function($) {
}
);
- chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
- if (notificationId !== NOTIFICATION_SYNC_FAILED)
- return;
-
- GSC.notifications.remove(notificationId);
+ GSC.onInitialize().then(response => {
+ /*
+ @Deprecated: remove browser notifications in version 9
+ */
+ if (!GSC.nativeNotificationsSupported(response))
+ {
+ chrome.notifications.onButtonClicked.addListener(onNotificationAction);
+ }
+ else
+ {
+ chrome.runtime.onMessage.addListener(
+ function (request, sender, sendResponse) {
+ if(
+ sender.id && sender.id === GS_CHROME_ID &&
+ request && request.signal)
+ {
+ if(request.signal == SIGNAL_NOTIFICATION_ACTION)
+ {
+ onNotificationAction(request.name,
request.button_id);
+ }
+ }
+ }
+ );
+ }
});
}
diff --git a/extension/include/update.js b/extension/include/update.js
index 5ac37c4..922cf74 100644
--- a/extension/include/update.js
+++ b/extension/include/update.js
@@ -180,6 +180,28 @@ GSC.update = (function($) {
}
function init() {
+ function onNotificationAction(notificationId, buttonIndex) {
+ if ($.inArray(notificationId, [NOTIFICATION_UPDATE_AVAILABLE,
NOTIFICATION_UPDATE_CHECK_FAILED]) === -1)
+ return;
+
+ if (notificationId === NOTIFICATION_UPDATE_CHECK_FAILED && buttonIndex == 0)
+ {
+ check();
+ }
+
+ GSC.notifications.remove(notificationId);
+ }
+
+ function onNotificationClicked(notificationId) {
+ if (notificationId === NOTIFICATION_UPDATE_AVAILABLE)
+ {
+ chrome.tabs.create({
+ url: EXTENSIONS_WEBSITE + 'local/',
+ active: true
+ });
+ }
+ }
+
chrome.alarms.onAlarm.addListener(function (alarm) {
if (alarm.name === ALARM_UPDATE_CHECK)
{
@@ -198,26 +220,38 @@ GSC.update = (function($) {
}
});
- chrome.notifications.onClicked.addListener(function (notificationId) {
- if (notificationId === NOTIFICATION_UPDATE_AVAILABLE)
+ GSC.onInitialize().then(response => {
+ /*
+ @Deprecated: remove browser notifications in version 9
+ */
+ if (!GSC.nativeNotificationsSupported(response))
{
- chrome.tabs.create({
- url: EXTENSIONS_WEBSITE + 'local/',
- active: true
+ chrome.notifications.onClicked.addListener(function (notificationId) {
+ onNotificationClicked(notificationId);
});
- }
- });
- chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
- if ($.inArray(notificationId, [NOTIFICATION_UPDATE_AVAILABLE,
NOTIFICATION_UPDATE_CHECK_FAILED]) === -1)
- return;
-
- if (notificationId === NOTIFICATION_UPDATE_CHECK_FAILED && buttonIndex === 0)
+ chrome.notifications.onButtonClicked.addListener(onNotificationAction);
+ }
+ else
{
- check();
+ chrome.runtime.onMessage.addListener(
+ function (request, sender, sendResponse) {
+ if(
+ sender.id && sender.id === GS_CHROME_ID &&
+ request && request.signal)
+ {
+ if(request.signal == SIGNAL_NOTIFICATION_ACTION)
+ {
+ onNotificationAction(request.name,
request.button_id);
+ }
+ else if(request.signal == SIGNAL_NOTIFICATION_CLICKED)
+ {
+ onNotificationClicked(request.name);
+ }
+ }
+ }
+ );
}
-
- GSC.notifications.remove(notificationId);
});
chrome.storage.onChanged.addListener(function (changes, areaName) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]