diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 604b75509..fe4f1a12c 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -96,6 +96,8 @@ void App::init() { } setQuitOnLastWindowClosed(true); // TODO: use settings to set it + qInfo() << log().arg("Display server : %1").arg(platformName()); + // QML mEngine = new QQmlApplicationEngine(this); // Provide `+custom` folders for custom components and `5.9` for old components. @@ -119,9 +121,13 @@ void App::init() { QObject::connect( mEngine, &QQmlApplicationEngine::objectCreated, this, [this, url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) { - qCritical() << log().arg("Main.qml couldn't be load. The app will exit"); - exit(-1); + if (url == objUrl) { + if (!obj) { + qCritical() << log().arg("Main.qml couldn't be load. The app will exit"); + exit(-1); + } + mMainWindow = qobject_cast(obj); + Q_ASSERT(mMainWindow); } }, Qt::QueuedConnection); @@ -149,7 +155,6 @@ void App::initCppInterfaces() { qmlRegisterType(Constants::MainQmlUri, 1, 0, "AccountProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "AccountGui"); qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "AccountCore", QLatin1String("Uncreatable")); - qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallGui"); qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "CallCore", QLatin1String("Uncreatable")); qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallProxy"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "CallGui"); @@ -240,14 +245,9 @@ QQuickWindow *App::getCallsWindow(QVariant callGui) { qCritical() << log().arg("Calls window could not be created."); return nullptr; } + // window->setParent(mMainWindow); 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; } @@ -260,13 +260,6 @@ void App::closeCallsWindow() { } } -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(); -} +QQuickWindow *App::getMainWindow() { + return mMainWindow; +} \ No newline at end of file diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index a65a0ae08..1f7b62264 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -93,7 +93,7 @@ public: QQuickWindow *getCallsWindow(QVariant callGui); void closeCallsWindow(); - Q_INVOKABLE static void smartShowWindow(QQuickWindow *window); + QQuickWindow *getMainWindow(); QQmlApplicationEngine *mEngine = nullptr; bool notify(QObject *receiver, QEvent *event) override; @@ -106,6 +106,7 @@ private: QCommandLineParser *mParser = nullptr; Thread *mLinphoneThread = nullptr; Notifier *mNotifier = nullptr; + QQuickWindow *mMainWindow = nullptr; QQuickWindow *mCallsWindow = nullptr; // TODO : changer ce count lorsqu'on aura liste d'appels int callsCount = 0; diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index 5ac59fbbb..1dcec141d 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -143,6 +143,9 @@ void CallCore::setSelf(QSharedPointer me) { mAccountModelConnection->makeConnectToCore(&CallCore::lTerminate, [this]() { mAccountModelConnection->invokeToModel([this]() { mCallModel->terminate(); }); }); + mAccountModelConnection->makeConnectToCore(&CallCore::lTerminateAllCalls, [this]() { + mAccountModelConnection->invokeToModel([this]() { mCallModel->terminateAllCalls(); }); + }); } QString CallCore::getPeerAddress() const { diff --git a/Linphone/core/call/CallCore.hpp b/Linphone/core/call/CallCore.hpp index 30e0c8d1b..3f55ca7f1 100644 --- a/Linphone/core/call/CallCore.hpp +++ b/Linphone/core/call/CallCore.hpp @@ -113,6 +113,7 @@ signals: void lAccept(bool withVideo); // Accept an incoming call void lDecline(); // Decline an incoming call void lTerminate(); // Hangup a call + void lTerminateAllCalls(); // Hangup all calls void lSetSpeakerMuted(bool muted); void lSetMicrophoneMuted(bool isMuted); void lSetCameraEnabled(bool enabled); diff --git a/Linphone/data/CMakeLists.txt b/Linphone/data/CMakeLists.txt index b2a88611f..3223891a8 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-list.svg" "data/image/phone-transfer.svg" "data/image/address-book.svg" "data/image/address-book-selected.svg" diff --git a/Linphone/data/image/phone-list.svg b/Linphone/data/image/phone-list.svg new file mode 100644 index 000000000..41c805315 --- /dev/null +++ b/Linphone/data/image/phone-list.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Linphone/data/image/settings.svg b/Linphone/data/image/settings.svg new file mode 100644 index 000000000..4ea4c6c7c --- /dev/null +++ b/Linphone/data/image/settings.svg @@ -0,0 +1,3 @@ + + + diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index 833c4c834..ce87c1b2f 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -34,6 +34,10 @@ CallModel::CallModel(const std::shared_ptr &call, QObject *paren mDurationTimer.setSingleShot(false); connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->durationChanged(mMonitor->getDuration()); }); mDurationTimer.start(); + connect(this, &CallModel::stateChanged, this, [this] { + auto state = mMonitor->getState(); + if (state == linphone::Call::State::Paused) setPaused(true); + }); } CallModel::~CallModel() { @@ -86,6 +90,10 @@ void CallModel::transferTo(const std::shared_ptr &address) { << log().arg(QStringLiteral("Unable to transfer: `%1`.")).arg(QString::fromStdString(address->asString())); } +void CallModel::terminateAllCalls() { + auto status = mMonitor->getCore()->terminateAllCalls(); +} + void CallModel::setMicrophoneMuted(bool isMuted) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setMicrophoneMuted(isMuted); diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp index a9748bf9a..b9cce7dbb 100644 --- a/Linphone/model/call/CallModel.hpp +++ b/Linphone/model/call/CallModel.hpp @@ -45,6 +45,7 @@ public: void setCameraEnabled(bool enabled); void setPaused(bool paused); void transferTo(const std::shared_ptr &address); + void terminateAllCalls(); std::shared_ptr getRemoteAddress(); bool getAuthenticationTokenVerified(); diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index d2b7cf611..421cb1aa5 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -88,7 +88,7 @@ VariantObject *Utils::createCall(const QString &sipAddress, App::postCoreSync([callGui]() { auto app = App::getInstance(); auto window = app->getCallsWindow(callGui); - window->show(); + smartShowWindow(window); }); return callGui; } else return QVariant(); @@ -104,7 +104,7 @@ void Utils::openCallsWindow(CallGui *call) { QQuickWindow *Utils::getCallsWindow(CallGui *callGui) { auto app = App::getInstance(); auto window = app->getCallsWindow(QVariant::fromValue(callGui)); - window->show(); + smartShowWindow(window); return window; } @@ -112,6 +112,12 @@ void Utils::closeCallsWindow() { App::getInstance()->closeCallsWindow(); } +QQuickWindow *Utils::getMainWindow() { + auto win = App::getInstance()->getMainWindow(); + smartShowWindow(win); + return win; +} + VariantObject *Utils::haveAccount() { VariantObject *result = new VariantObject(); @@ -124,6 +130,16 @@ VariantObject *Utils::haveAccount() { result->requestValue(); return result; } + +void Utils::smartShowWindow(QQuickWindow *window) { + if (!window) return; + if (window->visibility() == QWindow::Maximized) // Avoid to change visibility mode + window->showNormal(); + else window->show(); + window->raise(); // Raise ensure to get focus on Mac + window->requestActivate(); +} + QString Utils::createAvatar(const QUrl &fileUrl) { QString filePath = fileUrl.toLocalFile(); QString fileId; // uuid.ext diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index a03f1cee2..376ab83c3 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -58,9 +58,11 @@ public: const QString &prepareTransfertAddress = "", const QHash &headers = {}); Q_INVOKABLE static void openCallsWindow(CallGui *call); + Q_INVOKABLE static QQuickWindow *getMainWindow(); Q_INVOKABLE static QQuickWindow *getCallsWindow(CallGui *callGui); Q_INVOKABLE static void closeCallsWindow(); Q_INVOKABLE static VariantObject *haveAccount(); + Q_INVOKABLE static void smartShowWindow(QQuickWindow *window); Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path Q_INVOKABLE static QString formatElapsedTime(int seconds); // Return the elapsed time formated diff --git a/Linphone/view/App/CallsWindow.qml b/Linphone/view/App/CallsWindow.qml index 6a8d02a79..3b755733d 100644 --- a/Linphone/view/App/CallsWindow.qml +++ b/Linphone/view/App/CallsWindow.qml @@ -10,25 +10,43 @@ Window { id: mainWindow width: 1512 * DefaultStyle.dp height: 982 * DefaultStyle.dp + flags: Qt.Window + // modality: Qt.WindowModal property CallGui call - property int callsCount: 0 - onCallsCountChanged: console.log("calls count", callsCount) + onCallChanged: { + waitingTime.seconds = 0 + waitingTimer.restart() + console.log("call changed", call, waitingTime.seconds) + } 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 + + property var callState: call.core.state onCallStateChanged: { console.log("State:", callState) if (callState === LinphoneEnums.CallState.Error || callState === LinphoneEnums.CallState.End) { - endCall() + endCall(call) } } - onClosing: { - endCall() + property var transferState: call.core.transferState + onTransferStateChanged: { + console.log("Transfer state:", transferState) + if (call.core.transferState === LinphoneEnums.CallState.Error) { + transferErrorPopup.visible = true + + } + else if (call.core.transferState === LinphoneEnums.CallState.Connected){ + var mainWin = UtilsCpp.getMainWindow() + UtilsCpp.smartShowWindow(mainWin) + mainWin.transferCallSucceed() + } + } + onClosing: (close) => { + close.accepted = false + terminateAllCallsDialog.open() } Timer { @@ -39,24 +57,69 @@ Window { } } - 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) { + function endCall(callToFinish) { + if (callToFinish) callToFinish.core.lTerminate() + if (!callsModel.haveCall) { bottomButtonsLayout.setButtonsEnabled(false) autoCloseWindow.restart() + } else if (callToFinish.core === mainWindow.call.core) { + mainWindow.call = callsModel.currentCall } } - + + Popup { + id: terminateAllCallsDialog + modal: true + anchors.centerIn: parent + closePolicy: Control.Popup.NoAutoClose + padding: 10 * DefaultStyle.dp + contentItem: ColumnLayout { + height: terminateAllCallsDialog.height + width: 278 * DefaultStyle.dp + spacing: 8 * DefaultStyle.dp + Text { + text: qsTr("La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. Souhaitez vous continuer ?") + Layout.preferredWidth: parent.width + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + } + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } + RowLayout { + Layout.alignment: Qt.AlignHCenter + Button { + text: qsTr("Oui") + onClicked: { + call.core.lTerminateAllCalls() + terminateAllCallsDialog.close() + } + } + Button { + text: qsTr("Non") + onClicked: terminateAllCallsDialog.close() + } + } + } + } + CallProxy{ - id: callList + id: callsModel onCurrentCallChanged: { console.log("Current call changed:"+currentCall) - if(currentCall) mainWindow.call = currentCall + if(currentCall) { + mainWindow.call = currentCall + } + } + onHaveCallChanged: { + if (!haveCall) { + mainWindow.endCall() + } } } + component BottomButton : Button { required property string enabledIcon property string disabledIcon @@ -84,7 +147,7 @@ Window { colorizationColor: disabledIcon && bottomButton.checked ? DefaultStyle.main2_0 : DefaultStyle.grey_0 } } - Control.Popup { + Popup { id: waitingPopup visible: mainWindow.call.core.transferState === LinphoneEnums.CallState.OutgoingInit || mainWindow.call.core.transferState === LinphoneEnums.CallState.OutgoingProgress @@ -93,23 +156,8 @@ Window { closePolicy: Control.Popup.NoAutoClose anchors.centerIn: parent padding: 20 * DefaultStyle.dp - 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 * DefaultStyle.dp - } - Rectangle { - id: mainBackground - anchors.fill: parent - radius: 15 * DefaultStyle.dp - } - } + underlineColor: DefaultStyle.main1_500_main + radius: 15 * DefaultStyle.dp contentItem: ColumnLayout { BusyIndicator{ Layout.alignment: Qt.AlignHCenter @@ -127,9 +175,8 @@ Window { transferErrorPopup.close() } } - Control.Popup { + Popup { id: transferErrorPopup - visible: mainWindow.call.core.transferState === LinphoneEnums.CallState.Error onVisibleChanged: if (visible) autoClosePopup.restart() closePolicy: Control.Popup.NoAutoClose x : parent.x + parent.width - width @@ -137,26 +184,8 @@ Window { rightMargin: 20 * DefaultStyle.dp bottomMargin: 20 * DefaultStyle.dp padding: 20 * DefaultStyle.dp - background: Item { - anchors.fill: parent - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: parent.height + 2 * DefaultStyle.dp - color: DefaultStyle.danger_500main - } - Rectangle { - id: transferErrorBackground - anchors.fill: parent - } - MultiEffect { - anchors.fill: transferErrorBackground - shadowEnabled: true - shadowColor: DefaultStyle.grey_900 - shadowBlur: 1 - // shadowOpacity: 0.1 - } - } + underlineColor: DefaultStyle.danger_500main + radius: 0 contentItem: ColumnLayout { Text { text: qsTr("Erreur de transfert") @@ -189,24 +218,23 @@ Window { image.height: 15 * DefaultStyle.dp image.sourceSize.width: 15 * DefaultStyle.dp image.sourceSize.height: 15 * DefaultStyle.dp - image.source: (mainWindow.call.core.state === LinphoneEnums.CallState.Paused - || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) + image.source: mainWindow.call.core.paused ? AppIcons.pause - : (mainWindow.callState === LinphoneEnums.CallState.End - || mainWindow.callState === LinphoneEnums.CallState.Released) + : (mainWindow.call.core.state === LinphoneEnums.CallState.End + || mainWindow.call.core.state === 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_500main : undefined + colorizationColor: mainWindow.call.core.state === LinphoneEnums.CallState.Paused + || mainWindow.call.core.state === LinphoneEnums.CallState.PausedByRemote || mainWindow.call.core.state === LinphoneEnums.CallState.End + || mainWindow.call.core.state === LinphoneEnums.CallState.Released ? DefaultStyle.danger_500main : undefined } Text { id: callStatusText - text: (mainWindow.callState === LinphoneEnums.CallState.End || mainWindow.callState === LinphoneEnums.CallState.Released) + text: (mainWindow.call.core.state === LinphoneEnums.CallState.End || mainWindow.call.core.state === LinphoneEnums.CallState.Released) ? qsTr("End of the call") - : (mainWindow.callState === LinphoneEnums.CallState.Paused || mainWindow.callState === LinphoneEnums.CallState.PausedByRemote) + : (mainWindow.call.core.paused) ? qsTr("Appel mis en pause") : EnumsToStringCpp.dirToString(mainWindow.call.core.dir) + qsTr(" call") color: DefaultStyle.grey_0 @@ -216,8 +244,8 @@ Window { } } Rectangle { - visible: mainWindow.callState === LinphoneEnums.CallState.Connected - || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning + visible: mainWindow.call.core.state === LinphoneEnums.CallState.Connected + || mainWindow.call.core.state === LinphoneEnums.CallState.StreamsRunning Layout.preferredHeight: parent.height Layout.preferredWidth: 2 * DefaultStyle.dp } @@ -228,8 +256,8 @@ Window { pixelSize: 22 * DefaultStyle.dp weight: 800 * DefaultStyle.dp } - visible: mainWindow.callState === LinphoneEnums.CallState.Connected - || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning + visible: mainWindow.call.core.state === LinphoneEnums.CallState.Connected + || mainWindow.call.core.state === LinphoneEnums.CallState.StreamsRunning } } @@ -277,7 +305,7 @@ Window { Layout.alignment: Qt.AlignCenter background: Rectangle { anchors.fill: parent - color: DefaultStyle.grey_600 * DefaultStyle.dp + color: DefaultStyle.grey_600 radius: 15 * DefaultStyle.dp } contentItem: Item { @@ -288,7 +316,7 @@ Window { Connections { target: mainWindow onCallStateChanged: { - if (mainWindow.callState === LinphoneEnums.CallState.Error) { + if (mainWindow.call.core.state === LinphoneEnums.CallState.Error) { centerLayout.currentIndex = 2 } } @@ -299,7 +327,7 @@ Window { Layout.preferredWidth: parent.width Layout.preferredHeight: parent.height Timer { - id: secondsTimer + id: waitingTimer interval: 1000 repeat: true onTriggered: waitingTime.seconds += 1 @@ -308,11 +336,11 @@ Window { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 30 * DefaultStyle.dp - 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 + visible: mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingInit + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.call.core.state == LinphoneEnums.CallState.IncomingReceived BusyIndicator { indicatorColor: DefaultStyle.main2_100 Layout.alignment: Qt.AlignHCenter @@ -324,9 +352,12 @@ Window { color: DefaultStyle.grey_0 Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter - font.pointSize: 30 * DefaultStyle.dp + font { + pixelSize: 30 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp + } Component.onCompleted: { - secondsTimer.restart() + waitingTimer.restart() } } } @@ -402,25 +433,26 @@ Window { property int currentIndex: 0 Layout.rightMargin: 10 * DefaultStyle.dp visible: false - headerContent: StackLayout { - currentIndex: rightPanel.currentIndex + function replace(id) { + rightPanelStack.replace(id, Control.StackView.Immediate) + } + headerContent: Text { + id: rightPanelTitle anchors.verticalCenter: parent.verticalCenter - Text { - color: DefaultStyle.main2_700 - text: qsTr("Transfert d'appel") - font.bold: true - } - Text { - color: DefaultStyle.main2_700 - text: qsTr("Dialer") - font.bold: true + width: rightPanel.width + color: DefaultStyle.main2_700 + // text: qsTr("Transfert d'appel") + font { + pixelSize: 16 * DefaultStyle.dp + weight: 800 * DefaultStyle.dp } } - contentItem: StackLayout { - currentIndex: rightPanel.currentIndex + contentItem: Control.StackView { + id: rightPanelStack + } + Component { + id: contactsListPanel ContactsList { - Layout.fillWidth: true - Layout.fillHeight: true sideMargin: 10 * DefaultStyle.dp topMargin: 15 * DefaultStyle.dp groupCallVisible: false @@ -429,10 +461,13 @@ Window { onCallButtonPressed: (address) => { mainWindow.call.core.lTransferCall(address) } + Control.StackView.onActivated: rightPanelTitle.text = qsTr("Transfert d'appel") } + } + Component { + id: dialerPanel ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true + Control.StackView.onActivated: rightPanelTitle.text = qsTr("Dialer") Item { Layout.fillWidth: true Layout.fillHeight: true @@ -465,6 +500,200 @@ Window { } } } + Component { + id: callsListPanel + Control.Control { + Control.StackView.onActivated: rightPanelTitle.text = qsTr("Liste d'appel") + // width: callList.width + // height: callList.height + onHeightChanged: console.log("control height changed", height) + + // padding: 15 * DefaultStyle.dp + topPadding: 15 * DefaultStyle.dp + bottomPadding: 15 * DefaultStyle.dp + leftPadding: 15 * DefaultStyle.dp + rightPadding: 15 * DefaultStyle.dp + + background: Rectangle { + anchors.fill: parent + anchors.leftMargin: 10 * DefaultStyle.dp + anchors.rightMargin: 10 * DefaultStyle.dp + anchors.topMargin: 10 * DefaultStyle.dp + anchors.bottomMargin: 10 * DefaultStyle.dp + color: DefaultStyle.main2_0 + radius: 15 * DefaultStyle.dp + } + + contentItem: ListView { + id: callList + model: callsModel + height: contentHeight + onHeightChanged: console.log("height changzed lustviexw", height, contentHeight) + spacing: 15 * DefaultStyle.dp + + onCountChanged: forceLayout() + + delegate: Item { + anchors.left: parent.left + anchors.right: parent.right + id: callDelegate + height: 45 * DefaultStyle.dp + + RowLayout { + id: delegateContent + anchors.fill: parent + anchors.leftMargin: 10 * DefaultStyle.dp + anchors.rightMargin: 10 * DefaultStyle.dp + Avatar { + id: delegateAvatar + address: modelData.core.peerAddress + Layout.preferredWidth: 45 * DefaultStyle.dp + Layout.preferredHeight: 45 * DefaultStyle.dp + } + Text { + id: delegateName + text: UtilsCpp.getDisplayName(modelData.core.peerAddress).value + Connections { + target: modelData.core + } + } + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + Text { + id: callStateText + text: modelData.core.state === LinphoneEnums.CallState.Paused + || modelData.core.state === LinphoneEnums.CallState.PausedByRemote + ? qsTr("Appel en pause") : qsTr("Appel en cours") + } + Button { + id: listCallOptionsButton + checked: listCallOptionsMenu.visible + Layout.preferredWidth: 24 * DefaultStyle.dp + Layout.preferredHeight: 24 * DefaultStyle.dp + Layout.alignment: Qt.AlignRight + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 + background: Rectangle { + anchors.fill: listCallOptionsButton + opacity: listCallOptionsButton.checked ? 1 : 0 + color: DefaultStyle.main2_300 + radius: 40 * DefaultStyle.dp + } + contentItem: Image { + source: AppIcons.verticalDots + sourceSize.width: 24 * DefaultStyle.dp + sourceSize.height: 24 * DefaultStyle.dp + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + } + onPressed: { + console.log("listCallOptionsMenu visible", listCallOptionsMenu.visible, "opened", listCallOptionsMenu.opened) + if (listCallOptionsMenu.visible){ + console.log("close popup") + listCallOptionsMenu.close() + } + else { + console.log("open popup") + listCallOptionsMenu.open() + } + } + Control.Popup { + id: listCallOptionsMenu + x: - width + y: listCallOptionsButton.height + onVisibleChanged: console.log("popup visible", visible) + closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape + + padding: 20 * DefaultStyle.dp + + background: Item { + anchors.fill: parent + Rectangle { + id: callOptionsMenuPopup + anchors.fill: parent + color: DefaultStyle.grey_0 + radius: 16 * DefaultStyle.dp + } + MultiEffect { + source: callOptionsMenuPopup + anchors.fill: callOptionsMenuPopup + shadowEnabled: true + shadowBlur: 1 + shadowColor: DefaultStyle.grey_900 + shadowOpacity: 0.4 + } + } + + contentItem: ColumnLayout { + spacing: 0 + Control.Button { + background: Item {} + contentItem: RowLayout { + Image { + source: modelData.core.state === LinphoneEnums.CallState.Paused + || modelData.core.state === LinphoneEnums.CallState.PausedByRemote + ? AppIcons.phone : AppIcons.pause + sourceSize.width: 32 * DefaultStyle.dp + sourceSize.height: 32 * DefaultStyle.dp + Layout.preferredWidth: 32 * DefaultStyle.dp + Layout.preferredHeight: 32 * DefaultStyle.dp + fillMode: Image.PreserveAspectFit + } + Text { + text: modelData.core.state === LinphoneEnums.CallState.Paused + || modelData.core.state === LinphoneEnums.CallState.PausedByRemote + ? qsTr("Reprendre l'appel") : qsTr("Mettre en pause") + color: DefaultStyle.main2_500main + Layout.preferredWidth: metrics.width + } + TextMetrics { + id: metrics + text: qsTr("Reprendre l'appel") + } + Item { + Layout.fillWidth: true + } + } + onClicked: modelData.core.lSetPaused(!modelData.core.paused) + } + Control.Button { + background: Item {} + contentItem: RowLayout { + EffectImage { + image.source: AppIcons.endCall + colorizationColor: DefaultStyle.danger_500main + width: 32 * DefaultStyle.dp + height: 32 * DefaultStyle.dp + } + Text { + color: DefaultStyle.danger_500main + text: qsTr("Terminer l'appel") + } + Item { + Layout.fillWidth: true + } + } + onClicked: mainWindow.endCall(modelData) + } + } + } + } + } + + // MouseArea{ + // anchors.fill: delegateLayout + // onClicked: { + // callsModel.currentCall = modelData + // } + // } + } + } + } + } } } GridLayout { @@ -474,13 +703,22 @@ Window { Layout.alignment: Qt.AlignHCenter layoutDirection: Qt.LeftToRight columnSpacing: 20 * DefaultStyle.dp - Connections { - target: mainWindow - onCallStateChanged: if (mainWindow.callState === LinphoneEnums.CallState.Connected || mainWindow.callState === LinphoneEnums.CallState.StreamsRunning) { + + function refreshLayout() { + if (mainWindow.call.core.state === LinphoneEnums.CallState.Connected || mainWindow.call.core.state === LinphoneEnums.CallState.StreamsRunning) { bottomButtonsLayout.layoutDirection = Qt.RightToLeft connectedCallButtons.visible = true + } else if (mainWindow.callState === LinphoneEnums.CallState.OutgoingInit || mainWindow.callState === LinphoneEnums.CallState.End) { + connectedCallButtons.visible = false + bottomButtonsLayout.layoutDirection = Qt.LeftToRight } } + + Connections { + target: mainWindow + onCallStateChanged: bottomButtonsLayout.refreshLayout() + onCallChanged: bottomButtonsLayout.refreshLayout() + } function setButtonsEnabled(enabled) { for(var i=0; i < children.length; ++i) { children[i].enabled = false @@ -490,11 +728,11 @@ Window { 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 + Layout.column: mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingInit + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.call.core.state == LinphoneEnums.CallState.IncomingReceived ? 0 : bottomButtonsLayout.columns - 1 Layout.preferredWidth: 75 * DefaultStyle.dp Layout.preferredHeight: 55 * DefaultStyle.dp @@ -503,7 +741,7 @@ Window { color: DefaultStyle.danger_500main radius: 71 * DefaultStyle.dp } - onClicked: mainWindow.endCall() + onClicked: mainWindow.endCall(mainWindow.call) } RowLayout { id: connectedCallButtons @@ -524,30 +762,51 @@ Window { : DefaultStyle.grey_500 : DefaultStyle.grey_600 } - enabled: mainWindow.callState != LinphoneEnums.CallState.PausedByRemote + enabled: mainWindow.call.core.state != 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) + checked: mainWindow.call.core.paused + onClicked: { + mainWindow.call.core.lSetPaused(!callsModel.currentCall.core.paused) + } + } + BottomButton { + id: newCallButton + checkable: false + enabledIcon: AppIcons.newCall + Layout.preferredWidth: 55 * DefaultStyle.dp + Layout.preferredHeight: 55 * DefaultStyle.dp + onClicked: { + var mainWin = UtilsCpp.getMainWindow() + UtilsCpp.smartShowWindow(mainWin) + mainWin.goToNewCall() + } } BottomButton { id: transferCallButton enabledIcon: AppIcons.transferCall Layout.preferredWidth: 55 * DefaultStyle.dp Layout.preferredHeight: 55 * DefaultStyle.dp - onClicked: { - rightPanel.visible = true - rightPanel.currentIndex = 0 + onCheckedChanged: { + if (checked) { + rightPanel.visible = true + rightPanel.replace(contactsListPanel) + } else { + rightPanel.visible = false + } + } + Connections { + target: rightPanel + onVisibleChanged: if(!rightPanel.visible) transferCallButton.checked = false } } } 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 + Layout.column: mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingInit + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingProgress + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingRinging + || mainWindow.call.core.state == LinphoneEnums.CallState.OutgoingEarlyMedia + || mainWindow.call.core.state == LinphoneEnums.CallState.IncomingReceived ? bottomButtonsLayout.columns - 1 : 0 BottomButton { enabledIcon: AppIcons.videoCamera @@ -589,10 +848,30 @@ Window { spacing: 10 * DefaultStyle.dp Control.Button { - id: dialerButton - // width: 150 + id: callListButton + Layout.fillWidth: true + background: Item { + visible: false + } + contentItem: RowLayout { + Image { + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + source: AppIcons.callList + } + Text { + text: qsTr("Liste d'appel") + } + } + onClicked: { + rightPanel.visible = true + rightPanel.replace(callsListPanel) + moreOptionsMenu.close() + } + } + Control.Button { + id: dialerButton Layout.fillWidth: true - // height: 32 * DefaultStyle.dp background: Item { visible: false } @@ -608,14 +887,13 @@ Window { } onClicked: { rightPanel.visible = true - rightPanel.currentIndex = 1 + rightPanel.replace(dialerPanel) moreOptionsMenu.close() } } Control.Button { id: speakerButton Layout.fillWidth: true - // height: 32 * DefaultStyle.dp checkable: true background: Item { visible: false diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index d6ad780c6..0096fb533 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -16,6 +16,71 @@ Item { signal addAccountRequest() + function goToNewCall() { + tabbar.currentIndex = 0 + callPage.goToNewCall() + } + + function transferCallSucceed() { + transferSucceedPopup.open() + } + + Timer { + id: autoClosePopup + interval: 5000 + onTriggered: { + transferSucceedPopup.close() + } + } + Popup { + id: transferSucceedPopup + onVisibleChanged: if (visible) autoClosePopup.restart() + closePolicy: Control.Popup.NoAutoClose + x : parent.x + parent.width - width + y : parent.y + parent.height - height + rightMargin: 20 * DefaultStyle.dp + bottomMargin: 20 * DefaultStyle.dp + padding: 20 * DefaultStyle.dp + underlineColor: DefaultStyle.success_500main + radius: 0 + contentItem: RowLayout { + spacing: 15 * DefaultStyle.dp + EffectImage { + image.source: AppIcons.smiley + colorizationColor: DefaultStyle.success_500main + Layout.preferredWidth: 32 * DefaultStyle.dp + Layout.preferredHeight: 32 * DefaultStyle.dp + width: 32 * DefaultStyle.dp + height: 32 * DefaultStyle.dp + } + Rectangle { + Layout.preferredWidth: 1 * DefaultStyle.dp + Layout.preferredHeight: parent.height + color: DefaultStyle.main2_200 + } + ColumnLayout { + Text { + text: qsTr("Appel transféré") + color: DefaultStyle.success_500main + font { + pixelSize: 16 * DefaultStyle.dp + weight: 800 * DefaultStyle.dp + } + } + Text { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + text: qsTr("Votre correspondant a été transféré au contact sélectionné") + color: DefaultStyle.main2_500main + font { + pixelSize: 12 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp + } + } + } + } + } + RowLayout { anchors.fill: parent // spacing: 30 @@ -93,6 +158,7 @@ Item { currentIndex: tabbar.currentIndex CallPage { + id: callPage } //ContactPage{} //ConversationPage{} diff --git a/Linphone/view/App/Main.qml b/Linphone/view/App/Main.qml index 239e363e4..db4f8e7db 100644 --- a/Linphone/view/App/Main.qml +++ b/Linphone/view/App/Main.qml @@ -11,6 +11,16 @@ Window { visible: true title: qsTr("Linphone") property bool firstConnection: true + + function goToNewCall() { + mainWindowStackView.replace(mainPage, StackView.Immediate) + mainWindowStackView.currentItem.goToNewCall() + } + function transferCallSucceed() { + mainWindowStackView.replace(mainPage, StackView.Immediate) + mainWindowStackView.currentItem.transferCallSucceed() + } + AccountProxy{ id: accountProxy onHaveAccountChanged: { diff --git a/Linphone/view/Item/NumericPad.qml b/Linphone/view/Item/NumericPad.qml index b20431168..3888cdb39 100644 --- a/Linphone/view/Item/NumericPad.qml +++ b/Linphone/view/Item/NumericPad.qml @@ -31,7 +31,6 @@ Control.Popup { shadowEnabled: true shadowColor: DefaultStyle.grey_1000 shadowOpacity: 0.8 - shadowHorizontalOffset: 10 * DefaultStyle.dp shadowBlur: 1 } Rectangle { diff --git a/Linphone/view/Item/Popup.qml b/Linphone/view/Item/Popup.qml index 46b6f510b..03b2616bb 100644 --- a/Linphone/view/Item/Popup.qml +++ b/Linphone/view/Item/Popup.qml @@ -6,20 +6,29 @@ import Linphone Control.Popup{ id: mainItem padding: 0 + property color underlineColor + property int radius: 16 * DefaultStyle.dp background: Item{ + Rectangle { + visible: mainItem.underlineColor != undefined + width: mainItem.width + height: mainItem.height + 2 * DefaultStyle.dp + color: mainItem.underlineColor + radius: mainItem.radius + } Rectangle{ id: backgroundItem width: mainItem.width height: mainItem.height - radius: 16 * DefaultStyle.dp + radius: mainItem.radius border.color: DefaultStyle.grey_0 - border.width: 1 + // border.width: 1 } MultiEffect { anchors.fill: backgroundItem source: backgroundItem - maskSource: backgroundItem shadowEnabled: true + shadowColor: DefaultStyle.grey_900 shadowBlur: 1.0 shadowOpacity: 0.1 } diff --git a/Linphone/view/Page/Main/CallPage.qml b/Linphone/view/Page/Main/CallPage.qml index 9605cb3d1..4e72657fd 100644 --- a/Linphone/view/Page/Main/CallPage.qml +++ b/Linphone/view/Page/Main/CallPage.qml @@ -10,7 +10,11 @@ AbstractMainPage { emptyListText: qsTr("Historique d'appel vide") newItemIconSource: AppIcons.newCall - onNoItemButtonPressed: listStackView.push(newCallItem) + onNoItemButtonPressed: goToNewCall() + + function goToNewCall() { + listStackView.push(newCallItem) + } leftPanelContent: Item { Layout.fillWidth: true @@ -212,6 +216,7 @@ AbstractMainPage { var addressEnd = "@sip.linphone.org" if (!address.endsWith(addressEnd)) address += addressEnd var callVarObject = UtilsCpp.createCall(address) + // var window = UtilsCpp.getCallsWindow() } } } diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index a4ea06902..7e257b839 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 callList: "image://internal/phone-list.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"