diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 6bf24fbcc..1ae55bca2 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -26,8 +26,10 @@ #include #include #include +#include #include #include +#include #include #include "core/account/AccountCore.hpp" @@ -45,11 +47,14 @@ #include "core/singleapplication/singleapplication.h" #include "model/object/VariantObject.hpp" #include "tool/Constants.hpp" +#include "tool/EnumsToString.hpp" #include "tool/Utils.hpp" #include "tool/providers/AvatarProvider.hpp" #include "tool/providers/ImageProvider.hpp" #include "tool/thread/Thread.hpp" +DEFINE_ABSTRACT_OBJECT(App) + App::App(int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { mLinphoneThread = new Thread(this); @@ -83,7 +88,7 @@ void App::init() { if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true); if (!mLinphoneThread->isRunning()) { - qDebug() << "[App] Starting Thread"; + qDebug() << log().arg("Starting Thread"); mLinphoneThread->start(); } setQuitOnLastWindowClosed(true); // TODO: use settings to set it @@ -96,7 +101,7 @@ void App::init() { if (version.majorVersion() == 5 && version.minorVersion() == 9) selectors.push_back("5.9"); auto selector = new QQmlFileSelector(mEngine, mEngine); selector->setExtraSelectors(selectors); - qInfo() << QStringLiteral("[App] Activated selectors:") << selector->selector()->allSelectors(); + qInfo() << log().arg("Activated selectors:") << selector->selector()->allSelectors(); mEngine->addImportPath(":/"); mEngine->rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath()); @@ -110,9 +115,9 @@ void App::init() { const QUrl url(u"qrc:/Linphone/view/App/Main.qml"_qs); QObject::connect( mEngine, &QQmlApplicationEngine::objectCreated, this, - [url](QObject *obj, const QUrl &objUrl) { + [this, url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) { - qCritical() << "[App] Main.qml couldn't be load. The app will exit"; + qCritical() << log().arg("Main.qml couldn't be load. The app will exit"); exit(-1); } }, @@ -129,6 +134,9 @@ void App::initCppInterfaces() { [](QQmlEngine *engine, QJSEngine *) -> QObject * { return new Constants(engine); }); qmlRegisterSingletonType("UtilsCpp", 1, 0, "UtilsCpp", [](QQmlEngine *engine, QJSEngine *) -> QObject * { return new Utils(engine); }); + qmlRegisterSingletonType( + "EnumsToStringCpp", 1, 0, "EnumsToStringCpp", + [](QQmlEngine *engine, QJSEngine *) -> QObject * { return new EnumsToString(engine); }); qmlRegisterType(Constants::MainQmlUri, 1, 0, "PhoneNumberProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "VariantObject"); @@ -189,9 +197,70 @@ bool App::notify(QObject *receiver, QEvent *event) { try { done = QApplication::notify(receiver, event); } catch (const std::exception &ex) { - qCritical() << "[App] Exception has been catch in notify"; + qCritical() << log().arg("Exception has been catch in notify"); } catch (...) { - qCritical() << "[App] Generic exeption has been catch in notify"; + qCritical() << log().arg("Generic exeption has been catch in notify"); } return done; } + +QQuickWindow *App::getCallsWindow(QVariant callGui) { + mustBeInMainThread(getClassName()); + if (!mCallsWindow) { + const QUrl callUrl("qrc:/Linphone/view/App/CallsWindow.qml"); + + qInfo() << log().arg("Creating subwindow: `%1`.").arg(callUrl.toString()); + + QQmlComponent component(mEngine, callUrl); + if (component.isError()) { + qWarning() << component.errors(); + abort(); + } + qInfo() << log().arg("Subwindow status: `%1`.").arg(component.status()); + + QObject *object = component.createWithInitialProperties({{"call", callGui}}); + Q_ASSERT(object); + if (!object) { + qCritical() << log().arg("Calls window could not be created."); + return nullptr; + } + + // QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); + object->setParent(mEngine); + + auto window = qobject_cast(object); + Q_ASSERT(window); + if (!window) { + qCritical() << log().arg("Calls window could not be created."); + return nullptr; + } + mCallsWindow = window; + } + + postModelAsync([this]() { + auto core = CoreModel::getInstance()->getCore(); + auto callsNb = core->getCallsNb(); + postCoreAsync([this, callsNb] { mCallsWindow->setProperty("callsCount", callsNb); }); + }); + mCallsWindow->setProperty("call", callGui); + return mCallsWindow; +} + +void App::closeCallsWindow() { + if (mCallsWindow) { + mCallsWindow->close(); + mCallsWindow->deleteLater(); + mCallsWindow = nullptr; + } +} + +void App::smartShowWindow(QQuickWindow *window) { + if (!window) return; + window->setVisible(true); + // Force show, maybe redundant with setVisible + if (window->visibility() == QWindow::Maximized) // Avoid to change visibility mode + window->showMaximized(); + else window->show(); + window->raise(); // Raise ensure to get focus on Mac + window->requestActivate(); +} diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index afd9f6f4b..a65a0ae08 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -24,11 +24,14 @@ #include "core/singleapplication/singleapplication.h" #include "model/core/CoreModel.hpp" +#include "tool/AbstractObject.hpp" +class CallGui; class Thread; class Notifier; +class QQuickWindow; -class App : public SingleApplication { +class App : public SingleApplication, public AbstractObject { public: App(int &argc, char *argv[]); static App *getInstance(); @@ -53,6 +56,20 @@ public: QMetaObject::invokeMethod(App::getInstance(), callable); } template + static auto postCoreSync(Func &&callable, Args &&...args) { + if (QThread::currentThread() == CoreModel::getInstance()->thread()) { + bool end = false; + postCoreAsync([&end, callable, args...]() mutable { + QMetaObject::invokeMethod(App::getInstance(), callable, args..., Qt::DirectConnection); + end = true; + }); + while (!end) + qApp->processEvents(); + } else { + QMetaObject::invokeMethod(App::getInstance(), callable, Qt::DirectConnection); + } + } + template static auto postModelSync(Func &&callable, Args &&...args) { if (QThread::currentThread() != CoreModel::getInstance()->thread()) { bool end = false; @@ -73,8 +90,13 @@ public: void onLoggerInitialized(); + QQuickWindow *getCallsWindow(QVariant callGui); + void closeCallsWindow(); + + Q_INVOKABLE static void smartShowWindow(QQuickWindow *window); + QQmlApplicationEngine *mEngine = nullptr; - bool notify(QObject *receiver, QEvent *event); + bool notify(QObject *receiver, QEvent *event) override; enum class StatusCode { gRestartCode = 1000, gDeleteDataCode = 1001 }; @@ -84,4 +106,9 @@ private: QCommandLineParser *mParser = nullptr; Thread *mLinphoneThread = nullptr; Notifier *mNotifier = nullptr; + QQuickWindow *mCallsWindow = nullptr; + // TODO : changer ce count lorsqu'on aura liste d'appels + int callsCount = 0; + + DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index a5d0ef088..f20e2ed3a 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -21,6 +21,7 @@ #include "CallCore.hpp" #include "core/App.hpp" #include "model/object/VariantObject.hpp" +#include "model/tool/ToolModel.hpp" #include "tool/Utils.hpp" #include "tool/thread/SafeConnection.hpp" @@ -38,15 +39,25 @@ CallCore::CallCore(const std::shared_ptr &call) : QObject(nullpt App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); // Should be call from model Thread mustBeInLinphoneThread(getClassName()); + mDir = LinphoneEnums::fromLinphone(call->getDir()); + mCallModel = Utils::makeQObject_ptr(call); + mCallModel->setSelf(mCallModel); mDuration = call->getDuration(); mMicrophoneMuted = call->getMicrophoneMuted(); - mCallModel = Utils::makeQObject_ptr(call); - connect(mCallModel.get(), &CallModel::stateChanged, this, &CallCore::onStateChanged); - connect(this, &CallCore::lAccept, mCallModel.get(), &CallModel::accept); - connect(this, &CallCore::lDecline, mCallModel.get(), &CallModel::decline); - connect(this, &CallCore::lTerminate, mCallModel.get(), &CallModel::terminate); - mCallModel->setSelf(mCallModel); + // mSpeakerMuted = call->getSpeakerMuted(); + mCameraEnabled = call->cameraEnabled(); + mDuration = call->getDuration(); mState = LinphoneEnums::fromLinphone(call->getState()); + mPeerAddress = Utils::coreStringToAppString(mCallModel->getRemoteAddress()->asString()); + mStatus = LinphoneEnums::fromLinphone(call->getCallLog()->getStatus()); + mTransferState = LinphoneEnums::fromLinphone(call->getTransferState()); + auto encryption = LinphoneEnums::fromLinphone(call->getCurrentParams()->getMediaEncryption()); + auto tokenVerified = mCallModel->getAuthenticationTokenVerified(); + mPeerSecured = (encryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) || + encryption == LinphoneEnums::MediaEncryption::Srtp || + encryption == LinphoneEnums::MediaEncryption::Dtls; + mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused || + mState == LinphoneEnums::CallState::PausedByRemote; } CallCore::~CallCore() { @@ -65,9 +76,72 @@ void CallCore::setSelf(QSharedPointer me) { mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::microphoneMutedChanged, [this](bool isMuted) { mAccountModelConnection->invokeToCore([this, isMuted]() { setMicrophoneMuted(isMuted); }); }); + // mAccountModelConnection->makeConnect(this, &CallCore::lSetSpeakerMuted, [this](bool isMuted) { + // mAccountModelConnection->invokeToModel([this, isMuted]() { mCallModel->setSpeakerMuted(isMuted); }); + // }); + // mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::speakerMutedChanged, [this](bool isMuted) { + // mAccountModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); }); + // }); + mAccountModelConnection->makeConnect(this, &CallCore::lSetCameraEnabled, [this](bool enabled) { + mAccountModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); }); + }); + mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::cameraEnabledChanged, [this](bool enabled) { + mAccountModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); }); + }); mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::durationChanged, [this](int duration) { mAccountModelConnection->invokeToCore([this, duration]() { setDuration(duration); }); }); + connect(mCallModel.get(), &CallModel::stateChanged, this, + [this](linphone::Call::State state, const std::string &message) { + mAccountModelConnection->invokeToCore([this, state, message]() { + setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); + }); + }); + connect(mCallModel.get(), &CallModel::statusChanged, this, [this](linphone::Call::Status status) { + mAccountModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); }); + }); + mAccountModelConnection->makeConnect(this, &CallCore::lSetPaused, [this](bool paused) { + mAccountModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); }); + }); + mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::pausedChanged, [this](bool paused) { + mAccountModelConnection->invokeToCore([this, paused]() { setPaused(paused); }); + }); + + mAccountModelConnection->makeConnect(this, &CallCore::lTransferCall, [this](const QString &address) { + mAccountModelConnection->invokeToModel( + [this, address]() { mCallModel->transferTo(ToolModel::interpretUrl(address)); }); + }); + mAccountModelConnection->makeConnect( + mCallModel.get(), &CallModel::transferStateChanged, + [this](const std::shared_ptr &call, linphone::Call::State state) { + mAccountModelConnection->invokeToCore([this, state]() { + QString message; + if (state == linphone::Call::State::Error) { + message = "L'appel n'a pas pu être transféré."; + } + setTransferState(LinphoneEnums::fromLinphone(state), message); + }); + }); + mAccountModelConnection->makeConnect( + mCallModel.get(), &CallModel::encryptionChanged, + [this](const std::shared_ptr &call, bool on, const std::string &authenticationToken) { + auto encryption = LinphoneEnums::fromLinphone(call->getCurrentParams()->getMediaEncryption()); + auto tokenVerified = mCallModel->getAuthenticationTokenVerified(); + mAccountModelConnection->invokeToCore([this, call, encryption, tokenVerified]() { + setPeerSecured((encryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) || + encryption == LinphoneEnums::MediaEncryption::Srtp || + encryption == LinphoneEnums::MediaEncryption::Dtls); + }); + }); + mAccountModelConnection->makeConnect(this, &CallCore::lAccept, [this](bool withVideo) { + mAccountModelConnection->invokeToModel([this, withVideo]() { mCallModel->accept(withVideo); }); + }); + mAccountModelConnection->makeConnect(this, &CallCore::lDecline, [this]() { + mAccountModelConnection->invokeToModel([this]() { mCallModel->decline(); }); + }); + mAccountModelConnection->makeConnect(this, &CallCore::lTerminate, [this]() { + mAccountModelConnection->invokeToModel([this]() { mCallModel->terminate(); }); + }); } LinphoneEnums::CallStatus CallCore::getStatus() const { @@ -82,6 +156,18 @@ void CallCore::setStatus(LinphoneEnums::CallStatus status) { } } +LinphoneEnums::CallDir CallCore::getDir() const { + return mDir; +} + +void CallCore::setDir(LinphoneEnums::CallDir dir) { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); + if (mDir != dir) { + mDir = dir; + emit dirChanged(mDir); + } +} + LinphoneEnums::CallState CallCore::getState() const { return mState; } @@ -95,10 +181,6 @@ void CallCore::setState(LinphoneEnums::CallState state, const QString &message) } } -void CallCore::onStateChanged(linphone::Call::State state, const std::string &message) { - setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); -} - QString CallCore::getLastErrorMessage() const { return mLastErrorMessage; } @@ -130,3 +212,47 @@ void CallCore::setMicrophoneMuted(bool isMuted) { emit microphoneMutedChanged(); } } + +bool CallCore::getCameraEnabled() const { + return mCameraEnabled; +} + +void CallCore::setCameraEnabled(bool enabled) { + if (mCameraEnabled != enabled) { + mCameraEnabled = enabled; + emit cameraEnabledChanged(); + } +} + +bool CallCore::getPaused() const { + return mPaused; +} + +void CallCore::setPaused(bool paused) { + if (mPaused != paused) { + mPaused = paused; + emit pausedChanged(); + } +} + +bool CallCore::getPeerSecured() const { + return mPeerSecured; +} +void CallCore::setPeerSecured(bool secured) { + if (mPeerSecured != secured) { + mPeerSecured = secured; + emit peerSecuredChanged(); + } +} + +LinphoneEnums::CallState CallCore::getTransferState() const { + return mTransferState; +} + +void CallCore::setTransferState(LinphoneEnums::CallState state, const QString &message) { + if (mTransferState != state) { + mTransferState = state; + if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message); + emit transferStateChanged(); + } +} \ No newline at end of file diff --git a/Linphone/core/call/CallCore.hpp b/Linphone/core/call/CallCore.hpp index 8fae5b676..de056d003 100644 --- a/Linphone/core/call/CallCore.hpp +++ b/Linphone/core/call/CallCore.hpp @@ -32,11 +32,18 @@ class SafeConnection; class CallCore : public QObject, public AbstractObject { Q_OBJECT + // Q_PROPERTY(QString peerDisplayName MEMBER mPeerDisplayName) Q_PROPERTY(LinphoneEnums::CallStatus status READ getStatus NOTIFY statusChanged) + Q_PROPERTY(LinphoneEnums::CallDir dir READ getDir NOTIFY dirChanged) Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged) Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged) - Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged); + Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged) Q_PROPERTY(bool microphoneMuted READ getMicrophoneMuted WRITE lSetMicrophoneMuted NOTIFY microphoneMutedChanged) + Q_PROPERTY(bool cameraEnabled READ getCameraEnabled WRITE lSetCameraEnabled NOTIFY cameraEnabledChanged) + Q_PROPERTY(bool paused READ getPaused WRITE lSetPaused NOTIFY pausedChanged) + Q_PROPERTY(QString peerAddress MEMBER mPeerAddress CONSTANT) + Q_PROPERTY(bool peerSecured READ getPeerSecured WRITE setPeerSecured NOTIFY peerSecuredChanged) + Q_PROPERTY(LinphoneEnums::CallState transferState READ getTransferState NOTIFY transferStateChanged) public: // Should be call from model Thread. Will be automatically in App thread after initialization @@ -48,9 +55,11 @@ public: LinphoneEnums::CallStatus getStatus() const; void setStatus(LinphoneEnums::CallStatus status); + LinphoneEnums::CallDir getDir() const; + void setDir(LinphoneEnums::CallDir dir); + LinphoneEnums::CallState getState() const; void setState(LinphoneEnums::CallState state, const QString &message); - void onStateChanged(linphone::Call::State state, const std::string &message); QString getLastErrorMessage() const; void setLastErrorMessage(const QString &message); @@ -61,18 +70,39 @@ public: bool getMicrophoneMuted() const; void setMicrophoneMuted(bool isMuted); + bool getCameraEnabled() const; + void setCameraEnabled(bool enabled); + + bool getPaused() const; + void setPaused(bool paused); + + bool getPeerSecured() const; + void setPeerSecured(bool secured); + + LinphoneEnums::CallState getTransferState() const; + void setTransferState(LinphoneEnums::CallState state, const QString &message); + signals: void statusChanged(LinphoneEnums::CallStatus status); void stateChanged(LinphoneEnums::CallState state); + void dirChanged(LinphoneEnums::CallDir dir); void lastErrorMessageChanged(); + void peerAddressChanged(); void durationChanged(int duration); void microphoneMutedChanged(); + void cameraEnabledChanged(); + void pausedChanged(); + void transferStateChanged(); + void peerSecuredChanged(); // Linphone commands void lAccept(bool withVideo); // Accept an incoming call void lDecline(); // Decline an incoming call void lTerminate(); // Hangup a call void lSetMicrophoneMuted(bool isMuted); + void lSetCameraEnabled(bool enabled); + void lSetPaused(bool paused); + void lTransferCall(const QString &dest); /* TODO Q_INVOKABLE void acceptWithVideo(); @@ -99,9 +129,15 @@ private: std::shared_ptr mCallModel; LinphoneEnums::CallStatus mStatus; LinphoneEnums::CallState mState; + LinphoneEnums::CallState mTransferState; + LinphoneEnums::CallDir mDir; QString mLastErrorMessage; + QString mPeerAddress; + bool mPeerSecured; int mDuration = 0; bool mMicrophoneMuted; + bool mCameraEnabled; + bool mPaused = false; QSharedPointer mAccountModelConnection; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/data/CMakeLists.txt b/Linphone/data/CMakeLists.txt index 8850549be..b2a88611f 100644 --- a/Linphone/data/CMakeLists.txt +++ b/Linphone/data/CMakeLists.txt @@ -25,6 +25,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc "data/image/phone-selected.svg" "data/image/phone-plus.svg" "data/image/phone-disconnect.svg" + "data/image/phone-transfer.svg" "data/image/address-book.svg" "data/image/address-book-selected.svg" "data/image/chat-teardrop-text.svg" @@ -39,12 +40,23 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc "data/image/magnifying-glass.svg" "data/image/backspace-fill.svg" "data/image/x.svg" + "data/image/play.svg" "data/image/incoming_call.svg" "data/image/incoming_call_missed.svg" "data/image/incoming_call_rejected.svg" "data/image/outgoing_call.svg" "data/image/outgoing_call_missed.svg" "data/image/outgoing_call_rejected.svg" + "data/image/microphone.svg" + "data/image/microphone-slash.svg" + "data/image/video-camera.svg" + "data/image/video-camera-slash.svg" + "data/image/speaker-high.svg" + "data/image/speaker-slash.svg" + "data/image/trusted.svg" + "data/image/randomAvatar.png" + "data/image/pause.svg" + "data/image/smiley.svg" data/shaders/roundEffect.vert.qsb data/shaders/roundEffect.frag.qsb diff --git a/Linphone/data/image/phone-transfer.svg b/Linphone/data/image/phone-transfer.svg new file mode 100644 index 000000000..0aff39292 --- /dev/null +++ b/Linphone/data/image/phone-transfer.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Linphone/data/image/randomAvatar.png b/Linphone/data/image/randomAvatar.png new file mode 100644 index 000000000..5508dcf41 Binary files /dev/null and b/Linphone/data/image/randomAvatar.png differ diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index c4417fa4a..cd4321af1 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -23,6 +23,7 @@ #include #include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" DEFINE_ABSTRACT_OBJECT(CallModel) @@ -41,7 +42,6 @@ CallModel::~CallModel() { void CallModel::accept(bool withVideo) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - auto core = CoreModel::getInstance()->getCore(); auto params = core->createCallParams(mMonitor); params->enableVideo(withVideo); @@ -68,12 +68,53 @@ void CallModel::terminate() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->terminate(); } +void CallModel::setPaused(bool paused) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (paused) { + auto status = mMonitor->pause(); + if (status != -1) emit pausedChanged(paused); + } else { + auto status = mMonitor->resume(); + if (status != -1) emit pausedChanged(paused); + } +} + +void CallModel::transferTo(const std::shared_ptr &address) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (mMonitor->transferTo(address) == -1) + qWarning() << log() + .arg(QStringLiteral("Unable to transfer: `%1`.")) + .arg(Utils::coreStringToAppString(address->asString())); +} void CallModel::setMicrophoneMuted(bool isMuted) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setMicrophoneMuted(isMuted); emit microphoneMutedChanged(isMuted); } + +void CallModel::setSpeakerMuted(bool isMuted) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mMonitor->setSpeakerMuted(isMuted); + emit speakerMutedChanged(isMuted); +} + +void CallModel::setCameraEnabled(bool enabled) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mMonitor->enableCamera(enabled); + emit cameraEnabledChanged(enabled); +} + +std::shared_ptr CallModel::getRemoteAddress() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return mMonitor->getRemoteAddress(); +} + +bool CallModel::getAuthenticationTokenVerified() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return mMonitor->getAuthenticationTokenVerified(); +} + void CallModel::onDtmfReceived(const std::shared_ptr &call, int dtmf) { emit dtmfReceived(call, dtmf); } @@ -108,6 +149,14 @@ void CallModel::onStateChanged(const std::shared_ptr &call, emit stateChanged(state, message); } +void CallModel::onStatusChanged(const std::shared_ptr &call, linphone::Call::Status status) { + emit statusChanged(status); +} + +void CallModel::onDirChanged(const std::shared_ptr &call, linphone::Call::Dir dir) { + emit dirChanged(dir); +} + void CallModel::onStatsUpdated(const std::shared_ptr &call, const std::shared_ptr &stats) { emit statsUpdated(call, stats); diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp index 009c0ddf9..50ab567b6 100644 --- a/Linphone/model/call/CallModel.hpp +++ b/Linphone/model/call/CallModel.hpp @@ -41,10 +41,20 @@ public: void terminate(); void setMicrophoneMuted(bool isMuted); + void setSpeakerMuted(bool isMuted); + void setCameraEnabled(bool enabled); + void setPaused(bool paused); + void transferTo(const std::shared_ptr &address); + + std::shared_ptr getRemoteAddress(); + bool getAuthenticationTokenVerified(); signals: void microphoneMutedChanged(bool isMuted); + void speakerMutedChanged(bool isMuted); + void cameraEnabledChanged(bool enabled); void durationChanged(int); + void pausedChanged(bool paused); private: QTimer mDurationTimer; @@ -67,6 +77,8 @@ private: virtual void onStateChanged(const std::shared_ptr &call, linphone::Call::State state, const std::string &message) override; + virtual void onStatusChanged(const std::shared_ptr &call, linphone::Call::Status status); + virtual void onDirChanged(const std::shared_ptr &call, linphone::Call::Dir dir); virtual void onStatsUpdated(const std::shared_ptr &call, const std::shared_ptr &stats) override; virtual void onTransferStateChanged(const std::shared_ptr &call, @@ -94,6 +106,8 @@ signals: void infoMessageReceived(const std::shared_ptr &call, const std::shared_ptr &message); void stateChanged(linphone::Call::State state, const std::string &message); + void statusChanged(linphone::Call::Status status); + void dirChanged(linphone::Call::Dir dir); void statsUpdated(const std::shared_ptr &call, const std::shared_ptr &stats); void transferStateChanged(const std::shared_ptr &call, linphone::Call::State state); diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 02037a705..1f44bc02d 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -74,6 +74,7 @@ void CoreModel::start() { setPathAfterStart(); mCore->enableFriendListSubscription(true); mCore->enableRecordAware(true); + mCore->getCallsNb(); mIterateTimer->start(); } // ----------------------------------------------------------------------------- diff --git a/Linphone/tool/CMakeLists.txt b/Linphone/tool/CMakeLists.txt index 49ecefbba..5b1a32b3e 100644 --- a/Linphone/tool/CMakeLists.txt +++ b/Linphone/tool/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND _LINPHONEAPP_SOURCES tool/Constants.cpp + tool/EnumsToString.cpp tool/Utils.cpp tool/LinphoneEnums.cpp diff --git a/Linphone/tool/EnumsToString.cpp b/Linphone/tool/EnumsToString.cpp new file mode 100644 index 000000000..a02082d8e --- /dev/null +++ b/Linphone/tool/EnumsToString.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "EnumsToString.hpp" + +#include "core/App.hpp" +#include "model/call/CallModel.hpp" +#include "model/object/VariantObject.hpp" +#include "model/tool/ToolModel.hpp" + +// ============================================================================= diff --git a/Linphone/tool/EnumsToString.hpp b/Linphone/tool/EnumsToString.hpp new file mode 100644 index 000000000..bf80f2f2e --- /dev/null +++ b/Linphone/tool/EnumsToString.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ENUMSTOSTRING_H_ +#define ENUMSTOSTRING_H_ + +#include +#include + +#include "LinphoneEnums.hpp" + +// ============================================================================= + +/*** + * Class to make the link between qml and LinphoneEnums functions + * TODO : transform LinphoneEnums into a class so we can delete this one + */ + +class EnumsToString : public QObject { + Q_OBJECT +public: + EnumsToString(QObject *parent = nullptr) : QObject(parent) { + } + + Q_INVOKABLE QString dirToString(const LinphoneEnums::CallDir &data) { + return LinphoneEnums::toString(data); + } + Q_INVOKABLE QString statusToString(const LinphoneEnums::CallStatus &data) { + return LinphoneEnums::toString(data); + } +}; + +#endif // ENUMSTOSTRING_H_ diff --git a/Linphone/tool/LinphoneEnums.cpp b/Linphone/tool/LinphoneEnums.cpp index 8abf50e2f..686094710 100644 --- a/Linphone/tool/LinphoneEnums.cpp +++ b/Linphone/tool/LinphoneEnums.cpp @@ -115,6 +115,22 @@ QString LinphoneEnums::toString(const LinphoneEnums::CallStatus &data) { } } +LinphoneEnums::CallDir LinphoneEnums::fromLinphone(const linphone::Call::Dir &data) { + return static_cast(data); +} + +linphone::Call::Dir LinphoneEnums::toLinphone(const LinphoneEnums::CallDir &data) { + return static_cast(data); +} +QString LinphoneEnums::toString(const LinphoneEnums::CallDir &data) { + switch (data) { + case LinphoneEnums::CallDir::Incoming: + return "Incoming"; + case LinphoneEnums::CallDir::Outgoing: + return "Outgoing"; + } +} + linphone::Conference::Layout LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceLayout &layout) { if (layout != LinphoneEnums::ConferenceLayout::AudioOnly) return static_cast(layout); else return linphone::Conference::Layout::Grid; // Audio Only mode diff --git a/Linphone/tool/LinphoneEnums.hpp b/Linphone/tool/LinphoneEnums.hpp index abc31f53a..22de7a6c9 100644 --- a/Linphone/tool/LinphoneEnums.hpp +++ b/Linphone/tool/LinphoneEnums.hpp @@ -159,6 +159,13 @@ linphone::Call::Status toLinphone(const LinphoneEnums::CallStatus &data); LinphoneEnums::CallStatus fromLinphone(const linphone::Call::Status &data); QString toString(const LinphoneEnums::CallStatus &data); +enum class CallDir { Outgoing = int(linphone::Call::Dir::Outgoing), Incoming = int(linphone::Call::Dir::Incoming) }; +Q_ENUM_NS(CallDir) + +linphone::Call::Dir toLinphone(const LinphoneEnums::CallDir &data); +LinphoneEnums::CallDir fromLinphone(const linphone::Call::Dir &data); +QString toString(const LinphoneEnums::CallDir &data); + enum class ConferenceLayout { Grid = int(linphone::Conference::Layout::Grid), ActiveSpeaker = int(linphone::Conference::Layout::ActiveSpeaker), diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 9a850d10c..a9ccef066 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -27,6 +27,7 @@ #include "model/tool/ToolModel.hpp" #include "tool/providers/AvatarProvider.hpp" #include +#include // ============================================================================= @@ -81,7 +82,13 @@ VariantObject *Utils::createCall(const QString &sipAddress, data->makeRequest([sipAddress, prepareTransfertAddress, headers]() { auto call = ToolModel::createCall(sipAddress, prepareTransfertAddress, headers); if (call) { - return QVariant::fromValue(new CallGui(call)); + auto callGui = QVariant::fromValue(new CallGui(call)); + App::postCoreSync([callGui]() { + auto app = App::getInstance(); + auto window = app->getCallsWindow(callGui); + window->show(); + }); + return callGui; } else return QVariant(); }); data->requestValue(); @@ -89,6 +96,10 @@ VariantObject *Utils::createCall(const QString &sipAddress, return data; } +void Utils::closeCallsWindow() { + App::getInstance()->closeCallsWindow(); +} + VariantObject *Utils::haveAccount() { VariantObject *result = new VariantObject(); @@ -134,3 +145,36 @@ QString Utils::createAvatar(const QUrl &fileUrl) { } return fileUri; } + +QString Utils::formatElapsedTime(int seconds) { + // s, m, h, d, W, M, Y + // 1, 60, 3600, 86400, 604800, 2592000, 31104000 + auto y = floor(seconds / 31104000); + if (y > 0) return QString::number(y) + " years"; + auto M = floor(seconds / 2592000); + if (M > 0) return QString::number(M) + " months"; + auto w = floor(seconds / 604800); + if (w > 0) return QString::number(w) + " week"; + auto d = floor(seconds / 86400); + if (d > 0) return QString::number(d) + " days"; + + auto h = floor(seconds / 3600); + auto m = floor((seconds - h * 3600) / 60); + auto s = seconds - h * 3600 - m * 60; + + QString hours, min, sec; + + if (h < 10 && h > 0) { + hours = "0" + QString::number(h); + } + + if (m < 10) { + min = "0" + QString::number(m); + } + + if (s < 10) { + sec = "0" + QString::number(s); + } + + return (h == 0 ? "" : hours + ":") + min + ":" + sec; +} \ No newline at end of file diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 29bc6f843..3887097ab 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -40,6 +40,7 @@ #endif // if defined(__GNUC__) && __GNUC__ >= 7 #endif // ifndef UTILS_NO_BREAK +class QQuickWindow; class VariantObject; class Utils : public QObject { @@ -53,8 +54,10 @@ public: Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress, const QString &prepareTransfertAddress = "", const QHash &headers = {}); + Q_INVOKABLE static void closeCallsWindow(); Q_INVOKABLE static VariantObject *haveAccount(); Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path + Q_INVOKABLE static QString formatElapsedTime(int seconds); // Return the elapsed time formated static inline QString coreStringToAppString(const std::string &str) { if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str); diff --git a/Linphone/tool/providers/ImageProvider.cpp b/Linphone/tool/providers/ImageProvider.cpp index f49c61264..54c0e5dbd 100644 --- a/Linphone/tool/providers/ImageProvider.cpp +++ b/Linphone/tool/providers/ImageProvider.cpp @@ -45,6 +45,7 @@ ImageAsyncImageResponse::ImageAsyncImageResponse(const QString &id, const QSize QString path = ":/data/image/"; QStringList filters; filters << "*.svg"; + filters << "*.png"; QDir imageDir(path); if (!imageDir.exists()) { qDebug() << QStringLiteral("[ImageProvider] Dir doesn't exist: `%1`.").arg(path); diff --git a/Linphone/view/App/CallsWindow.qml b/Linphone/view/App/CallsWindow.qml new file mode 100644 index 000000000..fc9e8928d --- /dev/null +++ b/Linphone/view/App/CallsWindow.qml @@ -0,0 +1,531 @@ +import QtQuick 2.15 +import QtQuick.Layouts +import QtQuick.Effects +import QtQuick.Controls as Control +import Linphone +import EnumsToStringCpp 1.0 +import UtilsCpp 1.0 + +Window { + id: mainWindow + width: 1512 * DefaultStyle.dp + height: 982 * DefaultStyle.dp + + property CallGui call + + property bool isInContactList: false + + property int callsCount: 0 + onCallsCountChanged: console.log("calls count", callsCount) + + property var peerName: UtilsCpp.getDisplayName(call.core.peerAddress) + property string peerNameText: peerName ? peerName.value : "" + + // TODO : remove this, for debug only + property var callState: call && call.core.state + onCallStateChanged: { + console.log("State:", callState) + if (callState === LinphoneEnums.CallState.Error || callState === LinphoneEnums.CallState.End) { + endCall() + } + } + + onClosing: { + endCall() + } + + Timer { + id: autoCloseWindow + interval: 2000 + onTriggered: { + UtilsCpp.closeCallsWindow() + } + } + + function endCall() { + console.log("remaining calls before ending", mainWindow.callsCount) + callStatusText.text = qsTr("End of the call") + if (call) call.core.lTerminate() + if (callsCount === 1) { + bottomButtonsLayout.setButtonsEnabled(false) + autoCloseWindow.restart() + } + } + + component BottomButton : Button { + required property string enabledIcon + property string disabledIcon + id: bottomButton + enabled: call != undefined + padding: 18 * DefaultStyle.dp + checkable: true + background: Rectangle { + anchors.fill: parent + color: bottomButton.enabled + ? bottomButton.checked + ? disabledIcon + ? DefaultStyle.grey_0 + : DefaultStyle.main2_400 + : bottomButton.pressed + ? DefaultStyle.main2_400 + : DefaultStyle.grey_500 + : DefaultStyle.grey_600 + radius: 71 * DefaultStyle.dp + } + contentItem: EffectImage { + image.source: disabledIcon && bottomButton.checked ? disabledIcon : enabledIcon + anchors.fill: parent + image.width: 32 * DefaultStyle.dp + image.height: 32 * DefaultStyle.dp + colorizationColor: disabledIcon && bottomButton.checked ? DefaultStyle.main2_0 : DefaultStyle.grey_0 + } + } + Control.Popup { + id: waitingPopup + visible: mainWindow.call.core.transferState === LinphoneEnums.CallState.OutgoingInit + || mainWindow.call.core.transferState === LinphoneEnums.CallState.OutgoingProgress + || mainWindow.call.core.transferState === LinphoneEnums.CallState.OutgoingRinging || false + modal: true + closePolicy: Control.Popup.NoAutoClose + anchors.centerIn: parent + padding: 20 + background: Item { + + anchors.fill: parent + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.height + 2 + color: DefaultStyle.main1_500_main + radius: 15 + } + Rectangle { + id: mainBackground + anchors.fill: parent + radius: 15 + } + } + contentItem: ColumnLayout { + BusyIndicator{ + Layout.alignment: Qt.AlignHCenter + } + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Transfert en cours, veuillez patienter") + } + } + } + Control.Popup { + id: transferErrorPopup + visible: mainWindow.call.core.transferState === LinphoneEnums.CallState.Error + modal: true + closePolicy: Control.Popup.NoAutoClose + x : parent.x + parent.width - width + y : parent.y + parent.height - height + padding: 20 + background: Item { + + anchors.fill: parent + Rectangle { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.height + 2 + color: DefaultStyle.danger_500 + } + Rectangle { + id: transferErrorBackground + anchors.fill: parent + radius: 15 + } + MultiEffect { + anchors.fill: transferErrorBackground + shadowEnabled: true + shadowColor: DefaultStyle.grey_900 + shadowBlur: 10 + // shadowOpacity: 0.1 + } + } + contentItem: ColumnLayout { + Text { + text: qsTr("Erreur de transfert") + } + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Le transfert d'appel a échoué.") + } + } + } + + Rectangle { + anchors.fill: parent + color: DefaultStyle.ongoingCallWindowColor + ColumnLayout { + anchors.fill: parent + spacing: 5 + anchors.bottomMargin: 5 + Item { + Layout.margins: 10 + Layout.fillWidth: true + Layout.minimumHeight: 25 + RowLayout { + anchors.verticalCenter: parent.verticalCenter + spacing: 10 + EffectImage { + id: callStatusIcon + image.fillMode: Image.PreserveAspectFit + image.width: 15 + image.height: 15 + image.sourceSize.width: 15 + image.sourceSize.height: 15 + image.source: (mainWindow.call.core.state === LinphoneEnums.CallState.Paused + || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) + ? AppIcons.pause + : (mainWindow.callState === LinphoneEnums.CallState.End + || mainWindow.callState === LinphoneEnums.CallState.Released) + ? AppIcons.endCall + : mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing + ? AppIcons.outgoingCall + : AppIcons.incomingCall + colorizationColor: mainWindow.callState === LinphoneEnums.CallState.Paused + || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote || mainWindow.callState === LinphoneEnums.CallState.End + || mainWindow.callState === LinphoneEnums.CallState.Released ? DefaultStyle.danger_500 : undefined + } + Text { + id: callStatusText + text: (mainWindow.callState === LinphoneEnums.CallState.End || mainWindow.callState === LinphoneEnums.CallState.Released) + ? qsTr("End of the call") + : (mainWindow.callState === LinphoneEnums.CallState.Paused || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) + ? qsTr("Appel mis en pause") + : EnumsToStringCpp.dirToString(mainWindow.call.core.dir) + qsTr(" call") + color: DefaultStyle.grey_0 + font.bold: true + } + Rectangle { + visible: mainWindow.callState === LinphoneEnums.CallState.Connected + || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning + Layout.preferredHeight: parent.height + Layout.preferredWidth: 2 + } + Text { + text: UtilsCpp.formatElapsedTime(mainWindow.call.core.duration) + color: DefaultStyle.grey_0 + visible: mainWindow.callState === LinphoneEnums.CallState.Connected + || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning + } + } + + Control.Control { + anchors.centerIn: parent + topPadding: 8 + bottomPadding: 8 + leftPadding: 10 + rightPadding: 10 + visible: mainWindow.call.core.peerSecured + onVisibleChanged: console.log("peer secured", mainWindow.call.core.peerSecured) + background: Rectangle { + anchors.fill: parent + border.color: DefaultStyle.info_500_main + radius: 15 + } + contentItem: RowLayout { + Image { + source: AppIcons.trusted + Layout.preferredWidth: 15 + Layout.preferredHeight: 15 + sourceSize.width: 15 + sourceSize.height: 15 + fillMode: Image.PreserveAspectFit + } + Text { + text: "This call is completely secured" + color: DefaultStyle.info_500_main + } + } + } + } + RowLayout { + Control.Control { + id: centerItem + Layout.fillWidth: true + Layout.preferredWidth: 1059 * DefaultStyle.dp + Layout.fillHeight: true + Layout.leftMargin: 10 + Layout.rightMargin: 10 + Layout.alignment: Qt.AlignCenter + background: Rectangle { + anchors.fill: parent + color: DefaultStyle.ongoingCallBackgroundColor + radius: 15 + } + contentItem: Item { + anchors.fill: parent + StackLayout { + id: centerLayout + anchors.fill: parent + Connections { + target: mainWindow + onCallStateChanged: { + if (mainWindow.callState === LinphoneEnums.CallState.Error) { + centerLayout.currentIndex = 2 + } + } + } + Item { + id: audioCallItem + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + Timer { + id: secondsTimer + interval: 1000 + repeat: true + onTriggered: waitingTime.seconds += 1 + } + ColumnLayout { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 30 + visible: mainWindow.callState == LinphoneEnums.CallState.OutgoingInit + || mainWindow.callState == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.callState == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.callState == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.callState == LinphoneEnums.CallState.IncomingReceived + BusyIndicator { + indicatorColor: DefaultStyle.main2_100 + Layout.alignment: Qt.AlignHCenter + } + Text { + id: waitingTime + property int seconds + text: UtilsCpp.formatElapsedTime(seconds) + color: DefaultStyle.grey_0 + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.pointSize: DefaultStyle.ongoingCallElapsedTimeSize + Component.onCompleted: { + secondsTimer.restart() + } + } + } + ColumnLayout { + anchors.centerIn: parent + spacing: 2 + // Avatar { + // Layout.alignment: Qt.AlignCenter + // visible: mainWindow.isInContactList + // image.source: AppIcons.avatar + // size: 100 + // } + // DefaultAvatar { + // id: defaultAvatar + // Layout.alignment: Qt.AlignCenter + // visible: !mainWindow.isInContactList + // initials:{ + // var usernameList = mainWindow.peerNameText.split(' ') + // for (var i = 0; i < usernameList.length; ++i) { + // initials += usernameList[i][0] + // } + // } + // Connections { + // target: mainWindow + // onPeerNameChanged: { + // defaultAvatar.initials = "" + // var usernameList = mainWindow.peerName.value.split(' ') + // for (var i = 0; i < usernameList.length; ++i) { + // defaultAvatar.initials += usernameList[i][0] + // } + // } + // } + // width: 100 + // height: 100 + // } + Text { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 15 + visible: mainWindow.peerNameText.length > 0 + text: mainWindow.peerNameText + color: DefaultStyle.grey_0 + font { + pointSize: DefaultStyle.ongoingCallNameSize + capitalization: Font.Capitalize + } + } + Text { + Layout.alignment: Qt.AlignCenter + text: mainWindow.call.core.peerAddress + color: DefaultStyle.grey_0 + font.pointSize: DefaultStyle.ongoingCallAddressSize + } + } + } + Image { + id: videoCallItem + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + } + ColumnLayout { + id: userNotFoundLayout + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + Layout.alignment: Qt.AlignCenter + Text { + text: qsTr(mainWindow.call.core.lastErrorMessage) + Layout.alignment: Qt.AlignCenter + color: DefaultStyle.grey_0 + font.pointSize: DefaultStyle.ongoingCallNameSize + } + } + } + Text { + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: 10 + anchors.bottomMargin: 10 + text: mainWindow.peerNameText + color: DefaultStyle.grey_0 + font.pointSize: DefaultStyle.ongoingCallAddressSize + } + } + } + OngoingCallRightPanel { + id: rightPanel + Layout.fillHeight: true + Layout.preferredWidth: 393 * DefaultStyle.dp + property int currentIndex: 0 + Layout.rightMargin: 10 + visible: false + headerContent: StackLayout { + currentIndex: rightPanel.currentIndex + anchors.verticalCenter: parent.verticalCenter + Text { + color: DefaultStyle.mainPageTitleColor + text: qsTr("Transfert d'appel") + font.bold: true + } + } + contentItem: StackLayout { + currentIndex: rightPanel.currentIndex + ContactsList { + Layout.fillWidth: true + Layout.fillHeight: true + sideMargin: 10 + topMargin: 15 + groupCallVisible: false + searchBarColor: DefaultStyle.grey_0 + searchBarBorderColor: DefaultStyle.callRightPanelSearchBarBorderColor + onCallButtonPressed: (address) => { + mainWindow.call.core.lTransferCall(address) + } + } + } + } + } + GridLayout { + id: bottomButtonsLayout + rows: 1 + columns: 3 + Layout.alignment: Qt.AlignHCenter + layoutDirection: Qt.LeftToRight + columnSpacing: 20 + Connections { + target: mainWindow + onCallStateChanged: if (mainWindow.callState === LinphoneEnums.CallState.Connected || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning) { + bottomButtonsLayout.layoutDirection = Qt.RightToLeft + connectedCallButtons.visible = true + } + } + function setButtonsEnabled(enabled) { + for(var i=0; i < children.length; ++i) { + children[i].enabled = false + } + } + BottomButton { + Layout.row: 0 + enabledIcon: AppIcons.endCall + checkable: false + Layout.column: mainWindow.callState == LinphoneEnums.CallState.OutgoingInit + || mainWindow.callState == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.callState == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.callState == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.callState == LinphoneEnums.CallState.IncomingReceived + ? 0 : bottomButtonsLayout.columns - 1 + Layout.preferredWidth: 75 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + background: Rectangle { + anchors.fill: parent + color: DefaultStyle.danger_500 + radius: 71 * DefaultStyle.dp + } + onClicked: mainWindow.endCall() + } + RowLayout { + id: connectedCallButtons + visible: false + Layout.row: 0 + Layout.column: 1 + BottomButton { + Layout.preferredWidth: 55 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + background: Rectangle { + anchors.fill: parent + radius: 71 * DefaultStyle.dp + color: parent.enabled + ? parent.checked + ? DefaultStyle.success_500main + : parent.pressed + ? DefaultStyle.main2_400 + : DefaultStyle.grey_500 + : DefaultStyle.grey_600 + } + enabled: mainWindow.callState != LinphoneEnums.CallState.PausedByRemote + enabledIcon: enabled && checked ? AppIcons.play : AppIcons.pause + checked: mainWindow.call && (mainWindow.call.callState === LinphoneEnums.CallState.Paused + || mainWindow.call.callState === LinphoneEnums.CallState.PausedByRemote) || false + onClicked: mainWindow.call.core.lSetPaused(!mainWindow.call.core.paused) + } + BottomButton { + id: transferCallButton + enabledIcon: AppIcons.transferCall + Layout.preferredWidth: 55 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + onClicked: { + rightPanel.visible = !rightPanel.visible + rightPanel.currentIndex = 0 + } + } + } + RowLayout { + Layout.row: 0 + Layout.column: mainWindow.callState == LinphoneEnums.CallState.OutgoingInit + || mainWindow.callState == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.callState == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.callState == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.callState == LinphoneEnums.CallState.IncomingReceived + ? bottomButtonsLayout.columns - 1 : 0 + BottomButton { + enabled: false + enabledIcon: AppIcons.videoCamera + disabledIcon: AppIcons.videoCameraSlash + checked: !mainWindow.call.core.cameraEnabled + Layout.preferredWidth: 55 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + onClicked: mainWindow.call.core.lSetCameraEnabled(!mainWindow.call.core.cameraEnabled) + + } + BottomButton { + id: micButton + enabledIcon: AppIcons.microphone + disabledIcon: AppIcons.microphoneSlash + checked: mainWindow.call.core.microphoneMuted + Layout.preferredWidth: 55 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + onClicked: mainWindow.call.core.lSetMicrophoneMuted(!mainWindow.call.core.microphoneMuted) + } + } + } + } + } + +} \ No newline at end of file diff --git a/Linphone/view/App/Main.qml b/Linphone/view/App/Main.qml index 931f7c513..b9fcc0966 100644 --- a/Linphone/view/App/Main.qml +++ b/Linphone/view/App/Main.qml @@ -7,7 +7,7 @@ import Linphone Window { id: mainWindow width: 1512 * DefaultStyle.dp - height: 930 * DefaultStyle.dp + height: 982 * DefaultStyle.dp visible: true title: qsTr("Linphone") property bool firstConnection: true diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 239c6bfd8..c0ee430b9 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -1,11 +1,21 @@ list(APPEND _LINPHONEAPP_QML_FILES view/App/Main.qml + view/App/CallsWindow.qml view/App/Layout/LoginLayout.qml view/App/Layout/MainLayout.qml view/Item/Account/Accounts.qml + view/Item/Call/ContactsList.qml + view/Item/Call/OngoingCallRightPanel.qml + + view/Item/Notification/Notification.qml + view/Item/Notification/NotificationReceivedCall.qml + + view/Item/Prototype/CanvasCircle.qml + + view/Item/BusyIndicator.qml view/Item/Button.qml view/Item/Carousel.qml view/Item/CheckBox.qml @@ -17,10 +27,6 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Item/DesktopPopup.qml view/Item/DigitInput.qml - - view/Item/Notification/Notification.qml - view/Item/Notification/NotificationReceivedCall.qml - view/Item/EffectImage.qml view/Item/NumericPad.qml view/Item/PhoneNumberComboBox.qml @@ -48,7 +54,6 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Page/Main/AbstractMainPage.qml view/Page/Main/CallPage.qml - # view/Page/Main/OngoingCallPage.qml # Prototypes view/Prototype/PhoneNumberPrototype.qml diff --git a/Linphone/view/Item/BusyIndicator.qml b/Linphone/view/Item/BusyIndicator.qml new file mode 100644 index 000000000..1861c48cd --- /dev/null +++ b/Linphone/view/Item/BusyIndicator.qml @@ -0,0 +1,22 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Control +import QtQuick.Effects + +import Linphone + +Item { + id: mainItem + property color indicatorColor: DefaultStyle.main1_500_main + width: busyIndicator.width + height: busyIndicator.height + Control.BusyIndicator { + id: busyIndicator + running: mainItem.visible + } + MultiEffect { + source: busyIndicator + anchors.fill: busyIndicator + colorizationColor: mainItem.indicatorColor + colorization: 1.0 + } +} \ No newline at end of file diff --git a/Linphone/view/Item/Button.qml b/Linphone/view/Item/Button.qml index 43c84482a..fedc8ab4f 100644 --- a/Linphone/view/Item/Button.qml +++ b/Linphone/view/Item/Button.qml @@ -19,12 +19,12 @@ Control.Button { color: inversedColors ? mainItem.pressed ? DefaultStyle.buttonPressedInversedBackground - : DefaultStyle.buttonInversedBackground + : DefaultStyle.grey_0 : mainItem.pressed ? DefaultStyle.buttonPressedBackground - : DefaultStyle.buttonBackground + : DefaultStyle.main1_500_main radius: 24 - border.color: inversedColors ? DefaultStyle.buttonBackground : DefaultStyle.buttonInversedBackground + border.color: inversedColors ? DefaultStyle.main1_500_main : DefaultStyle.grey_0 MouseArea { anchors.fill: parent @@ -42,17 +42,12 @@ Control.Button { } } - leftPadding: 13 - rightPadding: 13 - topPadding: 10 - bottomPadding: 10 - contentItem: Text { horizontalAlignment: Text.AlignHCenter anchors.centerIn: parent wrapMode: Text.WordWrap text: mainItem.text - color: inversedColors ? DefaultStyle.buttonInversedTextColor : DefaultStyle.buttonTextColor + color: inversedColors ? DefaultStyle.main1_500_main : DefaultStyle.grey_0 font { bold: mainItem.boldText pointSize: mainItem.textSize diff --git a/Linphone/view/Item/Call/ContactsList.qml b/Linphone/view/Item/Call/ContactsList.qml new file mode 100644 index 000000000..cfc89659a --- /dev/null +++ b/Linphone/view/Item/Call/ContactsList.qml @@ -0,0 +1,168 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.2 as Control +import QtQuick.Effects + +import Linphone + +Item { + id: mainItem + property int sideMargin: 25 + property int topMargin: 5 + property bool groupCallVisible + property color searchBarColor: DefaultStyle.contactListSearchBarColor + property color searchBarBorderColor: "transparent" + signal callButtonPressed(string address) + clip: true + Control.Control { + id: listLayout + anchors.fill: parent + anchors.leftMargin: mainItem.sideMargin + anchors.rightMargin: mainItem.sideMargin + anchors.topMargin: mainItem.topMargin + background: Item { + anchors.fill: parent + } + contentItem: ColumnLayout { + anchors.fill: parent + spacing: 10 + SearchBar { + id: searchBar + Layout.alignment: Qt.AlignTop + Layout.fillWidth: true + Layout.maximumWidth: mainItem.width + color: mainItem.searchBarColor + borderColor: mainItem.searchBarBorderColor + placeholderText: qsTr("Rechercher un contact") + numericPad: numPad + } + Button { + visible: mainItem.groupCallVisible + Layout.fillWidth: true + leftPadding: 0 + topPadding: 0 + rightPadding: 0 + bottomPadding: 0 + background: Rectangle { + color: DefaultStyle.groupCallButtonColor + anchors.fill: parent + radius: 50 + } + contentItem: RowLayout { + Image { + source: AppIcons.groupCall + Layout.preferredWidth: 35 + sourceSize.width: 35 + fillMode: Image.PreserveAspectFit + } + Text { + text: "Appel de groupe" + font.bold: true + } + Item { + Layout.fillWidth: true + } + Image { + source: AppIcons.rightArrow + } + } + } + + RowLayout { + visible: searchBar.text.length > 0 // && contactList.count === 0 (pas trouvé dans la liste) + Layout.maximumWidth: parent.width + Layout.fillWidth: true + Text { + text: searchBar.text + maximumLineCount: 1 + elide: Text.ElideRight + } + Item { + Layout.fillWidth: true + } + Control.Button { + implicitWidth: 30 + implicitHeight: 30 + background: Item { + visible: false + } + contentItem: Image { + source: AppIcons.phone + width: 20 + sourceSize.width: 20 + fillMode: Image.PreserveAspectFit + } + onClicked: { + mainItem.callButtonPressed(searchBar.text) + } + } + } + ColumnLayout { + ListView { + id: contactList + Layout.fillWidth: true + Layout.fillHeight: true + // call history + model: 30 + delegate: Item { + required property int index + width:contactList.width + height: 30 + RowLayout { + anchors.fill: parent + Image { + source: AppIcons.info + } + ColumnLayout { + Text { + text: "John Doe" + } + // RowLayout { + // Image { + // source: AppIcons.incomingCall + // } + // Text { + // text: "info sur l'appel" + // } + // } + } + Item { + Layout.fillWidth: true + } + } + MouseArea { + hoverEnabled: true + Rectangle { + anchors.fill: parent + opacity: 0.1 + radius: 15 + color: DefaultStyle.comboBoxHoverColor + visible: parent.containsMouse + } + onClicked: contactList.currentIndex = parent.index + } + } + } + } + } + } + + Item { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: numPad.height + NumericPad { + id: numPad + // anchors.centerIn: parent + width: parent.width + onLaunchCall: { + var callVarObject = UtilsCpp.createCall(searchBar.text + "@sip.linphone.org") + // TODO : auto completion instead of sip linphone + var windowComp = Qt.createComponent("OngoingCallPage.qml") + var callWindow = windowComp.createObject({callVarObject: callVarObject}) + callWindow.show() + } + } + } +} \ No newline at end of file diff --git a/Linphone/view/Item/Call/OngoingCallRightPanel.qml b/Linphone/view/Item/Call/OngoingCallRightPanel.qml new file mode 100644 index 000000000..47bafd59d --- /dev/null +++ b/Linphone/view/Item/Call/OngoingCallRightPanel.qml @@ -0,0 +1,61 @@ +import QtQuick 2.7 +import QtQuick.Layouts +import QtQuick.Controls as Control +import Linphone + +Control.Page { + id: mainItem + property alias headerContent: header.children + + background: Rectangle { + width: mainItem.width + height: mainItem.height + color: DefaultStyle.callRightPanelBackgroundColor + radius: 15 + } + + header: Control.Control { + id: pageHeader + width: mainItem.width + background: Rectangle { + id: headerBackground + width: pageHeader.width + height: pageHeader.height + color: DefaultStyle.grey_0 + radius: 15 + Rectangle { + y: pageHeader.height/2 + height: pageHeader.height/2 + width: pageHeader.width + + } + } + contentItem: RowLayout { + width: pageHeader.width + height: pageHeader.height + anchors.leftMargin: 10 + anchors.left: pageHeader.left + Item { + id: header + } + Item { + Layout.fillWidth: true + } + Button { + id: closeButton + Layout.alignment: Qt.AlignRight + background: Item { + visible: false + } + contentItem: Image { + anchors.centerIn: closeButton + source: AppIcons.closeX + width: 10 + sourceSize.width: 10 + fillMode: Image.PreserveAspectFit + } + onClicked: mainItem.visible = false + } + } + } +} \ No newline at end of file diff --git a/Linphone/view/Item/Carousel.qml b/Linphone/view/Item/Carousel.qml index ee881a205..c825036a4 100644 --- a/Linphone/view/Item/Carousel.qml +++ b/Linphone/view/Item/Carousel.qml @@ -91,7 +91,7 @@ ColumnLayout { signal buttonClicked(int index) background: Rectangle { - color: stackLayout.currentIndex == slideIndex ? DefaultStyle.buttonBackground : DefaultStyle.carouselLightGrayColor + color: stackLayout.currentIndex == slideIndex ? DefaultStyle.main1_500_main : DefaultStyle.carouselLightGrayColor radius: 15 width: stackLayout.currentIndex == slideIndex ? 11 : 8 height: 8 diff --git a/Linphone/view/Item/CheckBox.qml b/Linphone/view/Item/CheckBox.qml index b3184e342..47d0e2f33 100644 --- a/Linphone/view/Item/CheckBox.qml +++ b/Linphone/view/Item/CheckBox.qml @@ -11,15 +11,15 @@ Control.CheckBox { x: (parent.width - width) / 2 y: (parent.height - height) / 2 radius: 3 - border.color: DefaultStyle.checkboxBorderColor + border.color: DefaultStyle.main1_500_main border.width: DefaultStyle.checkboxBorderWidth - // color: mainItem.checked ? DefaultStyle.checkboxBorderColor : "transparent" + // color: mainItem.checked ? DefaultStyle.main1_500_main : "transparent" Text { visible: mainItem.checked text: "\u2714" font.pointSize: 18 - color: DefaultStyle.checkboxBorderColor + color: DefaultStyle.main1_500_main anchors.centerIn: parent } } diff --git a/Linphone/view/Item/ComboBox.qml b/Linphone/view/Item/ComboBox.qml index e4022d99c..4f7ef007e 100644 --- a/Linphone/view/Item/ComboBox.qml +++ b/Linphone/view/Item/ComboBox.qml @@ -17,7 +17,7 @@ ColumnLayout { visible: label.length > 0 verticalAlignment: Text.AlignVCenter text: mainItem.label - color: combobox.activeFocus ? DefaultStyle.formItemFocusBorderColor : DefaultStyle.formItemLabelColor + color: combobox.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.formItemLabelColor font { pointSize: DefaultStyle.formItemLabelSize bold: true diff --git a/Linphone/view/Item/DigitInput.qml b/Linphone/view/Item/DigitInput.qml index 513a59cdd..255f9b32b 100644 --- a/Linphone/view/Item/DigitInput.qml +++ b/Linphone/view/Item/DigitInput.qml @@ -5,7 +5,7 @@ import Linphone Control.TextField { id: mainItem property int inputSize: 60 - color: activeFocus ? DefaultStyle.digitInputFocusedColor : DefaultStyle.digitInputColor + color: activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.digitInputColor rightPadding: inputSize / 4 leftPadding: inputSize / 4 validator: IntValidator{bottom: 0; top: 9} @@ -20,7 +20,7 @@ Control.TextField { background: Rectangle { // id: background - border.color: mainItem.activeFocus ? DefaultStyle.digitInputFocusedColor : DefaultStyle.digitInputColor + border.color: mainItem.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.digitInputColor radius: mainItem.inputSize / 8 } // cursorDelegate: Rectangle { @@ -34,6 +34,6 @@ Control.TextField { // // anchors.left: parent.left // // anchors.bottomMargin: inputSize/8 // // transform: [/*Translate {x: mainItem.cursorRectangle.height},*/ Rotation {angle: -90}] - // color:DefaultStyle.digitInputFocusedColor + // color:DefaultStyle.main1_500_main // } } diff --git a/Linphone/view/Item/EffectImage.qml b/Linphone/view/Item/EffectImage.qml index 4948e5b00..d5cbc61b4 100644 --- a/Linphone/view/Item/EffectImage.qml +++ b/Linphone/view/Item/EffectImage.qml @@ -17,19 +17,20 @@ Item { Image { id: image + visible: !effect2.enabled + sourceSize.width: parent.width + sourceSize.height: parent.height width: parent.width height: parent.height - //sourceSize.width: parent.width fillMode: Image.PreserveAspectFit anchors.centerIn: parent - visible: !effect2.enabled } MultiEffect { id: effect + visible: !effect2.enabled anchors.fill: image source: image maskSource: image - visible: !effect2.enabled brightness: effect2.enabled ? 1.0 : 0.0 } MultiEffect { diff --git a/Linphone/view/Item/Form/LoginForm.qml b/Linphone/view/Item/Form/LoginForm.qml index 39ee319cd..d254e7508 100644 --- a/Linphone/view/Item/Form/LoginForm.qml +++ b/Linphone/view/Item/Form/LoginForm.qml @@ -94,7 +94,7 @@ ColumnLayout { text: "Forgotten password?" font{ underline: true - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize } } onClicked: console.debug("[LoginForm]User: forgotten password button clicked") diff --git a/Linphone/view/Item/Notification/NotificationReceivedCall.qml b/Linphone/view/Item/Notification/NotificationReceivedCall.qml index 3ca19afd7..6134616a4 100644 --- a/Linphone/view/Item/Notification/NotificationReceivedCall.qml +++ b/Linphone/view/Item/Notification/NotificationReceivedCall.qml @@ -36,6 +36,10 @@ Notification { Layout.rightMargin: 20 onClicked: { notification.call.core.lAccept(true) + var windowComp = Qt.createComponent("OngoingCallPage.qml") + var callWindow = windowComp.createObject(null, {callVarObject: callVarObject}) + callWindow.modality = Qt.ApplicationModal + callWindow.show() } } Item{ diff --git a/Linphone/view/Item/NumericPad.qml b/Linphone/view/Item/NumericPad.qml index efdbdf25e..776d52997 100644 --- a/Linphone/view/Item/NumericPad.qml +++ b/Linphone/view/Item/NumericPad.qml @@ -8,6 +8,7 @@ Control.Popup { clip: true id: mainItem signal buttonPressed(string text) + signal launchCall() signal wipe() closePolicy: Control.Popup.CloseOnEscape leftPadding: closeButton.width @@ -61,7 +62,7 @@ Control.Popup { implicitHeight: 40 background: Rectangle { anchors.fill: parent - color: numPadButton.down ? DefaultStyle.numericPadPressedButtonColor : DefaultStyle.numericPadButtonColor + color: numPadButton.down ? DefaultStyle.numericPadPressedButtonColor : DefaultStyle.grey_0 radius: 20 } contentItem: Text { @@ -91,7 +92,7 @@ Control.Popup { implicitHeight: 40 background: Rectangle { anchors.fill: parent - color: digitButton.down ? DefaultStyle.numericPadPressedButtonColor : DefaultStyle.numericPadButtonColor + color: digitButton.down ? DefaultStyle.numericPadPressedButtonColor : DefaultStyle.grey_0 radius: 20 } contentItem: Item { @@ -131,7 +132,7 @@ Control.Popup { bottomPadding: 15 background: Rectangle { anchors.fill: parent - color: DefaultStyle.numericPadLaunchCallButtonColor + color: DefaultStyle.launchCallButtonColor radius: 15 } contentItem: EffectImage { @@ -142,8 +143,9 @@ Control.Popup { width: 20 height: 20 image.fillMode: Image.PreserveAspectFit - effect.brightness: 1.0 + colorizationColor: DefaultStyle.grey_0 } + onClicked: mainItem.launchCall() } Button { leftPadding: 5 diff --git a/Linphone/view/Item/PhoneNumberComboBox.qml b/Linphone/view/Item/PhoneNumberComboBox.qml index 474adc56a..c8f287ab3 100644 --- a/Linphone/view/Item/PhoneNumberComboBox.qml +++ b/Linphone/view/Item/PhoneNumberComboBox.qml @@ -15,7 +15,7 @@ ColumnLayout { visible: mainItem.label.length > 0 verticalAlignment: Text.AlignVCenter text: mainItem.label - color: combobox.activeFocus ? DefaultStyle.formItemFocusBorderColor : DefaultStyle.formItemLabelColor + color: combobox.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.formItemLabelColor font { pointSize: DefaultStyle.formItemLabelSize bold: true @@ -39,7 +39,7 @@ ColumnLayout { ? (mainItem.errorMessage.length > 0 ? DefaultStyle.errorMessageColor : textField.activeFocus - ? DefaultStyle.formItemFocusBorderColor + ? DefaultStyle.main1_500_main : DefaultStyle.formItemBorderColor) : "transparent" } diff --git a/Linphone/view/Item/PhoneNumberInput.qml b/Linphone/view/Item/PhoneNumberInput.qml index bc4391dc6..db15b7214 100644 --- a/Linphone/view/Item/PhoneNumberInput.qml +++ b/Linphone/view/Item/PhoneNumberInput.qml @@ -19,7 +19,7 @@ ColumnLayout { visible: label.length > 0 verticalAlignment: Text.AlignVCenter text: mainItem.label + (mainItem.mandatory ? "*" : "") - color: (combobox.hasActiveFocus || textField.hasActiveFocus) ? DefaultStyle.formItemFocusBorderColor : DefaultStyle.formItemLabelColor + color: (combobox.hasActiveFocus || textField.hasActiveFocus) ? DefaultStyle.main1_500_main : DefaultStyle.formItemLabelColor font { pointSize: DefaultStyle.formItemLabelSize bold: true @@ -34,7 +34,7 @@ ColumnLayout { border.color: mainItem.errorMessage.length > 0 ? DefaultStyle.errorMessageColor : (textField.hasActiveFocus || combobox.hasActiveFocus) - ? DefaultStyle.formItemFocusBorderColor + ? DefaultStyle.main1_500_main : DefaultStyle.formItemBorderColor RowLayout { anchors.fill: parent @@ -69,7 +69,7 @@ ColumnLayout { elide: Text.ElideRight wrapMode: Text.Wrap font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize family: DefaultStyle.defaultFont bold: true } diff --git a/Linphone/view/Item/Prototype/CanvasCircle.qml b/Linphone/view/Item/Prototype/CanvasCircle.qml new file mode 100644 index 000000000..ae5533486 --- /dev/null +++ b/Linphone/view/Item/Prototype/CanvasCircle.qml @@ -0,0 +1,61 @@ +import QtQuick 2.15 + +Item { + id: root + property int size: 150 + property color borderColor + property color innerColor + width: size + height: size + + onBorderColorChanged: c.requestPaint() + + function requestPaint(animated) { + c.animated = animated + if (animated) animationTimer.restart() + else { + animationTimer.stop() + c.requestPaint() + } + } + + Canvas { + id: c + property bool animated: false + property int offset: 0 + anchors.fill: parent + antialiasing: true + onOffsetChanged: requestPaint() + Timer { + id: animationTimer + interval: 200 + repeat: true + onTriggered: c.offset = (c.offset + 1)%360 + } + + onPaint: { + var ctx = getContext("2d"); + ctx.reset() + ctx.setLineDash([3, 2]); + ctx.lineWidth = 2; + + ctx.lineDashOffset = offset; + + var x = root.width / 2; + var y = root.height / 2; + + var radius = root.size / 2 + var startAngle = (Math.PI / 180) * 270; + var fullAngle = (Math.PI / 180) * (270 + 360); + + ctx.strokeStyle = root.borderColor; + ctx.fillStyle = root.innerColor; + ctx.beginPath(); + ctx.arc(x, y, radius - 1, 0, 2 * Math.PI); + ctx.fill(); + if (animated) { + ctx.stroke(); + } + } + } +} \ No newline at end of file diff --git a/Linphone/view/Item/RadioButton.qml b/Linphone/view/Item/RadioButton.qml index fe5925c2b..cd427de05 100644 --- a/Linphone/view/Item/RadioButton.qml +++ b/Linphone/view/Item/RadioButton.qml @@ -20,7 +20,7 @@ Control.RadioButton { background: Rectangle { color: DefaultStyle.formItemBackgroundColor - border.color: mainItem.checked ? DefaultStyle.radioButtonCheckedColor : "transparent" + border.color: mainItem.checked ? DefaultStyle.info_500_main : "transparent" radius: 20 } @@ -34,7 +34,7 @@ Control.RadioButton { implicitWidth: 16 implicitHeight: 16 radius: implicitWidth/2 - border.color: mainItem.checked ? DefaultStyle.radioButtonCheckedColor : DefaultStyle.radioButtonUncheckedColor + border.color: mainItem.checked ? DefaultStyle.info_500_main : DefaultStyle.main1_500_main Rectangle { width: parent.width/2 @@ -42,14 +42,14 @@ Control.RadioButton { x: parent.width/4 y: parent.width/4 radius: width/2 - color: DefaultStyle.radioButtonCheckedColor + color: DefaultStyle.info_500_main visible: mainItem.checked } } Text { text: mainItem.title font.bold: true - color: DefaultStyle.radioButtonTitleColor + color: DefaultStyle.grey_900 font.pointSize: DefaultStyle.radioButtonTitleSize } Control.Button { @@ -83,7 +83,7 @@ Control.RadioButton { verticalAlignment: Text.AlignVCenter Layout.preferredWidth: 220 Layout.preferredHeight: 100 - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.descriptionTextSize text: mainItem.contentText Layout.fillHeight: true } diff --git a/Linphone/view/Item/RectangleTest.qml b/Linphone/view/Item/RectangleTest.qml index 690af1172..74badd244 100644 --- a/Linphone/view/Item/RectangleTest.qml +++ b/Linphone/view/Item/RectangleTest.qml @@ -1,9 +1,15 @@ import QtQuick 2.7 Rectangle { + function genRandomColor(){ + return '#'+ Math.floor(Math.random()*255).toString(16) + +Math.floor(Math.random()*255).toString(16) + +Math.floor(Math.random()*255).toString(16) + } + anchors.fill: parent - color: "blue" + color: genRandomColor() //"blue" opacity: 0.2 - border.color: "green" + border.color: genRandomColor() //"red" border.width: 2 } diff --git a/Linphone/view/Item/SearchBar.qml b/Linphone/view/Item/SearchBar.qml index 916d5b10f..9b451acd8 100644 --- a/Linphone/view/Item/SearchBar.qml +++ b/Linphone/view/Item/SearchBar.qml @@ -8,11 +8,12 @@ Rectangle { id: mainItem property string placeholderText: "" property int textInputWidth: 350 - property var validator: RegularExpressionValidator{} + property color borderColor: "transparent" property string text: textField.text + property var validator: RegularExpressionValidator{} + property var numericPad property alias numericPadButton: dialerButton readonly property bool hasActiveFocus: textField.activeFocus - property var numericPad signal numericPadButtonPressed(bool checked) onVisibleChanged: if (!visible && numericPad) numericPad.close() @@ -32,7 +33,7 @@ Rectangle { implicitHeight: 30 radius: 20 color: DefaultStyle.formItemBackgroundColor - border.color: textField.activeFocus ? DefaultStyle.searchBarFocusBorderColor : "transparent" + border.color: textField.activeFocus ? DefaultStyle.searchBarFocusBorderColor : mainItem.borderColor Image { id: magnifier anchors.left: parent.left @@ -43,9 +44,10 @@ Rectangle { Control.TextField { id: textField anchors.left: magnifier.right - anchors.right: dialerButton.visible ? dialerButton.left : parent.right + anchors.right: clearTextButton.left anchors.verticalCenter: parent.verticalCenter placeholderText: mainItem.placeholderText + width: mainItem.width - dialerButton.width echoMode: (mainItem.hidden && !dialerButton.checked) ? TextInput.Password : TextInput.Normal font.family: DefaultStyle.defaultFont font.pointSize: DefaultStyle.defaultFontPointSize @@ -57,13 +59,13 @@ Rectangle { } cursorDelegate: Rectangle { visible: textField.activeFocus - color: DefaultStyle.formItemFocusBorderColor + color: DefaultStyle.main1_500_main width: 2 } } Control.Button { id: dialerButton - visible: numericPad != undefined + visible: numericPad != undefined && textField.text.length === 0 checkable: true checked: false background: Rectangle { @@ -82,4 +84,24 @@ Rectangle { else mainItem.numericPad.close() } } + Control.Button { + id: clearTextButton + visible: textField.text.length > 0 + checkable: true + checked: false + background: Rectangle { + color: "transparent" + } + contentItem: Image { + fillMode: Image.PreserveAspectFit + source: AppIcons.closeX + } + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.rightMargin: 10 + onCheckedChanged: { + textField.clear() + } + } } diff --git a/Linphone/view/Item/TabBar.qml b/Linphone/view/Item/TabBar.qml index 2e0d751fb..62170f0ab 100644 --- a/Linphone/view/Item/TabBar.qml +++ b/Linphone/view/Item/TabBar.qml @@ -25,7 +25,7 @@ Control.TabBar { // Quick.Rectangle { // height: 4 - // color: DefaultStyle.orangeColor + // color: DefaultStyle.main1_500_main // anchors.bottom: parent.bottom // // anchors.left: mainItem.currentItem.left // // anchors.right: mainItem.currentItem.right @@ -59,7 +59,7 @@ Control.TabBar { Quick.Rectangle { visible: mainItem.currentIndex === index height: 4 - color: DefaultStyle.orangeColor + color: DefaultStyle.main1_500_main anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right diff --git a/Linphone/view/Item/TextInput.qml b/Linphone/view/Item/TextInput.qml index c57c69c9c..94eb31c59 100644 --- a/Linphone/view/Item/TextInput.qml +++ b/Linphone/view/Item/TextInput.qml @@ -32,7 +32,7 @@ ColumnLayout { visible: mainItem.label.length > 0 verticalAlignment: Text.AlignVCenter text: mainItem.label + (mainItem.mandatory ? "*" : "") - color: textField.activeFocus ? DefaultStyle.formItemFocusBorderColor : DefaultStyle.formItemLabelColor + color: textField.activeFocus ? DefaultStyle.main1_500_main : DefaultStyle.formItemLabelColor elide: Text.ElideRight wrapMode: Text.Wrap maximumLineCount: 1 @@ -58,7 +58,7 @@ ColumnLayout { ? (mainItem.errorMessage.length > 0 ? DefaultStyle.errorMessageColor : textField.activeFocus - ? DefaultStyle.formItemFocusBorderColor + ? DefaultStyle.main1_500_main : DefaultStyle.formItemBorderColor) : "transparent" Control.TextField { @@ -78,7 +78,7 @@ ColumnLayout { } cursorDelegate: Rectangle { visible: textField.activeFocus - color: DefaultStyle.formItemFocusBorderColor + color: DefaultStyle.main1_500_main width: 2 } } @@ -107,7 +107,7 @@ ColumnLayout { wrapMode: Text.Wrap // maximumLineCount: 1 font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize family: DefaultStyle.defaultFont bold: true } diff --git a/Linphone/view/Item/VerticalTabBar.qml b/Linphone/view/Item/VerticalTabBar.qml index bb5763bd5..110cc8e4b 100644 --- a/Linphone/view/Item/VerticalTabBar.qml +++ b/Linphone/view/Item/VerticalTabBar.qml @@ -33,18 +33,18 @@ Control.TabBar { anchors.fill: parent Rectangle { anchors.fill: parent - color: DefaultStyle.verticalTabBarColor + color: DefaultStyle.main1_500_main radius: 25 } Rectangle { - color: DefaultStyle.verticalTabBarColor + color: DefaultStyle.main1_500_main anchors.left: parent.left anchors.top: parent.top width: parent.width/2 height: parent.height/2 } Rectangle { - color: DefaultStyle.verticalTabBarColor + color: DefaultStyle.main1_500_main x: parent.x + parent.width/2 y: parent.y + parent.height/2 width: parent.width/2 @@ -69,9 +69,8 @@ Control.TabBar { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Layout.alignment: Qt.AlignHCenter - image.sourceSize.width: buttonSize image.fillMode: Image.PreserveAspectFit - effect.brightness: 1.0 + colorizationColor: DefaultStyle.grey_0 } Text { id: buttonText diff --git a/Linphone/view/Page/Login/LoginPage.qml b/Linphone/view/Page/Login/LoginPage.qml index 3a79e9546..d2b6bdf9c 100644 --- a/Linphone/view/Page/Login/LoginPage.qml +++ b/Linphone/view/Page/Login/LoginPage.qml @@ -26,7 +26,7 @@ LoginLayout { Text { Layout.rightMargin: 15 text: "No account yet ?" - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.indicatorMessageTextSize } Button { Layout.alignment: Qt.AlignRight diff --git a/Linphone/view/Page/Login/RegisterCheckingPage.qml b/Linphone/view/Page/Login/RegisterCheckingPage.qml index 787edae2b..b632f12fd 100644 --- a/Linphone/view/Page/Login/RegisterCheckingPage.qml +++ b/Linphone/view/Page/Login/RegisterCheckingPage.qml @@ -85,7 +85,7 @@ LoginLayout { Layout.rightMargin: 15 text: "Didn't receive the code ?" color: DefaultStyle.questionTextColor - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.indicatorMessageTextSize } Button { Layout.alignment: Qt.AlignRight diff --git a/Linphone/view/Page/Login/RegisterPage.qml b/Linphone/view/Page/Login/RegisterPage.qml index 5c6705233..4075caf8d 100644 --- a/Linphone/view/Page/Login/RegisterPage.qml +++ b/Linphone/view/Page/Login/RegisterPage.qml @@ -31,7 +31,7 @@ LoginLayout { Layout.rightMargin: 15 color: DefaultStyle.questionTextColor text: "Already have an account ?" - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.indicatorMessageTextSize } Button { // Layout.alignment: Qt.AlignRight @@ -87,7 +87,7 @@ LoginLayout { Text { text: "The password must contain 6 characters minimum" font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize } } } @@ -101,7 +101,7 @@ LoginLayout { Text { text: "The password must contain 6 characters minimum" font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize } } } @@ -176,7 +176,7 @@ LoginLayout { Text { text: "The password must contain 6 characters minimum" font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize } } } @@ -191,7 +191,7 @@ LoginLayout { Text { text: "The password must contain 6 characters minimum" font { - pointSize: DefaultStyle.defaultTextSize + pointSize: DefaultStyle.indicatorMessageTextSize } } } diff --git a/Linphone/view/Page/Login/SIPLoginPage.qml b/Linphone/view/Page/Login/SIPLoginPage.qml index aeebc974a..64197bd2d 100644 --- a/Linphone/view/Page/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Login/SIPLoginPage.qml @@ -41,7 +41,7 @@ LoginLayout { Text { Layout.rightMargin: 15 text: "No account yet ?" - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.indicatorMessageTextSize } Button { Layout.alignment: Qt.AlignRight @@ -75,7 +75,7 @@ LoginLayout { width: rootStackView.width wrapMode: Text.WordWrap color: DefaultStyle.darkGrayColor - font.pointSize: DefaultStyle.defaultTextSize + font.pointSize: DefaultStyle.descriptionTextSize text: "

