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"