Video support and pinch to zoom on images

This commit is contained in:
Dusko Angirevic 2017-10-20 15:20:33 +02:00
parent 0048af9b13
commit e7db78274e
18 changed files with 274 additions and 94 deletions

View file

@ -12,6 +12,8 @@
# The name of your application # The name of your application
TARGET = harbour-tooter TARGET = harbour-tooter
QT += network dbus sql
CONFIG += sailfishapp CONFIG += sailfishapp
SOURCES += src/harbour-tooter.cpp \ SOURCES += src/harbour-tooter.cpp \

View file

@ -116,7 +116,7 @@ var notificationGenerator = function(item){
var notification; var notification;
switch (item.urgency){ switch (item.urgency){
case "normal": case "normal":
notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Normal; }', Qt.application, 'InternalQmlObject'); notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Normal; }', Qt.application, 'InternalQmlObject');
break; break;
case "critical": case "critical":
notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Critical; }', Qt.application, 'InternalQmlObject'); notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Critical; }', Qt.application, 'InternalQmlObject');
@ -124,6 +124,7 @@ var notificationGenerator = function(item){
default: default:
notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Low; }', Qt.application, 'InternalQmlObject'); notification = Qt.createQmlObject('import org.nemomobile.notifications 1.0; Notification { category: "x-nemo.example"; urgency: Notification.Low; }', Qt.application, 'InternalQmlObject');
} }
notification.timestamp = item.timestamp notification.timestamp = item.timestamp
notification.summary = item.summary notification.summary = item.summary
notification.body = item.body notification.body = item.body
@ -176,7 +177,7 @@ var notifier = function(item){
} }
break; break;
default: default:
console.log(JSON.stringify(messageObject.data)) //console.log(JSON.stringify(messageObject.data))
return; return;
} }
notificationGenerator(msg) notificationGenerator(msg)

View file

