Mercurial > libervia-desktop-kivy
view libervia/desktop_kivy/plugins/plugin_wid_remote.py @ 496:956f5db30223
core (constants): change name to highlight that this is the "Kivy" variant of Desktop frontend
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 01 Sep 2023 16:04:24 +0200 |
parents | b3cedbee561d |
children | 196483685a63 |
line wrap: on
line source
#!/usr/bin/env python3 #Libervia Desktop-Kivy # Copyright (C) 2016-2021 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from libervia.backend.core import log as logging from libervia.backend.core.i18n import _ from libervia.frontends.quick_frontend import quick_widgets from ..core import cagou_widget from ..core.constants import Const as C from ..core.behaviors import TouchMenuBehavior, FilterBehavior from ..core.common_widgets import (Identities, ItemWidget, DeviceWidget, CategorySeparator) from libervia.backend.tools.common import template_xmlui from libervia.backend.tools.common import data_format from libervia.desktop_kivy.core import xmlui from libervia.frontends.tools import jid from kivy import properties from kivy.uix.label import Label from kivy.uix.boxlayout import BoxLayout from kivy.uix.floatlayout import FloatLayout from libervia.desktop_kivy import G from functools import partial log = logging.getLogger(__name__) PLUGIN_INFO = { "name": _("remote control"), "main": "RemoteControl", "description": _("universal remote control"), "icon_symbol": "signal", } NOTE_TITLE = _("Media Player Remote Control") class RemoteItemWidget(ItemWidget): def __init__(self, device_jid, node, name, main_wid, **kw): self.device_jid = device_jid self.node = node super(RemoteItemWidget, self).__init__(name=name, main_wid=main_wid, **kw) def do_item_action(self, touch): self.main_wid.layout.clear_widgets() player_wid = MediaPlayerControlWidget(main_wid=self.main_wid, remote_item=self) self.main_wid.layout.add_widget(player_wid) class MediaPlayerControlWidget(BoxLayout): main_wid = properties.ObjectProperty() remote_item = properties.ObjectProperty() status = properties.OptionProperty("play", options=("play", "pause", "stop")) title = properties.StringProperty() identity = properties.StringProperty() command = properties.DictProperty() ui_tpl = properties.ObjectProperty() @property def profile(self): return self.main_wid.profile def update_ui(self, action_data_s): action_data = data_format.deserialise(action_data_s) xmlui_raw = action_data['xmlui'] ui_tpl = template_xmlui.create(G.host, xmlui_raw) self.ui_tpl = ui_tpl for prop in ('Title', 'Identity'): try: setattr(self, prop.lower(), ui_tpl.widgets[prop].value) except KeyError: log.warning(_("Missing field: {name}").format(name=prop)) playback_status = self.ui_tpl.widgets['PlaybackStatus'].value if playback_status == "Playing": self.status = "pause" elif playback_status == "Paused": self.status = "play" elif playback_status == "Stopped": self.status = "play" else: G.host.add_note( title=NOTE_TITLE, message=_("Unknown playback status: playback_status") .format(playback_status=playback_status), level=C.XMLUI_DATA_LVL_WARNING) self.commands = {v:k for k,v in ui_tpl.widgets['command'].options} def ad_hoc_run_cb(self, xmlui_raw): ui_tpl = template_xmlui.create(G.host, xmlui_raw) data = {xmlui.XMLUIPanel.escape("media_player"): self.remote_item.node, "session_id": ui_tpl.session_id} G.host.bridge.action_launch( ui_tpl.submit_id, data_format.serialise(data), self.profile, callback=self.update_ui, errback=self.main_wid.errback ) def on_remote_item(self, __, remote): NS_MEDIA_PLAYER = G.host.ns_map["mediaplayer"] G.host.bridge.ad_hoc_run(str(remote.device_jid), NS_MEDIA_PLAYER, self.profile, callback=self.ad_hoc_run_cb, errback=self.main_wid.errback) def do_cmd(self, command): try: cmd_value = self.commands[command] except KeyError: G.host.add_note( title=NOTE_TITLE, message=_("{command} command is not managed").format(command=command), level=C.XMLUI_DATA_LVL_WARNING) else: data = {xmlui.XMLUIPanel.escape("command"): cmd_value, "session_id": self.ui_tpl.session_id} # hidden values are normally transparently managed by XMLUIPanel # but here we have to add them by hand hidden = {xmlui.XMLUIPanel.escape(k):v for k,v in self.ui_tpl.hidden.items()} data.update(hidden) G.host.bridge.action_launch( self.ui_tpl.submit_id, data_format.serialise(data), self.profile, callback=self.update_ui, errback=self.main_wid.errback ) class RemoteDeviceWidget(DeviceWidget): def xmlui_cb(self, data, cb_id, profile): if 'xmlui' in data: xml_ui = xmlui.create( G.host, data['xmlui'], callback=self.xmlui_cb, profile=profile) if isinstance(xml_ui, xmlui.XMLUIDialog): self.main_wid.show_root_widget() xml_ui.show() else: xml_ui.set_close_cb(self.on_close) self.main_wid.layout.add_widget(xml_ui) else: if data: log.warning(_("Unhandled data: {data}").format(data=data)) self.main_wid.show_root_widget() def on_close(self, __, reason): if reason == C.XMLUI_DATA_CANCELLED: self.main_wid.show_root_widget() else: self.main_wid.layout.clear_widgets() def ad_hoc_run_cb(self, data): xml_ui = xmlui.create(G.host, data, callback=self.xmlui_cb, profile=self.profile) xml_ui.set_close_cb(self.on_close) self.main_wid.layout.add_widget(xml_ui) def do_item_action(self, touch): self.main_wid.layout.clear_widgets() G.host.bridge.ad_hoc_run(str(self.entity_jid), '', self.profile, callback=self.ad_hoc_run_cb, errback=self.main_wid.errback) class DevicesLayout(FloatLayout): """Layout used to show devices""" layout = properties.ObjectProperty() class RemoteControl(quick_widgets.QuickWidget, cagou_widget.LiberviaDesktopKivyWidget, FilterBehavior, TouchMenuBehavior): SINGLE=False layout = properties.ObjectProperty() def __init__(self, host, target, profiles): quick_widgets.QuickWidget.__init__(self, host, target, profiles) cagou_widget.LiberviaDesktopKivyWidget.__init__(self) FilterBehavior.__init__(self) TouchMenuBehavior.__init__(self) self.stack_layout = None self.show_root_widget() def errback(self, failure_): """Generic errback which add a warning note and go back to root widget""" G.host.add_note( title=NOTE_TITLE, message=_("Can't use remote control: {reason}").format(reason=failure_), level=C.XMLUI_DATA_LVL_WARNING) self.show_root_widget() def key_input(self, window, key, scancode, codepoint, modifier): if key == 27: self.show_root_widget() return True def show_root_widget(self): self.layout.clear_widgets() devices_layout = DevicesLayout() self.stack_layout = devices_layout.layout self.layout.add_widget(devices_layout) found = [] self.get_remotes(found) self.discover_devices(found) def ad_hoc_remotes_get_cb(self, remotes_data, found): found.insert(0, remotes_data) if len(found) == 2: self.show_devices(found) def ad_hoc_remotes_get_eb(self, failure_, found): G.host.errback(failure_, title=_("discovery error"), message=_("can't check remote controllers: {msg}")) found.insert(0, []) if len(found) == 2: self.show_devices(found) def get_remotes(self, found): self.host.bridge.ad_hoc_remotes_get( self.profile, callback=partial(self.ad_hoc_remotes_get_cb, found=found), errback=partial(self.ad_hoc_remotes_get_eb,found=found)) def _disco_find_by_features_cb(self, data, found): found.append(data) if len(found) == 2: self.show_devices(found) def _disco_find_by_features_eb(self, failure_, found): G.host.errback(failure_, title=_("discovery error"), message=_("can't check devices: {msg}")) found.append(({}, {}, {})) if len(found) == 2: self.show_devices(found) def discover_devices(self, found): """Looks for devices handling file "File Information Sharing" and display them""" try: namespace = self.host.ns_map['commands'] except KeyError: msg = _("can't find ad-hoc commands namespace, is the plugin running?") log.warning(msg) G.host.add_note(_("missing plugin"), msg, C.XMLUI_DATA_LVL_ERROR) return self.host.bridge.disco_find_by_features( [namespace], [], False, True, True, True, False, self.profile, callback=partial(self._disco_find_by_features_cb, found=found), errback=partial(self._disco_find_by_features_eb, found=found)) def show_devices(self, found): remotes_data, (entities_services, entities_own, entities_roster) = found if remotes_data: title = _("media players remote controls") self.stack_layout.add_widget(CategorySeparator(text=title)) for remote_data in remotes_data: device_jid, node, name = remote_data wid = RemoteItemWidget(device_jid, node, name, self) self.stack_layout.add_widget(wid) for entities_map, title in ((entities_services, _('services')), (entities_own, _('your devices')), (entities_roster, _('your contacts devices'))): if entities_map: self.stack_layout.add_widget(CategorySeparator(text=title)) for entity_str, entity_ids in entities_map.items(): entity_jid = jid.JID(entity_str) item = RemoteDeviceWidget( self, entity_jid, Identities(entity_ids)) self.stack_layout.add_widget(item) if (not remotes_data and not entities_services and not entities_own and not entities_roster): self.stack_layout.add_widget(Label( size_hint=(1, 1), halign='center', text_size=self.size, text=_("No sharing device found")))