mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 04:55:48 +03:00
Close call notifications when the call has been joined by the user, or the call has ended
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
parent
974939eb6f
commit
a504dbf965
9 changed files with 293 additions and 11 deletions
|
@ -72,6 +72,8 @@ set(client_SRCS
|
|||
application.cpp
|
||||
invalidfilenamedialog.h
|
||||
invalidfilenamedialog.cpp
|
||||
callstatechecker.h
|
||||
callstatechecker.cpp
|
||||
conflictdialog.h
|
||||
conflictdialog.cpp
|
||||
conflictsolver.h
|
||||
|
|
184
src/gui/callstatechecker.cpp
Normal file
184
src/gui/callstatechecker.cpp
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "callstatechecker.h"
|
||||
#include "account.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCallStateChecker, "nextcloud.gui.callstatechecker", QtInfoMsg)
|
||||
|
||||
constexpr int successStatusCode = 200;
|
||||
|
||||
CallStateChecker::CallStateChecker(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
void CallStateChecker::setup()
|
||||
{
|
||||
_notificationTimer.setSingleShot(true);
|
||||
_notificationTimer.setInterval(60 * 1000);
|
||||
connect(&_notificationTimer, &QTimer::timeout, this, &CallStateChecker::slotNotificationTimerElapsed);
|
||||
|
||||
_statusCheckTimer.setInterval(2 * 1000);
|
||||
connect(&_statusCheckTimer, &QTimer::timeout, this, &CallStateChecker::slotStatusCheckTimerElapsed);
|
||||
}
|
||||
|
||||
QString CallStateChecker::token() const
|
||||
{
|
||||
return _token;
|
||||
}
|
||||
|
||||
void CallStateChecker::setToken(const QString &token)
|
||||
{
|
||||
_token = token;
|
||||
Q_EMIT tokenChanged();
|
||||
reset();
|
||||
}
|
||||
|
||||
AccountState* CallStateChecker::accountState() const
|
||||
{
|
||||
return _accountState;
|
||||
}
|
||||
|
||||
void CallStateChecker::setAccountState(AccountState *state)
|
||||
{
|
||||
_accountState = state;
|
||||
Q_EMIT accountStateChanged();
|
||||
reset();
|
||||
}
|
||||
|
||||
bool CallStateChecker::checking() const
|
||||
{
|
||||
return _checking;
|
||||
}
|
||||
|
||||
void CallStateChecker::setChecking(const bool checking)
|
||||
{
|
||||
if(checking) {
|
||||
qCInfo(lcCallStateChecker) << "Starting to check state of call with token:" << _token;
|
||||
_notificationTimer.start();
|
||||
_statusCheckTimer.start();
|
||||
} else {
|
||||
qCInfo(lcCallStateChecker) << "Stopping checking of call state for call with token:" << _token;
|
||||
_notificationTimer.stop();
|
||||
_statusCheckTimer.stop();
|
||||
_stateCheckJob.clear();
|
||||
}
|
||||
|
||||
_checking = checking;
|
||||
Q_EMIT checkingChanged();
|
||||
}
|
||||
|
||||
void CallStateChecker::reset()
|
||||
{
|
||||
qCInfo(lcCallStateChecker, "Resetting call check");
|
||||
setChecking(false);
|
||||
setChecking(true);
|
||||
}
|
||||
|
||||
void CallStateChecker::slotNotificationTimerElapsed()
|
||||
{
|
||||
qCInfo(lcCallStateChecker) << "Notification timer elapsed, stopping call checking of call with token:" << _token;
|
||||
setChecking(false);
|
||||
Q_EMIT stopNotifying();
|
||||
}
|
||||
|
||||
void CallStateChecker::slotStatusCheckTimerElapsed()
|
||||
{
|
||||
// Don't run check if another check is still ongoing
|
||||
if (_stateCheckJob) {
|
||||
return;
|
||||
}
|
||||
|
||||
startCallStateCheck();
|
||||
}
|
||||
|
||||
bool CallStateChecker::isAccountServerVersion22OrLater() const
|
||||
{
|
||||
if(!_accountState || !_accountState->account()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto accountNcVersion = _accountState->account()->serverVersionInt();
|
||||
constexpr auto ncVersion22 = OCC::Account::makeServerVersion(22, 0, 0);
|
||||
|
||||
return accountNcVersion >= ncVersion22;
|
||||
}
|
||||
|
||||
void CallStateChecker::startCallStateCheck()
|
||||
{
|
||||
// check connectivity and credentials
|
||||
if (!(_accountState && _accountState->isConnected() &&
|
||||
_accountState->account() && _accountState->account()->credentials() &&
|
||||
_accountState->account()->credentials()->ready())) {
|
||||
qCInfo(lcCallStateChecker, "Could not connect, can't check call state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for token
|
||||
if(_token.isEmpty()) {
|
||||
qCInfo(lcCallStateChecker, "No call token set, can't check without it.");
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(lcCallStateChecker) << "Checking state of call with token: " << _token;
|
||||
|
||||
const auto spreedPath = QStringLiteral("ocs/v2.php/apps/spreed/");
|
||||
const auto callApiPath = isAccountServerVersion22OrLater() ? QStringLiteral("api/v4/call/") : QStringLiteral("api/v1/call/");
|
||||
const QString callPath = spreedPath + callApiPath + _token; // Make sure it's a QString and not a QStringBuilder
|
||||
|
||||
_stateCheckJob = new JsonApiJob(_accountState->account(), callPath, this);
|
||||
connect(_stateCheckJob.data(), &JsonApiJob::jsonReceived, this, &CallStateChecker::slotCallStateReceived);
|
||||
|
||||
_stateCheckJob->setVerb(JsonApiJob::Verb::Get);
|
||||
_stateCheckJob->start();
|
||||
}
|
||||
|
||||
void CallStateChecker::slotCallStateReceived(const QJsonDocument &json, const int statusCode)
|
||||
{
|
||||
if (statusCode != successStatusCode) {
|
||||
qCInfo(lcCallStateChecker) << "Failed to retrieve call state data. Server returned status code: " << statusCode;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto participantsJsonArray = json.object().value("ocs").toObject().value("data").toArray();
|
||||
|
||||
if (participantsJsonArray.empty()) {
|
||||
qCInfo(lcCallStateChecker, "Call has no participants and has therefore been abandoned.");
|
||||
Q_EMIT stopNotifying();
|
||||
setChecking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &participant : participantsJsonArray) {
|
||||
const auto participantDataObject = participant.toObject();
|
||||
const auto participantId = isAccountServerVersion22OrLater() ? participantDataObject.value("actorId").toString() : participantDataObject.value("userId").toString();
|
||||
|
||||
if (participantId == _accountState->account()->davUser()) {
|
||||
qCInfo(lcCallStateChecker, "Found own account ID in participants list, meaning call has been joined.");
|
||||
Q_EMIT stopNotifying();
|
||||
setChecking(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
70
src/gui/callstatechecker.h
Normal file
70
src/gui/callstatechecker.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "networkjobs.h"
|
||||
#include "accountstate.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class CallStateChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString token READ token WRITE setToken NOTIFY tokenChanged)
|
||||
Q_PROPERTY(AccountState* accountState READ accountState WRITE setAccountState NOTIFY accountStateChanged)
|
||||
Q_PROPERTY(bool checking READ checking WRITE setChecking NOTIFY checkingChanged)
|
||||
|
||||
public:
|
||||
explicit CallStateChecker(QObject *parent = nullptr);
|
||||
|
||||
QString token() const;
|
||||
AccountState* accountState() const;
|
||||
bool checking() const;
|
||||
|
||||
signals:
|
||||
void tokenChanged();
|
||||
void accountStateChanged();
|
||||
void checkingChanged();
|
||||
|
||||
void stopNotifying();
|
||||
|
||||
public slots:
|
||||
void setToken(const QString &token);
|
||||
void setAccountState(OCC::AccountState *accountState);
|
||||
void setChecking(const bool checking);
|
||||
|
||||
private slots:
|
||||
void slotStatusCheckTimerElapsed();
|
||||
void slotNotificationTimerElapsed();
|
||||
void slotCallStateReceived(const QJsonDocument &json, const int statusCode);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void setup();
|
||||
void startCallStateCheck();
|
||||
bool isAccountServerVersion22OrLater() const;
|
||||
|
||||
AccountState *_accountState = nullptr;
|
||||
QString _token;
|
||||
QTimer _statusCheckTimer; // How often we check the status of the call
|
||||
QTimer _notificationTimer; // How long we present the call notification for
|
||||
QPointer<JsonApiJob> _stateCheckJob;
|
||||
bool _checking = false;
|
||||
};
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#include "tray/trayimageprovider.h"
|
||||
#include "configfile.h"
|
||||
#include "accessmanager.h"
|
||||
#include "callstatechecker.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QGuiApplication>
|
||||
|
@ -98,6 +99,7 @@ Systray::Systray()
|
|||
);
|
||||
|
||||
qmlRegisterType<WheelHandler>("com.nextcloud.desktopclient", 1, 0, "WheelHandler");
|
||||
qmlRegisterType<CallStateChecker>("com.nextcloud.desktopclient", 1, 0, "CallStateChecker");
|
||||
|
||||
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
setUserNotificationCenterDelegate();
|
||||
|
@ -183,7 +185,7 @@ void Systray::setupContextMenu()
|
|||
});
|
||||
}
|
||||
|
||||
void Systray::createCallDialog(const Activity &callNotification)
|
||||
void Systray::createCallDialog(const Activity &callNotification, const AccountStatePtr accountState)
|
||||
{
|
||||
qCDebug(lcSystray) << "Starting a new call dialog for notification with id: " << callNotification._id << "with text: " << callNotification._subject;
|
||||
|
||||
|
@ -208,6 +210,7 @@ void Systray::createCallDialog(const Activity &callNotification)
|
|||
}
|
||||
|
||||
const QVariantMap initialProperties{
|
||||
{"accountState", QVariant::fromValue(accountState.data())},
|
||||
{"talkNotificationData", talkNotificationData},
|
||||
{"links", links},
|
||||
{"subject", callNotification._subject},
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
bool isOpen();
|
||||
QString windowTitle() const;
|
||||
bool useNormalWindow() const;
|
||||
void createCallDialog(const Activity &callNotification);
|
||||
void createCallDialog(const Activity &callNotification, const AccountStatePtr accountState);
|
||||
|
||||
Q_INVOKABLE void pauseResumeSync();
|
||||
Q_INVOKABLE bool syncIsPaused();
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Camila Ayres <camila@nextcloud.com>
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import Style 1.0
|
||||
|
@ -20,6 +35,7 @@ Window {
|
|||
readonly property string deleteIcon: svgImage.arg("delete")
|
||||
|
||||
// We set talkNotificationData, subject, and links properties in C++
|
||||
property var accountState: ({})
|
||||
property var talkNotificationData: ({})
|
||||
property string subject: ""
|
||||
property var links: []
|
||||
|
@ -29,6 +45,7 @@ Window {
|
|||
readonly property bool usingUserAvatar: root.talkNotificationData.userAvatar !== ""
|
||||
|
||||
function closeNotification() {
|
||||
callStateChecker.checking = false;
|
||||
ringSound.stop();
|
||||
root.close();
|
||||
}
|
||||
|
@ -45,14 +62,22 @@ Window {
|
|||
root.requestActivate();
|
||||
|
||||
ringSound.play();
|
||||
}
|
||||
callStateChecker.checking = true;
|
||||
}
|
||||
|
||||
CallStateChecker {
|
||||
id: callStateChecker
|
||||
token: root.talkNotificationData.conversationToken
|
||||
accountState: root.accountState
|
||||
|
||||
onStopNotifying: root.closeNotification()
|
||||
}
|
||||
|
||||
Audio {
|
||||
id: ringSound
|
||||
source: root.ringtonePath
|
||||
loops: 9 // about 45 seconds of audio playing
|
||||
audioRole: Audio.RingtoneRole
|
||||
onStopped: root.closeNotification()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -143,7 +143,7 @@ void User::slotBuildIncomingCallDialogs(const ActivityList &list)
|
|||
|
||||
if(systray) {
|
||||
for(const auto &activity : list) {
|
||||
systray->createCallDialog(activity);
|
||||
systray->createCallDialog(activity, _account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -657,11 +657,6 @@ int Account::serverVersionInt() const
|
|||
components.value(2).toInt());
|
||||
}
|
||||
|
||||
int Account::makeServerVersion(int majorVersion, int minorVersion, int patchVersion)
|
||||
{
|
||||
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
|
||||
}
|
||||
|
||||
bool Account::serverVersionUnsupported() const
|
||||
{
|
||||
if (serverVersionInt() == 0) {
|
||||
|
|
|
@ -223,7 +223,10 @@ public:
|
|||
*/
|
||||
int serverVersionInt() const;
|
||||
|
||||
static int makeServerVersion(int majorVersion, int minorVersion, int patchVersion);
|
||||
static constexpr int makeServerVersion(const int majorVersion, const int minorVersion, const int patchVersion) {
|
||||
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
|
||||
};
|
||||
|
||||
void setServerVersion(const QString &version);
|
||||
|
||||
/** Whether the server is too old.
|
||||
|
|
Loading…
Reference in a new issue