blob: a7e94e53044a76a645ede6315af28e6c246d4b79 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2020 by Savoir-faire Linux
3 * Author: Albert Babí <albert.babi@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.15
20import QtQuick.Controls 2.14
21import QtQuick.Layouts 1.14
22import QtQuick.Controls.Styles 1.4
23import QtQuick.Dialogs 1.3
Sébastien Blin1f915762020-08-03 13:27:42 -040024import net.jami.Models 1.0
ababidf651a22020-07-30 13:38:57 +020025import QtGraphicalEffects 1.15
26import QtQuick.Shapes 1.15
27
Sébastien Blin1f915762020-08-03 13:27:42 -040028import "../../commoncomponents"
29
30
31Rectangle {
32
33 enum States {
34 INIT,
35 RECORDING,
36 REC_SUCCESS
37 }
38
ababidf651a22020-07-30 13:38:57 +020039 id: recBox
Sébastien Blin1f915762020-08-03 13:27:42 -040040 color: "#FFFFFF"
41 width: 320
42 height: 240
43 radius: 5
44 border.color: JamiTheme.tabbarBorderColor
45
46 property string pathRecorder: ""
47 property string timeText: "00:00"
48 property int duration: 0
49 property int state: RecordBox.States.INIT
50 property bool isVideo: false
51 property bool previewAvailable: false
ababidf651a22020-07-30 13:38:57 +020052 property int preferredWidth: 320
53 property int preferredHeight: 240
54 property int btnSize: 40
Sébastien Blin1f915762020-08-03 13:27:42 -040055
ababidf651a22020-07-30 13:38:57 +020056 property int offset: 3
57 property int curveRadius: 6
58 property int x_offset: 0
59 property int y_offset: 0
Sébastien Blin1f915762020-08-03 13:27:42 -040060
61 function openRecorder(set_x, set_y, vid) {
ababidf651a22020-07-30 13:38:57 +020062
Sébastien Blin1f915762020-08-03 13:27:42 -040063 focus = true
64 visible = true
ababidf651a22020-07-30 13:38:57 +020065 isVideo = vid
66
67 x_offset = (isVideo ? -34 : -80)
68 scaleHeight()
69 y_offset = -64-height
Sébastien Blin1f915762020-08-03 13:27:42 -040070
71 updateState(RecordBox.States.INIT)
72
Sébastien Blin1f915762020-08-03 13:27:42 -040073 if (isVideo){
74 ClientWrapper.accountAdaptor.startPreviewing(false)
75 previewAvailable = true
76 }
77 }
78
ababidf651a22020-07-30 13:38:57 +020079 function scaleHeight(){
80 height = preferredHeight
81 if (isVideo) {
82 var device = ClientWrapper.avmodel.getDefaultDevice()
83 var settings = ClientWrapper.settingsAdaptor.get_Video_Settings_Size(device)
84 var res = settings.split("x")
ababi6fa08612020-08-10 19:13:28 +020085 var aspectRatio = res[1] / res[0]
ababidf651a22020-07-30 13:38:57 +020086 if (aspectRatio) {
87 height = preferredWidth*aspectRatio
88 } else {
89 console.error("Could not scale recording video preview")
90 }
91 }
92 }
93
Sébastien Blin1f915762020-08-03 13:27:42 -040094 onActiveFocusChanged: {
95 if (visible) {
96 closeRecorder()
97 }
98 }
99
100 onVisibleChanged: {
ababidf651a22020-07-30 13:38:57 +0200101 if (!visible) {
Sébastien Blin1f915762020-08-03 13:27:42 -0400102 closeRecorder()
103 }
104 }
105
106 function closeRecorder() {
107 if (isVideo && ClientWrapper.accountAdaptor.isPreviewing()) {
108 ClientWrapper.accountAdaptor.stopPreviewing()
109 }
110 stopRecording()
111 visible = false
112 }
113
114 function updateState(new_state) {
115 state = new_state
116 recordButton.visible = (state == RecordBox.States.INIT)
117 btnStop.visible = (state == RecordBox.States.RECORDING)
118 btnRestart.visible = (state == RecordBox.States.REC_SUCCESS)
119 btnSend.visible = (state == RecordBox.States.REC_SUCCESS)
120
121 if (state == RecordBox.States.INIT) {
122 duration = 0
123 time.text = "00:00"
124 timer.stop()
125 } else if (state == RecordBox.States.REC_SUCCESS) {
126 timer.stop()
127 }
128 }
129
130 function startRecording() {
131 timer.start()
132 pathRecorder = ClientWrapper.avmodel.startLocalRecorder(!isVideo)
133 if (pathRecorder == "") {
134 timer.stop()
135 }
136 }
137
138 function stopRecording() {
ababi0b686642020-08-18 17:21:28 +0200139 if (pathRecorder !== "") {
Sébastien Blin1f915762020-08-03 13:27:42 -0400140 ClientWrapper.avmodel.stopLocalRecorder(pathRecorder)
141 }
142 }
143
144 function sendRecord() {
ababi0b686642020-08-18 17:21:28 +0200145 if (pathRecorder !== "") {
Sébastien Blin1f915762020-08-03 13:27:42 -0400146 MessagesAdapter.sendFile(pathRecorder)
147 }
148 }
149
150 function updateTimer() {
151
152 duration += 1
153
154 var m = Math.trunc(duration / 60)
155 var s = (duration % 60)
156
157 var min = (m < 10) ? "0" + String(m) : String(m)
158 var sec = (s < 10) ? "0" + String(s) : String(s)
159
160 time.text = min + ":" + sec;
161 }
162
163
164 Connections{
165 target: ClientWrapper.renderManager
166 }
167
ababidf651a22020-07-30 13:38:57 +0200168 Shape {
169 id: backgroundShape
170 width: recBox.width
171 height: recBox.height
172 anchors.centerIn: parent
173 x: -offset
174 y: -offset
175 ShapePath {
176 fillColor: "white"
177
178 strokeWidth: 1
179 strokeColor: JamiTheme.tabbarBorderColor
180
181 startX: -offset+curveRadius; startY: -offset
182 joinStyle: ShapePath.RoundJoin
183
184 PathLine { x: width+offset-curveRadius; y: -offset }
185
186 PathArc {
187 x: width+offset; y: -offset+curveRadius
188 radiusX: curveRadius; radiusY: curveRadius
189 }
190
191 PathLine { x: width+offset; y: height+offset-curveRadius }
192
193 PathArc {
194 x: width+offset-curveRadius; y: height+offset
195 radiusX: curveRadius; radiusY: curveRadius
196 }
197
198 PathLine { x: width/2+10; y: height+offset }
199 PathLine { x: width/2; y: height+offset+10 }
200 PathLine { x: width/2-10; y: height+offset }
201
202
203 PathLine { x: -offset+curveRadius; y: height+offset }
204
205 PathArc {
206 x: -offset; y: height+offset-curveRadius
207 radiusX: curveRadius; radiusY: curveRadius
208 }
209
210 PathLine { x: -offset; y: -offset+curveRadius }
211
212 PathArc {
213 x: -offset+curveRadius; y: -offset
214 radiusX: curveRadius; radiusY: curveRadius
215 }
216 }
217 }
218
Sébastien Blin1f915762020-08-03 13:27:42 -0400219 Rectangle {
220 id: rectBox
221 visible: (isVideo && previewAvailable)
222 Layout.alignment: Qt.AlignHCenter
223 anchors.fill: parent
224 color: "black"
225 radius: 5
226
227 PreviewRenderer{
228 id: previewWidget
229 anchors.fill: rectBox
230 anchors.centerIn: rectBox
231 layer.enabled: true
232 layer.effect: OpacityMask {
233 maskSource: rectBox
234 }
235 }
236 }
237
238 Label {
239 visible: (isVideo && !previewAvailable)
240
241 Layout.fillWidth: true
242 Layout.alignment: Qt.AlignCenter
243
244 text: qsTr("Preview unavailable")
245 font.pointSize: 10
246 font.kerning: true
247 }
248
249 Timer {
250 id: timer
251 interval: 1000;
252 running: false;
253 repeat: true
254 onTriggered: updateTimer()
255 }
256
257 Text {
258 id: time
259 visible: true
260 text: "00:00"
261 color: (isVideo ? "white" : "black")
262 font.pointSize: (isVideo ? 12 : 20)
Sébastien Blin1f915762020-08-03 13:27:42 -0400263
ababidf651a22020-07-30 13:38:57 +0200264 anchors.centerIn: recordButton
265 anchors.horizontalCenterOffset: (isVideo ? 100 : 0)
266 anchors.verticalCenterOffset: (isVideo ? 0 : -100)
267 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400268
269 HoverableRadiusButton {
270 id: recordButton
Sébastien Blin1f915762020-08-03 13:27:42 -0400271
ababidf651a22020-07-30 13:38:57 +0200272 width: btnSize
273 height: btnSize
274
275 anchors.horizontalCenter: recBox.horizontalCenter
276 anchors.bottom: recBox.bottom
277 anchors.bottomMargin: 5
Sébastien Blin1f915762020-08-03 13:27:42 -0400278
279 buttonImageHeight: height
280 buttonImageWidth: height
281 backgroundColor: isVideo? "#000000cc" : "white"
282
Sébastien Blin1f915762020-08-03 13:27:42 -0400283 radius: height / 2
284
285 icon.source: "qrc:/images/icons/av_icons/fiber_manual_record-24px.svg"
286 icon.height: 24
287 icon.width: 24
288 icon.color: "#dc2719"
289 onClicked: {
290 updateState(RecordBox.States.RECORDING)
291 startRecording()
292 }
293 }
294
295 HoverableRadiusButton {
296 id: btnStop
Sébastien Blin1f915762020-08-03 13:27:42 -0400297
ababidf651a22020-07-30 13:38:57 +0200298 width: btnSize
299 height: btnSize
300
301 anchors.horizontalCenter: recBox.horizontalCenter
302 anchors.bottom: recBox.bottom
303 anchors.bottomMargin: 5
Sébastien Blin1f915762020-08-03 13:27:42 -0400304
305 buttonImageHeight: height
306 buttonImageWidth: height
307 backgroundColor: isVideo? "#000000cc" : "white"
308
Sébastien Blin1f915762020-08-03 13:27:42 -0400309 radius: height / 2
310
311 icon.source: "qrc:/images/icons/av_icons/stop-24px-red.svg"
312 icon.height: 24
313 icon.width: 24
314 icon.color: isVideo? "white" : "black"
315 onClicked: {
316 stopRecording()
317 updateState(RecordBox.States.REC_SUCCESS)
318 }
319 }
320
321 HoverableRadiusButton {
322 id: btnRestart
Sébastien Blin1f915762020-08-03 13:27:42 -0400323
ababidf651a22020-07-30 13:38:57 +0200324 width: btnSize
325 height: btnSize
326
327 anchors.horizontalCenter: recBox.horizontalCenter
328 anchors.horizontalCenterOffset: -25
329 anchors.bottom: recBox.bottom
330 anchors.bottomMargin: 5
Sébastien Blin1f915762020-08-03 13:27:42 -0400331
332 buttonImageHeight: height
333 buttonImageWidth: height
334 backgroundColor: isVideo? "#000000cc" : "white"
335
Sébastien Blin1f915762020-08-03 13:27:42 -0400336 radius: height / 2
337
338 icon.source: "qrc:/images/icons/av_icons/re-record-24px.svg"
339 icon.height: 24
340 icon.width: 24
341 icon.color: isVideo? "white" : "black"
342 onClicked: {
343 stopRecording()
344 updateState(RecordBox.States.INIT)
345 }
346 }
347
348 HoverableRadiusButton {
349 id: btnSend
Sébastien Blin1f915762020-08-03 13:27:42 -0400350
ababidf651a22020-07-30 13:38:57 +0200351 width: btnSize
352 height: btnSize
353
354 anchors.horizontalCenter: recBox.horizontalCenter
355 anchors.horizontalCenterOffset: 25
356 anchors.bottom: parent.bottom
357 anchors.bottomMargin: 5
Sébastien Blin1f915762020-08-03 13:27:42 -0400358
359 buttonImageHeight: height
360 buttonImageWidth: height
361 backgroundColor: isVideo? "#000000cc" : "white"
362
Sébastien Blin1f915762020-08-03 13:27:42 -0400363 radius: height / 2
364
365 icon.source: "qrc:/images/icons/av_icons/send-24px.svg"
366 icon.height: 24
367 icon.width: 24
368 icon.color: isVideo? "white" : "black"
369 onClicked: {
370 stopRecording()
371 sendRecord()
372 closeRecorder()
373 updateState(RecordBox.States.INIT)
374 }
375 }
376}
ababidf651a22020-07-30 13:38:57 +0200377