File indexing completed on 2024-06-02 05:18:53

0001 // SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "matrixbeacon.h"
0005 #include "matrixmanager.h"
0006 
0007 #include <Quotient/room.h>
0008 #include <Quotient/csapi/room_state.h>
0009 #include <Quotient/events/stateevent.h>
0010 
0011 #include <QDateTime>
0012 #include <QJsonObject>
0013 
0014 MatrixBeacon::MatrixBeacon(QObject *parent)
0015     : QObject(parent)
0016 {
0017 }
0018 
0019 MatrixBeacon::~MatrixBeacon()
0020 {
0021     stop();
0022 }
0023 
0024 Quotient::Connection *MatrixBeacon::connection() const
0025 {
0026     return m_connection;
0027 }
0028 
0029 void MatrixBeacon::setConnection(Quotient::Connection *connection)
0030 {
0031     if (m_connection == connection) {
0032         return;
0033     }
0034 
0035     m_connection = connection;
0036     Q_EMIT connectionChanged();
0037 }
0038 
0039 QString MatrixBeacon::roomId() const
0040 {
0041     return m_roomId;
0042 }
0043 
0044 void MatrixBeacon::setRoomId(const QString &roomId)
0045 {
0046     if (m_roomId == roomId) {
0047         return;
0048     }
0049 
0050     m_roomId = roomId;
0051     Q_EMIT roomIdChanged();
0052 }
0053 
0054 bool MatrixBeacon::isActive() const
0055 {
0056     return !m_beaconInfoId.isEmpty();
0057 }
0058 
0059 void MatrixBeacon::start(const QString &description)
0060 {
0061     if (!m_connection || m_roomId.isEmpty()) {
0062         return;
0063     }
0064 
0065     auto room = m_connection->room(m_roomId);
0066     if (!room) {
0067         qWarning() << "invalid room" << m_roomId;
0068         return;
0069     }
0070 
0071     // TODO beacon id according to MSC3757
0072     Quotient::StateEvent ev(QLatin1StringView("org.matrix.msc3672.beacon_info"), m_connection->userId(), QJsonObject{
0073         {QLatin1StringView("description"), description},
0074         {QLatin1StringView("live"), true},
0075         {QLatin1StringView("org.matrix.msc3488.ts"), QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()},
0076         {QLatin1StringView("timeout"), 300000},
0077         {QLatin1StringView("org.matrix.msc3488.asset"), QJsonObject{
0078             {QLatin1StringView("type"), QLatin1StringView("m.self")},
0079         }},
0080     });
0081     auto job = room->setState(ev);
0082     connect(job, &Quotient::SetRoomStateWithKeyJob::result, this, [job, this]() {
0083         qDebug() << job->errorString() << job->jsonData();
0084         if (job->error() == Quotient::BaseJob::NoError) {
0085             setBeaconInfoId(job->jsonData().value(QLatin1StringView("event_id")).toString());
0086             updateLocation(m_latitude, m_longitude);
0087         } else {
0088             qWarning() << job->errorString();
0089         }
0090     });
0091 }
0092 
0093 void MatrixBeacon::stop()
0094 {
0095     setBeaconInfoId(QString());
0096     if (!m_connection || m_roomId.isEmpty()) {
0097         return;
0098     }
0099 
0100     auto room = m_connection->room(m_roomId);
0101     if (!room) {
0102         qWarning() << "invalid room" << m_roomId;
0103         return;
0104     }
0105 
0106     Quotient::StateEvent ev(QLatin1StringView("org.matrix.msc3672.beacon_info"), m_connection->userId(), QJsonObject{
0107         {QLatin1StringView("live"), false},
0108         {QLatin1StringView("org.matrix.msc3488.ts"), QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()},
0109         {QLatin1StringView("timeout"), 300000},
0110         {QLatin1StringView("org.matrix.msc3488.asset"), QJsonObject{
0111             {QLatin1StringView("type"), QLatin1StringView("m.self")},
0112         }},
0113     });
0114     room->setState(ev);
0115 }
0116 
0117 static QString geoUri(float latitude, float longitude, float altitude)
0118 {
0119     QString uri = QLatin1StringView("geo:") + QString::number(latitude)
0120         + QLatin1Char(',') + QString::number(longitude);
0121     if (!std::isnan(altitude)) {
0122         uri += QLatin1Char(',') + QString::number(altitude);
0123     }
0124     return uri;
0125 }
0126 
0127 void MatrixBeacon::updateLocation(float latitude, float longitude, float heading, float speed, float altitude)
0128 {
0129     m_latitude = latitude;
0130     m_longitude = longitude;
0131     if (!m_connection || m_roomId.isEmpty() || !isActive() || std::isnan(m_latitude) || std::isnan(m_longitude)) {
0132         return;
0133     }
0134 
0135     auto room = m_connection->room(m_roomId);
0136     if (!room) {
0137         qWarning() << "invalid room" << m_roomId;
0138         return;
0139     }
0140 
0141     QJsonObject location({
0142         {QLatin1StringView("uri"), geoUri(m_latitude, m_longitude, altitude)}
0143     });
0144     if (!std::isnan(heading)) {
0145         location.insert(QLatin1StringView("org.kde.itinerary.heading"), QString::number(heading));
0146     }
0147     if (!std::isnan(speed)) {
0148         location.insert(QLatin1StringView("org.kde.itinerary.speed"), QString::number(speed));
0149     }
0150 
0151     QJsonObject content{
0152         {QLatin1StringView("msgtype"), QLatin1StringView("org.matrix.msc3672.beacon")},
0153         {QLatin1StringView("org.matrix.msc3488.ts"), QDateTime::currentDateTime().toMSecsSinceEpoch()},
0154         {QLatin1StringView("org.matrix.msc3488.location"), location},
0155         {QLatin1StringView("m.relates_to"), QJsonObject{
0156             {QLatin1StringView("rel_type"), QLatin1StringView("m.reference")},
0157             {QLatin1StringView("event_id"), m_beaconInfoId},
0158         }},
0159     };
0160     room->postJson(QLatin1StringView("org.matrix.msc3672.beacon"), content);
0161 }
0162 
0163 void MatrixBeacon::setBeaconInfoId(const QString& id)
0164 {
0165     if (m_beaconInfoId == id) {
0166         return;
0167     }
0168 
0169     m_beaconInfoId = id;
0170     Q_EMIT activeChanged();
0171 }
0172 
0173 #include "moc_matrixbeacon.cpp"