blob: 75774d34f1aa8aa61465ba1fc0ff6eae44070346 [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
113
114 /*
115 * Timer to decide when overlay fade out.
116 */
117 Timer {
118 id: callOverlayTimer
119 interval: 5000
120 onTriggered: {
121 if (overlayUpperPartRect.state !== 'freezed') {
122 overlayUpperPartRect.state = 'freezed'
123 }
124 if (callOverlayButtonGroup.state !== 'freezed') {
125 callOverlayButtonGroup.state = 'freezed'
126 }
127 }
128 }
129
130 Rectangle {
131 id: overlayUpperPartRect
132
133 anchors.top: callOverlayRect.top
134
135 width: callOverlayRect.width
136 height: 50
137 opacity: 0
138
139 RowLayout {
140 id: overlayUpperPartRectRowLayout
141
142 anchors.fill: parent
143
Sébastien Blin1f915762020-08-03 13:27:42 -0400144 Text {
145 id: jamiBestNameText
146
147 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
148 Layout.preferredWidth: overlayUpperPartRect.width / 3
149 Layout.preferredHeight: 50
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400150 leftPadding: 16
Sébastien Blin1f915762020-08-03 13:27:42 -0400151
152 font.pointSize: JamiTheme.textFontSize
153
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400154 horizontalAlignment: Text.AlignLeft
Sébastien Blin1f915762020-08-03 13:27:42 -0400155 verticalAlignment: Text.AlignVCenter
156
157 text: textMetricsjamiBestNameText.elidedText
158 color: "white"
159
160 TextMetrics {
161 id: textMetricsjamiBestNameText
162 font: jamiBestNameText.font
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400163 text: videoCallPageRect.bestName
Sébastien Blin1f915762020-08-03 13:27:42 -0400164 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400165 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400166 }
167 }
168
169 Text {
170 id: callTimerText
Sébastien Blin1f915762020-08-03 13:27:42 -0400171 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
172 Layout.preferredWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400173 Layout.preferredHeight: 48
Sébastien Blin1f915762020-08-03 13:27:42 -0400174 font.pointSize: JamiTheme.textFontSize
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400175 horizontalAlignment: Text.AlignRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400176 verticalAlignment: Text.AlignVCenter
Sébastien Blin1f915762020-08-03 13:27:42 -0400177 text: textMetricscallTimerText.elidedText
178 color: "white"
Sébastien Blin1f915762020-08-03 13:27:42 -0400179 TextMetrics {
180 id: textMetricscallTimerText
181 font: callTimerText.font
182 text: timeText
183 elideWidth: overlayUpperPartRect.width / 3
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400184 elide: Qt.ElideRight
Sébastien Blin1f915762020-08-03 13:27:42 -0400185 }
186 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400187
188 Rectangle {
189 id: recordingRect
190 Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
191 height: 16
192 width: 16
193 radius: height / 2
194 color: "red"
195
196 SequentialAnimation on color {
197 loops: Animation.Infinite
198 running: true
199 ColorAnimation { from: "red"; to: "transparent"; duration: 500 }
200 ColorAnimation { from: "transparent"; to: "red"; duration: 500 }
201 }
202 }
203
204 Item {
205 width: 8
206 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400207 }
208
209 color: "transparent"
210
211
212 /*
213 * Rect states: "entered" state should make overlay fade in,
214 * "freezed" state should make overlay fade out.
215 * Combine with PropertyAnimation of opacity.
216 */
217 states: [
218 State {
219 name: "entered"
220 PropertyChanges {
221 target: overlayUpperPartRect
222 opacity: 1
223 }
224 },
225 State {
226 name: "freezed"
227 PropertyChanges {
228 target: overlayUpperPartRect
229 opacity: 0
230 }
231 }
232 ]
233
234 transitions: Transition {
235 PropertyAnimation {
236 target: overlayUpperPartRect
237 property: "opacity"
238 duration: 1000
239 }
240 }
241 }
242
243 Image {
244 id: onHoldImage
245
246 anchors.verticalCenter: callOverlayRect.verticalCenter
247 anchors.horizontalCenter: callOverlayRect.horizontalCenter
248
249 width: 200
250 height: 200
251
252 visible: false
253
254 fillMode: Image.PreserveAspectFit
255 source: "qrc:/images/icons/ic_pause_white_100px.png"
256 asynchronous: true
257 }
258
259 CallOverlayButtonGroup {
260 id: callOverlayButtonGroup
261
262 anchors.bottom: callOverlayRect.bottom
263 anchors.bottomMargin: 10
264 anchors.horizontalCenter: callOverlayRect.horizontalCenter
265
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400266 height: 56
267 width: callOverlayRect.width
Sébastien Blin1f915762020-08-03 13:27:42 -0400268 opacity: 0
269
270 onChatButtonClicked: {
271 callOverlayRect.overlayChatButtonClicked()
272 }
273
274 onAddToConferenceButtonClicked: {
Sébastien Blin1f915762020-08-03 13:27:42 -0400275 /*
276 * Create contact picker - conference.
277 */
278 ContactPickerCreation.createContactPickerObjects(
279 ContactPicker.ContactPickerType.JAMICONFERENCE,
280 callOverlayRect)
281 ContactPickerCreation.calculateCurrentGeo(
282 callOverlayRect.width / 2, callOverlayRect.height / 2)
283 ContactPickerCreation.openContactPicker()
284 }
285
Sébastien Blin1f915762020-08-03 13:27:42 -0400286 states: [
287 State {
288 name: "entered"
289 PropertyChanges {
290 target: callOverlayButtonGroup
291 opacity: 1
292 }
293 },
294 State {
295 name: "freezed"
296 PropertyChanges {
297 target: callOverlayButtonGroup
298 opacity: 0
299 }
300 }
301 ]
302
303 transitions: Transition {
304 PropertyAnimation {
305 target: callOverlayButtonGroup
306 property: "opacity"
307 duration: 1000
308 }
309 }
310 }
311
312
313 /*
314 * MouseAreas to make sure that overlay states are correctly set.
315 */
316 MouseArea {
317 id: callOverlayButtonGroupLeftSideMouseArea
318
319 anchors.bottom: callOverlayRect.bottom
320 anchors.left: callOverlayRect.left
321
322 width: callOverlayRect.width / 6
323 height: 60
324
325 hoverEnabled: true
326 propagateComposedEvents: true
327 acceptedButtons: Qt.NoButton
328
329 onEntered: {
330 callOverlayRectMouseArea.entered()
331 }
332
333 onMouseXChanged: {
334 callOverlayRectMouseArea.entered()
335 }
336 }
337
338 MouseArea {
339 id: callOverlayButtonGroupRightSideMouseArea
340
341 anchors.bottom: callOverlayRect.bottom
342 anchors.right: callOverlayRect.right
343
344 width: callOverlayRect.width / 6
345 height: 60
346
347 hoverEnabled: true
348 propagateComposedEvents: true
349 acceptedButtons: Qt.NoButton
350
351 onEntered: {
352 callOverlayRectMouseArea.entered()
353 }
354
355 onMouseXChanged: {
356 callOverlayRectMouseArea.entered()
357 }
358 }
359
360 MouseArea {
361 id: callOverlayRectMouseArea
362
363 anchors.top: callOverlayRect.top
Sébastien Blin1f915762020-08-03 13:27:42 -0400364
365 width: callOverlayRect.width
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400366 height: callOverlayRect.height
Sébastien Blin1f915762020-08-03 13:27:42 -0400367
368 hoverEnabled: true
369 propagateComposedEvents: true
370 acceptedButtons: Qt.NoButton
371
372 onEntered: {
373 if (overlayUpperPartRect.state !== 'entered') {
374 overlayUpperPartRect.state = 'entered'
375 }
376 if (callOverlayButtonGroup.state !== 'entered') {
377 callOverlayButtonGroup.state = 'entered'
378 }
379 callOverlayTimer.restart()
380 }
381
382 onMouseXChanged: {
383 if (overlayUpperPartRect.state !== 'entered') {
384 overlayUpperPartRect.state = 'entered'
385 }
386 if (callOverlayButtonGroup.state !== 'entered') {
387 callOverlayButtonGroup.state = 'entered'
388 }
389 callOverlayTimer.restart()
390 }
391 }
392
393 color: "transparent"
394
Sébastien Blin1f915762020-08-03 13:27:42 -0400395 onWidthChanged: {
396 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
397 callOverlayRect.height / 2)
agsantos655d8e22020-08-10 17:36:47 -0400398 MediaHandlerPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
399 callOverlayRect.height / 2)
Sébastien Blin1f915762020-08-03 13:27:42 -0400400 }
401
402 onHeightChanged: {
403 ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
404 callOverlayRect.height / 2)
agsantos655d8e22020-08-10 17:36:47 -0400405 MediaHandlerPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2,
406 callOverlayRect.height / 2)
Sébastien Blin1f915762020-08-03 13:27:42 -0400407 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400408
409 CallViewContextMenu {
410 id: callViewContextMenu
411
412 onTransferCallButtonClicked: {
413 /*
414 * Create contact picker - sip transfer.
415 */
416 ContactPickerCreation.createContactPickerObjects(
417 ContactPicker.ContactPickerType.SIPTRANSFER,
418 callOverlayRect)
419 ContactPickerCreation.calculateCurrentGeo(
420 callOverlayRect.width / 2, callOverlayRect.height / 2)
421 ContactPickerCreation.openContactPicker()
422 }
agsantos655d8e22020-08-10 17:36:47 -0400423
424 onPluginItemClicked: {
425 // Create media handler picker - PLUGINS
426 MediaHandlerPickerCreation.createMediaHandlerPickerObjects(callOverlayRect)
427 MediaHandlerPickerCreation.calculateCurrentGeo(
428 callOverlayRect.width / 2, callOverlayRect.height / 2)
429 MediaHandlerPickerCreation.openMediaHandlerPicker()
430 }
Sébastien Blin8940f3c2020-07-23 17:03:11 -0400431 }
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400432
433 ParticipantContextMenu {
434 id: participantContextMenu
435 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400436}