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 @@
<file>assets/images/video_call_hovered.svg</file>
<file>assets/images/video_call_normal.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/BusyIndicator.qml</file>
<file>ui/modules/Common/Collapse.qml</file>
<file>ui/modules/Common/Colors.qml</file>
<file>ui/modules/Common/Constants.qml</file>
......
......@@ -123,3 +123,11 @@ void CallModel::setPausedByUser (bool status) {
emit pausedByUserChanged(false);
}
}
bool CallModel::getVideoOutputEnabled () const {
// TODO
}
void CallModel::setVideoOutputEnabled (bool status) {
// TODO
}
......@@ -14,6 +14,7 @@ class CallModel : public QObject {
Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT);
Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged);
Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY pausedByUserChanged);
Q_PROPERTY(bool videoOutputEnabled READ getVideoOutputEnabled WRITE setVideoOutputEnabled NOTIFY videoOutputEnabled);
public:
enum CallStatus {
......@@ -39,6 +40,7 @@ signals:
void statusChanged (CallStatus status);
void pausedByUserChanged (bool status);
void microMutedChanged (bool status);
void videoOutputEnabled (bool status);
private:
QString getSipAddress () const;
......@@ -54,6 +56,9 @@ private:
bool getPausedByUser () const;
void setPausedByUser (bool status);
bool getVideoOutputEnabled () const;
void setVideoOutputEnabled (bool status);
bool m_micro_muted = false;
linphone::CallState m_linphone_call_status = linphone::CallStateIdle;
......
......@@ -2,6 +2,7 @@
#include <QTimer>
#include "../../app/App.hpp"
#include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "CallsListModel.hpp"
......@@ -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) {
return removeRows(row, 1, parent);
}
......@@ -86,6 +100,9 @@ void CallsListModel::addCall (const shared_ptr<linphone::Call> &linphone_call) {
App::getInstance()->getCallsWindow()->show();
CallModel *call = new CallModel(linphone_call);
qInfo() << "Add call:" << call;
App::getInstance()->getEngine()->setObjectOwnership(call, QQmlEngine::CppOwnership);
linphone_call->setData("call-model", *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) {
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.
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;
int index = m_list.indexOf(call);
......
......@@ -21,6 +21,9 @@ public:
QHash<int, QByteArray> roleNames () 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:
bool removeRow (int row, const QModelIndex &parent = QModelIndex());
bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;
......
......@@ -10,6 +10,8 @@ BusyIndicator {
// ---------------------------------------------------------------------------
property color color: BusyIndicatorStyle.color
readonly property int _rotation: 360
readonly property int _size: width < height ? width : height
......@@ -58,7 +60,7 @@ BusyIndicator {
height: item.height / 3
width: item.width / 3
color: BusyIndicatorStyle.color
color: busyIndicator.color
radius: (width > height ? width : height) / 2
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
# Components -------------------------------------------------------------------
# Animations
CaterpillarAnimation 1.0 Animations/CaterpillarAnimation.qml
BusyIndicator 1.0 Animations/BusyIndicator.qml
# Chat
Borders 1.0 Borders.qml
# BusyIndicator
BusyIndicator 1.0 BusyIndicator.qml
# Collapse
Collapse 1.0 Collapse.qml
......
......@@ -44,14 +44,14 @@ ListView {
_mapStatusToParams[CallModel.CallStatusConnected] = {
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,
string: 'connected'
......@@ -85,14 +85,14 @@ ListView {
_mapStatusToParams[CallModel.CallStatusPaused] = {
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,
string: 'paused'
......@@ -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 {
id: _callControls
......
......@@ -17,7 +17,8 @@ Rectangle {
property var call
default property alias _actionArea: actionArea.data
property var _contact: SipAddressesModel.mapSipAddressToContact(call.sipAddress)
property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress)
// ---------------------------------------------------------------------------
......@@ -45,12 +46,16 @@ Rectangle {
height: StartingCallStyle.contactDescriptionHeight
horizontalTextAlignment: Text.AlignHCenter
sipAddress: call.sipAddress
username: LinphoneUtils.getContactUsername(_contact || call.sipAddress)
username: LinphoneUtils.getContactUsername(_contactObserver.contact || call.sipAddress)
width: parent.width
}
CaterpillarAnimation {
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
color: StartingCallStyle.busyIndicator.color
height: StartingCallStyle.busyIndicator.height
width: StartingCallStyle.busyIndicator.width
visible: call.isOutgoing
}
}
......@@ -81,7 +86,7 @@ Rectangle {
anchors.centerIn: parent
backgroundColor: StartingCallStyle.avatar.backgroundColor
image: _contact && _contact.avatar
image: _contactObserver.contact && _contactObserver.contact.avatar
username: contactDescription.username
height: _computeAvatarSize()
......
......@@ -15,10 +15,7 @@ Window {
// ---------------------------------------------------------------------------
readonly property var call: {
console.log('hihi')
return calls.selectedCall
}
readonly property var call: calls.selectedCall
readonly property var sipAddress: {
if (call) {
return call.sipAddress
......@@ -27,18 +24,6 @@ Window {
// ---------------------------------------------------------------------------
function launchAudioCall (sipAddress) {
window.show()
}
function launchVideoCall (sipAddress) {
window.show()
}
// ---------------------------------------------------------------------------
minimumHeight: CallsWindowStyle.minimumHeight
minimumWidth: CallsWindowStyle.minimumWidth
title: CallsWindowStyle.title
......@@ -125,7 +110,6 @@ Window {
id: incomingCall
IncomingCall {
anchors.fill: parent
call: window.call
}
}
......@@ -134,7 +118,6 @@ Window {
id: outgoingCall
OutgoingCall {
anchors.fill: parent
call: window.call
}
}
......@@ -143,11 +126,20 @@ Window {
id: incall
Incall {
anchors.fill: parent
call: window.call
}
}
Component {
id: chat
Chat {
proxyModel: ChatProxyModel {
sipAddress: window.sipAddress
}
}
}
// -----------------------------------------------------------------------
childA: Loader {
......@@ -158,11 +150,12 @@ Window {
if (!call) {
return null
}
return incomingCall
var status = call.status
if (status === CallModel.CallStatusIncoming) {
return incomingCall
}
if (status === CallModel.CallStatusOutgoing) {
return outgoingCall
}
......@@ -174,13 +167,7 @@ Window {
childB: Loader {
active: Boolean(window.call)
anchors.fill: parent
sourceComponent: Chat {
anchors.fill: parent
proxyModel: ChatProxyModel {
sipAddress: window.sipAddress || ''
}
}
sourceComponent: window.call ? chat : null
}
}
}
......
......@@ -11,10 +11,6 @@ import App.Styles 1.0
// =============================================================================
Rectangle {
id: call
// ---------------------------------------------------------------------------
property var call
property var _contactObserver: SipAddressesModel.getContactObserver(sipAddress)
......
......@@ -2,7 +2,7 @@ import Common 1.0
import App.Styles 1.0
// ===================================================================
// =============================================================================
AbstractStartingCall {
ActionBar {
......@@ -11,18 +11,22 @@ AbstractStartingCall {
ActionButton {
icon: 'video_call_accept'
onClicked: call.acceptWithVideo()
}
ActionButton {
icon: 'call_accept'
onClicked: call.accept()
}
}
ActionBar {
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: StartingCallStyle.rightButtonsGroupMargin
verticalCenter: parent.verticalCenter
}
iconSize: StartingCallStyle.iconSize
......
......@@ -10,8 +10,8 @@ import App.Styles 1.0
AbstractStartingCall {
GridLayout {
columns: parent.width < 415 && call.videoOutputEnabled ? 1 : 2
rowSpacing: ActionBarStyle.spacing
columns: parent.width < 415 && call.isVideoCall ? 1 : 2
anchors {
left: parent.left
......@@ -24,7 +24,7 @@ AbstractStartingCall {
icon: 'micro'
iconSize: StartingCallStyle.iconSize
onClicked: call.microMuted = !enabled
onClicked: call.microMuted = enabled
}
ActionSwitch {
......@@ -40,7 +40,7 @@ AbstractStartingCall {
height: StartingCallStyle.userVideo.height
width: StartingCallStyle.userVideo.width
visible: isVideoCall
visible: call.videoOutputEnabled
}
ActionBar {
......
......@@ -131,12 +131,12 @@ ColumnLayout {
ActionButton {
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 {
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 {
......
......@@ -79,12 +79,12 @@ ColumnLayout {
ActionButton {
icon: 'video_call'
onClicked: CallsWindow.launchVideoCall(conversation.sipAddress)
onClicked: CallsListModel.launchVideoCall(conversation.sipAddress)
}
ActionButton {
icon: 'call'
onClicked: CallsWindow.launchAudioCall(conversation.sipAddress)
onClicked: CallsListModel.launchAudioCall(conversation.sipAddress)
}
}
......@@ -92,7 +92,7 @@ ColumnLayout {
anchors.verticalCenter: parent.verticalCenter
ActionButton {
icon: _contact ? 'contact_add' : 'contact_edit'
icon: !_contact ? 'contact_add' : 'contact_edit'
iconSize: ConversationStyle.bar.actions.edit.iconSize
onClicked: window.setView('ContactEdit', {
......
......@@ -160,14 +160,14 @@ ApplicationWindow {
})
}
onLaunchCall: CallsWindow.launchAudioCall(sipAddress)
onLaunchCall: CallsListModel.launchAudioCall(sipAddress)
onLaunchChat: {
window.ensureCollapsed()
window.setView('Conversation', {
sipAddress: sipAddress
})
}
onLaunchVideoCall: CallsWindow.launchVideoCall(sipAddress)
onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress)
onEntryClicked: {
window.ensureCollapsed()
......
......@@ -19,6 +19,12 @@ QtObject {
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 int spacing: 10
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