Introduce video preview
This commit is contained in:
parent
7fb1344be3
commit
c741a157b7
13 changed files with 560 additions and 2 deletions
|
@ -14,6 +14,8 @@ TARGET = harbour-fernschreiber
|
||||||
|
|
||||||
CONFIG += sailfishapp sailfishapp_i18n
|
CONFIG += sailfishapp sailfishapp_i18n
|
||||||
|
|
||||||
|
QT += core dbus
|
||||||
|
|
||||||
SOURCES += src/harbour-fernschreiber.cpp \
|
SOURCES += src/harbour-fernschreiber.cpp \
|
||||||
src/chatlistmodel.cpp \
|
src/chatlistmodel.cpp \
|
||||||
src/chatmodel.cpp \
|
src/chatmodel.cpp \
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 131 KiB |
Binary file not shown.
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 129 KiB |
BIN
images/icon-l-fullscreen.png
Normal file
BIN
images/icon-l-fullscreen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
467
qml/components/VideoPreview.qml
Normal file
467
qml/components/VideoPreview.qml
Normal file
|
@ -0,0 +1,467 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf
|
||||||
|
|
||||||
|
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.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import QtMultimedia 5.0
|
||||||
|
import "../js/functions.js" as Functions
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: videoMessageComponent
|
||||||
|
|
||||||
|
property variant videoData;
|
||||||
|
property string videoUrl;
|
||||||
|
property int previewFileId;
|
||||||
|
property int videoFileId;
|
||||||
|
property bool fullscreen : false;
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: screensaverTimer
|
||||||
|
interval: 30000
|
||||||
|
running: false
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: {
|
||||||
|
tdLibWrapper.controlScreenSaver(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeString(rawSeconds) {
|
||||||
|
var minutes = Math.floor( rawSeconds / 60 );
|
||||||
|
var seconds = rawSeconds - ( minutes * 60 );
|
||||||
|
|
||||||
|
if ( minutes < 10 ) {
|
||||||
|
minutes = "0" + minutes;
|
||||||
|
}
|
||||||
|
if ( seconds < 10 ) {
|
||||||
|
seconds = "0" + seconds;
|
||||||
|
}
|
||||||
|
return minutes + ":" + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableScreensaver() {
|
||||||
|
screensaverTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableScreensaver() {
|
||||||
|
screensaverTimer.stop();
|
||||||
|
tdLibWrapper.controlScreenSaver(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
updateVideoThumbnail();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVideoThumbnail() {
|
||||||
|
if (typeof videoData === "object") {
|
||||||
|
previewFileId = videoData.thumbnail.photo.id;
|
||||||
|
videoFileId = videoData.video.id;
|
||||||
|
if (videoData.thumbnail.photo.local.is_downloading_completed) {
|
||||||
|
placeholderImage.source = videoData.thumbnail.photo.local.path;
|
||||||
|
} else {
|
||||||
|
tdLibWrapper.downloadFile(previewFileId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePlay() {
|
||||||
|
if (videoData.video.local.is_downloading_completed) {
|
||||||
|
videoUrl = videoData.video.local.path;
|
||||||
|
videoComponentLoader.active = true;
|
||||||
|
} else {
|
||||||
|
videoDownloadBusyIndicator.running = true;
|
||||||
|
tdLibWrapper.downloadFile(videoFileId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: tdLibWrapper
|
||||||
|
onFileUpdated: {
|
||||||
|
if (typeof videoData === "object") {
|
||||||
|
if (fileInformation.local.is_downloading_completed) {
|
||||||
|
if (fileId === previewFileId) {
|
||||||
|
videoData.thumbnail.photo = fileInformation;
|
||||||
|
placeholderImage.source = fileInformation.local.path;
|
||||||
|
}
|
||||||
|
if (fileId === videoFileId) {
|
||||||
|
videoDownloadBusyIndicator.running = false;
|
||||||
|
videoData.video = fileInformation;
|
||||||
|
videoUrl = fileInformation.local.path;
|
||||||
|
videoComponentLoader.active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: placeholderImage
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
visible: status === Image.Ready ? true : false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imageLoadingBackgroundImage
|
||||||
|
source: "../../images/background" + ( Theme.colorScheme ? "-black" : "-white" ) + ".png"
|
||||||
|
anchors {
|
||||||
|
centerIn: parent
|
||||||
|
}
|
||||||
|
width: parent.width - Theme.paddingSmall
|
||||||
|
height: parent.height - Theme.paddingSmall
|
||||||
|
visible: placeholderImage.status !== Image.Ready
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
opacity: 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: placeholderBackground
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.3
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
visible: playButton.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
Item {
|
||||||
|
height: parent.height
|
||||||
|
width: videoMessageComponent.fullscreen ? parent.width : ( parent.width / 2 )
|
||||||
|
Image {
|
||||||
|
id: playButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.iconSizeLarge
|
||||||
|
height: Theme.iconSizeLarge
|
||||||
|
source: "image://theme/icon-l-play?white"
|
||||||
|
visible: placeholderImage.status === Image.Ready ? true : false
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
fullscreenItem.visible = false;
|
||||||
|
handlePlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BusyIndicator {
|
||||||
|
id: videoDownloadBusyIndicator
|
||||||
|
running: false
|
||||||
|
visible: running
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: BusyIndicatorSize.Large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
id: fullscreenItem
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width / 2
|
||||||
|
visible: !videoMessageComponent.fullscreen
|
||||||
|
Image {
|
||||||
|
id: fullscreenButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.iconSizeLarge
|
||||||
|
height: Theme.iconSizeLarge
|
||||||
|
source: "../../images/icon-l-fullscreen.png"
|
||||||
|
visible: ( placeholderImage.status === Image.Ready && !videoMessageComponent.fullscreen ) ? true : false
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
// TODO show video page once it's there...
|
||||||
|
//pageStack.push(Qt.resolvedUrl("../pages/VideoPage.qml"), {"tweetModel": tweet.retweeted_status ? tweet.retweeted_status : tweet});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: videoErrorShade
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
color: "lightgrey"
|
||||||
|
visible: placeholderImage.status === Image.Error ? true : false
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: errorTextOverlay
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.8
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: errorText
|
||||||
|
visible: false
|
||||||
|
width: parent.width
|
||||||
|
color: Theme.primaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: videoComponentLoader
|
||||||
|
active: false
|
||||||
|
width: parent.width
|
||||||
|
height: Functions.getVideoHeight(parent.width, videoData)
|
||||||
|
sourceComponent: videoComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: videoComponent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent ? parent.width : 0
|
||||||
|
height: parent ? parent.height : 0
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: messageVideo
|
||||||
|
onPlaying: {
|
||||||
|
playButton.visible = false;
|
||||||
|
placeholderImage.visible = false;
|
||||||
|
messageVideo.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Video {
|
||||||
|
id: messageVideo
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (messageVideo.error === MediaPlayer.NoError) {
|
||||||
|
messageVideo.play();
|
||||||
|
timeLeftTimer.start();
|
||||||
|
} else {
|
||||||
|
errorText.text = qsTr("Error loading video! " + messageVideo.errorString)
|
||||||
|
errorTextOverlay.visible = true;
|
||||||
|
errorText.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status == MediaPlayer.NoMedia) {
|
||||||
|
console.log("No Media");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.Loading) {
|
||||||
|
console.log("Loading");
|
||||||
|
videoBusyIndicator.visible = true;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.Loaded) {
|
||||||
|
console.log("Loaded");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.Buffering) {
|
||||||
|
console.log("Buffering");
|
||||||
|
videoBusyIndicator.visible = true;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.Stalled) {
|
||||||
|
console.log("Stalled");
|
||||||
|
videoBusyIndicator.visible = true;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.Buffered) {
|
||||||
|
console.log("Buffered");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.EndOfMedia) {
|
||||||
|
console.log("End of Media");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.InvalidMedia) {
|
||||||
|
console.log("Invalid Media");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
if (status == MediaPlayer.UnknownStatus) {
|
||||||
|
console.log("Unknown Status");
|
||||||
|
videoBusyIndicator.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
source: videoUrl
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (messageVideo.playbackState === MediaPlayer.PlayingState) {
|
||||||
|
enableScreensaver();
|
||||||
|
messageVideo.pause();
|
||||||
|
timeLeftItem.visible = true;
|
||||||
|
} else {
|
||||||
|
disableScreensaver();
|
||||||
|
messageVideo.play();
|
||||||
|
timeLeftTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onStopped: {
|
||||||
|
enableScreensaver();
|
||||||
|
messageVideo.visible = false;
|
||||||
|
placeholderImage.visible = true;
|
||||||
|
playButton.visible = true;
|
||||||
|
videoComponentLoader.active = false;
|
||||||
|
fullscreenItem.visible = !videoMessageComponent.fullscreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: videoBusyIndicator
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: false
|
||||||
|
running: visible
|
||||||
|
size: BusyIndicatorSize.Medium
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
enableScreensaver();
|
||||||
|
} else {
|
||||||
|
disableScreensaver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timeLeftTimer
|
||||||
|
repeat: false
|
||||||
|
interval: 2000
|
||||||
|
onTriggered: {
|
||||||
|
timeLeftItem.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: timeLeftItem
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: messageVideo.visible
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
Behavior on opacity { NumberAnimation {} }
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: positionTextOverlay
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.3
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: pausedRow.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: pausedRow
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - ( messageVideoSlider.visible ? messageVideoSlider.height : 0 ) - ( positionText.visible ? positionText.height : 0 )
|
||||||
|
visible: videoComponentLoader.active && messageVideo.playbackState === MediaPlayer.PausedState
|
||||||
|
Item {
|
||||||
|
height: parent.height
|
||||||
|
width: videoMessageComponent.fullscreen ? parent.width : ( parent.width / 2 )
|
||||||
|
Image {
|
||||||
|
id: pausedPlayButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.iconSizeLarge
|
||||||
|
height: Theme.iconSizeLarge
|
||||||
|
source: "image://theme/icon-l-play?white"
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
disableScreensaver();
|
||||||
|
messageVideo.play();
|
||||||
|
timeLeftTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
id: pausedFullscreenItem
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width / 2
|
||||||
|
visible: !videoMessageComponent.fullscreen
|
||||||
|
Image {
|
||||||
|
id: pausedFullscreenButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Theme.iconSizeLarge
|
||||||
|
height: Theme.iconSizeLarge
|
||||||
|
source: "../../images/icon-l-fullscreen.png"
|
||||||
|
visible: ( videoComponentLoader.active && messageVideo.playbackState === MediaPlayer.PausedState ) ? true : false
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
// TODO go to video page once it's done
|
||||||
|
// pageStack.push(Qt.resolvedUrl("../pages/VideoPage.qml"), {"tweetModel": videoMessageComponent.tweet});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: messageVideoSlider
|
||||||
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: positionText.top
|
||||||
|
minimumValue: 0
|
||||||
|
maximumValue: messageVideo.duration ? messageVideo.duration : 0
|
||||||
|
stepSize: 1
|
||||||
|
value: messageVideo.position
|
||||||
|
enabled: messageVideo.seekable
|
||||||
|
visible: (messageVideo.duration > 0)
|
||||||
|
onReleased: {
|
||||||
|
messageVideo.seek(Math.floor(value));
|
||||||
|
messageVideo.play();
|
||||||
|
timeLeftTimer.start();
|
||||||
|
}
|
||||||
|
valueText: getTimeString(Math.round((messageVideo.duration - messageVideoSlider.value) / 1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: positionText
|
||||||
|
visible: messageVideo.visible && messageVideo.duration === 0
|
||||||
|
color: Theme.primaryColor
|
||||||
|
font.pixelSize: videoMessageComponent.fullscreen ? Theme.fontSizeSmall : Theme.fontSizeTiny
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: Theme.paddingSmall
|
||||||
|
horizontalCenter: positionTextOverlay.horizontalCenter
|
||||||
|
}
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: ( messageVideo.duration - messageVideo.position ) > 0 ? getTimeString(Math.round((messageVideo.duration - messageVideo.position) / 1000)) : "-:-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -187,3 +187,12 @@ function handleLink(link) {
|
||||||
Qt.openUrlExternally(link);
|
Qt.openUrlExternally(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVideoHeight(videoWidth, videoData) {
|
||||||
|
if (typeof videoData !== "undefined") {
|
||||||
|
var aspectRatio = videoData.height / videoData.width;
|
||||||
|
return Math.round(videoWidth * aspectRatio);
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -440,6 +440,33 @@ Page {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoPreview {
|
||||||
|
id: messageVideoPreview
|
||||||
|
videoData: ( display.content['@type'] === "messageVideo" ) ? display.content.video : ""
|
||||||
|
width: parent.width
|
||||||
|
height: Functions.getVideoHeight(width, display.content.video)
|
||||||
|
visible: display.content['@type'] === "messageVideo"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row {
|
||||||
|
// id: audioRow
|
||||||
|
// visible: display.content['@type'] === "messageVoiceNote"
|
||||||
|
// width: parent.width
|
||||||
|
// Image {
|
||||||
|
// id: audioPlayButton
|
||||||
|
// width: Theme.iconSizeLarge
|
||||||
|
// height: Theme.iconSizeLarge
|
||||||
|
// source: "image://theme/icon-l-play?white"
|
||||||
|
// visible: placeholderImage.status === Image.Ready ? true : false
|
||||||
|
// MouseArea {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// onClicked: {
|
||||||
|
// // Play the stuff...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: messageDateUpdater
|
id: messageDateUpdater
|
||||||
interval: 60000
|
interval: 60000
|
||||||
|
|
|
@ -12,7 +12,7 @@ Name: harbour-fernschreiber
|
||||||
|
|
||||||
Summary: Fernschreiber is a Telegram client for Sailfish OS
|
Summary: Fernschreiber is a Telegram client for Sailfish OS
|
||||||
Version: 0.1
|
Version: 0.1
|
||||||
Release: 1
|
Release: 2
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
License: LICENSE
|
License: LICENSE
|
||||||
URL: http://werkwolf.eu/
|
URL: http://werkwolf.eu/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Name: harbour-fernschreiber
|
Name: harbour-fernschreiber
|
||||||
Summary: Fernschreiber is a Telegram client for Sailfish OS
|
Summary: Fernschreiber is a Telegram client for Sailfish OS
|
||||||
Version: 0.1
|
Version: 0.1
|
||||||
Release: 1
|
Release: 2
|
||||||
# The contents of the Group field should be one of the groups listed here:
|
# The contents of the Group field should be one of the groups listed here:
|
||||||
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
|
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "chatmodel.h"
|
#include "chatmodel.h"
|
||||||
|
|
||||||
#include <QListIterator>
|
#include <QListIterator>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QBitArray>
|
||||||
|
|
||||||
ChatModel::ChatModel(TDLibWrapper *tdLibWrapper)
|
ChatModel::ChatModel(TDLibWrapper *tdLibWrapper)
|
||||||
{
|
{
|
||||||
|
@ -87,6 +89,7 @@ void ChatModel::handleMessagesReceived(const QVariantList &messages)
|
||||||
while (messagesIterator.hasNext()) {
|
while (messagesIterator.hasNext()) {
|
||||||
QVariantMap currentMessage = messagesIterator.next().toMap();
|
QVariantMap currentMessage = messagesIterator.next().toMap();
|
||||||
if (currentMessage.value("chat_id").toString() == this->chatId) {
|
if (currentMessage.value("chat_id").toString() == this->chatId) {
|
||||||
|
|
||||||
this->messagesToBeAdded.append(currentMessage);
|
this->messagesToBeAdded.append(currentMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,3 +147,34 @@ void ChatModel::insertMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap ChatModel::enhanceMessage(const QVariantMap &message)
|
||||||
|
{
|
||||||
|
QVariantMap enhancedMessage = message;
|
||||||
|
if (enhancedMessage.value("content").toMap().value("@type").toString() == "messageVoiceNote" ) {
|
||||||
|
QVariantMap contentMap = enhancedMessage.value("content").toMap();
|
||||||
|
QVariantMap voiceNoteMap = contentMap.value("voice_note").toMap();
|
||||||
|
QByteArray waveBytes = QByteArray::fromBase64(voiceNoteMap.value("waveform").toByteArray());
|
||||||
|
QBitArray waveBits(waveBytes.count() * 8);
|
||||||
|
|
||||||
|
for (int i = 0; i < waveBytes.count(); i++) {
|
||||||
|
for (int b = 0; b < 8; b++) {
|
||||||
|
waveBits.setBit( i * 8 + b, waveBytes.at(i) & (1 << (7 - b)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int waveSize = 10;
|
||||||
|
int waveformSets = waveBits.size() / waveSize;
|
||||||
|
QVariantList decodedWaveform;
|
||||||
|
for (int i = 0; i < waveformSets; i++) {
|
||||||
|
int waveformHeight = 0;
|
||||||
|
for (int j = 0; j < waveSize; j++) {
|
||||||
|
waveformHeight = waveformHeight + ( waveBits.at(i * waveSize + j) * (2 ^ (j)) );
|
||||||
|
}
|
||||||
|
decodedWaveform.append(waveformHeight);
|
||||||
|
}
|
||||||
|
voiceNoteMap.insert("decoded_voice_note", decodedWaveform);
|
||||||
|
contentMap.insert("voice_note", voiceNoteMap);
|
||||||
|
enhancedMessage.insert("content", contentMap);
|
||||||
|
}
|
||||||
|
return enhancedMessage;
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ private:
|
||||||
bool inIncrementalUpdate;
|
bool inIncrementalUpdate;
|
||||||
|
|
||||||
void insertMessages();
|
void insertMessages();
|
||||||
|
QVariantMap enhanceMessage(const QVariantMap &message);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATMODEL_H
|
#endif // CHATMODEL_H
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
|
||||||
TDLibWrapper::TDLibWrapper(QObject *parent) : QObject(parent)
|
TDLibWrapper::TDLibWrapper(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
@ -274,6 +276,21 @@ void TDLibWrapper::handleAdditionalInformation(const QString &additionalInformat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::controlScreenSaver(const bool &enabled)
|
||||||
|
{
|
||||||
|
qDebug() << "[TDLibWrapper] Controlling device screen saver" << enabled;
|
||||||
|
QDBusConnection dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system");
|
||||||
|
QDBusInterface dbusInterface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", dbusConnection);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
qDebug() << "Enabling screensaver";
|
||||||
|
dbusInterface.call("req_display_cancel_blanking_pause");
|
||||||
|
} else {
|
||||||
|
qDebug() << "Disabling screensaver";
|
||||||
|
dbusInterface.call("req_display_blanking_pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::handleVersionDetected(const QString &version)
|
void TDLibWrapper::handleVersionDetected(const QString &version)
|
||||||
{
|
{
|
||||||
this->version = version;
|
this->version = version;
|
||||||
|
|
|
@ -69,6 +69,7 @@ public:
|
||||||
Q_INVOKABLE QVariantMap getSuperGroup(const QString &groupId);
|
Q_INVOKABLE QVariantMap getSuperGroup(const QString &groupId);
|
||||||
Q_INVOKABLE void copyPictureToDownloads(const QString &filePath);
|
Q_INVOKABLE void copyPictureToDownloads(const QString &filePath);
|
||||||
Q_INVOKABLE void handleAdditionalInformation(const QString &additionalInformation);
|
Q_INVOKABLE void handleAdditionalInformation(const QString &additionalInformation);
|
||||||
|
Q_INVOKABLE void controlScreenSaver(const bool &enabled);
|
||||||
|
|
||||||
// Direct TDLib functions
|
// Direct TDLib functions
|
||||||
Q_INVOKABLE void sendRequest(const QVariantMap &requestObject);
|
Q_INVOKABLE void sendRequest(const QVariantMap &requestObject);
|
||||||
|
|
Loading…
Reference in a new issue