@ -207,6 +207,7 @@ function getDate(dateStr){
function parseToot (data){ function parseToot (data){
//console.log(JSON.stringify(data)) //console.log(JSON.stringify(data))
var item = {}; var item = {};
item['type'] = "toot" item['type'] = "toot"
item['highlight'] = false item['highlight'] = false
item['status_id'] = data["id"] item['status_id'] = data["id"]

View file

@ -147,6 +147,7 @@ Page {
RemorseItem { id: remorse } RemorseItem { id: remorse }
Image { Image {
anchors.fill: parent anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: model.preview_url source: model.preview_url
} }

View file

@ -3,117 +3,205 @@ import Sailfish.Silica 1.0
import QtMultimedia 5.0 import QtMultimedia 5.0
Page { Page {
id: page id: imagePage
property string type: "" property string type: ""
property string previewURL: "" property string previewURL: ""
property string mediaURL: "" property string mediaURL: ""
allowedOrientations: Orientation.All allowedOrientations: Orientation.All
Component.onCompleted: { Component.onCompleted: function(){
console.log(type) console.log(type)
console.log(previewURL) console.log(previewURL)
console.log(mediaURL) console.log(mediaURL)
} if (type != 'gifv' && type != 'video') {
onStateChanged: { imagePreview.source = mediaURL
if (status === PageStatus.Deactivating){ imageFlickable.visible = true;
video.stop() } else {
} video.source = mediaURL
if (status === PageStatus.Activating){ video.fillMode = VideoOutput.PreserveAspectFit
if (type !== "image" ) video.play()
video.play() videoFlickable.visible = true;
} }
} }
BusyIndicator { Flickable {
running: image.status !== Image.Ready id: videoFlickable
size: BusyIndicatorSize.Large visible: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
Item {
anchors.fill: parent anchors.fill: parent
contentWidth: imageContainer.width; contentHeight: imageContainer.height
clip: true clip: true
Image { Image {
id: image id: videoPreview
anchors.centerIn: parent fillMode: Image.PreserveAspectFit
fillMode: Image.PreserveAspectCrop anchors.fill: parent
asynchronous: true source: previewURL
opacity: status === Image.Ready ? 1.0 : 0.0
Behavior on opacity { FadeAnimator {} }
source: type === "image" ? mediaURL : previewURL
onStatusChanged: {
if (status === Image.Ready) {
console.log('Loaded')
width = sourceSize.width
height = sourceSize.height
if (width > height)
pinch.scale = page.width / width
else
pinch.scale = page.height / height
}
}
Video {
id: video
anchors.fill: parent
autoLoad: true
onStateChanged: {
switch(status){
case MediaPlayer.Loaded:
play();
break;
case MediaPlayer.Loading:
loader.running = true;
break;
case MediaPlayer.EndOfMedia:
if(seekable)
seek(0)
break;
default:
loader.running = false;
}
}
source: type !== "image" ? mediaURL : ""
onErrorStringChanged: {
console.log(errorString)
}
BusyIndicator {
id: loader
size: BusyIndicatorSize.Small
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play()
}
}
focus: true
}
} }
Video {
id: video
anchors.fill: parent
onPositionChanged: function(){
console.log(duration)
console.log(bufferProgress)
console.log(position)
progressRec.width = parent.width * position/duration
}
onStopped: function(){
play()
}
}
Rectangle {
id: progressRec
anchors.bottom: parent.bottom
width: 0
height: Theme.paddingSmall
color: Theme.highlightBackgroundColor
}
}
Flickable {
id: imageFlickable
visible: false
anchors.fill: parent
contentWidth: imageContainer.width; contentHeight: imageContainer.height
clip: true
onHeightChanged: if (imagePreview.status === Image.Ready) imagePreview.fitToScreen();
Item {
id: imageContainer
width: Math.max(imagePreview.width * imagePreview.scale, imageFlickable.width)
height: Math.max(imagePreview.height * imagePreview.scale, imageFlickable.height)
Image {
id: imagePreview
property real prevScale
function fitToScreen() {
scale = Math.min(imageFlickable.width / width, imageFlickable.height / height, 1)
pinchArea.minScale = scale
prevScale = scale
}
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
cache: true
asynchronous: true
sourceSize.height: 1000;
smooth: false
onStatusChanged: {
if (status == Image.Ready) {
fitToScreen()
loadedAnimation.start()
}
}
NumberAnimation {
id: loadedAnimation
target: imagePreview
property: "opacity"
duration: 250
from: 0; to: 1
easing.type: Easing.InOutQuad
}
onScaleChanged: {
if ((width * scale) > imageFlickable.width) {
var xoff = (imageFlickable.width / 2 + imageFlickable.contentX) * scale / prevScale;
imageFlickable.contentX = xoff - imageFlickable.width / 2
}
if ((height * scale) > imageFlickable.height) {
var yoff = (imageFlickable.height / 2 + imageFlickable.contentY) * scale / prevScale;
imageFlickable.contentY = yoff - imageFlickable.height / 2
}
prevScale = scale
}
}
}
PinchArea { PinchArea {
id: pinch id: pinchArea
visible: type === "image" opacity: 0.3
property real minScale: 1.0
property real maxScale: 3.0
anchors.fill: parent anchors.fill: parent
pinch.target: image enabled: imagePreview.status === Image.Ready
pinch.minimumScale: 0.1 pinch.target: imagePreview
pinch.maximumScale: 10 pinch.minimumScale: minScale * 0.5 // This is to create "bounce back effect"
pinch.dragAxis: Pinch.XAndYAxis pinch.maximumScale: maxScale * 1.5 // when over zoomed
}
Label { onPinchFinished: {
visible: type !== "image" imageFlickable.returnToBounds()
anchors { if (imagePreview.scale < pinchArea.minScale) {
bottom: parent.bottom bounceBackAnimation.to = pinchArea.minScale
left: parent.left bounceBackAnimation.start()
right: parent.right }
else if (imagePreview.scale > pinchArea.maxScale) {
bounceBackAnimation.to = pinchArea.maxScale
bounceBackAnimation.start()
}
}
NumberAnimation {
id: bounceBackAnimation
target: imagePreview
duration: 250
property: "scale"
from: imagePreview.scale
}
}
}
Loader {
anchors.centerIn: parent
sourceComponent: {
switch (imagePreview.status) {
case Image.Loading:
return loadingIndicator
case Image.Error:
return failedLoading
default:
return undefined
} }
text: "Video playing is faulty... may break app... Just to know :)"
} }
Component {
id: loadingIndicator
Item {
height: childrenRect.height
width: imagePage.width
ProgressCircle {
id: imageLoadingIndicator
anchors.horizontalCenter: parent.horizontalCenter
progressValue: imagePreview.progress
}
}
}
Component {
id: failedLoading
Text {
font.pixelSize: Theme.fontSizeSmall;
text: qsTr("Error loading image")
color: Theme.highlightColor
}
}
}
VerticalScrollDecorator { flickable: imageFlickable }
IconButton {
visible: false
anchors{
right: imagePage.right;
rightMargin: Theme.paddingLarge;
bottom: imagePage.bottom;
bottomMargin: Theme.paddingLarge;
}
width: Theme.iconSizeMedium+Theme.paddingMedium*2
icon.source: "image://theme/icon-m-cloud-download"
onClicked: {
//py.saveImg(MD5.hex_md5(strThumbnailUrl),strHpTitle+"."+Script.parseDate(currentDay));
}
} }
} }

View file

@ -10,7 +10,6 @@ Item {
opacity: 0.2 opacity: 0.2
anchors.fill: parent anchors.fill: parent
color: Theme.highlightDimmerColor color: Theme.highlightDimmerColor
} }
Image { Image {
anchors.centerIn: parent anchors.centerIn: parent

View file

@ -57,6 +57,19 @@ SilicaListView {
} }
PullDownMenu { PullDownMenu {
MenuItem {
text: "NOTIFIKACIJA"
onClicked: {
Logic.notifier({
type: "favourite",
urgency: "critical",
created_at: new Date(),
reblog_account_display_name: "@akakakak",
content: "blaaaaaa blaaaaaablaaaaaablaaaaaa"
})
}
}
MenuItem { MenuItem {
text: qsTr("Settings") text: qsTr("Settings")
onClicked: { onClicked: {

View file

@ -186,4 +186,5 @@ BackgroundItem {
type: "reply" type: "reply"
}) })
} }
} }

View file

@ -9,6 +9,16 @@ BackgroundItem {
signal navigateTo(string link) signal navigateTo(string link)
width: parent.width width: parent.width
height: mnu.height + miniHeader.height + (typeof attachments !== "undefined" && attachments.count ? media.height + Theme.paddingLarge + Theme.paddingMedium: Theme.paddingLarge) + lblContent.height + Theme.paddingLarge + (ministatus.visible ? ministatus.height : 0) height: mnu.height + miniHeader.height + (typeof attachments !== "undefined" && attachments.count ? media.height + Theme.paddingLarge + Theme.paddingMedium: Theme.paddingLarge) + lblContent.height + Theme.paddingLarge + (ministatus.visible ? ministatus.height : 0)
Rectangle {
x: 0;
y: 0;
visible: status_visibility == 'direct'
width: parent.width
height: parent.height
opacity: 0.3
color: Theme.highlightBackgroundColor;
}
MiniStatus { MiniStatus {
id: ministatus id: ministatus
anchors { anchors {
@ -284,4 +294,5 @@ BackgroundItem {
onDoubleClicked: { onDoubleClicked: {
console.log("double click") console.log("double click")
} }
} }

View file

@ -78,6 +78,13 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Κτυπήστε για εισαγωγή</translation> <translation>Κτυπήστε για εισαγωγή</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Tap to insert</translation> <translation>Tap to insert</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Toca para insertar</translation> <translation>Toca para insertar</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Appuyez pour insérer</translation> <translation>Appuyez pour insérer</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Tustejar per inserir</translation> <translation>Tustejar per inserir</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Тапни за убацивање</translation> <translation>Тапни за убацивање</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>

View file

@ -78,6 +78,13 @@
<translation>Tap to insert</translation> <translation>Tap to insert</translation>
</message> </message>
</context> </context>
<context>
<name>ImageFullScreen</name>
<message>
<source>Error loading image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>ImageUploader</name> <name>ImageUploader</name>
<message> <message>