Some features require a Linphone account, such as group messaging, video conferences...

These features are hidden when you register with a third party SIP account.

To enable it in a commercial projet, please contact us.

" diff --git a/Linphone/view/Page/Main/AbstractMainPage.qml b/Linphone/view/Page/Main/AbstractMainPage.qml index a7a56fa50..38576b92d 100644 --- a/Linphone/view/Page/Main/AbstractMainPage.qml +++ b/Linphone/view/Page/Main/AbstractMainPage.qml @@ -15,9 +15,9 @@ Item { property string newItemIconSource property string emptyListText property alias leftPanelContent: leftPanel.children - property Component rightPanelContent + property var rightPanelContent: rightPanelItem.children property bool showDefaultItem: true - onShowDefaultItemChanged: stackView.replace(showDefaultItem ? defaultItem : rightPanel) + // onShowDefaultItemChanged: stackView.replace(showDefaultItem ? defaultItem : rightPanelItem) signal noItemButtonPressed() Control.SplitView { @@ -37,64 +37,74 @@ Item { id: rightPanel clip: true color: DefaultStyle.mainPageRightPanelBackgroundColor - Control.StackView { - id: stackView - initialItem: defaultItem + StackLayout { + currentIndex: mainItem.showDefaultItem ? 0 : 1 anchors.fill: parent - Layout.alignment: Qt.AlignCenter - } - Component { - id: defaultItem ColumnLayout { - Item { - Layout.fillHeight: true - } - ColumnLayout { + id: defaultItem + Layout.fillWidth: true + Layout.fillHeight: true + RowLayout { Layout.fillHeight: true Layout.fillWidth: true - visible: mainItem.showDefaultItem - // anchors.centerIn: parent Layout.alignment: Qt.AlignHCenter spacing: 25 - Image { - Layout.alignment: Qt.AlignHCenter - source: AppIcons.noItemImage - Layout.preferredWidth: 250 - Layout.preferredHeight: 250 - fillMode: Image.PreserveAspectFit + Item { + Layout.fillWidth: true } - Text { - text: mainItem.emptyListText - Layout.alignment: Qt.AlignHCenter - font.bold: true - } - Button { - Layout.alignment: Qt.AlignHCenter - contentItem: RowLayout { - Layout.alignment: Qt.AlignVCenter - EffectImage { - effect.brightness: 1 - image.source: mainItem.newItemIconSource - image.width: 20 - image.fillMode: Image.PreserveAspectFit - } - Text { - text: mainItem.noItemButtonText - wrapMode: Text.WordWrap - color: DefaultStyle.buttonTextColor - font { - bold: true - pointSize: DefaultStyle.buttonTextSize - family: DefaultStyle.defaultFont + ColumnLayout { + Item { + Layout.fillHeight: true + } + Image { + Layout.alignment: Qt.AlignHCenter + source: AppIcons.noItemImage + Layout.preferredWidth: 250 + Layout.preferredHeight: 250 + fillMode: Image.PreserveAspectFit + } + Text { + text: mainItem.emptyListText + Layout.alignment: Qt.AlignHCenter + font.bold: true + } + Button { + Layout.alignment: Qt.AlignHCenter + contentItem: RowLayout { + Layout.alignment: Qt.AlignVCenter + EffectImage { + colorizationColor: DefaultStyle.grey_0 + image.source: mainItem.newItemIconSource + image.width: 20 + image.fillMode: Image.PreserveAspectFit + } + Text { + text: mainItem.noItemButtonText + wrapMode: Text.WordWrap + color: DefaultStyle.grey_0 + font { + bold: true + pointSize: DefaultStyle.buttonTextSize + family: DefaultStyle.defaultFont + } } } + onPressed: mainItem.noItemButtonPressed() } - onPressed: mainItem.noItemButtonPressed() + Item { + Layout.fillHeight: true + } + } + Item { + Layout.fillWidth: true } } - Item { - Layout.fillHeight: true - } + + } + Item { + id: rightPanelItem + Layout.fillWidth: true + Layout.fillHeight: true } } } diff --git a/Linphone/view/Page/Main/CallPage.qml b/Linphone/view/Page/Main/CallPage.qml index 4fef92f0e..c25c20306 100644 --- a/Linphone/view/Page/Main/CallPage.qml +++ b/Linphone/view/Page/Main/CallPage.qml @@ -2,6 +2,7 @@ import QtQuick 2.15 import QtQuick.Layouts import QtQuick.Controls as Control import Linphone +import UtilsCpp 1.0 AbstractMainPage { id: mainItem @@ -16,10 +17,12 @@ AbstractMainPage { Layout.fillHeight: true Control.StackView { id: listStackView + clip: true initialItem: listItem anchors.fill: parent - anchors.leftMargin: 25 - anchors.rightMargin: 25 + property int sideMargin: 25 + // anchors.leftMargin: 25 + // anchors.rightMargin: 25 } Component { id: listItem @@ -27,6 +30,8 @@ AbstractMainPage { ColumnLayout { RowLayout { Layout.fillWidth: true + Layout.leftMargin: listStackView.sideMargin + Layout.rightMargin: listStackView.sideMargin Text { text: qsTr("Appels") color: DefaultStyle.mainPageTitleColor @@ -65,6 +70,8 @@ AbstractMainPage { id: listLayout Layout.fillWidth: true Layout.fillHeight: true + Layout.leftMargin: listStackView.sideMargin + Layout.rightMargin: listStackView.sideMargin background: Rectangle { anchors.fill: parent @@ -148,6 +155,11 @@ AbstractMainPage { onCountChanged: mainItem.showDefaultItem = listView.count === 0 + Connections { + target: mainItem + onShowDefaultItemChanged: mainItem.showDefaultItem = mainItem.showDefaultItem && listView.count === 0 + } + Control.ScrollIndicator.vertical: Control.ScrollIndicator { } } } @@ -158,7 +170,13 @@ AbstractMainPage { Component { id: newCallItem ColumnLayout { + Control.StackView.onActivating: { + mainItem.showDefaultItem = false + } + Control.StackView.onDeactivating: mainItem.showDefaultItem = true RowLayout { + Layout.leftMargin: listStackView.sideMargin + Layout.rightMargin: listStackView.sideMargin Control.Button { background: Item { } @@ -180,74 +198,26 @@ AbstractMainPage { Layout.fillWidth: true } } - Control.Control { - id: listLayout + ContactsList { Layout.fillWidth: true Layout.fillHeight: true - background: Rectangle { - anchors.fill: parent - } - ColumnLayout { - anchors.fill: parent - SearchBar { - id: searchBar - Layout.alignment: Qt.AlignTop - Layout.fillWidth: true - placeholderText: qsTr("Rechercher un appel") - numericPad: numPad - } - Button { - Layout.fillWidth: true - leftPadding: 0 - topPadding: 0 - rightPadding: 0 - bottomPadding: 0 - background: Rectangle { - color: DefaultStyle.groupCallButtonColor - anchors.fill: parent - radius: 50 - } - contentItem: RowLayout { - Image { - source: AppIcons.groupCall - Layout.preferredWidth: 35 - sourceSize.width: 35 - fillMode: Image.PreserveAspectFit - } - Text { - text: "Appel de groupe" - font.bold: true - } - Item { - Layout.fillWidth: true - } - Image { - source: AppIcons.rightArrow - } - } - } - ColumnLayout { - ListView { - Layout.fillHeight: true - // call history - } - } + Layout.maximumWidth: parent.width + groupCallVisible: true + searchBarColor: DefaultStyle.contactListSearchBarColor + + onCallButtonPressed: (address) => { + var addressEnd = "@sip.linphone.org" + if (!address.endsWith(addressEnd)) address += addressEnd + var callVarObject = UtilsCpp.createCall(address) + // var windowComp = Qt.createComponent("CallsWindow.qml") + // var call = callVarObject.value + // var callWindow = windowComp.createObject(null, {callVarObject: callVarObject}) + // callWindow.modality = Qt.ApplicationModal + // callWindow.show() } } } } - - Item { - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: numPad.height - NumericPad { - id: numPad - // anchors.centerIn: parent - width: parent.width - } - } } rightPanelContent: ColumnLayout { diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 3801363ed..a4ea06902 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -25,6 +25,7 @@ QtObject { property string phoneSelected: "image://internal/phone-selected.svg" property string newCall: "image://internal/phone-plus.svg" property string endCall: "image://internal/phone-disconnect.svg" + property string transferCall: "image://internal/phone-transfer.svg" property string adressBook: "image://internal/address-book.svg" property string adressBookSelected: "image://internal/address-book-selected.svg" property string chatTeardropText: "image://internal/chat-teardrop-text.svg" @@ -44,5 +45,15 @@ QtObject { property string incomingCallRejected: "image://internal/incoming_call_rejected.svg" property string outgoingCall: "image://internal/outgoing_call.svg" property string outgoingCallMissed: "image://internal/outgoing_call_missed.svg" - property string outgoingCallRejected: "image://internal/outgoing_call_rejected.svg" + property string microphone: "image://internal/microphone.svg" + property string microphoneSlash: "image://internal/microphone-slash.svg" + property string videoCamera: "image://internal/video-camera.svg" + property string videoCameraSlash: "image://internal/video-camera-slash.svg" + property string speaker: "image://internal/speaker-high.svg" + property string speakerSlash: "image://internal/speaker-slash.svg" + property string trusted: "image://internal/trusted.svg" + property string avatar: "image://internal/randomAvatar.png" + property string pause: "image://internal/pause.svg" + property string play: "image://internal/play.svg" + property string smiley: "image://internal/smiley.svg" } diff --git a/Linphone/view/Style/DefaultStyle.qml b/Linphone/view/Style/DefaultStyle.qml index bc86c1a1d..ebb6da929 100644 --- a/Linphone/view/Style/DefaultStyle.qml +++ b/Linphone/view/Style/DefaultStyle.qml @@ -2,21 +2,34 @@ pragma Singleton import QtQuick 2.15 QtObject { + property color main1_500_main: "#FE5E00" + + property color main2_0: "#FAFEFF" + property color main2_100: "#EEF6F8" + property color main2_200: "#DFECF2" + property color main2_300: "#C0D1D9" + property color main2_400: "#9AABB5" + property color main2_500main: "#6C7A87" + property color main2_700: "#364860" + + property color warning_600: "#DBB820" + + property color grey_0: "#FFFFFF" + property color grey_500: "#4E4E4E" + property color grey_600: "#2E3030" + property color grey_900: "#070707" + + property color danger_500: "#DD5F5F" + + property color info_500_main: "#4AA8FF" + + property color success_500main: "#4FAE80" property string emojiFont: "Noto Color Emoji" - property color orangeColor: "#FE5E00" - property color buttonBackground: "#FE5E00" property color buttonPressedBackground: "#c74b02" - property color buttonInversedBackground: "white" property color buttonPressedInversedBackground: "#fff1e8" - property color buttonTextColor: "white" - property color radioButtonCheckedColor: "#4AA8FF" - property color radioButtonUncheckedColor: "#FE5E00" - property color radioButtonTitleColor: "#070707" property int radioButtonTextSize: 8 property int radioButtonTitleSize: 9 - property color buttonInversedTextColor: "#FE5E00" - property color checkboxBorderColor: "#FE5E00" property double checkboxBorderWidth: 2 property int buttonTextSize: 10 property color carouselLightGrayColor: "#DFECF2" @@ -26,10 +39,8 @@ QtObject { property color formItemDisableBackgroundColor: "#EDEDED" property color formItemBackgroundColor: "#F9F9F9" property color formItemBorderColor: "#EDEDED" - property color formItemFocusBorderColor: "#FE5E00" property int tabButtonTextSize: 11 - property color verticalTabBarColor: "#FE5E00" property color verticalTabBarTextColor: "white" property int verticalTabButtonTextSize: 6 @@ -43,7 +54,6 @@ QtObject { property color tooltipBackgroundColor: "#DFECF2" - property color digitInputFocusedColor: "#FE5E00" property color digitInputColor: "#6C7A87" property color darkBlueColor: "#22334D" @@ -53,7 +63,8 @@ QtObject { property color defaultTextColor: "#4E6074" property color disableTextColor: "#9AABB5" - property int defaultTextSize: 7 + property int descriptionTextSize: 7 + property int indicatorMessageTextSize: 7 property string defaultFont: "Noto Sans" property int defaultFontPointSize: 10 property int title1FontPointSize: 50 @@ -65,29 +76,30 @@ QtObject { property int mainPageTitleSize: 15 property color searchBarFocusBorderColor: "#6C7A87" + property color contactListSearchBarColor: "#F9F9F9" + property color callRightPanelSearchBarBorderColor: "#EDEDED" property color numericPadBackgroundColor: "#F9F9F9" property color numericPadShadowColor: Qt.rgba(0.0, 0.0, 0.0, 0.1) - property color numericPadButtonColor: "#FFFFFF" property int numericPadButtonTextSize: 15 property int numericPadButtonSubtextSize: 6 property color numericPadPressedButtonColor: "#EEF7F8" - property color numericPadLaunchCallButtonColor: "#4FAE80" property color groupCallButtonColor: "#EEF7F8" + property color launchCallButtonColor: "#4FAE80" + property color callCheckedButtonColor: "#9AABB5" property color splitViewHandleColor: "#F9F9F9" property color splitViewHoveredHandleColor: "#EDEDED" - - property color danger_500main: "#DD5F5F" - property color grey_0: "#FFFFFF" - property color success_500main: "#4FAE80" - property color warning_600: "#DBB820" - property color main2_200: "#DFECF2" - property color main2_300: "#C0D1D9" - property color main2_400: "#9AABB5" - property color main2_700: "#364860" - property color main2_500main: "#6C7A87" + + property color ongoingCallWindowColor: "#000000" + property color ongoingCallBackgroundColor: "#2E3030" + property int ongoingCallElapsedTimeSize: 15 + property int ongoingCallNameSize: 10 + property int ongoingCallAddressSize: 6 + property color callRightPanelBackgroundColor: "#F9F9F9" + + property color defaultAvatarBackgroundColor: "#DFECF2" property double dp: 1.0//0.66 }