blob: 1f2ed4edea58c9b53fa4eaea90b3effcb17bafe1 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001
2/*
3 * Copyright (C) 2020 by Savoir-faire Linux
4 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
Sébastien Blin8940f3c2020-07-23 17:03:11 -04005 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
agsantos655d8e22020-08-10 17:36:47 -04006 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
Sébastien Blin1f915762020-08-03 13:27:42 -04007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21import QtQuick 2.14
22import QtQuick.Controls 2.14
23import QtQuick.Layouts 1.14
24import QtQuick.Controls.Universal 2.12
25import QtQml 2.14
26import net.jami.Models 1.0
27
28import "../js/contactpickercreation.js" as ContactPickerCreation
agsantos655d8e22020-08-10 17:36:47 -040029import "../js/mediahandlerpickercreation.js" as MediaHandlerPickerCreation
Sébastien Blin1f915762020-08-03 13:27:42 -040030
31import "../../commoncomponents"
32
33Rectangle {
34 id: callOverlayRect
35
Sébastien Blin1f915762020-08-03 13:27:42 -040036 property string timeText: "00:00"
37
Sébastien Blin1f915762020-08-03 13:27:42 -040038 signal overlayChatButtonClicked
39
Sébastien Blin6607e0e2020-07-24 15:15:47 -040040 property var participantOverlays: []
41 property var participantComponent: Qt.createComponent("ParticipantOverlay.qml")
42
Sébastien Blin8940f3c2020-07-23 17:03:11 -040043 function setRecording(isRecording) {
44 callViewContextMenu.isRecording = isRecording
45 recordingRect.visible = isRecording
46 }
47
Sébastien Blin1f915762020-08-03 13:27:42 -040048 function updateButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall) {
Sébastien Blin8940f3c2020-07-23 17:03:11 -040049 callViewContextMenu.isSIP = isSIP
50 callViewContextMenu.isPaused = isPaused
51 callViewContextMenu.isAudioOnly = isAudioOnly
52 callViewContextMenu.isRecording = isRecording
53 recordingRect.visible = isRecording
Sébastien Blin1f915762020-08-03 13:27:42 -040054 callOverlayButtonGroup.setButtonStatus(isPaused, isAudioOnly,
55 isAudioMuted, isVideoMuted,
56 isRecording, isSIP,
57 isConferenceCall)
58 }
59
Sébastien Blin6607e0e2020-07-24 15:15:47 -040060 function updateMaster() {
61 callOverlayButtonGroup.updateMaster()
62 }
63
Sébastien Blin1f915762020-08-03 13:27:42 -040064 function showOnHoldImage(visible) {
65 onHoldImage.visible = visible
66 }
67
68 function closePotentialContactPicker() {
69 ContactPickerCreation.closeContactPicker()
70 }
71
agsantos655d8e22020-08-10 17:36:47 -040072 function closePotentialMediaHandlerPicker() {
73 MediaHandlerPickerCreation.closeMediaHandlerPicker()
74 }
75
Sébastien Blin6607e0e2020-07-24 15:15:47 -040076 function handleParticipantsInfo(infos) {
77 videoCallOverlay.updateMaster()
78 var isMaster = CallAdapter.isCurrentMaster()
79 for (var p in participantOverlays) {
80 if (participantOverlays[p])
81 participantOverlays[p].destroy()
82 }
83 participantOverlays = []
84 if (infos.length == 0) {
85 previewRenderer.visible = true
86 } else {
87 previewRenderer.visible = false
88 for (var infoVariant in infos) {
89 var hover = participantComponent.createObject(callOverlayRectMouseArea, {
90 x: distantRenderer.getXOffset() + infos[infoVariant].x * distantRenderer.getScaledWidth(),
91 y: distantRenderer.getYOffset() + infos[infoVariant].y * distantRenderer.getScaledHeight(),
92 width: infos[infoVariant].w * distantRenderer.getScaledWidth(),
93 height: infos[infoVariant].h * distantRenderer.getScaledHeight(),
94 visible: infos[infoVariant].w != 0 && infos[infoVariant].h != 0
95 })
96 if (!hover) {
97 console.log("Error when creating the hover")
98 return
99 }
100 hover.setParticipantName(infos[infoVariant].bestName)
101 hover.active = infos[infoVariant].active;
102 hover.isLocal = infos[infoVariant].isLocal;
103 hover.setMenuVisible(isMaster)
104 hover.uri = infos[infoVariant].uri
105 hover.injectedContextMenu = participantContextMenu
106 participantOverlays.push(hover)
107 }
108 }
109 }
110
Sébastien Blin1f915762020-08-03 13:27:42 -0400111 anchors.fill: parent
112
Ming Rui Zhang44dba712020-08-25 14:32:34 -0400113 SipInputPanel {
114 id: sipInputPanel
115
116 x: callOverlayRect.width / 2 - sipInputPanel.width / 2
117 y: callOverlayRect.height / 2 - sipInputPanel.height / 2
118 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400119
120 /*
121 * Timer to decide when overlay fade out.
122 */
123 Timer {
124 id: callOverlayTimer
125 interval: 5000
126 onTriggered: {
127 if (overlayUpperPartRect.state !== 'freezed') {
128 overlayUpperPartRect.state = 'freezed'
129 }
130 if (callOverlayButtonGroup.state !== 'freezed') {
131 callOverlayButtonGroup.state = 'freezed'
132 }
133 }
134 }
135
136 Rectangle {
137 id: overlayUpperPartRect
138
139 anchors.top: callOverlayRect.top
140
141 width: callOverlayRect.width
142 height: 50
143 opacity: 0
144
145 RowLayout {
146 id: overlayUpperPartRectRowLayout
147
148 anchors.fill: parent
149
Sébastien Blin1f915762020-08-03 13:27:42 -0400150 Text {
151 id: jamiBestNameText
152
153 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
154 Layout.preferredWidth: overlayUpperPartRect.width / 3
155 Layout.preferredHeight: 50
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400156 leftPadding: 16
Sébastien Blin1f915762020-08-03 13:27:42 -0400157
158 font.pointSize: JamiTheme.textFontSize
159
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400160 horizontalAlignment: Text.AlignLeft
Sébastien Blin1f915762020-08-03 13:27:42 -0400161 verticalAlignment: Text.AlignVCenter
162
163 text: textMetricsjamiBestNameText.elidedText
164 color: "white"
165
166 TextMetrics {
167 id: textMetricsjamiBestNameText
168 font: jamiBestNameText.font
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400169 text: videoCallPageRect.bestName
Sébastien Blin1f915762020-08-03 13:27:42 -0400170 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400171 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400172 }
173 }
174
175 Text {
176 id: callTimerText
Sébastien Blin1f915762020-08-03 13:27:42 -0400177 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
178 Layout.preferredWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400179 Layout.preferredHeight: 48
Sébastien Blin1f915762020-08-03 13:27:42 -0400180 font.pointSize: JamiTheme.textFontSize
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400181 horizontalAlignment: Text.AlignRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400182 verticalAlignment: Text.AlignVCenter
Sébastien Blin1f915762020-08-03 13:27:42 -0400183 text: textMetricscallTimerText.elidedText
184 color: "white"
Sébastien Blin1f915762020-08-03 13:27:42 -0400185 TextMetrics {
186 id: textMetricscallTimerText
187 font: callTimerText.font
188 text: timeText
189 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400190 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400191 }
192 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400193
194 Rectangle {
195 id: recordingRect
196 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
197 height: 16
198 width: 16
199 radius: height / 2
200 color: "red"
201
202 SequentialAnimation on color {
203 loops: Animation.Infinite
204 running: true
205 ColorAnimation { from: "red"; to: "transparent"; duration: 500 }
206 ColorAnimation { from: "transparent"; to: "red"; duration: 500 }
207 }
208 }
209
210 Item {
211 width: 8
212 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400213 }
214
215 color: "transparent"
216
217
218 /*
219 * Rect states: "entered" state should make overlay fade in,
220 * "freezed" state should make overlay fade out.
221 * Combine with PropertyAnimation of opacity.
222 */
223 states: [
224 State {
225 name: "entered"
226 PropertyChanges {
227 target: overlayUpperPartRect
228 opacity: 1
229 }
230 },
231 State {
232 name: "freezed"
233 PropertyChanges {
234 target: overlayUpperPartRect
235 opacity: 0
236 }
237 }
238 ]
239
240 transitions: Transition {
241 PropertyAnimation {
242 target: overlayUpperPartRect
243 property: "opacity"
244 duration: 1000
245 }
246 }
247 }
248
249 Image {
250 id: onHoldImage
251
252 anchors.verticalCenter: callOverlayRect.verticalCenter
253 anchors.horizontalCenter: callOverlayRect.horizontalCenter
254
255 width: 200
256 height: 200
257
258 visible: false
259
260 fillMode: Image.PreserveAspectFit
261 source: "qrc:/images/icons/ic_pause_white_100px.png"
262 asynchronous: true
263 }
264
265 CallOverlayButtonGroup {
266 id: callOverlayButtonGroup
267
268 anchors.bottom: callOverlayRect.bottom
269 anchors.bottomMargin: 10
270 anchors.horizontalCenter: callOverlayRect.horizontalCenter
271
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400272 height: 56
273 width: callOverlayRect.width
Sébastien Blin1f915762020-08-03 13:27:42 -0400274 opacity: 0
275
276 onChatButtonClicked: {
277 callOverlayRect.overlayChatButtonClicked()
278 }
279
280 onAddToConferenceButtonClicked: {
Sébastien Blin1f915762020-08-03 13:27:42 -0400281 /*
282 * Create contact picker - conference.
283 */
284 ContactPickerCreation.createContactPickerObjects(
285 ContactPicker.ContactPickerType.JAMICONFERENCE,
286 callOverlayRect)
287 ContactPickerCreation.calculateCurrentGeo(
288 callOverlayRect.width / 2, callOverlayRect.height / 2)
289 ContactPickerCreation.openContactPicker()
290 }
291
Sébastien Blin1f915762020-08-03 13:27:42 -0400292 states: [
293 State {
294 name: "entered"
295 PropertyChanges {
296 target: callOverlayButtonGroup
297 opacity: 1
298 }
299 },
300 State {
301 name: "freezed"
302 PropertyChanges {
303 target: callOverlayButtonGroup
304 opacity: 0
305 }
306 }
307 ]
308
309 transitions: Transition {
310 PropertyAnimation {
311 target: callOverlayButtonGroup
312 property: "opacity"
313 duration: 1000
314 }
315 }
316 }
317
318
319 /*
320 * MouseAreas to make sure that overlay states are correctly set.
321 */
322 MouseArea {
323 id: callOverlayButtonGroupLeftSideMouseArea
324
325 anchors.bottom: callOverlayRect.bottom
326 anchors.left: callOverlayRect.left
327
328 width: callOverlayRect.width / 6
329 height: 60
330
331 hoverEnabled: true
332 propagateComposedEvents: true
333 acceptedButtons: Qt.NoButton
334
335 onEntered: {
336 callOverlayRectMouseArea.entered()
337 }
338
339 onMouseXChanged: {
340 callOverlayRectMouseArea.entered()
341 }
342 }
343
344 MouseArea {
345 id: callOverlayButtonGroupRightSideMouseArea
346
347 anchors.bottom: callOverlayRect.bottom
348 anchors.right: callOverlayRect.right
349
350 width: callOverlayRect.width / 6
351 height: 60
352
353 hoverEnabled: true
354 propagateComposedEvents: true
355 acceptedButtons: Qt.NoButton
356
357 onEntered: {
358 callOverlayRectMouseArea.entered()
359 }
360
361 onMouseXChanged: {
362 callOverlayRectMouseArea.entered()
363 }
364 }
365
366 MouseArea {
367 id: callOverlayRectMouseArea
368
369 anchors.top: callOverlayRect.top
Sébastien Blin1f915762020-08-03 13:27:42 -0400370
371 width: callOverlayRect.width
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400372 height: callOverlayRect.height
Sébastien Blin1f915762020-08-03 13:27:42 -0400373
374 hoverEnabled: true
375 propagateComposedEvents: true
376 acceptedButtons: Qt.NoButton
377
378 onEntered: {
379 if (overlayUpperPartRect.state !== 'entered') {
380 overlayUpperPartRect.state = 'entered'
381 }
382 if (callOverlayButtonGroup.state !== 'entered') {
383 callOverlayButtonGroup.state = 'entered'
384 }
385 callOverlayTimer.restart()
386 }
387
388 onMouseXChanged: {
389 if (overlayUpperPartRect.state !== 'entered') {
390 overlayUpperPartRect.state = 'entered'
391 }
392 if (callOverlayButtonGroup.state !== 'entered') {
393 callOverlayButtonGroup.state = 'entered'
394 }
395 callOverlayTimer.restart()
396 }
397 }
398
399 color: "transparent"
400
Sébastien Blin1f915762020-08-03 13:27:42 -0400401 onWidthChanged: {
402 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
403 callOverlayRect.height / 2)
agsantos655d8e22020-08-10 17:36:47 -0400404 MediaHandlerPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
405 callOverlayRect.height / 2)
Sébastien Blin1f915762020-08-03 13:27:42 -0400406 }
407
408 onHeightChanged: {
409 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
410 callOverlayRect.height / 2)
agsantos655d8e22020-08-10 17:36:47 -0400411 MediaHandlerPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
412 callOverlayRect.height / 2)
Sébastien Blin1f915762020-08-03 13:27:42 -0400413 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400414
415 CallViewContextMenu {
416 id: callViewContextMenu
417
418 onTransferCallButtonClicked: {
419 /*
420 * Create contact picker - sip transfer.
421 */
422 ContactPickerCreation.createContactPickerObjects(
423 ContactPicker.ContactPickerType.SIPTRANSFER,
424 callOverlayRect)
425 ContactPickerCreation.calculateCurrentGeo(
426 callOverlayRect.width / 2, callOverlayRect.height / 2)
427 ContactPickerCreation.openContactPicker()
428 }
agsantos655d8e22020-08-10 17:36:47 -0400429
430 onPluginItemClicked: {
431 // Create media handler picker - PLUGINS
432 MediaHandlerPickerCreation.createMediaHandlerPickerObjects(callOverlayRect)
433 MediaHandlerPickerCreation.calculateCurrentGeo(
434 callOverlayRect.width / 2, callOverlayRect.height / 2)
435 MediaHandlerPickerCreation.openMediaHandlerPicker()
436 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400437 }
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400438
439 ParticipantContextMenu {
440 id: participantContextMenu
441 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400442}