250 lines
6.3 KiB
JavaScript
250 lines
6.3 KiB
JavaScript
|
// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
|
||
|
//
|
||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
||
|
import Gdk from 'gi://Gdk';
|
||
|
import Gio from 'gi://Gio';
|
||
|
import GObject from 'gi://GObject';
|
||
|
import Gtk from 'gi://Gtk';
|
||
|
|
||
|
import * as Components from '../components/index.js';
|
||
|
import Plugin from '../plugin.js';
|
||
|
|
||
|
|
||
|
export const Metadata = {
|
||
|
label: _('Find My Phone'),
|
||
|
description: _('Ring your paired device'),
|
||
|
id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.FindMyPhone',
|
||
|
incomingCapabilities: ['kdeconnect.findmyphone.request'],
|
||
|
outgoingCapabilities: ['kdeconnect.findmyphone.request'],
|
||
|
actions: {
|
||
|
ring: {
|
||
|
label: _('Ring'),
|
||
|
icon_name: 'phonelink-ring-symbolic',
|
||
|
|
||
|
parameter_type: null,
|
||
|
incoming: [],
|
||
|
outgoing: ['kdeconnect.findmyphone.request'],
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FindMyPhone Plugin
|
||
|
* https://github.com/KDE/kdeconnect-kde/tree/master/plugins/findmyphone
|
||
|
*/
|
||
|
const FindMyPhonePlugin = GObject.registerClass({
|
||
|
GTypeName: 'GSConnectFindMyPhonePlugin',
|
||
|
}, class FindMyPhonePlugin extends Plugin {
|
||
|
|
||
|
_init(device) {
|
||
|
super._init(device, 'findmyphone');
|
||
|
|
||
|
this._dialog = null;
|
||
|
this._player = Components.acquire('sound');
|
||
|
this._mixer = Components.acquire('pulseaudio');
|
||
|
}
|
||
|
|
||
|
handlePacket(packet) {
|
||
|
switch (packet.type) {
|
||
|
case 'kdeconnect.findmyphone.request':
|
||
|
this._handleRequest();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle an incoming location request.
|
||
|
*/
|
||
|
_handleRequest() {
|
||
|
try {
|
||
|
// If this is a second request, stop announcing and return
|
||
|
if (this._dialog !== null) {
|
||
|
this._dialog.response(Gtk.ResponseType.DELETE_EVENT);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this._dialog = new Dialog({
|
||
|
device: this.device,
|
||
|
plugin: this,
|
||
|
});
|
||
|
|
||
|
this._dialog.connect('response', () => {
|
||
|
this._dialog = null;
|
||
|
});
|
||
|
} catch (e) {
|
||
|
this._cancelRequest();
|
||
|
logError(e, this.device.name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Cancel any ongoing ringing and destroy the dialog.
|
||
|
*/
|
||
|
_cancelRequest() {
|
||
|
if (this._dialog !== null)
|
||
|
this._dialog.response(Gtk.ResponseType.DELETE_EVENT);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Request that the remote device announce it's location
|
||
|
*/
|
||
|
ring() {
|
||
|
this.device.sendPacket({
|
||
|
type: 'kdeconnect.findmyphone.request',
|
||
|
body: {},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
this._cancelRequest();
|
||
|
|
||
|
if (this._mixer !== undefined)
|
||
|
this._mixer = Components.release('pulseaudio');
|
||
|
|
||
|
if (this._player !== undefined)
|
||
|
this._player = Components.release('sound');
|
||
|
|
||
|
super.destroy();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Used to ensure 'audible-bell' is enabled for fallback
|
||
|
*/
|
||
|
const _WM_SETTINGS = new Gio.Settings({
|
||
|
schema_id: 'org.gnome.desktop.wm.preferences',
|
||
|
path: '/org/gnome/desktop/wm/preferences/',
|
||
|
});
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A custom GtkMessageDialog for alerting of incoming requests
|
||
|
*/
|
||
|
const Dialog = GObject.registerClass({
|
||
|
GTypeName: 'GSConnectFindMyPhoneDialog',
|
||
|
Properties: {
|
||
|
'device': GObject.ParamSpec.object(
|
||
|
'device',
|
||
|
'Device',
|
||
|
'The device associated with this window',
|
||
|
GObject.ParamFlags.READWRITE,
|
||
|
GObject.Object
|
||
|
),
|
||
|
'plugin': GObject.ParamSpec.object(
|
||
|
'plugin',
|
||
|
'Plugin',
|
||
|
'The plugin providing messages',
|
||
|
GObject.ParamFlags.READWRITE,
|
||
|
GObject.Object
|
||
|
),
|
||
|
},
|
||
|
}, class Dialog extends Gtk.MessageDialog {
|
||
|
_init(params) {
|
||
|
super._init({
|
||
|
buttons: Gtk.ButtonsType.CLOSE,
|
||
|
device: params.device,
|
||
|
image: new Gtk.Image({
|
||
|
icon_name: 'phonelink-ring-symbolic',
|
||
|
pixel_size: 512,
|
||
|
halign: Gtk.Align.CENTER,
|
||
|
hexpand: true,
|
||
|
valign: Gtk.Align.CENTER,
|
||
|
vexpand: true,
|
||
|
visible: true,
|
||
|
}),
|
||
|
plugin: params.plugin,
|
||
|
urgency_hint: true,
|
||
|
});
|
||
|
|
||
|
this.set_keep_above(true);
|
||
|
this.maximize();
|
||
|
this.message_area.destroy();
|
||
|
|
||
|
// If an output stream is available start fading the volume up
|
||
|
if (this.plugin._mixer && this.plugin._mixer.output) {
|
||
|
this._stream = this.plugin._mixer.output;
|
||
|
|
||
|
this._previousMuted = this._stream.muted;
|
||
|
this._previousVolume = this._stream.volume;
|
||
|
|
||
|
this._stream.muted = false;
|
||
|
this._stream.fade(0.85, 15);
|
||
|
|
||
|
// Otherwise ensure audible-bell is enabled
|
||
|
} else {
|
||
|
this._previousBell = _WM_SETTINGS.get_boolean('audible-bell');
|
||
|
_WM_SETTINGS.set_boolean('audible-bell', true);
|
||
|
}
|
||
|
|
||
|
// Start the alarm
|
||
|
if (this.plugin._player !== undefined)
|
||
|
this.plugin._player.loopSound('phone-incoming-call', this.cancellable);
|
||
|
|
||
|
// Show the dialog
|
||
|
this.show_all();
|
||
|
}
|
||
|
|
||
|
vfunc_key_press_event(event) {
|
||
|
this.response(Gtk.ResponseType.DELETE_EVENT);
|
||
|
|
||
|
return Gdk.EVENT_STOP;
|
||
|
}
|
||
|
|
||
|
vfunc_motion_notify_event(event) {
|
||
|
this.response(Gtk.ResponseType.DELETE_EVENT);
|
||
|
|
||
|
return Gdk.EVENT_STOP;
|
||
|
}
|
||
|
|
||
|
vfunc_response(response_id) {
|
||
|
// Stop the alarm
|
||
|
this.cancellable.cancel();
|
||
|
|
||
|
// Restore the mixer level
|
||
|
if (this._stream) {
|
||
|
this._stream.muted = this._previousMuted;
|
||
|
this._stream.fade(this._previousVolume);
|
||
|
|
||
|
// Restore the audible-bell
|
||
|
} else {
|
||
|
_WM_SETTINGS.set_boolean('audible-bell', this._previousBell);
|
||
|
}
|
||
|
|
||
|
this.destroy();
|
||
|
}
|
||
|
|
||
|
get cancellable() {
|
||
|
if (this._cancellable === undefined)
|
||
|
this._cancellable = new Gio.Cancellable();
|
||
|
|
||
|
return this._cancellable;
|
||
|
}
|
||
|
|
||
|
get device() {
|
||
|
if (this._device === undefined)
|
||
|
this._device = null;
|
||
|
|
||
|
return this._device;
|
||
|
}
|
||
|
|
||
|
set device(device) {
|
||
|
this._device = device;
|
||
|
}
|
||
|
|
||
|
get plugin() {
|
||
|
if (this._plugin === undefined)
|
||
|
this._plugin = null;
|
||
|
|
||
|
return this._plugin;
|
||
|
}
|
||
|
|
||
|
set plugin(plugin) {
|
||
|
this._plugin = plugin;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
export default FindMyPhonePlugin;
|