mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-29 04:08:54 +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
|
application.cpp
|
||||||
invalidfilenamedialog.h
|
invalidfilenamedialog.h
|
||||||
invalidfilenamedialog.cpp
|
invalidfilenamedialog.cpp
|
||||||
|
callstatechecker.h
|
||||||
|
callstatechecker.cpp
|
||||||
conflictdialog.h
|
conflictdialog.h
|
||||||
conflictdialog.cpp
|
conflictdialog.cpp
|
||||||
conflictsolver.h
|
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 "tray/trayimageprovider.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "accessmanager.h"
|
#include "accessmanager.h"
|
||||||
|
#include "callstatechecker.h"
|
||||||
|
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
@ -98,6 +99,7 @@ Systray::Systray()
|
||||||
);
|
);
|
||||||
|
|
||||||
qmlRegisterType<WheelHandler>("com.nextcloud.desktopclient", 1, 0, "WheelHandler");
|
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)
|
#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||||
setUserNotificationCenterDelegate();
|
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;
|
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{
|
const QVariantMap initialProperties{
|
||||||
|
{"accountState", QVariant::fromValue(accountState.data())},
|
||||||
{"talkNotificationData", talkNotificationData},
|
{"talkNotificationData", talkNotificationData},
|
||||||
{"links", links},
|
{"links", links},
|
||||||
{"subject", callNotification._subject},
|
{"subject", callNotification._subject},
|
||||||
|
|
|
@ -85,7 +85,7 @@ public:
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
QString windowTitle() const;
|
QString windowTitle() const;
|
||||||
bool useNormalWindow() const;
|
bool useNormalWindow() const;
|
||||||
void createCallDialog(const Activity &callNotification);
|
void createCallDialog(const Activity &callNotification, const AccountStatePtr accountState);
|
||||||
|
|
||||||
Q_INVOKABLE void pauseResumeSync();
|
Q_INVOKABLE void pauseResumeSync();
|
||||||
Q_INVOKABLE bool syncIsPaused();
|
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 2.15
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
@ -20,6 +35,7 @@ Window {
|
||||||
readonly property string deleteIcon: svgImage.arg("delete")
|
readonly property string deleteIcon: svgImage.arg("delete")
|
||||||
|
|
||||||
// We set talkNotificationData, subject, and links properties in C++
|
// We set talkNotificationData, subject, and links properties in C++
|
||||||
|
property var accountState: ({})
|
||||||
property var talkNotificationData: ({})
|
property var talkNotificationData: ({})
|
||||||
property string subject: ""
|
property string subject: ""
|
||||||
property var links: []
|
property var links: []
|
||||||
|
@ -29,6 +45,7 @@ Window {
|
||||||
readonly property bool usingUserAvatar: root.talkNotificationData.userAvatar !== ""
|
readonly property bool usingUserAvatar: root.talkNotificationData.userAvatar !== ""
|
||||||
|
|
||||||
function closeNotification() {
|
function closeNotification() {
|
||||||
|
callStateChecker.checking = false;
|
||||||
ringSound.stop();
|
ringSound.stop();
|
||||||
root.close();
|
root.close();
|
||||||
}
|
}
|
||||||
|
@ -45,6 +62,15 @@ Window {
|
||||||
root.requestActivate();
|
root.requestActivate();
|
||||||
|
|
||||||
ringSound.play();
|
ringSound.play();
|
||||||
|
callStateChecker.checking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallStateChecker {
|
||||||
|
id: callStateChecker
|
||||||
|
token: root.talkNotificationData.conversationToken
|
||||||
|
accountState: root.accountState
|
||||||
|
|
||||||
|
onStopNotifying: root.closeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio {
|
Audio {
|
||||||
|
@ -52,7 +78,6 @@ Window {
|
||||||
source: root.ringtonePath
|
source: root.ringtonePath
|
||||||
loops: 9 // about 45 seconds of audio playing
|
loops: 9 // about 45 seconds of audio playing
|
||||||
audioRole: Audio.RingtoneRole
|
audioRole: Audio.RingtoneRole
|
||||||
onStopped: root.closeNotification()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
|
@ -143,7 +143,7 @@ void User::slotBuildIncomingCallDialogs(const ActivityList &list)
|
||||||
|
|
||||||
if(systray) {
|
if(systray) {
|
||||||
for(const auto &activity : list) {
|
for(const auto &activity : list) {
|
||||||
systray->createCallDialog(activity);
|
systray->createCallDialog(activity, _account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -657,11 +657,6 @@ int Account::serverVersionInt() const
|
||||||
components.value(2).toInt());
|
components.value(2).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
int Account::makeServerVersion(int majorVersion, int minorVersion, int patchVersion)
|
|
||||||
{
|
|
||||||
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Account::serverVersionUnsupported() const
|
bool Account::serverVersionUnsupported() const
|
||||||
{
|
{
|
||||||
if (serverVersionInt() == 0) {
|
if (serverVersionInt() == 0) {
|
||||||
|
|
|
@ -223,7 +223,10 @@ public:
|
||||||
*/
|
*/
|
||||||
int serverVersionInt() const;
|
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);
|
void setServerVersion(const QString &version);
|
||||||
|
|
||||||
/** Whether the server is too old.
|
/** Whether the server is too old.
|
||||||
|
|
Loading…
Reference in a new issue