Введение
Голосовые ассистенты в мобильных устройствах не стоят на месте и непрерывно развиваются. Голосовой помощник для Sailfish OS, представленный осенью прошлого года, не исключение и тоже обрастает новым функционалом.
В той статье рассматривался базовый принцип внутренней работы приложения. Данный материал открывает серию из двух статей, в которой он будет рассмотрен подробнее:
- Работа с недокументированным API для управления устройством (текущая);
- Работа с интерфейсами D-Bus, предоставляемыми операционной системой.
В текущей статье описывается как управлять яркостью экрана и системной громкостью, а также как включать и выключать Bluetooth и режим полёта.
Подразумевается, что читатель уже установил Sailfish OS SDK и разрабатывал приложения с его использованием.
Как получать информацию
Во время разработки под Sailfish OS нельзя обойтись без документации, представленной и на официальном сайте, и в IDE. Документация написана качественно, с примерами, но не без недостатка — она коротка и освещает только базовый функционал, доступный программисту.
Но Sailfish OS, как и другие операционные системы, скрывает в себе больше описанного в документации. Надо только знать где искать. А искать надо в исходном коде, как в самом достоверном источнике информации. Благо Sailfish OS — достаточно открытый программный продукт на базе GNU/Linux.
Для начала потребуется подключиться к телефону по SSH (рисунок 1). Для этого надо разрешить удалённое соединение (1), установить для него пароль (2) и воспользоваться одним из указанных IP-адресов (3). После проделанных действий выполнить переход в каталог
/usr/share
. В данном каталоге находятся коды интерфейса и скрипты практически всех приложений, установленных на телефоне.[Рисунок 1 — Настройка подключения к телефону по SSH.]
Каталоги приложений сторонних разработчиков начинаются (в большинстве случаев) с приставки
harbour
, за которой следует идентификатор программы. Такой префикс позволяет избежать возможного пересечения названий с системными именами. Каталоги стандартных приложений сопровождаются приставками jolla
и sailfish
. Остальное относится к системным функциям.[Рисунок 2 — Пример каталогов приложений.]
Настройка яркости экрана
Управление яркостью экрана относится к пользовательским настройкам системы, следовательно интерес представляет каталог
/usr/share/jolla-settings
, который содержит следующие объекты:- Каталог
entries
— хранит информацию о структуре настроек; - Каталог
pages
— хранит страницы и элементы настроек; - Каталог
widgets
— обычно не используется; - Файл
settings.qml
— главный файл запуска приложения настроек; - Файл
x-openvpn.xml
— хранит информацию о соединении с VPN-сервером.
Из списка видно, что в обозначенной задаче требуется обратиться к каталогу
pages
. Он разбит на подкаталоги согласно типам настроек: bluetooth
, sounds
и др. Настройки экрана расположены в директории display
. Следовательно /usr/share/jolla-settings/pages/display
является полным путём, относительно которого будет проходить последующая работа.Первым делом выполняется поиск по ключевому слову:
grep -H 'brightness' *
$ cd /usr/share/jolla-settings/pages/display
$ grep -H 'brightness' *
BrightnessSlider.qml: label: qsTrId("settings_display-la-brightness")
BrightnessSlider.qml: value: displaySettings.brightness
BrightnessSlider.qml: onValueChanged: displaySettings.brightness = Math.round(value)
BrightnessSlider.qml: onBrightnessChanged: slider.value = brightness
display.qml: id: brightnessSlider
display.qml: entryPath: "system_settings/look_and_feel/display/brightness_slider"
display.qml: text: qsTrId("settings_display-la-adaptive_brightness")
Из полученных результатов видно, что код настройки яркости экрана находится в файле
BrightnessSlider.qml
.Далее требуется уточнить как именно выполняется управление:
grep -C 1 'displaySettings' ./BrightnessSlider.qml
$ grep -C 1 'displaySettings' ./BrightnessSlider.qml
label: qsTrId("settings_display-la-brightness")
maximumValue: displaySettings.maximumBrightness
minimumValue: 1
value: displaySettings.brightness
stepSize: 1
onValueChanged: displaySettings.brightness = Math.round(value)
DisplaySettings {
id: displaySettings
onBrightnessChanged: slider.value = brightness
После поиска по файлу становится очевидным, что для управления яркостью устройства требуется создать объект
DisplaySettings
и менять значение поля brightness
, принимая во внимание, что минимальное значение яркости равно единице, а максимальное хранится в поле maximumBrightness
.Последний штрих — определить необходимый импорт:
grep 'import' ./BrightnessSlider.qml
$ grep 'import' ./BrightnessSlider.qml
import QtQuick 2.0
import Sailfish.Silica 1.0
import com.jolla.settings.system 1.0
import org.nemomobile.systemsettings 1.0
QtQuick
и Sailfish.Silica
являются стандартными модулями для разработки интерфейса и не предоставляют доступ к настройкам. com.jolla.settings.system
содержит внутренние объекты для работы приложения настроек и не представляет интереса для других программ. Остаётся org.nemomobile.systemsettings
, описывающий необходимый компонент.Таким образом, становится возможным сформировать свой элемент управления яркостью экрана для последующего включения в проект:
BrightnessControl.qml
import QtQuick 2.0
import org.nemomobile.systemsettings 1.0
Item {
id: brightnessControl
/* Установка яркости на указанное значение.
* @param: value -- число от 1 (минимум) до maximumBrightness (максимум)
*/
function setBrightness(value) {
if (value <= 1) setMinimumBrightness();
else if (value >= displaySettings.maximumBrightness) setMaximumBrightness();
else displaySettings.brightness = value;
}
/* Увеличение яркости.
* @param: percents -- процент от диапазона яркости.
*/
function increaseBrightness(percents) {
setBrightness(displaySettings.brightness + _calcDelta(percents))
}
/* Уменьшение яркости.
* @param: percents -- процент от диапазона яркости.
*/
function decreaseBrightness(percents) {
setBrightness(displaySettings.brightness - _calcDelta(percents))
}
/* Установка минимальной яркости.
*/
function setMinimumBrightness() {
displaySettings.brightness = 1
}
/* Установка максимальной яркости.
*/
function setMaximumBrightness() {
displaySettings.brightness = displaySettings.maximumBrightness
}
/* Расчёт абсолютного значения изменения.
* @param: percents -- процент от диапазона яркости.
* @return: Значение изменения яркости в абсолютных единицах.
*/
function _calcDelta(percents) {
return Math.round(displaySettings.maximumBrightness / 100 * percents)
}
DisplaySettings { id: displaySettings }
}
Настройка системной громкости
Здесь используется уже известный метод поиска информации. Переходим в каталог
/usr/share/jolla-settings/pages/sounds
и проверяем ключевые слова:grep -i -C 1 -H 'volume' *
$ cd /usr/share/jolla-settings/pages/sounds
$ grep -i -C 1 -H 'volume' *
SoundsPage.qml-
SoundsPage.qml: VolumeSlider {
SoundsPage.qml: id: volumeSlider
SoundsPage.qml: property string entryPath: "system_settings/look_and_feel/sounds/ringer_volume"
SoundsPage.qml- width: parent.width
--
VolumeSlider.qml- height: implicitHeight + valueLabel.height + Theme.paddingSmall
VolumeSlider.qml: //% "Ringtone volume"
VolumeSlider.qml: label: qsTrId("settings_sounds_la_volume")
VolumeSlider.qml- maximumValue: 100
--
VolumeSlider.qml- if (!externalChange) {
VolumeSlider.qml: profileControl.ringerVolume = value
VolumeSlider.qml- profileControl.profile = (value > 0) ? "general" : "silent"
--
VolumeSlider.qml- slider.externalChange = true
VolumeSlider.qml: slider.value = profileControl.ringerVolume
VolumeSlider.qml- slider.externalChange = false
--
VolumeSlider.qml-
VolumeSlider.qml: onRingerVolumeChanged: {
VolumeSlider.qml- slider.externalChange = true
VolumeSlider.qml: slider.value = profileControl.ringerVolume
VolumeSlider.qml- slider.externalChange = false
Из результата выполнения команды видно, что максимально допустимое значение громкости — 100, минимальное — 0; а управление выполняется с помощью элемента
profileControl
, и требует не только указания значения громкости, но и переключения профиля. Получим более подробную информацию об упомянутом элементе:grep -C 1 'profileControl' ./VolumeSlider.qml
$ grep -C 1 'profileControl' ./VolumeSlider.qml
if (!externalChange) {
profileControl.ringerVolume = value
profileControl.profile = (value > 0) ? "general" : "silent"
}
--
slider.externalChange = true
slider.value = profileControl.ringerVolume
slider.externalChange = false
--
ProfileControl {
id: profileControl
--
slider.externalChange = true
slider.value = profileControl.ringerVolume
slider.externalChange = false
$ grep 'import' ./VolumeSlider.qml
import QtQuick 2.0
import Sailfish.Silica 1.0
import com.jolla.settings.system 1.0
import org.nemomobile.systemsettings 1.0
На основе результатов поиска и информации из предыдущего раздела становится возможным реализовать модуль для управления системной громкостью:
VolumeControl.qml
import QtQuick 2.0
import org.nemomobile.systemsettings 1.0
Item {
id: volumeControl
/* Установка громкости на указанное значение.
* @param: value -- число от 0 (минимум) до 100 (максимум).
*/
function setVolume(value) {
if (value <= 0) {
setMinimumVolume();
} else if (value >= 100) {
setMaximumVolume();
} else {
profileControl.ringerVolume = value;
_setProfile();
}
}
/* Увеличение громкости.
* @param: percents -- процент от диапазона громкости.
*/
function increaseVolume(percents) {
setVolume(profileControl.ringerVolume + percents)
}
/* Уменьшение громкости.
* @param: percents -- процент от диапазона громкости.
*/
function decreaseVolume(percents) {
setVolume(profileControl.ringerVolume - percents)
}
/* Установка минимальной громкости.
*/
function setMinimumVolume() {
profileControl.ringerVolume = 0;
_setProfile();
}
/* Установка максимальной громкости.
*/
function setMaximumVolume() {
profileControl.ringerVolume = 100;
_setProfile();
}
/* Установка профиля относительно выставленного значения громкости.
*/
function _setProfile() {
profileControl.profile = (profileControl.ringerVolume > 0) ? "general" : "silent"
}
ProfileControl { id: profileControl }
}
Здесь стоит обратить внимание на использование ключевых слов
general
и silent
для управления звуковым профилем системы (основной и бесшумный соответственно). Это два зарезервированных значения, определяющих поведение системы в зависимости от выставленного значения громкости.Переключение режима полёта и Bluetooth
Принцип поиска API абсолютно идентичен описанному выше, поэтому перейдём непосредственно к рассмотрению уже готовых элементов.
Для управления режимом полёта требуется подключить модуль
MeeGo.Connman
, предоставляющий компонент NetworkManagerFactory
. Выставляя значение поля instance.offlineMode
данного объекта в true
или false
, становится возможным включать и выключать указанный режим соответственно.FlightControl.qml
import QtQuick 2.0
import MeeGo.Connman 0.2
Item {
id: flightControl
/* Включение режима “В самолёте”.
*/
function turnOnFlightMode() {
connMgr.instance.offlineMode = true
}
/* Выключение режима “В самолёте”.
*/
function turnOffFlightMode() {
connMgr.instance.offlineMode = false
}
/* Переключение режима “В самолёте”.
*/
function switchFlightMode() {
connMgr.instance.offlineMode = !connMgr.instance.offlineMode
}
NetworkManagerFactory { id: connMgr }
}
Для управления состоянием Bluetooth также подключается модуль
MeeGo.Connman
, но при этом используется компонент TechnologyModel
, значение поля powered
которого принимает true
или false
для включения или выключения Bluetooth соответственно. Стоит заметить, что остальная работа с интерфейсом производится с помощью стандартных возможностей Qt.BluetoothControl.qml
import QtQuick 2.0
import MeeGo.Connman 0.2
Item {
id: bluetoothControl
/* Включение bluetooth.
*/
function turnOnBluetooth() {
btTechModel.powered = true
}
/* Выключение bluetooth.
*/
function turnOffBluetooth() {
btTechModel.powered = false
}
/* Переключение bluetooth.
*/
function switchBluetooth() {
btTechModel.powered = !btTechModel.powered
}
TechnologyModel {
id: btTechModel
name: "bluetooth"
}
}
Заключение
В данной статье показан один из способов поиска недокументированных возможностей в Sailfish OS и представлены примеры кода для контроля яркости, громкости, Bluetooth и режима полёта. Более подробно ознакомиться с реализацией этих и других модулей управления системными настройками (а также со способами их применения) можно на GitHub.
Однако, прежде чем смотреть готовый код, попробуйте самостоятельно реализовать и подключить какой-нибудь модуль, и только потом сравнивайте с опубликованным примером. Можно, например, попробовать сменить оформление (поиск по
ambience
) или системный размер шрифта (fontSize
), управлять Wi-Fi (wifi
) или общим доступом к интернету (tethering
).Следующая статья будет посвящена тому, какие интерфейсы и функции D-Bus предоставляет Sailfish OS программисту.
Возникающие по ходу разработки вопросы и идеи всегда можно обсудить в Telegram-чате и в группе ВКонтакте.
Выражаю благодарность хабраюзеру ragequit за инвайт, давший возможность опубликовать эту и последующие статьи.
lieff
Меня больше всего интересует как использовать GLES API без QML. GL код обычно уже готовый, переписывать все на QML ради одной платформы — не вариант. Видел что там вроде разрешили SDL приложения, но как штатно инициализировать GLES так и не нашел.
chuvilin
А если через Scene Graph попробовать?
lieff
Если делать новое приложение с нуля и только под QT — то подойдет. А если уже есть нативный GL код — то все равно придется портировать. И не понятно, зачем нужен SDL без GL без QT.