2024-07-08 22:46:35 +02:00

250 lines
6.3 KiB
JavaScript
Executable File

// 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;