diff options
author | Scott Murray <scott.murray@konsulko.com> | 2020-01-15 14:22:17 -0500 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2020-01-16 14:18:32 -0500 |
commit | fdc78ebadb8c86efd323e57fdc7f64c59d4aa967 (patch) | |
tree | 3959a2ba4173fdc201b93615a19dae54b83dd585 /voice-capabilities/guimetadata.cpp | |
parent | 77392aeda562e3bbb4f311a99469156cb1a66d75 (diff) |
Add initial voice-capabilities binding supporthalibut_8.0.6halibut_8.0.5halibut/8.0.6halibut/8.0.58.0.68.0.5halibut
Add support for subscribing to and using the guimetadata events from
the vshl-capabilities binding. A GuiMetadata class is used to wrap
things as it is likely that users of the other voice capabilities
would not overlap if e.g. navigation event support is later added.
At the moment, only the BodyTemplate1, BodyTemplate2, and
WeatherTemplate guimetadata types are handled, and there is room for
significant improvement in the Qt model exposed, which is somewhat
crude with respect to image URL handling. As well, the weather
template handling does not expose the full forecast, a full
implementation of that would require adding an actual model that
maps to a list view rather than a simple QObject.
Bug-AGL: SPEC-3110
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
Change-Id: I33fc2440ebdc2a4a2de79ff0a49acbc422c47da2
Diffstat (limited to 'voice-capabilities/guimetadata.cpp')
-rw-r--r-- | voice-capabilities/guimetadata.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/voice-capabilities/guimetadata.cpp b/voice-capabilities/guimetadata.cpp new file mode 100644 index 0000000..2ab86c5 --- /dev/null +++ b/voice-capabilities/guimetadata.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2020 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "message.h" +#include "messageengine.h" +#include "guimetadata.h" +#include "guimetadatamessage.h" + +#include <QJsonArray> + +GuiMetadata::GuiMetadata(QUrl &url, QQmlContext *context, QObject * parent) : + QObject(parent), + m_mloop(nullptr) +{ + m_mloop = new MessageEngine(url); + m_context = context; + QObject::connect(m_mloop, &MessageEngine::connected, this, &GuiMetadata::onConnected); + QObject::connect(m_mloop, &MessageEngine::disconnected, this, &GuiMetadata::onDisconnected); + QObject::connect(m_mloop, &MessageEngine::messageReceived, this, &GuiMetadata::onMessageReceived); +} + +GuiMetadata::~GuiMetadata() +{ + delete m_mloop; +} + +// Qt UI Context + +void GuiMetadata::clearMetadata() +{ + m_type = ""; + m_title = ""; + m_subtitle = ""; + + m_bodyText = ""; + m_bodyImageSmallUrl = ""; + m_bodyImageMediumUrl = ""; + m_bodyImageLargeUrl = ""; + + m_weatherCurrentTemperature = ""; + m_weatherCurrentWeatherIconSmallUrl = ""; + m_weatherCurrentWeatherIconSmallDarkBgUrl = ""; + m_weatherCurrentWeatherIconMediumUrl = ""; + m_weatherCurrentWeatherIconMediumDarkBgUrl = ""; + m_weatherCurrentWeatherIconLargeUrl = ""; + m_weatherCurrentWeatherIconLargeDarkBgUrl = ""; + + m_weatherLowTemperature = ""; + m_weatherLowTemperatureArrowSmallUrl = ""; + m_weatherLowTemperatureArrowSmallDarkBgUrl = ""; + m_weatherLowTemperatureArrowMediumUrl = ""; + m_weatherLowTemperatureArrowMediumDarkBgUrl = ""; + m_weatherLowTemperatureArrowLargeUrl = ""; + m_weatherLowTemperatureArrowLargeDarkBgUrl = ""; + + m_weatherHighTemperature = ""; + m_weatherHighTemperatureArrowSmallUrl = ""; + m_weatherHighTemperatureArrowSmallDarkBgUrl = ""; + m_weatherHighTemperatureArrowMediumUrl = ""; + m_weatherHighTemperatureArrowMediumDarkBgUrl = ""; + m_weatherHighTemperatureArrowLargeUrl = ""; + m_weatherHighTemperatureArrowLargeDarkBgUrl = ""; +} + +bool GuiMetadata::parseImageMetadata(QJsonObject &imageObj, + QUrl &smallUrl, QUrl &mediumUrl, QUrl &largeUrl, + QUrl *pSmallDarkBgUrl, QUrl *pMediumDarkBgUrl, QUrl *pLargeDarkBgUrl) +{ + if(!(imageObj.contains("sources") && imageObj["sources"].isArray())) { + // error + qWarning() << "Could not find image.sources parameter!"; + return false; + } + QJsonArray sourcesObj = imageObj["sources"].toArray(); + bool found = false; + for(QJsonArray::iterator it = sourcesObj.begin(); it != sourcesObj.end(); it++) { + if(!it->isObject()) { + // unexpected, skip + continue; + } + QJsonObject sourceObj = it->toObject(); + if(!(sourceObj.contains("url") && sourceObj["url"].isString())) { + // error + qWarning() << "Missing image.sources.url parameter!"; + continue; + } + QString url = sourceObj["url"].toString(); + QString darkBackgroundUrl = ""; + if(sourceObj.contains("darkBackgroundUrl") && sourceObj["darkBackgroundUrl"].isString()) { + darkBackgroundUrl = sourceObj["darkBackgroundUrl"].toString(); + } + if(sourceObj.contains("size") && sourceObj["size"].isString()) { + QString size = sourceObj["size"].toString(); + if(size == "SMALL") { + smallUrl = url; + if(pSmallDarkBgUrl) + pSmallDarkBgUrl->setUrl(darkBackgroundUrl); + found = true; + } else if(size == "MEDIUM") { + mediumUrl = url; + if(pMediumDarkBgUrl) + pMediumDarkBgUrl->setUrl(darkBackgroundUrl); + found = true; + } else if(size == "LARGE") { + largeUrl = url; + if(pLargeDarkBgUrl) + pLargeDarkBgUrl->setUrl(darkBackgroundUrl); + found = true; + } + // else ignore (X-SMALL, X-LARGE) + } + // FIXME: Should handle image sources w/o size fields, + // parse width/height if present + } + return found; +} + +bool GuiMetadata::updateMetadata(QJsonObject data) +{ + if(!data.contains("type")) + return false; + + clearMetadata(); + + QString type = data["type"].toString(); + if(!(type == "BodyTemplate1" || + type == "BodyTemplate2" || + type == "WeatherTemplate")) { + // Show unsupported type message + m_type = "Unsupported"; + return true; + } + m_type = type; + + // All template types have title + if(data.contains("title") && data["title"].isObject()) { + QJsonObject titleObj = data["title"].toObject(); + if(titleObj.contains("mainTitle")) { + m_title = titleObj["mainTitle"].toString(); + } else { + qWarning() << "Could not find title.mainTitle parameter!"; + return false; + } + // subTitle is apparently optional + if(titleObj.contains("subTitle")) + m_subtitle = titleObj["subTitle"].toString(); + } else { + // error + qWarning() << "Could not find title parameter!"; + return false; + } + + if(type == "BodyTemplate1" || type == "BodyTemplate2") + return updateBodyMetadata(data); + else + return updateWeatherMetadata(data); +} + +bool GuiMetadata::updateBodyMetadata(QJsonObject &data) +{ + if(!data.contains("type")) + return false; + + QString type = data["type"].toString(); + if(!(type == "BodyTemplate1" || type == "BodyTemplate2")) + return false; + + // BodyTemplate1/2 have text field + if(data.contains("textField")) { + m_bodyText = data["textField"].toString(); + } else { + // error + qWarning() << "Could not find textField parameter!"; + return false; + } + + // BodyTemplate2 has image + if(type == "BodyTemplate2") { + if(!(data.contains("image") && data["image"].isObject())) { + // error + qWarning() << "Could not find image parameter!"; + return false; + } + QJsonObject imageObj = data["image"].toObject(); + if(!parseImageMetadata(imageObj, + m_bodyImageSmallUrl, + m_bodyImageMediumUrl, + m_bodyImageLargeUrl)) { + qWarning() << "Could not parse image parameter!"; + return false; + } + } + + return true; +} + +bool GuiMetadata::updateWeatherMetadata(QJsonObject &data) +{ + if(!data.contains("type")) + return false; + + QString type = data["type"].toString(); + if(type != "WeatherTemplate") + return false; + + if(data.contains("currentWeather")) { + m_weatherCurrentTemperature = data["currentWeather"].toString(); + } else { + // error + qWarning() << "Could not find currentWeather parameter!"; + return false; + } + + if(!(data.contains("currentWeatherIcon") && data["currentWeatherIcon"].isObject())) { + // error + qWarning() << "Could not find currentWeatherIcon parameter!"; + return false; + } + QJsonObject imageObj = data["currentWeatherIcon"].toObject(); + if(!parseImageMetadata(imageObj, + m_weatherCurrentWeatherIconSmallUrl, + m_weatherCurrentWeatherIconMediumUrl, + m_weatherCurrentWeatherIconLargeUrl, + &m_weatherCurrentWeatherIconSmallDarkBgUrl, + &m_weatherCurrentWeatherIconMediumDarkBgUrl, + &m_weatherCurrentWeatherIconLargeDarkBgUrl)) { + qWarning() << "Could not parse currentWeatherIcon.image parameter!"; + return false; + } + + if(!(data.contains("lowTemperature") && data["lowTemperature"].isObject())) { + // error + qWarning() << "Could not find lowTemperature parameter!"; + return false; + } + QJsonObject tempObj = data["lowTemperature"].toObject(); + if(!(tempObj.contains("value") && tempObj["value"].isString())) { + // error + qWarning() << "Could not find lowTemperature.value parameter!"; + return false; + } + m_weatherLowTemperature = tempObj["value"].toString(); + + if(!(tempObj.contains("arrow") && tempObj["arrow"].isObject())) { + // error + qWarning() << "Could not find lowTemperature.arrow parameter!"; + return false; + } + imageObj = tempObj["arrow"].toObject(); + if(!parseImageMetadata(imageObj, + m_weatherLowTemperatureArrowSmallUrl, + m_weatherLowTemperatureArrowMediumUrl, + m_weatherLowTemperatureArrowLargeUrl, + &m_weatherLowTemperatureArrowSmallDarkBgUrl, + &m_weatherLowTemperatureArrowMediumDarkBgUrl, + &m_weatherLowTemperatureArrowLargeDarkBgUrl)) { + qWarning() << "Could not parse lowTemperature.arrow parameter!"; + return false; + } + + if(!(data.contains("highTemperature") && data["highTemperature"].isObject())) { + // error + qWarning() << "Could not find highTemperature parameter!"; + return false; + } + tempObj = data["highTemperature"].toObject(); + if(!(tempObj.contains("value") && tempObj["value"].isString())) { + // error + qWarning() << "Could not find highTemperature.value parameter!"; + return false; + } + m_weatherHighTemperature = tempObj["value"].toString(); + + if(!(tempObj.contains("arrow") && tempObj["arrow"].isObject())) { + // error + qWarning() << "Could not find highTemperature.arrow parameter!"; + return false; + } + imageObj = tempObj["arrow"].toObject(); + if(!parseImageMetadata(imageObj, + m_weatherHighTemperatureArrowSmallUrl, + m_weatherHighTemperatureArrowMediumUrl, + m_weatherHighTemperatureArrowLargeUrl, + &m_weatherHighTemperatureArrowSmallDarkBgUrl, + &m_weatherHighTemperatureArrowMediumDarkBgUrl, + &m_weatherHighTemperatureArrowLargeDarkBgUrl)) { + qWarning() << "Could not parse highTemperature.arrow parameter!"; + return false; + } + + return true; + +} + +void GuiMetadata::onConnected() +{ + QStringListIterator eventIterator(events); + GuiMetadataCapabilityMessage *tmsg; + + tmsg = new GuiMetadataCapabilityMessage(); + QJsonObject parameter; + QJsonArray actions; + while (eventIterator.hasNext()) { + actions.append(QJsonValue(eventIterator.next())); + } + parameter.insert("actions", actions); + tmsg->createRequest("guimetadata/subscribe", parameter); + m_mloop->sendMessage(tmsg); + delete tmsg; +} + +void GuiMetadata::onDisconnected() +{ + // vshl-capabilities currently has no unsubscribe verb... +} + +void GuiMetadata::onMessageReceived(MessageType type, Message *message) +{ + if (type == GuiMetadataCapabilityEventMessage) { + GuiMetadataCapabilityMessage *tmsg = qobject_cast<GuiMetadataCapabilityMessage*>(message); + if (tmsg->isEvent()) { + if (tmsg->isGuiMetadataRenderTemplateEvent()) { + if(updateMetadata(tmsg->eventData())) + emit renderTemplate(); + } else if (tmsg->isGuiMetadataClearTemplateEvent()) { + emit clearTemplate(); + } + } + } + message->deleteLater(); +} |