Введение
Данная статья является продолжением материала об использовании системного API в Sailfish OS и посвящена функциям D-Bus в данной операционной системе. Подробно будет разобрано взаимодействие со стандартным календарём и вспышкой. Список остальных основных функций D-Bus системы представлен в конце статьи.
Для понимания изложенного материала необходимо знание основ разработки для Sailfish OS и принципов взаимодействия с D-Bus в рамках операционной системы. Хорошей стартовой точкой являются соответствующие статьи от FRUCT:
[1] Начало разработки для Sailfish OS;
[2] Разработка для Sailfish OS: работа с D-Bus.
Теория
Имеется два способа получить информацию о доступных интерфейсах и функциях D-Bus. Первый подход заключается в изучении зарегистрированных в системе интерфейсов с помощью специальных утилит (например,
Visual D-Bus
и D-Bus Inspector
) или командной строки (рисунок 1). Второй подход использует описанную в предыдущей статье методологию изучения исходного кода.[Рисунок 1 — Пример получения списка интерфейсов D-Bus.]
Большим плюсом первого подхода является возможность получения информации об интерфейсах, “зашитых” в бинарные файлы и недоступных в текстовом виде, но таким образом можно анализировать только активные на текущий момент интерфейсы. Второй подход, используемый в рамках данной статьи, инвертирует описанные плюс и минус первого подхода.
Техника
Функции D-Bus могут быть вызваны (или определены) из кода на языке C++ (который в системе недоступен), из QML-кода и из desktop-файлов приложений. Соответственно, при поиске информации об интерфейсах необходимо иметь представление, во-первых, о модуле
org.nemomobile.dbus
; во-вторых, о способе обращения к D-Bus из desktop-файлов.QML-модуль
org.nemomobile.dbus
предоставляет два основных компонента для работы с D-Bus: DBusAdaptor
и DBusInterface
. Первый позволяет создать приложению свой интерфейс, второй — использовать существующий. При общем поиске информации интерес представляют оба компонента, так как позволяют узнать, как можно взаимодействовать с приложением извне, и как приложение использует сторонние интерфейсы соответственно.Например, чтобы узнать какие интерфейсы D-Bus использует приложение настроек, требуется перейти в каталог
/usr/share/jolla-settings
и выполнить проверку на использование D-Bus:grep -i -H 'dbus' * && grep -A 5 'DBusAdaptor' ./settings.qml
$ cd /usr/share/jolla-settings/
$ grep -i -H 'dbus' *
settings.qml:import org.nemomobile.dbus 2.0
settings.qml: DBusAdaptor {
$ grep -A 5 'DBusAdaptor' ./settings.qml
DBusAdaptor {
service: "com.jolla.settings"
path: "/com/jolla/settings/ui"
iface: "com.jolla.settings.ui"
function showSettings() {
В то же время, при анализе desktop-файлов необходимо помнить, что, во-первых, desktop-файлы хранятся в каталоге
/usr/share/applications
; во-вторых, использование описанных функций запускает экземпляр приложения; в-третьих, при описании взаимодействия с D-Bus используются поля с префиксом X-Maemo
:X-Maemo-Service
— сервис, или местоположение приложения на пользовательской или системной шине;X-Maemo-Object-Path
— путь, или наименование объекта;X-Maemo-Method
— наименование вызываемого метода.
Интересно знать
Префиксы
[Рисунок 2 — Зависимость Sailfish OS от других операционных систем (Википедия).]
Стоит заметить, что в desktop-файлах возможно использование поля
X-Maemo
появились в Sailfish OS как наследство от операционной системы Maemo (рисунок 2), ранее разрабатываемой компанией Nokia. Они позволяют объявить функции D-Bus таким образом, что к ним можно обращаться без предварительного запуска программы. Эти функции позволяют осуществить запуск программы с осуществлением перед её открытием некоторой предварительной работы.[Рисунок 2 — Зависимость Sailfish OS от других операционных систем (Википедия).]
Стоит заметить, что в desktop-файлах возможно использование поля
MimeType
для указания типов файлов, которые может обработать программа. Пример реализации доступен на GitHub.Таким образом, чтобы получить список всех приложений, поддерживающих запуск с использованием D-Bus, требуется перейти в каталог
/usr/share/applications
и выполнить поиск по одному из ключевых слов:grep -H 'X-Maemo-Service' *
$ cd /usr/share/applications/
$ grep -H 'X-Maemo-Service' *
jolla-calendar-import.desktop:X-Maemo-Service=com.jolla.calendar.ui
jolla-calendar.desktop:X-Maemo-Service=com.jolla.calendar.ui
jolla-camera-viewfinder.desktop:X-Maemo-Service=com.jolla.camera
jolla-clock.desktop:X-Maemo-Service=com.jolla.clock
jolla-contacts-import.desktop:X-Maemo-Service=com.jolla.contacts.ui
jolla-email.desktop:X-Maemo-Service=com.jolla.email.ui
jolla-gallery-openfile.desktop:X-Maemo-Service=com.jolla.gallery
jolla-gallery-playvideostream.desktop:X-Maemo-Service=com.jolla.gallery
jolla-mediaplayer-openfile.desktop:X-Maemo-Service=com.jolla.mediaplayer
jolla-mediaplayer.desktop:X-Maemo-Service=com.jolla.mediaplayer
jolla-messages-openurl.desktop:X-Maemo-Service=org.nemomobile.qmlmessages
jolla-messages.desktop:X-Maemo-Service=org.nemomobile.qmlmessages
jolla-notes-import.desktop:X-Maemo-Service=com.jolla.notes
jolla-notes.desktop:X-Maemo-Service=com.jolla.notes
jolla-settings.desktop:X-Maemo-Service=com.jolla.settings
new-mail.desktop:X-Maemo-Service=com.jolla.email.ui
open-url.desktop:X-Maemo-Service=org.sailfishos.browser.ui
ovpn-import.desktop:X-Maemo-Service=com.jolla.settings
sailfish-office-openfile.desktop:X-Maemo-Service=org.sailfish.office
sailfish-office.desktop:X-Maemo-Service=org.sailfish.office
simkit.desktop:X-Maemo-Service=org.sailfish.simkit
voicecall-ui-openurl.desktop:X-Maemo-Service=com.jolla.voicecall.ui
voicecall-ui.desktop:X-Maemo-Service=com.jolla.voicecall.ui
Сводная таблица основных системных функций API и D-Bus приведена в конце статьи.
Практика
Теоретический материал, изложенный в текущей и предыдущей статьях, позволяет реализовывать свои компоненты для работы с системными функциями D-Bus.
Создадим компонент, позволяющий открыть расписание на текущий день. Для этого требуется перейти в каталог
/usr/share/jolla-calendar
(принципы именования директорий описывались в предыдущей статье) и узнать, в каких файлах имеется обращение к D-Bus:grep -r -i -H 'dbus' *
$ cd /usr/share/jolla-calendar/
$ grep -r -i -H 'dbus' *
DbusInvoker.qml:import org.nemomobile.dbus 2.0
DbusInvoker.qml:DBusAdaptor {
calendar.qml:import org.nemomobile.dbus 2.0
calendar.qml: DbusInvoker {}
Из результата выполнения команды видно, что определение интерфейсов производится в файле
DbusInvoker.qml
, который содержит только определения интерфейса и функций D-Bus:cat ./DbusInvoker.qml -n
$ cat ./DbusInvoker.qml -n
1 import QtQuick 2.0
2 import Sailfish.Silica 1.0
3 import org.nemomobile.dbus 2.0
4 import Calendar.dateParser 1.0
5
6 DBusAdaptor {
7 service: "com.jolla.calendar.ui"
8 path: "/com/jolla/calendar/ui"
9 iface: "com.jolla.calendar.ui"
10
11 function viewEvent(id, recurrenceId, startDate) {
12 var occurrence = DateParser.parseTime(startDate)
13 if (isNaN(occurrence.getTime())) {
14 console.warn("Invalid event start date, unable to show event")
15 return
16 }
17
18 if (pageStack.currentPage.objectName === "EventViewPage") {
19 pageStack.currentPage.uniqueId = id
20 pageStack.currentPage.recurrenceId = recurrenceId
21 pageStack.currentPage.startTime = occurrence
22 } else {
23 pageStack.push("pages/EventViewPage.qml",
24 { uniqueId: id, recurrenceId: recurrenceId, startTime: occurrence },
25 PageStackAction.Immediate)
26 }
27 requestActive.start()
28 }
29
30 function viewDate(dateTime) {
31 var parsedDate = new Date(dateTime)
32 if (isNaN(parsedDate.getTime())) {
33 console.warn("Invalid date, unable to show events for date")
34 return
35 }
36
37 if (pageStack.currentPage.objectName === "DayPage") {
38 pageStack.currentPage.date = parsedDate
39 } else {
40 pageStack.push("pages/DayPage.qml", { date: parsedDate }, PageStackAction.Immediate)
41 }
42 requestActive.start()
43 }
44
45 function importFile(fileName) {
46 if (pageStack.currentPage.objectName === "ImportPage") {
47 pageStack.currentPage.fileName = fileName
48 } else {
49 pageStack.push("pages/ImportPage.qml", { "fileName": fileName }, PageStackAction.Immediate)
50 }
51 requestActive.start()
52 }
53
54 function activateWindow(arg) {
55 app.activate()
56 }
57 }
Из кода адаптера видно, что в создаваемом компоненте необходимо подключиться к сервису
com.jolla.calendar.ui
и использовать путь /com/jolla/calendar/ui
и интерфейс com.jolla.calendar.ui
(строки 7-9). После этого станут доступными объявленные функции, из которых, в рамках поставленной задачи, интерес представляет только viewDate
(строки 30-43), принимающая в качестве аргумента одно из представлений даты, распознаваемое объектом Date
. Полученные результаты позволяют реализовать свой компонент для работы с календарём:CalendarController.qml
import QtQuick 2.0
import Sailfish.Silica 1.0
import org.nemomobile.dbus 2.0
Item {
id: calendarControl
/* Открыть календарь для текущей даты.
*/
function showAgenda() {
calendar.call('viewDate', Date.now())
}
DBusInterface {
id: calendar
service: 'com.jolla.calendar.ui'
path: '/com/jolla/calendar/ui'
iface: 'com.jolla.calendar.ui'
}
}
По такому же принципу формируются и другие компоненты, взаимодействующие с функциями D-Bus системы.
Рассмотрим процесс управления вспышкой. Для этого, согласно анализу каталога
/usr/share/jolla-settings/pages/flashlight
, необходимо подключиться к сервису com.jolla.settings.system.flashlight
и использовать путь /com/jolla/settings/system/flashlight
и интерфейс com.jolla.settings.system.flashlight
. После этого, вызывая функцию toggleFlashlight без параметров, становится возможным включать и выключать вспышку.Однако, в некоторых задачах может потребоваться получить информацию о текущем состоянии вспышки — включена она или выключена. Для этого используется функция
getProperty
с передаваемым в неё параметром "flashlightOn"
. В данном случае возвращается булево значение.grep -i -H 'dbus' ./pages/flashlight/* && grep -A 5 -i -H 'DBusInterface' ./pages/flashlight/Flashlight.qml
$ cd /usr/share/jolla-settings
$ grep -i -H "dbus" ./pages/flashlight/*
./pages/flashlight/Flashlight.qml:import org.nemomobile.dbus 2.0
./pages/flashlight/Flashlight.qml: flashlightDbus.call("toggleFlashlight", undefined, handleToggle, handleError)
./pages/flashlight/Flashlight.qml: property QtObject flashlightDbus: DBusInterface {
./pages/flashlight/Flashlight.qml: flashlight.flashlightOn = flashlightDbus.getProperty("flashlightOn")
$ grep -A 5 -i -H "DBusInterface" ./pages/flashlight/Flashlight.qml
./pages/flashlight/Flashlight.qml: property QtObject flashlightDbus: DBusInterface {
./pages/flashlight/Flashlight.qml- signalsEnabled: true
./pages/flashlight/Flashlight.qml- service: "com.jolla.settings.system.flashlight"
./pages/flashlight/Flashlight.qml- path: "/com/jolla/settings/system/flashlight"
./pages/flashlight/Flashlight.qml- iface: "com.jolla.settings.system.flashlight"
./pages/flashlight/Flashlight.qml- function flashlightOnChanged(newOn) {
Учитывая вышенаписанное, реализуется компонент для взаимодействия со вспышкой:
FlashlightController.qml
import QtQuick 2.0
import Sailfish.Silica 1.0
import org.nemomobile.dbus 2.0
Item {
id: flashlightControl
// Состояние вспышки.
property bool flashlightOn
// Включает или выключает вспышку.
function toggleFlashlight() {
flashlightOn = !flashlightOn;
flashlight.call("toggleFlashlight", undefined);
}
DBusInterface {
id: flashlight
service: "com.jolla.settings.system.flashlight"
path: "/com/jolla/settings/system/flashlight"
iface: "com.jolla.settings.system.flashlight"
signalsEnabled: true
function flashlightOnChanged(newOn) {
flashlightControl.flashlightOn = newOn
}
}
Component.onCompleted: {
flashlightControl.flashlightOn = flashlight.getProperty("flashlightOn")
}
}
Заключение
В данной статье указаны два способа поиска информации о функциях D-Bus в Sailfish OS, и подробно разобран один из них — поиск в исходном коде. С его использованием разработаны и прокомментированы примеры компонентов для взаимодействия со вспышкой и стандартным календарём. Код этих и других модулей доступен на GitHub.
Однако не стоит забывать, что решение части задач возможно простым запуском интересующей программы с требуемыми аргументами. Например, запуск стандартного браузера с открытием сайта может быть осуществлён с помощью команды
sailfish-browser https://google.com/
. Но это выходит за рамки описываемого в статье материала.sailfish-browser https://google.com/
$ sailfish-browser https://google.com/
[D] unknown:0 - Using Wayland-EGL
greHome from GRE_HOME:/usr/bin
libxul.so is not found, in /usr/bin/libxul.so
Created LOG for EmbedLite
[D] onCompleted:103 - ViewPlaceholder requires a SilicaFlickable parent
Loaded xulDir:/usr/lib/xulrunner-qt5-38.8.0/libxul.so, appDir:/usr/bin
EmbedLiteExt virtual nsresult EmbedChromeManager::Observe(nsISupports*, const char*, const char16_t*):82: obj:(nil), top:app-startup
EmbedLiteExt virtual nsresult EmbedTouchManager::Observe(nsISupports*, const char*, const char16_t*):86: obj:(nil), top:app-startup
EmbedLiteGlobalHelper app-startup
EmbedLiteSyncService app-startup
PREFS SERVICE INITAILIZED
EmbedPrefService app-startup
EmbedliteDownloadManager initialized
UserAgentOverrideHelper app-startup
1505073762747 addons.manager DEBUG Application has been upgraded
1505073762892 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/XPIProvider.jsm: ["XPIProvider"]
1505073762912 addons.manager DEBUG Loaded provider scope for resource://gre/modules/LightweightThemeManager.jsm: ["LightweightThemeManager"]
1505073762942 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/GMPProvider.jsm
1505073762961 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/PluginProvider.jsm
1505073762968 addons.manager DEBUG Starting provider: XPIProvider
1505073762973 addons.xpi DEBUG startup
1505073762982 addons.xpi DEBUG checkForChanges
1505073762993 addons.xpi DEBUG Loaded add-on state from prefs: {}
1505073763000 addons.xpi DEBUG getInstallState changed: false, state: {}
1505073763009 addons.xpi DEBUG Empty XPI database, setting schema version preference to 16
1505073763012 addons.xpi DEBUG No changes found
1505073763015 addons.manager DEBUG Registering shutdown blocker for XPIProvider
1505073763021 addons.manager DEBUG Provider finished startup: XPIProvider
1505073763022 addons.manager DEBUG Starting provider: LightweightThemeManager
1505073763024 addons.manager DEBUG Registering shutdown blocker for LightweightThemeManager
1505073763029 addons.manager DEBUG Provider finished startup: LightweightThemeManager
1505073763032 addons.manager DEBUG Starting provider: GMPProvider
1505073763046 addons.manager DEBUG Registering shutdown blocker for GMPProvider
1505073763050 addons.manager DEBUG Provider finished startup: GMPProvider
1505073763052 addons.manager DEBUG Starting provider: PluginProvider
1505073763055 addons.manager DEBUG Registering shutdown blocker for PluginProvider
1505073763059 addons.manager DEBUG Provider finished startup: PluginProvider
1505073763060 addons.manager DEBUG Completed startup sequence
Created LOG for EmbedPrefs
[D] QMozWindowPrivate::setSize:71 - Trying to set empty size: QSize(-1, -1)
Attempting load of libEGL.so
EmbedLiteExt virtual nsresult EmbedTouchManager::Observe(nsISupports*, const char*, const char16_t*):86: obj:0xb225a130, top:domwindowopened
EmbedLiteExt void EmbedChromeManager::WindowCreated(nsIDOMWindow*):91: WindowOpened: 0xb225a140
EmbedLiteExt void EmbedTouchManager::WindowCreated(nsIDOMWindow*):95: WindowOpened: 0xb225a140
EmbedLiteExt void EmbedTouchManager::WindowCreated(nsIDOMWindow*):108: id for window: 1
###################################### SelectAsyncHelper.js loaded
###################################### embedhelper.js loaded
### ContextMenuHandler.js loaded
### SelectionPrototype.js loaded
### SelectionHandler.js loaded
Init Called:[object Object]
JavaScript warning: https://www.google.ru/xjs/_/js/k=xjs.mhp.en_US.2vKAz7DqmvI.O/m=sb_mobh,hjsa,d,csi/am=AAAD/rt=j/d=1/t=zcms/rs=ACT90oFx8AHVqc9lMfPQBwURKXyQ4qaFiA, line 7: mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
Основные системные API функции:
Описание | Необходимые данные |
---|---|
Создать атмосферу |
|
Настройка яркость экрана |
|
Настройка ориентации экрана |
|
Автоматическая настройка яркости экрана |
|
Настройка времени включения спящего режима |
|
Настройка состояния дисплея во время зарядки |
|
Настройка системного размера шрифта |
|
Настройка системной громкости |
|
Настройка вибрации |
|
Включить/выключить звуки на системные события |
|
Включить/выключить WLAN |
|
Включить/выключить общий доступ к интернету |
|
Включить/выключить режим полёта |
|
Включить/выключить Bluetooth |
|
Основные системные D-Bus функции:
Описание | Необходимые данные |
---|---|
Просмотр события в календаре | Сервис:com.jolla.calendar.ui Путь: /com/jolla/calendar/ui Интерфейс: com.jolla.calendar.ui Функция: viewEvent(id, recurrenceId, startDate) |
Просмотр дня в календаре | Сервис:com.jolla.calendar.ui Путь: /com/jolla/calendar/ui Интерфейс: com.jolla.calendar.ui Функция: viewDate(dateTime) |
Открытие камеры в последнем состоянии | Сервис:com.jolla.camera Путь: / Интерфейс: com.jolla.camera.ui Функция: showViewfinder() |
Открытие фронтальной камеры | Сервис:com.jolla.camera Путь: / Интерфейс: com.jolla.camera.ui Функция: showFrontViewfinder() |
Создание будильника | Сервис:com.jolla.clock Путь: / Интерфейс: com.jolla.clock Функция: newAlarm() |
Просмотр контакта | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: showContact(int contactId) |
Редактирование контакта | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: editContact(int contactId) |
Импортирование контактов | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: importWizard() |
Воспроизвести аудиофайл по URL | Сервис:com.jolla.mediaplayer Путь: /com/jolla/mediaplayer/ui Интерфейс: com.jolla.mediaplayer.ui Функция: openUrl(url) |
Создание новой заметки | Сервис:com.jolla.notes Путь: / Интерфейс: com.jolla.notes Функция: newNote() |
Просмотр настроек | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showSettings() |
Просмотр списка загрузок | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showTransfers() |
Просмотр списка аккаунтов | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showAccounts() |
Просмотр настройки записи телефонных разговоров | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showCallRecordings() |
Включить/выключить поддержку Android | Сервис:com.jolla.apkd Путь: /com/jolla/apkd Интерфейс: com.jolla.apkd Функция: controlService(true/false) |
Включить/выключить вспышку | Сервис:com.jolla.settings.system.flashlight Путь: /com/jolla/settings/system/flashlight Интерфейс: com.jolla.settings.system.flashlight Функция: toggleFlashlight() |
Перезагрузить устройство | Сервис:com.nokia.dsme Путь: /com/nokia/dsme/request Интерфейс: com.nokia.dsme.request Функция: req_reboot() |
Выключить устройство | Сервис:com.nokia.dsme Путь: /com/nokia/dsme/request Интерфейс: com.nokia.dsme.request Функция: req_shutdown |
Позвонить | Сервис:com.jolla.voicecall.ui Путь: / Интерфейс: com.jolla.voicecall.ui Функция: dial(number) |
Открыть изображения в галерее | Сервис:com.jolla.gallery Путь: /com/jolla/gallery/ui Интерфейс: com.jolla.gallery.ui Функция: showImages(array_of_urls) |
Открыть видео в галерее | Сервис:com.jolla.gallery Путь: /com/jolla/gallery/ui Интерфейс: com.jolla.gallery.ui Функция: playVideoStream(url) |
Создать новое письмо | Сервис:com.jolla.email.ui Путь: /com/jolla/email/ui Интерфейс: com.jolla.email.ui Функция: mailto() |
Поиск WLAN-сетей | Сервис:com.jolla.lipstick.ConnectionSelector Путь: / Интерфейс: com.jolla.lipstick.ConnectionSelectorIf Функция: openConnectionNow('wifi') |
Возникающие по ходу разработки вопросы и идеи всегда можно обсудить в Telegram-чате и в группе ВКонтакте.