[gnome-shell/T29763: 132/249] appActivation: Add launcher for 'endlessm-app://' schemes



commit bc499a233e10915b9a0377f4cf8870f51d2e9a5d
Author: Mario Sanchez Prada <mario endlessm com>
Date:   Mon Jan 29 14:58:09 2018 +0000

    appActivation: Add launcher for 'endlessm-app://' schemes
    
     * 2020-03-24:
          + Squash with 9ec74bde7
          + Squash with 4a2375c4c
    
    Signed-off-by: Andre Moreira Magalhaes <andre endlessm com>
    
    https://phabricator.endlessm.com/T1388

 data/dbus-interfaces/meson.build                   |  1 +
 .../org.gnome.Shell.AppLauncher.xml                | 16 ++++++
 data/eos-launch.desktop.in.in                      |  8 +++
 data/gnome-shell-dbus-interfaces.gresource.xml     |  1 +
 data/meson.build                                   |  2 +
 js/misc/eos-launch.js                              | 56 ++++++++++++++++++
 js/misc/meson.build                                |  2 +
 js/ui/appActivation.js                             | 35 +++++++++++
 js/ui/shellDBus.js                                 | 67 +++++++++++++++++++++-
 9 files changed, 187 insertions(+), 1 deletion(-)
---
diff --git a/data/dbus-interfaces/meson.build b/data/dbus-interfaces/meson.build
index 107888bec8..e77b73a269 100644
--- a/data/dbus-interfaces/meson.build
+++ b/data/dbus-interfaces/meson.build
@@ -1,4 +1,5 @@
 dbus_interfaces = [
+  'org.gnome.Shell.AppLauncher.xml',
   'org.gnome.Shell.AppStore.xml',
   'org.gnome.Shell.Extensions.xml',
   'org.gnome.Shell.Introspect.xml',
diff --git a/data/dbus-interfaces/org.gnome.Shell.AppLauncher.xml 
b/data/dbus-interfaces/org.gnome.Shell.AppLauncher.xml
new file mode 100644
index 0000000000..f02d8bc5b4
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Shell.AppLauncher.xml
@@ -0,0 +1,16 @@
+<node>
+  <interface name="org.gnome.Shell.AppLauncher">
+    <method name="Launch">
+      <arg type="s" direction="in" name="name" />
+      <arg type="u" direction="in" name="timestamp" />
+    </method>
+    <method name="LaunchViaDBusCall">
+      <arg type="s" direction="in" name="name" />
+      <arg type="s" direction="in" name="busName" />
+      <arg type="s" direction="in" name="objectPath" />
+      <arg type="s" direction="in" name="interfaceName" />
+      <arg type="s" direction="in" name="methodName" />
+      <arg type="v" direction="in" name="args" />
+    </method>
+  </interface>
+</node>
diff --git a/data/eos-launch.desktop.in.in b/data/eos-launch.desktop.in.in
new file mode 100644
index 0000000000..820557a345
--- /dev/null
+++ b/data/eos-launch.desktop.in.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=EOS Launch
+Comment=Utility to launch EndlessOS applications
+Exec=gjs @pkgdatadir@/eos-launch.js %U
+Categories=EndlessOS;Utility;Core;
+MimeType=x-scheme-handler/endlessm-app;
+Type=Application
+NoDisplay=true
diff --git a/data/gnome-shell-dbus-interfaces.gresource.xml b/data/gnome-shell-dbus-interfaces.gresource.xml
index 536c1daa6a..df3b64b074 100644
--- a/data/gnome-shell-dbus-interfaces.gresource.xml
+++ b/data/gnome-shell-dbus-interfaces.gresource.xml
@@ -42,6 +42,7 @@
     <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Power.Screen.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Rfkill.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Wacom.xml</file>
+    <file preprocess="xml-stripblanks">org.gnome.Shell.AppLauncher.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.AppStore.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.AudioDeviceSelection.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.CalendarServer.xml</file>
diff --git a/data/meson.build b/data/meson.build
index 2146ce5bf1..29257bbcd5 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -1,5 +1,6 @@
 desktop_files = [
   'org.gnome.Shell.desktop',
+  'eos-launch.desktop',
 ]
 service_files = []
 
@@ -12,6 +13,7 @@ desktopconf = configuration_data()
 # We substitute in bindir so it works as an autostart
 # file when built in a non-system prefix
 desktopconf.set('bindir', bindir)
+desktopconf.set('pkgdatadir', pkgdatadir)
 desktopconf.set('systemd_hidden', have_systemd ? 'true' : 'false')
 
 foreach desktop_file : desktop_files
diff --git a/js/misc/eos-launch.js b/js/misc/eos-launch.js
new file mode 100644
index 0000000000..e4cda69d9a
--- /dev/null
+++ b/js/misc/eos-launch.js
@@ -0,0 +1,56 @@
+const { Gio, GLib } = imports.gi;
+
+const AppLauncherIface = '<node> \
+<interface name="org.gnome.Shell.AppLauncher"> \
+<method name="Launch"> \
+    <arg type="s" direction="in" name="name" /> \
+    <arg type="u" direction="in" name="timestamp" /> \
+</method> \
+</interface> \
+</node>';
+
+const AppLauncherProxy = Gio.DBusProxy.makeProxyWrapper(AppLauncherIface);
+
+function getLocalizedAppNames(appName) {
+    // trim .desktop part, if present
+    let idx = appName.indexOf('.desktop');
+    if (idx !== -1)
+        appName = appName.substring(0, idx);
+
+    let appNames = [appName];
+
+    let languageNames = GLib.get_language_names();
+    let variants = GLib.get_locale_variants(languageNames[0]);
+    variants.filter(variant => {
+        // discard variants with an encoding
+        return variant.indexOf('.') === -1;
+    }).forEach(variant => {
+        appNames.push(`${appName}.${variant}`);
+    });
+
+    appNames.push(`${appName}.en`);
+    return appNames;
+}
+
+function createProxyAndLaunch(appName) {
+    try {
+        let proxy = new AppLauncherProxy(
+            Gio.DBus.session,
+            'org.gnome.Shell',
+            '/org/gnome/Shell');
+        proxy.LaunchSync(appName, 0);
+    } catch (error) {
+        logError(error, `Failed to launch application '${appName}'`);
+    }
+}
+
+var uri = ARGV[0];
+if (!uri) {
+    print('Usage: eos-launch endlessm-app://<application-name>\n');
+} else {
+    let tokens = uri.match(/(endlessm-app:\/\/|)([0-9,a-z,A-Z,-_.]+)/);
+    if (tokens) {
+        let appNames = getLocalizedAppNames(tokens[2]);
+        appNames.some(createProxyAndLaunch);
+    }
+}
diff --git a/js/misc/meson.build b/js/misc/meson.build
index 6b56c9ef69..3947ae66ee 100644
--- a/js/misc/meson.build
+++ b/js/misc/meson.build
@@ -14,3 +14,5 @@ config_js = configure_file(
   output: 'config.js',
   configuration: jsconf
 )
+
+install_data('eos-launch.js', install_dir: pkgdatadir)
diff --git a/js/ui/appActivation.js b/js/ui/appActivation.js
index 53a479704f..88cfe13ac9 100644
--- a/js/ui/appActivation.js
+++ b/js/ui/appActivation.js
@@ -82,6 +82,41 @@ var AppActivationContext = class {
             this.showSplash();
     }
 
+    // Activate an app via a D-Bus call and keep track of its splash screen
+    // if we had to open a new window for it. Otherwise, if there were
+    // windows already open, just do the D-Bus call
+    activateViaDBusCall(busName, path, interfaceName, method, args, done) {
+        Gio.bus_get(Gio.BusType.SESSION, null, (source, result) => {
+            let bus = null;
+            try {
+                bus = Gio.bus_get_finish(result);
+            } catch (error) {
+                done(error, null);
+                return;
+            }
+
+            let proxy = new Gio.DBusProxy({
+                g_bus_type: Gio.BusType.SESSION,
+                g_connection: bus,
+                g_default_timeout: GLib.MAXINT32,
+                g_flags: Gio.DBusProxyFlags.NONE,
+                g_interface_name: interfaceName,
+                g_name: busName,
+                g_object_path: path,
+            });
+
+            this.showSplash();
+
+            proxy.call(method, args, Gio.DBusCallFlags.NONE, -1, null, (source2, result2) => {
+                try {
+                    done(null, proxy.call_finish(result2));
+                } catch (error) {
+                    done(error, null);
+                }
+            });
+        });
+    }
+
     activate(event, timestamp) {
         let button = event ? event.get_button() : 0;
         let modifiers = event ? event.get_state() : 0;
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index d734b46499..69a84410e9 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -1,8 +1,9 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported GnomeShell, ScreenSaverDBus */
 
-const { EosMetrics, Gio, GLib, Meta } = imports.gi;
+const { EosMetrics, Gio, GLib, Meta, Shell } = imports.gi;
 
+const AppActivation = imports.ui.appActivation;
 const Config = imports.misc.config;
 const ExtensionDownloader = imports.ui.extensionDownloader;
 const ExtensionUtils = imports.misc.extensionUtils;
@@ -27,6 +28,7 @@ var GnomeShell = class {
         this._screenshotService = new Screenshot.ScreenshotService();
 
         this._appstoreService = null;
+        this._appLauncherService = new AppLauncher();
 
         Main.sessionMode.connect('updated', this._sessionModeChanged.bind(this));
         this._sessionModeChanged();
@@ -516,3 +518,66 @@ var AppStoreService = class {
         this._dbusImpl.emit_signal('ApplicationsChanged', GLib.Variant.new('(as)', [allApps]));
     }
 };
+
+const AppLauncherIface = loadInterfaceXML('org.gnome.Shell.AppLauncher');
+
+var AppLauncher = class {
+    constructor() {
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AppLauncherIface, this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+
+        this._appSys = Shell.AppSystem.get_default();
+    }
+
+    LaunchAsync(params, invocation) {
+        let [appName, timestamp] = params;
+
+        let activationContext = this._activationContextForAppName(appName);
+        if (!activationContext) {
+            invocation.return_error_literal(
+                Gio.IOErrorEnum,
+                Gio.IOErrorEnum.NOT_FOUND,
+                `Unable to launch app ${appName}: Not installed`);
+            return;
+        }
+
+        activationContext.activate(null, timestamp);
+        invocation.return_value(null);
+    }
+
+    LaunchViaDBusCallAsync(params, invocation) {
+        let [appName, busName, path, interfaceName, method, args] = params;
+
+        let activationContext = this._activationContextForAppName(appName);
+        if (!activationContext) {
+            invocation.return_error_literal(
+                Gio.IOErrorEnum,
+                Gio.IOErrorEnum.NOT_FOUND,
+                `Unable to launch app ${appName}: Not installed`);
+            return;
+        }
+
+        activationContext.activateViaDBusCall(busName, path, interfaceName, method, args, (error, result) => 
{
+            if (error) {
+                logError(error);
+                invocation.return_error_literal(
+                    Gio.IOErrorEnum,
+                    Gio.IOErrorEnum.FAILED,
+                    `Unable to launch app ${appName} through DBus call on ${busName} ${path} 
${interfaceName} ${method}: ${String(error)}`);
+            } else {
+                invocation.return_value(result);
+            }
+        });
+    }
+
+    _activationContextForAppName(appName) {
+        if (!appName.endsWith('.desktop'))
+            appName += '.desktop';
+
+        let app = this._appSys.lookup_app(appName);
+        if (!app)
+            return null;
+
+        return new AppActivation.AppActivationContext(app);
+    }
+};


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]