Merge pull request #268 from Wunderfitz/voicenotes
Support sending voice notes and locations
This commit is contained in:
commit
71283df6c5
19 changed files with 1216 additions and 91 deletions
|
@ -16,7 +16,7 @@ CONFIG += sailfishapp sailfishapp_i18n
|
||||||
|
|
||||||
PKGCONFIG += nemonotifications-qt5 zlib
|
PKGCONFIG += nemonotifications-qt5 zlib
|
||||||
|
|
||||||
QT += core dbus sql
|
QT += core dbus sql multimedia positioning
|
||||||
|
|
||||||
DEFINES += QT_STATICPLUGIN
|
DEFINES += QT_STATICPLUGIN
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
|
||||||
qml/components/ReplyMarkupButtons.qml \
|
qml/components/ReplyMarkupButtons.qml \
|
||||||
qml/components/StickerPicker.qml \
|
qml/components/StickerPicker.qml \
|
||||||
qml/components/PhotoTextsListItem.qml \
|
qml/components/PhotoTextsListItem.qml \
|
||||||
|
qml/components/VoiceNoteOverlay.qml \
|
||||||
qml/components/WebPagePreview.qml \
|
qml/components/WebPagePreview.qml \
|
||||||
qml/components/chatInformationPage/ChatInformationEditArea.qml \
|
qml/components/chatInformationPage/ChatInformationEditArea.qml \
|
||||||
qml/components/chatInformationPage/ChatInformationPageContent.qml \
|
qml/components/chatInformationPage/ChatInformationPageContent.qml \
|
||||||
|
|
222
qml/components/VoiceNoteOverlay.qml
Normal file
222
qml/components/VoiceNoteOverlay.qml
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import QtQuick 2.6
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import WerkWolf.Fernschreiber 1.0
|
||||||
|
import "../components"
|
||||||
|
import "../js/twemoji.js" as Emoji
|
||||||
|
import "../js/debug.js" as Debug
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: voiceNoteOverlayItem
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property int recordingState: fernschreiberUtils.getVoiceNoteRecordingState();
|
||||||
|
property int recordingDuration: 0;
|
||||||
|
property bool recordingDone: false;
|
||||||
|
|
||||||
|
function handleRecordingState() {
|
||||||
|
switch (recordingState) {
|
||||||
|
case FernschreiberUtilities.Unavailable:
|
||||||
|
recordingStateLabel.text = qsTr("Unavailable");
|
||||||
|
break;
|
||||||
|
case FernschreiberUtilities.Ready:
|
||||||
|
recordingStateLabel.text = qsTr("Ready");
|
||||||
|
break;
|
||||||
|
case FernschreiberUtilities.Starting:
|
||||||
|
recordingStateLabel.text = qsTr("Starting");
|
||||||
|
break;
|
||||||
|
case FernschreiberUtilities.Recording:
|
||||||
|
recordingStateLabel.text = qsTr("Recording");
|
||||||
|
break;
|
||||||
|
case FernschreiberUtilities.Stopping:
|
||||||
|
recordingStateLabel.text = qsTr("Stopping");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTwoDigitString(numberToBeConverted) {
|
||||||
|
var numberString = "00";
|
||||||
|
if (numberToBeConverted > 0 && numberToBeConverted < 10) {
|
||||||
|
numberString = "0" + String(numberToBeConverted);
|
||||||
|
}
|
||||||
|
if (numberToBeConverted >= 10) {
|
||||||
|
numberString = String(numberToBeConverted);
|
||||||
|
}
|
||||||
|
return numberString;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRecordingDuration() {
|
||||||
|
var minutes = Math.floor(recordingDuration / 60);
|
||||||
|
var seconds = recordingDuration % 60;
|
||||||
|
recordingDurationLabel.text = getTwoDigitString(minutes) + ":" + getTwoDigitString(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
handleRecordingState();
|
||||||
|
handleRecordingDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: fernschreiberUtils
|
||||||
|
onVoiceNoteDurationChanged: {
|
||||||
|
Debug.log("New duration received: " + duration);
|
||||||
|
recordingDuration = Math.round(duration / 1000);
|
||||||
|
handleRecordingDuration();
|
||||||
|
}
|
||||||
|
onVoiceNoteRecordingStateChanged: {
|
||||||
|
Debug.log("New state received: " + state);
|
||||||
|
recordingState = state;
|
||||||
|
handleRecordingState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: stickerPickerOverlayBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
color: Theme.overlayBackgroundColor
|
||||||
|
opacity: Theme.opacityHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: voiceNoteFlickable
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.paddingMedium
|
||||||
|
|
||||||
|
Behavior on opacity { NumberAnimation {} }
|
||||||
|
|
||||||
|
contentHeight: voiceNoteColumn.height
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: voiceNoteColumn
|
||||||
|
spacing: Theme.paddingMedium
|
||||||
|
width: voiceNoteFlickable.width
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
text: qsTr("Record a Voice Note")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Press the button to start recording")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Theme.iconSizeExtraLarge
|
||||||
|
height: Theme.iconSizeExtraLarge
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
color: Theme.primaryColor
|
||||||
|
opacity: Theme.opacityOverlay
|
||||||
|
width: Theme.iconSizeExtraLarge
|
||||||
|
height: Theme.iconSizeExtraLarge
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: width / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: recordButton
|
||||||
|
color: "red"
|
||||||
|
width: Theme.iconSizeExtraLarge * 0.6
|
||||||
|
height: Theme.iconSizeExtraLarge * 0.6
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: width / 2
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
recordButton.visible = false;
|
||||||
|
recordingDone = false;
|
||||||
|
recordingDuration = 0;
|
||||||
|
handleRecordingDuration();
|
||||||
|
fernschreiberUtils.startRecordingVoiceNote();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: stopButton
|
||||||
|
visible: !recordButton.visible
|
||||||
|
color: Theme.overlayBackgroundColor
|
||||||
|
width: Theme.iconSizeExtraLarge * 0.4
|
||||||
|
height: Theme.iconSizeExtraLarge * 0.4
|
||||||
|
anchors.centerIn: parent
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
recordButton.visible = true;
|
||||||
|
fernschreiberUtils.stopRecordingVoiceNote();
|
||||||
|
recordingDone = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: recordingStateLabel
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: recordingDurationLabel
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
visible: recordingDone
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
text: qsTr("Use recording")
|
||||||
|
onClicked: {
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
attachmentPreviewRow.isVoiceNote = true;
|
||||||
|
attachmentPreviewRow.attachmentDescription = qsTr("Voice Note (%1)").arg(recordingDurationLabel.text);
|
||||||
|
attachmentPreviewRow.visible = true;
|
||||||
|
controlSendButton();
|
||||||
|
voiceNoteOverlayLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -211,14 +211,21 @@ Page {
|
||||||
attachmentPreviewRow.isPicture = false;
|
attachmentPreviewRow.isPicture = false;
|
||||||
attachmentPreviewRow.isVideo = false;
|
attachmentPreviewRow.isVideo = false;
|
||||||
attachmentPreviewRow.isDocument = false;
|
attachmentPreviewRow.isDocument = false;
|
||||||
|
attachmentPreviewRow.isVoiceNote = false;
|
||||||
|
attachmentPreviewRow.isLocation = false;
|
||||||
attachmentPreviewRow.fileProperties = {};
|
attachmentPreviewRow.fileProperties = {};
|
||||||
|
attachmentPreviewRow.locationData = {};
|
||||||
|
attachmentPreviewRow.attachmentDescription = "";
|
||||||
|
fernschreiberUtils.stopGeoLocationUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
function controlSendButton() {
|
function controlSendButton() {
|
||||||
if (newMessageTextField.text.length !== 0
|
if (newMessageTextField.text.length !== 0
|
||||||
|| attachmentPreviewRow.isPicture
|
|| attachmentPreviewRow.isPicture
|
||||||
|| attachmentPreviewRow.isDocument
|
|| attachmentPreviewRow.isDocument
|
||||||
|| attachmentPreviewRow.isVideo) {
|
|| attachmentPreviewRow.isVideo
|
||||||
|
|| attachmentPreviewRow.isVoiceNote
|
||||||
|
|| attachmentPreviewRow.isLocation) {
|
||||||
newMessageSendButton.enabled = true;
|
newMessageSendButton.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
newMessageSendButton.enabled = false;
|
newMessageSendButton.enabled = false;
|
||||||
|
@ -239,6 +246,12 @@ Page {
|
||||||
if (attachmentPreviewRow.isDocument) {
|
if (attachmentPreviewRow.isDocument) {
|
||||||
tdLibWrapper.sendDocumentMessage(chatInformation.id, attachmentPreviewRow.fileProperties.filePath, newMessageTextField.text, newMessageColumn.replyToMessageId);
|
tdLibWrapper.sendDocumentMessage(chatInformation.id, attachmentPreviewRow.fileProperties.filePath, newMessageTextField.text, newMessageColumn.replyToMessageId);
|
||||||
}
|
}
|
||||||
|
if (attachmentPreviewRow.isVoiceNote) {
|
||||||
|
tdLibWrapper.sendVoiceNoteMessage(chatInformation.id, fernschreiberUtils.voiceNotePath(), newMessageTextField.text, newMessageColumn.replyToMessageId);
|
||||||
|
}
|
||||||
|
if (attachmentPreviewRow.isLocation) {
|
||||||
|
tdLibWrapper.sendLocationMessage(chatInformation.id, attachmentPreviewRow.locationData.latitude, attachmentPreviewRow.locationData.longitude, attachmentPreviewRow.locationData.horizontalAccuracy, newMessageColumn.replyToMessageId);
|
||||||
|
}
|
||||||
clearAttachmentPreviewRow();
|
clearAttachmentPreviewRow();
|
||||||
} else {
|
} else {
|
||||||
tdLibWrapper.sendTextMessage(chatInformation.id, newMessageTextField.text, newMessageColumn.replyToMessageId);
|
tdLibWrapper.sendTextMessage(chatInformation.id, newMessageTextField.text, newMessageColumn.replyToMessageId);
|
||||||
|
@ -251,6 +264,7 @@ Page {
|
||||||
controlSendButton();
|
controlSendButton();
|
||||||
newMessageInReplyToRow.inReplyToMessage = null;
|
newMessageInReplyToRow.inReplyToMessage = null;
|
||||||
newMessageColumn.editMessageId = "0";
|
newMessageColumn.editMessageId = "0";
|
||||||
|
fernschreiberUtils.stopGeoLocationUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWordBoundaries(text, cursorPosition) {
|
function getWordBoundaries(text, cursorPosition) {
|
||||||
|
@ -380,6 +394,7 @@ Page {
|
||||||
if (chatPage.canSendMessages) {
|
if (chatPage.canSendMessages) {
|
||||||
tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text);
|
tdLibWrapper.setChatDraftMessage(chatInformation.id, 0, newMessageColumn.replyToMessageId, newMessageTextField.text);
|
||||||
}
|
}
|
||||||
|
fernschreiberUtils.stopGeoLocationUpdates();
|
||||||
tdLibWrapper.closeChat(chatInformation.id);
|
tdLibWrapper.closeChat(chatInformation.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +642,7 @@ Page {
|
||||||
contentWidth: width
|
contentWidth: width
|
||||||
|
|
||||||
PullDownMenu {
|
PullDownMenu {
|
||||||
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !messageOverlayLoader.active
|
visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !voiceNoteOverlayLoader.active && !messageOverlayLoader.active
|
||||||
MenuItem {
|
MenuItem {
|
||||||
id: closeSecretChatMenuItem
|
id: closeSecretChatMenuItem
|
||||||
visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
|
visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
|
||||||
|
@ -880,7 +895,7 @@ Page {
|
||||||
id: chatView
|
id: chatView
|
||||||
|
|
||||||
visible: !blurred
|
visible: !blurred
|
||||||
property bool blurred: messageOverlayLoader.item
|
property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
opacity: chatPage.loading ? 0 : 1
|
opacity: chatPage.loading ? 0 : 1
|
||||||
|
@ -1114,7 +1129,7 @@ Page {
|
||||||
Debug.log("Sticker picked: " + stickerId);
|
Debug.log("Sticker picked: " + stickerId);
|
||||||
tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId);
|
tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId);
|
||||||
stickerPickerLoader.active = false;
|
stickerPickerLoader.active = false;
|
||||||
attachmentOptionsRow.isNeeded = false;
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,6 +1153,20 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: voiceNoteOverlayLoader
|
||||||
|
active: false
|
||||||
|
asynchronous: true
|
||||||
|
width: parent.width
|
||||||
|
height: active ? parent.height : 0
|
||||||
|
source: "../components/VoiceNoteOverlay.qml"
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active) {
|
||||||
|
fernschreiberUtils.stopRecordingVoiceNote();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -1145,7 +1174,6 @@ Page {
|
||||||
spacing: Theme.paddingSmall
|
spacing: Theme.paddingSmall
|
||||||
topPadding: Theme.paddingSmall
|
topPadding: Theme.paddingSmall
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
clip: true
|
|
||||||
visible: height > 0
|
visible: height > 0
|
||||||
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
||||||
height: isNeeded ? implicitHeight : 0
|
height: isNeeded ? implicitHeight : 0
|
||||||
|
@ -1177,90 +1205,146 @@ Page {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Flickable {
|
||||||
id: attachmentOptionsRow
|
id: attachmentOptionsFlickable
|
||||||
|
|
||||||
property bool isNeeded: false
|
property bool isNeeded: false
|
||||||
visible: height > 0
|
width: chatPage.width
|
||||||
height: isNeeded ? implicitHeight : 0
|
x: -Theme.horizontalPageMargin
|
||||||
anchors.right: parent.right
|
height: isNeeded ? attachmentOptionsRow.height : 0
|
||||||
width: parent.width
|
|
||||||
layoutDirection: Qt.RightToLeft
|
|
||||||
spacing: Theme.paddingMedium
|
|
||||||
clip: true
|
|
||||||
Behavior on height { SmoothedAnimation { duration: 200 } }
|
Behavior on height { SmoothedAnimation { duration: 200 } }
|
||||||
IconButton {
|
visible: height > 0
|
||||||
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
contentHeight: attachmentOptionsRow.height
|
||||||
icon.source: "image://theme/icon-m-image"
|
contentWidth: Math.max(width, attachmentOptionsRow.width)
|
||||||
onClicked: {
|
property bool fadeRight: (attachmentOptionsRow.width-contentX) > width
|
||||||
var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", {
|
property bool fadeLeft: !fadeRight && contentX > 0
|
||||||
allowedOrientations: chatPage.allowedOrientations
|
layer.enabled: fadeRight || fadeLeft
|
||||||
})
|
layer.effect: OpacityRampEffectBase {
|
||||||
picker.selectedContentPropertiesChanged.connect(function(){
|
direction: attachmentOptionsFlickable.fadeRight ? OpacityRamp.LeftToRight : OpacityRamp.RightToLeft
|
||||||
attachmentOptionsRow.isNeeded = false;
|
source: attachmentOptionsFlickable
|
||||||
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
|
slope: 1 + 6 * (chatPage.width) / Screen.width
|
||||||
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
offset: 1 - 1 / slope
|
||||||
attachmentPreviewRow.isPicture = true;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: attachmentOptionsRow
|
||||||
|
|
||||||
|
height: attachImageIconButton.height
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
layoutDirection: Qt.RightToLeft
|
||||||
|
spacing: Theme.paddingMedium
|
||||||
|
leftPadding: Theme.horizontalPageMargin
|
||||||
|
rightPadding: Theme.horizontalPageMargin
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: attachImageIconButton
|
||||||
|
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
||||||
|
icon.source: "image://theme/icon-m-image"
|
||||||
|
onClicked: {
|
||||||
|
var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", {
|
||||||
|
allowedOrientations: chatPage.allowedOrientations
|
||||||
|
})
|
||||||
|
picker.selectedContentPropertiesChanged.connect(function(){
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
|
||||||
|
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
||||||
|
attachmentPreviewRow.isPicture = true;
|
||||||
|
attachmentPreviewRow.visible = true;
|
||||||
|
controlSendButton();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
||||||
|
icon.source: "image://theme/icon-m-video"
|
||||||
|
onClicked: {
|
||||||
|
var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", {
|
||||||
|
allowedOrientations: chatPage.allowedOrientations
|
||||||
|
})
|
||||||
|
picker.selectedContentPropertiesChanged.connect(function(){
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
|
||||||
|
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
||||||
|
attachmentPreviewRow.isVideo = true;
|
||||||
|
attachmentPreviewRow.visible = true;
|
||||||
|
controlSendButton();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
||||||
|
icon.source: "image://theme/icon-m-mic"
|
||||||
|
icon.sourceSize {
|
||||||
|
width: Theme.iconSizeMedium
|
||||||
|
height: Theme.iconSizeMedium
|
||||||
|
}
|
||||||
|
highlighted: down || voiceNoteOverlayLoader.active
|
||||||
|
onClicked: {
|
||||||
|
voiceNoteOverlayLoader.active = !voiceNoteOverlayLoader.active;
|
||||||
|
stickerPickerLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
||||||
|
icon.source: "image://theme/icon-m-document"
|
||||||
|
onClicked: {
|
||||||
|
var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", {
|
||||||
|
allowedOrientations: chatPage.allowedOrientations
|
||||||
|
})
|
||||||
|
picker.selectedContentPropertiesChanged.connect(function(){
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
|
||||||
|
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
||||||
|
attachmentPreviewRow.isDocument = true;
|
||||||
|
attachmentPreviewRow.visible = true;
|
||||||
|
controlSendButton();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: chatPage.hasSendPrivilege("can_send_other_messages")
|
||||||
|
icon.source: "../../images/icon-m-sticker.svg"
|
||||||
|
icon.sourceSize {
|
||||||
|
width: Theme.iconSizeMedium
|
||||||
|
height: Theme.iconSizeMedium
|
||||||
|
}
|
||||||
|
highlighted: down || stickerPickerLoader.active
|
||||||
|
onClicked: {
|
||||||
|
stickerPickerLoader.active = !stickerPickerLoader.active;
|
||||||
|
voiceNoteOverlayLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: !(chatPage.isPrivateChat || chatPage.isSecretChat) && chatPage.hasSendPrivilege("can_send_polls")
|
||||||
|
icon.source: "image://theme/icon-m-question"
|
||||||
|
onClicked: {
|
||||||
|
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton {
|
||||||
|
visible: fernschreiberUtils.supportsGeoLocation() && newMessageTextField.text === ""
|
||||||
|
icon.source: "image://theme/icon-m-location"
|
||||||
|
icon.sourceSize {
|
||||||
|
width: Theme.iconSizeMedium
|
||||||
|
height: Theme.iconSizeMedium
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
fernschreiberUtils.startGeoLocationUpdates();
|
||||||
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
|
attachmentPreviewRow.isLocation = true;
|
||||||
|
attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
|
||||||
attachmentPreviewRow.visible = true;
|
attachmentPreviewRow.visible = true;
|
||||||
controlSendButton();
|
controlSendButton();
|
||||||
})
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
IconButton {
|
|
||||||
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
|
||||||
icon.source: "image://theme/icon-m-video"
|
|
||||||
onClicked: {
|
|
||||||
var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", {
|
|
||||||
allowedOrientations: chatPage.allowedOrientations
|
|
||||||
})
|
|
||||||
picker.selectedContentPropertiesChanged.connect(function(){
|
|
||||||
attachmentOptionsRow.isNeeded = false;
|
|
||||||
Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
|
|
||||||
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
|
||||||
attachmentPreviewRow.isVideo = true;
|
|
||||||
attachmentPreviewRow.visible = true;
|
|
||||||
controlSendButton();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IconButton {
|
|
||||||
visible: chatPage.hasSendPrivilege("can_send_media_messages")
|
|
||||||
icon.source: "image://theme/icon-m-document"
|
|
||||||
onClicked: {
|
|
||||||
var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", {
|
|
||||||
allowedOrientations: chatPage.allowedOrientations
|
|
||||||
})
|
|
||||||
picker.selectedContentPropertiesChanged.connect(function(){
|
|
||||||
attachmentOptionsRow.isNeeded = false;
|
|
||||||
Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
|
|
||||||
attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
|
|
||||||
attachmentPreviewRow.isDocument = true;
|
|
||||||
attachmentPreviewRow.visible = true;
|
|
||||||
controlSendButton();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IconButton {
|
|
||||||
visible: chatPage.hasSendPrivilege("can_send_other_messages")
|
|
||||||
icon.source: "../../images/icon-m-sticker.svg"
|
|
||||||
icon.sourceSize {
|
|
||||||
width: Theme.iconSizeMedium
|
|
||||||
height: Theme.iconSizeMedium
|
|
||||||
}
|
|
||||||
highlighted: down || stickerPickerLoader.active
|
|
||||||
onClicked: {
|
|
||||||
stickerPickerLoader.active = !stickerPickerLoader.active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IconButton {
|
|
||||||
visible: !(chatPage.isPrivateChat || chatPage.isSecretChat) && chatPage.hasSendPrivilege("can_send_polls")
|
|
||||||
icon.source: "image://theme/icon-m-question"
|
|
||||||
onClicked: {
|
|
||||||
pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
|
|
||||||
attachmentOptionsRow.isNeeded = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: attachmentPreviewRow
|
id: attachmentPreviewRow
|
||||||
visible: false
|
visible: false
|
||||||
|
@ -1272,7 +1356,21 @@ Page {
|
||||||
property bool isPicture: false;
|
property bool isPicture: false;
|
||||||
property bool isVideo: false;
|
property bool isVideo: false;
|
||||||
property bool isDocument: false;
|
property bool isDocument: false;
|
||||||
|
property bool isVoiceNote: false;
|
||||||
|
property bool isLocation: false;
|
||||||
|
property var locationData: ({});
|
||||||
property var fileProperties:({});
|
property var fileProperties:({});
|
||||||
|
property string attachmentDescription: "";
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: fernschreiberUtils
|
||||||
|
onNewPositionInformation: {
|
||||||
|
attachmentPreviewRow.locationData = positionInformation;
|
||||||
|
if (attachmentPreviewRow.isLocation) {
|
||||||
|
attachmentPreviewRow.attachmentDescription = qsTr("Location (%1/%2)").arg(attachmentPreviewRow.locationData.latitude).arg(attachmentPreviewRow.locationData.longitude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton {
|
||||||
id: removeAttachmentsIconButton
|
id: removeAttachmentsIconButton
|
||||||
|
@ -1299,13 +1397,13 @@ Page {
|
||||||
Label {
|
Label {
|
||||||
id: attachmentPreviewText
|
id: attachmentPreviewText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
text: typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.fileName || "" : "";
|
text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( typeof attachmentPreviewRow.fileProperties !== "undefined" ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
truncationMode: TruncationMode.Fade
|
truncationMode: TruncationMode.Fade
|
||||||
color: Theme.secondaryColor
|
color: Theme.secondaryColor
|
||||||
visible: attachmentPreviewRow.isDocument
|
visible: attachmentPreviewRow.isDocument || attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1507,6 +1605,7 @@ Page {
|
||||||
labelVisible: false
|
labelVisible: false
|
||||||
textLeftMargin: 0
|
textLeftMargin: 0
|
||||||
textTopMargin: 0
|
textTopMargin: 0
|
||||||
|
enabled: !attachmentPreviewRow.isLocation
|
||||||
EnterKey.onClicked: {
|
EnterKey.onClicked: {
|
||||||
if (appSettings.sendByEnter) {
|
if (appSettings.sendByEnter) {
|
||||||
sendMessage();
|
sendMessage();
|
||||||
|
@ -1528,16 +1627,17 @@ Page {
|
||||||
|
|
||||||
IconButton {
|
IconButton {
|
||||||
id: attachmentIconButton
|
id: attachmentIconButton
|
||||||
icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsRow.isNeeded ? Theme.highlightColor : Theme.primaryColor)
|
icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsFlickable.isNeeded ? Theme.highlightColor : Theme.primaryColor)
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: Theme.paddingSmall
|
anchors.bottomMargin: Theme.paddingSmall
|
||||||
enabled: !attachmentPreviewRow.visible
|
enabled: !attachmentPreviewRow.visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (attachmentOptionsRow.isNeeded) {
|
if (attachmentOptionsFlickable.isNeeded) {
|
||||||
attachmentOptionsRow.isNeeded = false;
|
attachmentOptionsFlickable.isNeeded = false;
|
||||||
stickerPickerLoader.active = false;
|
stickerPickerLoader.active = false;
|
||||||
|
voiceNoteOverlayLoader.active = false;
|
||||||
} else {
|
} else {
|
||||||
attachmentOptionsRow.isNeeded = true;
|
attachmentOptionsFlickable.isNeeded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,74 @@
|
||||||
#include "fernschreiberutils.h"
|
/*
|
||||||
|
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fernschreiberutils.h"
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QAudioEncoderSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QGeoCoordinate>
|
||||||
|
#include <QGeoLocation>
|
||||||
|
|
||||||
|
#define DEBUG_MODULE FernschreiberUtils
|
||||||
|
#include "debuglog.h"
|
||||||
|
|
||||||
FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent)
|
FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
LOG("Initializing audio recorder...");
|
||||||
|
|
||||||
|
QString temporaryDirectoryPath = this->getTemporaryDirectoryPath();
|
||||||
|
QDir temporaryDirectory(temporaryDirectoryPath);
|
||||||
|
if (!temporaryDirectory.exists()) {
|
||||||
|
temporaryDirectory.mkpath(temporaryDirectoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
QAudioEncoderSettings encoderSettings;
|
||||||
|
encoderSettings.setCodec("audio/vorbis");
|
||||||
|
encoderSettings.setChannelCount(1);
|
||||||
|
encoderSettings.setQuality(QMultimedia::LowQuality);
|
||||||
|
this->audioRecorder.setEncodingSettings(encoderSettings);
|
||||||
|
this->audioRecorder.setContainerFormat("ogg");
|
||||||
|
|
||||||
|
QMediaRecorder::Status audioRecorderStatus = this->audioRecorder.status();
|
||||||
|
this->handleAudioRecorderStatusChanged(audioRecorderStatus);
|
||||||
|
|
||||||
|
connect(&audioRecorder, SIGNAL(durationChanged(qlonglong)), this, SIGNAL(voiceNoteDurationChanged(qlonglong)));
|
||||||
|
connect(&audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SLOT(handleAudioRecorderStatusChanged(QMediaRecorder::Status)));
|
||||||
|
|
||||||
|
this->geoPositionInfoSource = QGeoPositionInfoSource::createDefaultSource(this);
|
||||||
|
if (this->geoPositionInfoSource) {
|
||||||
|
LOG("Geolocation successfully initialized...");
|
||||||
|
this->geoPositionInfoSource->setUpdateInterval(5000);
|
||||||
|
connect(geoPositionInfoSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(handleGeoPositionUpdated(QGeoPositionInfo)));
|
||||||
|
} else {
|
||||||
|
LOG("Unable to initialize geolocation!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FernschreiberUtils::~FernschreiberUtils()
|
||||||
|
{
|
||||||
|
this->cleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender)
|
QString FernschreiberUtils::getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender)
|
||||||
|
@ -128,3 +191,117 @@ QString FernschreiberUtils::getUserName(const QVariantMap &userInformation)
|
||||||
const QString lastName = userInformation.value("last_name").toString();
|
const QString lastName = userInformation.value("last_name").toString();
|
||||||
return QString(firstName + " " + lastName).trimmed();
|
return QString(firstName + " " + lastName).trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::startRecordingVoiceNote()
|
||||||
|
{
|
||||||
|
LOG("Start recording voice note...");
|
||||||
|
QDateTime thisIsNow = QDateTime::currentDateTime();
|
||||||
|
this->audioRecorder.setOutputLocation(QUrl::fromLocalFile(this->getTemporaryDirectoryPath() + "/voicenote-" + thisIsNow.toString("yyyy-MM-dd-HH-mm-ss") + ".ogg"));
|
||||||
|
this->audioRecorder.setVolume(1);
|
||||||
|
this->audioRecorder.record();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::stopRecordingVoiceNote()
|
||||||
|
{
|
||||||
|
LOG("Stop recording voice note...");
|
||||||
|
this->audioRecorder.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FernschreiberUtils::voiceNotePath()
|
||||||
|
{
|
||||||
|
return this->audioRecorder.outputLocation().toLocalFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
FernschreiberUtils::VoiceNoteRecordingState FernschreiberUtils::getVoiceNoteRecordingState()
|
||||||
|
{
|
||||||
|
return this->voiceNoteRecordingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::startGeoLocationUpdates()
|
||||||
|
{
|
||||||
|
if (this->geoPositionInfoSource) {
|
||||||
|
this->geoPositionInfoSource->startUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::stopGeoLocationUpdates()
|
||||||
|
{
|
||||||
|
if (this->geoPositionInfoSource) {
|
||||||
|
this->geoPositionInfoSource->stopUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FernschreiberUtils::supportsGeoLocation()
|
||||||
|
{
|
||||||
|
return this->geoPositionInfoSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::handleAudioRecorderStatusChanged(QMediaRecorder::Status status)
|
||||||
|
{
|
||||||
|
LOG("Audio recorder status changed:" << status);
|
||||||
|
switch (status) {
|
||||||
|
case QMediaRecorder::UnavailableStatus:
|
||||||
|
case QMediaRecorder::UnloadedStatus:
|
||||||
|
case QMediaRecorder::LoadingStatus:
|
||||||
|
this->voiceNoteRecordingState = VoiceNoteRecordingState::Unavailable;
|
||||||
|
break;
|
||||||
|
case QMediaRecorder::LoadedStatus:
|
||||||
|
case QMediaRecorder::PausedStatus:
|
||||||
|
this->voiceNoteRecordingState = VoiceNoteRecordingState::Ready;
|
||||||
|
break;
|
||||||
|
case QMediaRecorder::StartingStatus:
|
||||||
|
this->voiceNoteRecordingState = VoiceNoteRecordingState::Starting;
|
||||||
|
break;
|
||||||
|
case QMediaRecorder::FinalizingStatus:
|
||||||
|
this->voiceNoteRecordingState = VoiceNoteRecordingState::Stopping;
|
||||||
|
break;
|
||||||
|
case QMediaRecorder::RecordingStatus:
|
||||||
|
this->voiceNoteRecordingState = VoiceNoteRecordingState::Recording;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
emit voiceNoteRecordingStateChanged(this->voiceNoteRecordingState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::handleGeoPositionUpdated(const QGeoPositionInfo &info)
|
||||||
|
{
|
||||||
|
LOG("Geo position was updated");
|
||||||
|
QVariantMap positionInformation;
|
||||||
|
if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) {
|
||||||
|
positionInformation.insert("horizontalAccuracy", info.attribute(QGeoPositionInfo::HorizontalAccuracy));
|
||||||
|
} else {
|
||||||
|
positionInformation.insert("horizontalAccuracy", 0);
|
||||||
|
}
|
||||||
|
if (info.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) {
|
||||||
|
positionInformation.insert("verticalAccuracy", info.attribute(QGeoPositionInfo::VerticalAccuracy));
|
||||||
|
} else {
|
||||||
|
positionInformation.insert("verticalAccuracy", 0);
|
||||||
|
}
|
||||||
|
QGeoCoordinate geoCoordinate = info.coordinate();
|
||||||
|
positionInformation.insert("latitude", geoCoordinate.latitude());
|
||||||
|
positionInformation.insert("longitude", geoCoordinate.longitude());
|
||||||
|
|
||||||
|
|
||||||
|
emit newPositionInformation(positionInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FernschreiberUtils::cleanUp()
|
||||||
|
{
|
||||||
|
if (this->geoPositionInfoSource) {
|
||||||
|
this->geoPositionInfoSource->stopUpdates();
|
||||||
|
}
|
||||||
|
QString temporaryDirectoryPath = this->getTemporaryDirectoryPath();
|
||||||
|
QDirIterator temporaryDirectoryIterator(temporaryDirectoryPath, QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks, QDirIterator::Subdirectories);
|
||||||
|
while (temporaryDirectoryIterator.hasNext()) {
|
||||||
|
QString nextFilePath = temporaryDirectoryIterator.next();
|
||||||
|
if (QFile::remove(nextFilePath)) {
|
||||||
|
LOG("Temporary file removed " << nextFilePath);
|
||||||
|
} else {
|
||||||
|
LOG("Error removing temporary file " << nextFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FernschreiberUtils::getTemporaryDirectoryPath()
|
||||||
|
{
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + "/harbour-fernschreiber";
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef FERNSCHREIBERUTILS_H
|
#ifndef FERNSCHREIBERUTILS_H
|
||||||
#define FERNSCHREIBERUTILS_H
|
#define FERNSCHREIBERUTILS_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QAudioRecorder>
|
||||||
|
#include <QGeoPositionInfo>
|
||||||
|
#include <QGeoPositionInfoSource>
|
||||||
#include "tdlibwrapper.h"
|
#include "tdlibwrapper.h"
|
||||||
|
|
||||||
class FernschreiberUtils : public QObject
|
class FernschreiberUtils : public QObject
|
||||||
|
@ -9,13 +31,46 @@ class FernschreiberUtils : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FernschreiberUtils(QObject *parent = nullptr);
|
explicit FernschreiberUtils(QObject *parent = nullptr);
|
||||||
|
~FernschreiberUtils();
|
||||||
|
|
||||||
|
enum VoiceNoteRecordingState {
|
||||||
|
Unavailable,
|
||||||
|
Ready,
|
||||||
|
Starting,
|
||||||
|
Recording,
|
||||||
|
Stopping
|
||||||
|
};
|
||||||
|
Q_ENUM(VoiceNoteRecordingState)
|
||||||
|
|
||||||
static QString getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender);
|
static QString getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender);
|
||||||
static QString getUserName(const QVariantMap &userInformation);
|
static QString getUserName(const QVariantMap &userInformation);
|
||||||
|
|
||||||
signals:
|
Q_INVOKABLE void startRecordingVoiceNote();
|
||||||
|
Q_INVOKABLE void stopRecordingVoiceNote();
|
||||||
|
Q_INVOKABLE QString voiceNotePath();
|
||||||
|
Q_INVOKABLE VoiceNoteRecordingState getVoiceNoteRecordingState();
|
||||||
|
Q_INVOKABLE void startGeoLocationUpdates();
|
||||||
|
Q_INVOKABLE void stopGeoLocationUpdates();
|
||||||
|
Q_INVOKABLE bool supportsGeoLocation();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void voiceNoteDurationChanged(qlonglong duration);
|
||||||
|
void voiceNoteRecordingStateChanged(VoiceNoteRecordingState state);
|
||||||
|
void newPositionInformation(const QVariantMap &positionInformation);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleAudioRecorderStatusChanged(QMediaRecorder::Status status);
|
||||||
|
void handleGeoPositionUpdated(const QGeoPositionInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAudioRecorder audioRecorder;
|
||||||
|
VoiceNoteRecordingState voiceNoteRecordingState;
|
||||||
|
|
||||||
|
QGeoPositionInfoSource *geoPositionInfoSource;
|
||||||
|
|
||||||
|
void cleanUp();
|
||||||
|
QString getTemporaryDirectoryPath();
|
||||||
|
|
||||||
public slots:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FERNSCHREIBERUTILS_H
|
#endif // FERNSCHREIBERUTILS_H
|
||||||
|
|
|
@ -82,6 +82,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data());
|
FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data());
|
||||||
context->setContextProperty("fernschreiberUtils", fernschreiberUtils);
|
context->setContextProperty("fernschreiberUtils", fernschreiberUtils);
|
||||||
|
qmlRegisterUncreatableType<FernschreiberUtils>(uri, 1, 0, "FernschreiberUtilities", QString());
|
||||||
|
|
||||||
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
|
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
|
||||||
context->setContextProperty("dBusAdaptor", dBusAdaptor);
|
context->setContextProperty("dBusAdaptor", dBusAdaptor);
|
||||||
|
|
|
@ -473,6 +473,56 @@ void TDLibWrapper::sendDocumentMessage(const QString &chatId, const QString &fil
|
||||||
this->sendRequest(requestObject);
|
this->sendRequest(requestObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId)
|
||||||
|
{
|
||||||
|
LOG("Sending voice note message" << chatId << filePath << message << replyToMessageId);
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "sendMessage");
|
||||||
|
requestObject.insert(CHAT_ID, chatId);
|
||||||
|
if (replyToMessageId != "0") {
|
||||||
|
requestObject.insert("reply_to_message_id", replyToMessageId);
|
||||||
|
}
|
||||||
|
QVariantMap inputMessageContent;
|
||||||
|
inputMessageContent.insert(_TYPE, "inputMessageVoiceNote");
|
||||||
|
QVariantMap formattedText;
|
||||||
|
formattedText.insert("text", message);
|
||||||
|
formattedText.insert(_TYPE, "formattedText");
|
||||||
|
inputMessageContent.insert("caption", formattedText);
|
||||||
|
QVariantMap documentInputFile;
|
||||||
|
documentInputFile.insert(_TYPE, "inputFileLocal");
|
||||||
|
documentInputFile.insert("path", filePath);
|
||||||
|
inputMessageContent.insert("voice_note", documentInputFile);
|
||||||
|
|
||||||
|
requestObject.insert("input_message_content", inputMessageContent);
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId)
|
||||||
|
{
|
||||||
|
LOG("Sending location message" << chatId << latitude << longitude << horizontalAccuracy << replyToMessageId);
|
||||||
|
QVariantMap requestObject;
|
||||||
|
requestObject.insert(_TYPE, "sendMessage");
|
||||||
|
requestObject.insert(CHAT_ID, chatId);
|
||||||
|
if (replyToMessageId != "0") {
|
||||||
|
requestObject.insert("reply_to_message_id", replyToMessageId);
|
||||||
|
}
|
||||||
|
QVariantMap inputMessageContent;
|
||||||
|
inputMessageContent.insert(_TYPE, "inputMessageLocation");
|
||||||
|
QVariantMap location;
|
||||||
|
location.insert("latitude", latitude);
|
||||||
|
location.insert("longitude", longitude);
|
||||||
|
location.insert("horizontal_accuracy", horizontalAccuracy);
|
||||||
|
location.insert(_TYPE, "location");
|
||||||
|
inputMessageContent.insert("location", location);
|
||||||
|
|
||||||
|
inputMessageContent.insert("live_period", 0);
|
||||||
|
inputMessageContent.insert("heading", 0);
|
||||||
|
inputMessageContent.insert("proximity_alert_radius", 0);
|
||||||
|
|
||||||
|
requestObject.insert("input_message_content", inputMessageContent);
|
||||||
|
this->sendRequest(requestObject);
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId)
|
void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId)
|
||||||
{
|
{
|
||||||
LOG("Sending sticker message" << chatId << fileId << replyToMessageId);
|
LOG("Sending sticker message" << chatId << fileId << replyToMessageId);
|
||||||
|
|
|
@ -139,6 +139,8 @@ public:
|
||||||
Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
Q_INVOKABLE void sendPhotoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
||||||
Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
Q_INVOKABLE void sendVideoMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
||||||
Q_INVOKABLE void sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
Q_INVOKABLE void sendDocumentMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
||||||
|
Q_INVOKABLE void sendVoiceNoteMessage(const QString &chatId, const QString &filePath, const QString &message, const QString &replyToMessageId = "0");
|
||||||
|
Q_INVOKABLE void sendLocationMessage(const QString &chatId, double latitude, double longitude, double horizontalAccuracy, const QString &replyToMessageId = "0");
|
||||||
Q_INVOKABLE void sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId = "0");
|
Q_INVOKABLE void sendStickerMessage(const QString &chatId, const QString &fileId, const QString &replyToMessageId = "0");
|
||||||
Q_INVOKABLE void sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &replyToMessageId = "0");
|
Q_INVOKABLE void sendPollMessage(const QString &chatId, const QString &question, const QVariantList &options, bool anonymous, int correctOption, bool multiple, const QString &replyToMessageId = "0");
|
||||||
Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption);
|
Q_INVOKABLE void forwardMessages(const QString &chatId, const QString &fromChatId, const QVariantList &messageIds, bool sendCopy, bool removeCaption);
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>Im Chat suchen...</translation>
|
<translation>Im Chat suchen...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation>Standort: Erlange Position...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation>Standort (%1/%2)</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1444,6 +1452,45 @@
|
||||||
<translation>Download fehlgeschlagen.</translation>
|
<translation>Download fehlgeschlagen.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation>Eine Sprachnachricht aufzeichnen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation>Drücken Sie den Knopf, um die Aufzeichnung zu starten</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation>Nicht verfügbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation>Bereit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation>Startet</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation>Zeichnet auf</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation>Stoppt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation>Aufzeichnung verwenden</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation>Sprachnachricht (%1)</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>Search in chat...</translation>
|
<translation>Search in chat...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation>Location: Obtaining position...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation>Location (%1/%2)</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1444,6 +1452,45 @@
|
||||||
<translation>Download failed.</translation>
|
<translation>Download failed.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation>Record a Voice Note</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation>Press the button to start recording</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation>Unavailable</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation>Starting</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation>Recording</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation>Stopping</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation>Use recording</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation>Voice Note (%1)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation>Ready</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -401,6 +401,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>Buscar</translation>
|
<translation>Buscar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1423,6 +1431,45 @@
|
||||||
<translation>Error al bajar</translation>
|
<translation>Error al bajar</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1445,6 +1453,45 @@
|
||||||
<translation>Lataus epäonnistui.</translation>
|
<translation>Lataus epäonnistui.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -401,6 +401,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1423,6 +1431,45 @@
|
||||||
<translation>A letöltés nem sikerült.</translation>
|
<translation>A letöltés nem sikerült.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1444,6 +1452,45 @@
|
||||||
<translation>Download non riuscito.</translation>
|
<translation>Download non riuscito.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -421,6 +421,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>Wyszukaj w czacie</translation>
|
<translation>Wyszukaj w czacie</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1465,6 +1473,45 @@
|
||||||
<translation>Nieudane pobieranie</translation>
|
<translation>Nieudane pobieranie</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -421,6 +421,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1465,6 +1473,45 @@
|
||||||
<translation>Ошибка скачивания.</translation>
|
<translation>Ошибка скачивания.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>Sök i chatten...</translation>
|
<translation>Sök i chatten...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1444,6 +1452,45 @@
|
||||||
<translation>Nerladdning misslyckades.</translation>
|
<translation>Nerladdning misslyckades.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -401,6 +401,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation>正在搜索对话内容…</translation>
|
<translation>正在搜索对话内容…</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1423,6 +1431,45 @@
|
||||||
<translation>下载失败</translation>
|
<translation>下载失败</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
|
@ -411,6 +411,14 @@
|
||||||
<source>Search in chat...</source>
|
<source>Search in chat...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location: Obtaining position...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Location (%1/%2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ChatSelectionPage</name>
|
<name>ChatSelectionPage</name>
|
||||||
|
@ -1444,6 +1452,45 @@
|
||||||
<translation>Download failed.</translation>
|
<translation>Download failed.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VoiceNoteOverlay</name>
|
||||||
|
<message>
|
||||||
|
<source>Record a Voice Note</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Press the button to start recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unavailable</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starting</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stopping</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use recording</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Voice Note (%1)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ready</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>WebPagePreview</name>
|
<name>WebPagePreview</name>
|
||||||
<message>
|
<message>
|
||||||
|
|
Loading…
Reference in a new issue