Commit 4effc5dc authored by Ronan Abhamon's avatar Ronan Abhamon

feat(app): calls in progress (fix crash, remove `CaterpillarAnimation`)

parent 3632197a
...@@ -143,9 +143,8 @@ ...@@ -143,9 +143,8 @@
<file>assets/images/video_call_hovered.svg</file> <file>assets/images/video_call_hovered.svg</file>
<file>assets/images/video_call_normal.svg</file> <file>assets/images/video_call_normal.svg</file>
<file>assets/images/video_call_pressed.svg</file> <file>assets/images/video_call_pressed.svg</file>
<file>ui/modules/Common/Animations/CaterpillarAnimation.qml</file> <file>ui/modules/Common/Animations/BusyIndicator.qml</file>
<file>ui/modules/Common/Borders.qml</file> <file>ui/modules/Common/Borders.qml</file>
<file>ui/modules/Common/BusyIndicator.qml</file>
<file>ui/modules/Common/Collapse.qml</file> <file>ui/modules/Common/Collapse.qml</file>
<file>ui/modules/Common/Colors.qml</file> <file>ui/modules/Common/Colors.qml</file>
<file>ui/modules/Common/Constants.qml</file> <file>ui/modules/Common/Constants.qml</file>
......
...@@ -123,3 +123,11 @@ void CallModel::setPausedByUser (bool status) { ...@@ -123,3 +123,11 @@ void CallModel::setPausedByUser (bool status) {
emit pausedByUserChanged(false); emit pausedByUserChanged(false);
} }
} }
bool CallModel::getVideoOutputEnabled () const {
// TODO
}
void CallModel::setVideoOutputEnabled (bool status) {
// TODO
}
...@@ -14,6 +14,7 @@ class CallModel : public QObject { ...@@ -14,6 +14,7 @@ class CallModel : public QObject {
Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT); Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT);
Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged); Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged);
Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY pausedByUserChanged); Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY pausedByUserChanged);
Q_PROPERTY(bool videoOutputEnabled READ getVideoOutputEnabled WRITE setVideoOutputEnabled NOTIFY videoOutputEnabled);
public: public:
enum CallStatus { enum CallStatus {
...@@ -39,6 +40,7 @@ signals: ...@@ -39,6 +40,7 @@ signals:
void statusChanged (CallStatus status); void statusChanged (CallStatus status);
void pausedByUserChanged (bool status); void pausedByUserChanged (bool status);
void microMutedChanged (bool status); void microMutedChanged (bool status);
void videoOutputEnabled (bool status);
private: private:
QString getSipAddress () const; QString getSipAddress () const;
...@@ -54,6 +56,9 @@ private: ...@@ -54,6 +56,9 @@ private:
bool getPausedByUser () const; bool getPausedByUser () const;
void setPausedByUser (bool status); void setPausedByUser (bool status);
bool getVideoOutputEnabled () const;
void setVideoOutputEnabled (bool status);
bool m_micro_muted = false; bool m_micro_muted = false;
linphone::CallState m_linphone_call_status = linphone::CallStateIdle; linphone::CallState m_linphone_call_status = linphone::CallStateIdle;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <QTimer> #include <QTimer>
#include "../../app/App.hpp" #include "../../app/App.hpp"
#include "../../utils.hpp"
#include "../core/CoreManager.hpp" #include "../core/CoreManager.hpp"
#include "CallsListModel.hpp" #include "CallsListModel.hpp"
...@@ -60,6 +61,19 @@ QVariant CallsListModel::data (const QModelIndex &index, int role) const { ...@@ -60,6 +61,19 @@ QVariant CallsListModel::data (const QModelIndex &index, int role) const {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void CallsListModel::launchAudioCall (const QString &sip_uri) const {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
core->inviteAddress(
core->interpretUrl(::Utils::qStringToLinphoneString(sip_uri))
);
}
void CallsListModel::launchVideoCall (const QString &sip_uri) const {
// TODO
}
// -----------------------------------------------------------------------------
bool CallsListModel::removeRow (int row, const QModelIndex &parent) { bool CallsListModel::removeRow (int row, const QModelIndex &parent) {
return removeRows(row, 1, parent); return removeRows(row, 1, parent);
} }
...@@ -86,6 +100,9 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &linphone_call) { ...@@ -86,6 +100,9 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &linphone_call) {
App::getInstance()->getCallsWindow()->show(); App::getInstance()->getCallsWindow()->show();
CallModel *call = new CallModel(linphone_call); CallModel *call = new CallModel(linphone_call);
qInfo() << "Add call:" << call;
App::getInstance()->getEngine()->setObjectOwnership(call, QQmlEngine::CppOwnership); App::getInstance()->getEngine()->setObjectOwnership(call, QQmlEngine::CppOwnership);
linphone_call->setData("call-model", *call); linphone_call->setData("call-model", *call);
...@@ -97,12 +114,12 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &linphone_call) { ...@@ -97,12 +114,12 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &linphone_call) {
} }
void CallsListModel::removeCall (const shared_ptr<linphone::Call> &linphone_call) { void CallsListModel::removeCall (const shared_ptr<linphone::Call> &linphone_call) {
CallModel *call = &linphone_call->getData<CallModel>("call-model");
linphone_call->unsetData("call-model");
// TODO: It will be (maybe) necessary to use a single scheduled function in the future. // TODO: It will be (maybe) necessary to use a single scheduled function in the future.
QTimer::singleShot( QTimer::singleShot(
DELAY_BEFORE_REMOVE_CALL, this, [this, call]() { DELAY_BEFORE_REMOVE_CALL, this, [this, linphone_call]() {
CallModel *call = &linphone_call->getData<CallModel>("call-model");
linphone_call->unsetData("call-model");
qInfo() << "Removing call:" << call; qInfo() << "Removing call:" << call;
int index = m_list.indexOf(call); int index = m_list.indexOf(call);
......
...@@ -21,6 +21,9 @@ public: ...@@ -21,6 +21,9 @@ public:
QHash<int, QByteArray> roleNames () const override; QHash<int, QByteArray> roleNames () const override;
QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
Q_INVOKABLE void launchAudioCall (const QString &sip_uri) const;
Q_INVOKABLE void launchVideoCall (const QString &sip_uri) const;
private: private:
bool removeRow (int row, const QModelIndex &parent = QModelIndex()); bool removeRow (int row, const QModelIndex &parent = QModelIndex());
bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;
......
...@@ -10,6 +10,8 @@ BusyIndicator { ...@@ -10,6 +10,8 @@ BusyIndicator {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
property color color: BusyIndicatorStyle.color
readonly property int _rotation: 360 readonly property int _rotation: 360
readonly property int _size: width < height ? width : height readonly property int _size: width < height ? width : height
...@@ -58,7 +60,7 @@ BusyIndicator { ...@@ -58,7 +60,7 @@ BusyIndicator {
height: item.height / 3 height: item.height / 3
width: item.width / 3 width: item.width / 3
color: BusyIndicatorStyle.color color: busyIndicator.color
radius: (width > height ? width : height) / 2 radius: (width > height ? width : height) / 2
transform: [ transform: [
......
import QtQuick 2.7
import Common.Styles 1.0
// =============================================================================
Row {
id: container
property color sphereColor: CaterpillarAnimationStyle.sphere.color
property int animationDuration: CaterpillarAnimationStyle.animation.duration
property int nSpheres: CaterpillarAnimationStyle.nSpheres
property int sphereSize: CaterpillarAnimationStyle.sphere.size
property int animationSpace: CaterpillarAnimationStyle.animation.space
spacing: CaterpillarAnimationStyle.spacing
Repeater {
id: repeater
model: nSpheres
Rectangle {
id: sphere
property bool forceRunning: false
property int previousY: 0
function startAnimation () {
if (!animator.running) {
animator.running = true
} else {
forceRunning = true
}
}
color: sphereColor
height: width
radius: width / 2
width: container.sphereSize
// y can be: `0`, `animationSpace` or `animationSpace / 2`
onYChanged: {
// No call executed by last sphere.
if (index === nSpheres - 1) {
return
}
if (y === (animationSpace / 2) && previousY === 0) {
repeater.itemAt(index + 1).startAnimation()
}
previousY = y
}
Component.onCompleted: {
// Only start first sphere.
if (index === 0) {
animator.running = true
}
}
YAnimator on y {
id: animator
duration: container.animationDuration
from: 0
running: false
to: animationSpace / 2
onRunningChanged: {
if (running) {
return
}
var mid = animationSpace / 2
if (from === animationSpace && to === mid) {
from = mid
to = 0
} else if (from === mid && to === 0) {
from = 0
to = mid
if (index !== 0 && !forceRunning) {
return
}
} else if (from === 0 && to === mid) {
from = mid
to = animationSpace
} else {
from = animationSpace
to = mid
}
forceRunning = false
animator.running = true
}
}
}
}
}
...@@ -12,14 +12,11 @@ singleton Constants 1.0 Constants.qml ...@@ -12,14 +12,11 @@ singleton Constants 1.0 Constants.qml
# Components ------------------------------------------------------------------- # Components -------------------------------------------------------------------
# Animations # Animations
CaterpillarAnimation 1.0 Animations/CaterpillarAnimation.qml BusyIndicator 1.0 Animations/BusyIndicator.qml
# Chat # Chat
Borders 1.0 Borders.qml Borders 1.0 Borders.qml
# BusyIndicator
BusyIndicator 1.0 BusyIndicator.qml
# Collapse # Collapse
Collapse 1.0 Collapse.qml Collapse 1.0 Collapse.qml
......
...@@ -44,14 +44,14 @@ ListView { ...@@ -44,14 +44,14 @@ ListView {
_mapStatusToParams[CallModel.CallStatusConnected] = { _mapStatusToParams[CallModel.CallStatusConnected] = {
actions: [{ actions: [{
name: qsTr('resumeCall'), handler: (function (call) { call.pausedByUser = false }),
handler: (function (call) { call.pausedByUser = false }) name: qsTr('resumeCall')
}, { }, {
name: qsTr('transferCall'), handler: (function (call) { call.transfer() }),
handler: (function (call) { call.transfer() }) name: qsTr('transferCall')
}, { }, {
name: qsTr('terminateCall'), handler: (function (call) { call.terminate() }),
handler: (function (call) { call.terminate() }) name: qsTr('terminateCall')
}], }],
component: callActions, component: callActions,
string: 'connected' string: 'connected'
...@@ -85,14 +85,14 @@ ListView { ...@@ -85,14 +85,14 @@ ListView {
_mapStatusToParams[CallModel.CallStatusPaused] = { _mapStatusToParams[CallModel.CallStatusPaused] = {
actions: [{ actions: [{
name: qsTr('pauseCall'), handler: (function (call) { call.pausedByUser = true }),
handler: (function (call) { call.pausedByUser = true }) name: qsTr('pauseCall')
}, { }, {
name: qsTr('transferCall'), handler: (function (call) { call.transfer() }),
handler: (function (call) { call.transfer() }) name: qsTr('transferCall')
}, { }, {
name: qsTr('terminateCall'), handler: (function (call) { call.terminate() }),
handler: (function (call) { call.terminate() }) name: qsTr('terminateCall')
}], }],
component: callActions, component: callActions,
string: 'paused' string: 'paused'
...@@ -160,6 +160,29 @@ ListView { ...@@ -160,6 +160,29 @@ ListView {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
SmartConnect {
Component.onCompleted: {
this.connect(model, 'rowsAboutToBeRemoved', function (_, first, last) {
var index = calls.currentIndex
if (index >= first && index <= last) { // Remove current call.
if (model.rowCount() - (last - first + 1) <= 0) {
calls.currentIndex = -1
} else {
calls.currentIndex = 0
}
} else if (last < index) { // Remove before current call.
calls.currentIndex = index - (last - first + 1)
}
})
this.connect(model, 'rowsInserted', function (_, first, last) {
calls.currentIndex = first
})
}
}
// ---------------------------------------------------------------------------
delegate: CallControls { delegate: CallControls {
id: _callControls id: _callControls
......
...@@ -17,7 +17,8 @@ Rectangle { ...@@ -17,7 +17,8 @@ Rectangle {
property var call property var call
default property alias _actionArea: actionArea.data default property alias _actionArea: actionArea.data
property var _contact: SipAddressesModel.mapSipAddressToContact(call.sipAddress)
property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -45,12 +46,16 @@ Rectangle { ...@@ -45,12 +46,16 @@ Rectangle {
height: StartingCallStyle.contactDescriptionHeight height: StartingCallStyle.contactDescriptionHeight
horizontalTextAlignment: Text.AlignHCenter horizontalTextAlignment: Text.AlignHCenter
sipAddress: call.sipAddress sipAddress: call.sipAddress
username: LinphoneUtils.getContactUsername(_contact || call.sipAddress) username: LinphoneUtils.getContactUsername(_contactObserver.contact || call.sipAddress)
width: parent.width width: parent.width
} }
CaterpillarAnimation { BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
color: StartingCallStyle.busyIndicator.color
height: StartingCallStyle.busyIndicator.height
width: StartingCallStyle.busyIndicator.width
visible: call.isOutgoing visible: call.isOutgoing
} }
} }
...@@ -81,7 +86,7 @@ Rectangle { ...@@ -81,7 +86,7 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
backgroundColor: StartingCallStyle.avatar.backgroundColor backgroundColor: StartingCallStyle.avatar.backgroundColor
image: _contact && _contact.avatar image: _contactObserver.contact && _contactObserver.contact.avatar
username: contactDescription.username username: contactDescription.username
height: _computeAvatarSize() height: _computeAvatarSize()
......
...@@ -15,10 +15,7 @@ Window { ...@@ -15,10 +15,7 @@ Window {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
readonly property var call: { readonly property var call: calls.selectedCall
console.log('hihi')
return calls.selectedCall
}
readonly property var sipAddress: { readonly property var sipAddress: {
if (call) { if (call) {
return call.sipAddress return call.sipAddress
...@@ -27,18 +24,6 @@ Window { ...@@ -27,18 +24,6 @@ Window {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function launchAudioCall (sipAddress) {
window.show()
}
function launchVideoCall (sipAddress) {
window.show()
}
// ---------------------------------------------------------------------------
minimumHeight: CallsWindowStyle.minimumHeight minimumHeight: CallsWindowStyle.minimumHeight
minimumWidth: CallsWindowStyle.minimumWidth minimumWidth: CallsWindowStyle.minimumWidth
title: CallsWindowStyle.title title: CallsWindowStyle.title
...@@ -125,7 +110,6 @@ Window { ...@@ -125,7 +110,6 @@ Window {
id: incomingCall id: incomingCall
IncomingCall { IncomingCall {
anchors.fill: parent
call: window.call call: window.call
} }
} }
...@@ -134,7 +118,6 @@ Window { ...@@ -134,7 +118,6 @@ Window {
id: outgoingCall id: outgoingCall
OutgoingCall { OutgoingCall {
anchors.fill: parent
call: window.call call: window.call
} }
} }
...@@ -143,11 +126,20 @@ Window { ...@@ -143,11 +126,20 @@ Window {
id: incall id: incall
Incall { Incall {
anchors.fill: parent
call: window.call call: window.call
} }
} }
Component {
id: chat
Chat {
proxyModel: ChatProxyModel {
sipAddress: window.sipAddress
}
}
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
childA: Loader { childA: Loader {
...@@ -158,11 +150,12 @@ Window { ...@@ -158,11 +150,12 @@ Window {
if (!call) { if (!call) {
return null return null
} }
return incomingCall
var status = call.status var status = call.status
if (status === CallModel.CallStatusIncoming) { if (status === CallModel.CallStatusIncoming) {
return incomingCall return incomingCall
} }
if (status === CallModel.CallStatusOutgoing) { if (status === CallModel.CallStatusOutgoing) {
return outgoingCall return outgoingCall
} }
...@@ -174,13 +167,7 @@ Window { ...@@ -174,13 +167,7 @@ Window {
childB: Loader { childB: Loader {
active: Boolean(window.call) active: Boolean(window.call)
anchors.fill: parent anchors.fill: parent
sourceComponent: window.call ? chat : null
sourceComponent: Chat {
anchors.fill: parent
proxyModel: ChatProxyModel {
sipAddress: window.sipAddress || ''
}
}
} }
} }
} }
......
...@@ -11,10 +11,6 @@ import App.Styles 1.0 ...@@ -11,10 +11,6 @@ import App.Styles 1.0
// ============================================================================= // =============================================================================
Rectangle { Rectangle {
id: call
// ---------------------------------------------------------------------------
property var call property var call
property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress) property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress)
......
...@@ -2,7 +2,7 @@ import Common 1.0 ...@@ -2,7 +2,7 @@ import Common 1.0
import App.Styles 1.0 import App.Styles 1.0
// =================================================================== // =============================================================================
AbstractStartingCall { AbstractStartingCall {
ActionBar { ActionBar {
...@@ -11,18 +11,22 @@ AbstractStartingCall { ...@@ -11,18 +11,22 @@ AbstractStartingCall {
ActionButton { ActionButton {
icon: 'video_call_accept' icon: 'video_call_accept'
onClicked: call.acceptWithVideo()
} }
ActionButton { ActionButton {
icon: 'call_accept' icon: 'call_accept'
onClicked: call.accept()
} }
} }
ActionBar { ActionBar {
anchors { anchors {
verticalCenter: parent.verticalCenter
right: parent.right right: parent.right
rightMargin: StartingCallStyle.rightButtonsGroupMargin rightMargin: StartingCallStyle.rightButtonsGroupMargin
verticalCenter: parent.verticalCenter
} }
iconSize: StartingCallStyle.iconSize iconSize: StartingCallStyle.iconSize
......
...@@ -10,8 +10,8 @@ import App.Styles 1.0 ...@@ -10,8 +10,8 @@ import App.Styles 1.0
AbstractStartingCall { AbstractStartingCall {
GridLayout { GridLayout {
columns: parent.width < 415 && call.videoOutputEnabled ? 1 : 2
rowSpacing: ActionBarStyle.spacing rowSpacing: ActionBarStyle.spacing
columns: parent.width < 415 && call.isVideoCall ? 1 : 2
anchors { anchors {
left: parent.left left: parent.left
...@@ -24,7 +24,7 @@ AbstractStartingCall { ...@@ -24,7 +24,7 @@ AbstractStartingCall {
icon: 'micro' icon: 'micro'
iconSize: StartingCallStyle.iconSize iconSize: StartingCallStyle.iconSize
onClicked: call.microMuted = !enabled onClicked: call.microMuted = enabled
} }
ActionSwitch { ActionSwitch {
...@@ -40,7 +40,7 @@ AbstractStartingCall { ...@@ -40,7 +40,7 @@ AbstractStartingCall {
height: StartingCallStyle.userVideo.height height: StartingCallStyle.userVideo.height
width: StartingCallStyle.userVideo.width width: StartingCallStyle.userVideo.width
visible: isVideoCall visible: call.videoOutputEnabled
} }
ActionBar { ActionBar {
......
...@@ -131,12 +131,12 @@ ColumnLayout { ...@@ -131,12 +131,12 @@ ColumnLayout {
ActionButton { ActionButton {
icon: 'video_call' icon: 'video_call'
onClicked: CallsWindow.launchVideoCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses. onClicked: CallsListModel.launchVideoCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses.
} }
ActionButton { ActionButton {
icon: 'call' icon: 'call'
onClicked: CallsWindow.launchAudioCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses. onClicked: CallsListModel.launchAudioCall($contact.vcard.sipAddresses[0]) // FIXME: Display menu if many addresses.
} }
ActionButton { ActionButton {
......
...@@ -79,12 +79,12 @@ ColumnLayout { ...@@ -79,12 +79,12 @@ ColumnLayout {
ActionButton { ActionButton {
icon: 'video_call' icon: 'video_call'
onClicked: CallsWindow.launchVideoCall(conversation.sipAddress) onClicked: CallsListModel.launchVideoCall(conversation.sipAddress)
} }
ActionButton { ActionButton {
icon: 'call' icon: 'call'
onClicked: CallsWindow.launchAudioCall(conversation.sipAddress) onClicked: CallsListModel.launchAudioCall(conversation.sipAddress)
} }
} }
...@@ -92,7 +92,7 @@ ColumnLayout { ...@@ -92,7 +92,7 @@ ColumnLayout {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
ActionButton { ActionButton {
icon: _contact ? 'contact_add' : 'contact_edit' icon: !_contact ? 'contact_add' : 'contact_edit'
iconSize: ConversationStyle.bar.actions.edit.iconSize iconSize: ConversationStyle.bar.actions.edit.iconSize
onClicked: window.setView('ContactEdit', { onClicked: window.setView('ContactEdit', {
......
...@@ -160,14 +160,14 @@ ApplicationWindow { ...@@ -160,14 +160,14 @@ ApplicationWindow {
}) })
} }
onLaunchCall: CallsWindow.launchAudioCall(sipAddress) onLaunchCall: CallsListModel.launchAudioCall(sipAddress)
onLaunchChat: { onLaunchChat: {
window.ensureCollapsed() window.ensureCollapsed()
window.setView('Conversation', { window.setView('Conversation', {
sipAddress: sipAddress sipAddress: sipAddress
}) })
} }
onLaunchVideoCall: CallsWindow.launchVideoCall(sipAddress) onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress)
onEntryClicked: { onEntryClicked: {
window.ensureCollapsed() window.ensureCollapsed()
......
...@@ -19,6 +19,12 @@ QtObject { ...@@ -19,6 +19,12 @@ QtObject {
property int maxSize: 300 property int maxSize: 300
} }
property QtObject busyIndicator: QtObject {
property color color: Colors.g
property int height: 30
property int width: 30
}
property QtObject header: QtObject { property QtObject header: QtObject {
property int spacing: 10 property int spacing: 10
property int topMargin: 26 property int topMargin: 26
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment