More work on Multimedia

This commit is contained in:
Michael Zanetti 2019-07-08 12:16:56 +02:00
parent 8ad40dcf56
commit 2ad624329c
7 changed files with 364 additions and 89 deletions

View File

@ -185,5 +185,8 @@
<file>ui/connection/SetupWizard.qml</file>
<file>ui/system/NetworkSettingsPage.qml</file>
<file>ui/devicepages/DeviceBrowserPage.qml</file>
<file>ui/devicelistpages/MediaDeviceListPage.qml</file>
<file>ui/components/MediaControls.qml</file>
<file>ui/components/MediaArtworkImage.qml</file>
</qresource>
</RCC>

View File

@ -30,6 +30,8 @@ Item {
anchors.fill: parent
anchors.margins: 1
z: -1
fillMode: Image.PreserveAspectCrop
// horizontalAlignment: Image.AlignTop
// opacity: .5
// Rectangle {
// anchors.fill: parent

View File

@ -0,0 +1,47 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
Item {
id: root
property Device device: null
readonly property StateType artworkStateType: device ? device.deviceClass.stateTypes.findByName("artwork") : null
readonly property State artworkState: artworkStateType ? device.states.getState(artworkStateType.id) : null
readonly property StateType playerTypeStateType: device ? device.deviceClass.stateTypes.findByName("playerType") : null
readonly property State playerTypeState: playerTypeStateType ? device.states.getState(playerTypeStateType.id) : null
Pane {
Material.elevation: 2
anchors.centerIn: parent
height: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedHeight - 1
width: fallback.visible ? Math.min(parent.height, parent.width) : artworkImage.paintedWidth - 1
padding: 0
contentItem: Rectangle {
color: "black"
}
}
Image {
id: artworkImage
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: root.artworkState.value
visible: source !== ""
}
ColorIcon {
id: fallback
anchors.centerIn: parent
width: Math.min(parent.height, parent.width) - app.margins * 2
height: Math.min(parent.height, parent.width) - app.margins * 2
name: root.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
// color: app.primaryColor
color: "white"
}
}

View File

@ -0,0 +1,73 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
RowLayout {
id: root
implicitHeight: iconSize + app.margins
property Device device: null
property int iconSize: app.iconSize * 1.5
readonly property StateType playbackStateType: device ? device.deviceClass.stateTypes.findByName("playbackStatus") : null
readonly property State playbackState: playbackStateType ? device.states.getState(playbackStateType.id) : null
function executeAction(actionName, params) {
var actionTypeId = device.deviceClass.actionTypes.findByName(actionName).id;
engine.deviceManager.executeAction(device.id, actionTypeId, params)
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: root.iconSize * .6
Layout.preferredWidth: height
imageSource: "../images/media-skip-backward.svg"
longpressImageSource: "../images/media-seek-backward.svg"
enabled: root.playbackState.value !== "Stopped"
repeat: true
onClicked: {
root.executeAction("skipBack")
}
onLongpressed: {
root.executeAction("fastRewind")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: root,iconSize
Layout.preferredWidth: height
imageSource: root.playbackState && root.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
longpressImageSource: "../images/media-playback-stop.svg"
longpressEnabled: root.playbackState.value !== "Stopped"
onClicked: {
if (root.playbackState.value === "Playing") {
root.executeAction("pause")
} else {
root.executeAction("play")
}
}
onLongpressed: {
root.executeAction("stop")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: root.iconSize * .6
Layout.preferredWidth: height
imageSource: "../images/media-skip-forward.svg"
longpressImageSource: "../images/media-seek-forward.svg"
enabled: root.playbackState.value !== "Stopped"
repeat: true
onClicked: {
root.executeAction("skipNext")
}
onLongpressed: {
root.executeAction("fastForward")
}
}
Item { Layout.fillWidth: true }
}

View File

@ -0,0 +1,170 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.1
import Nymea 1.0
import QtGraphicalEffects 1.0
import "../components"
DeviceListPageBase {
id: root
header: NymeaHeader {
text: qsTr("Media")
onBackPressed: pageStack.pop()
}
ListView {
anchors.fill: parent
model: root.devicesProxy
delegate: ItemDelegate {
id: itemDelegate
width: parent.width
property bool inline: width > 500
property Device device: devicesProxy.get(index);
property DeviceClass deviceClass: device.deviceClass
readonly property StateType playbackStateType: deviceClass.stateTypes.findByName("playbackStatus")
readonly property State playbackState: playbackStateType ? device.states.getState(playbackStateType.id) : null
bottomPadding: index === ListView.view.count - 1 ? topPadding : 0
contentItem: Pane {
id: contentItem
Material.elevation: 2
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ItemDelegate {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.mediumFont + app.margins
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
RowLayout {
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
}
ColorIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
name: "../images/battery/battery-020.svg"
visible: itemDelegate.deviceClass.interfaces.indexOf("battery") >= 0 && itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("batteryCritical").id).value === true
}
ColorIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
name: "../images/dialog-warning-symbolic.svg"
visible: itemDelegate.deviceClass.interfaces.indexOf("connectable") >= 0 && itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("connected").id).value === false
color: "red"
}
}
}
RowLayout {
ColumnLayout {
id: leftColummn
Layout.margins: app.margins
Label {
Layout.fillWidth: true
text: itemDelegate.playbackState.value === "Stopped" ?
qsTr("No playback")
: itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("title").id).value
horizontalAlignment: Text.AlignHCenter
// font.pixelSize: app.largeFont
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("artist").id).value
font.pixelSize: app.smallFont
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("collection").id).value
horizontalAlignment: Text.AlignHCenter
font.pixelSize: app.smallFont
elide: Text.ElideRight
}
MediaControls {
visible: itemDelegate.deviceClass.interfaces.indexOf("mediacontroller") >= 0
device: itemDelegate.device
}
}
Item {
Layout.preferredHeight: leftColummn.height + app.margins * 2
Layout.preferredWidth: height * .7
Item {
id: artworkContainer
anchors.fill: parent
Image {
id: artworkImage
width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
readonly property StateType artworkStateType: device ? device.deviceClass.stateTypes.findByName("artwork") : null
readonly property State artworkState: artworkStateType ? device.states.getState(artworkStateType.id) : null
source: artworkState ? artworkState.value : ""
}
}
Rectangle {
id: maskRect
anchors.centerIn: parent
height: parent.width
width: parent.height
gradient: Gradient {
GradientStop { position: 0; color: "transparent" }
GradientStop { position: 1; color: "red" }
}
}
ShaderEffect {
anchors.fill: parent
property variant source: ShaderEffectSource {
sourceItem: artworkContainer
hideSource: true
}
property variant mask: ShaderEffectSource {
sourceItem: maskRect
hideSource: true
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform sampler2D mask;
void main(void)
{
highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
highp float alpha = texture2D(mask, vec2(qt_TexCoord0.y, qt_TexCoord0.x)).a;
sourceColor *= alpha;
gl_FragColor = sourceColor;
}
"
}
}
}
}
onClicked: {
enterPage(index, false)
}
}
}
}
}
}

View File

@ -41,6 +41,7 @@ DevicePageBase {
SwipeView {
id: swipeView
anchors.fill: parent
interactive: root.deviceClass.browsable
Item {
GridLayout {
@ -51,32 +52,10 @@ DevicePageBase {
columnSpacing: app.margins
rowSpacing: app.margins
Pane {
Layout.fillWidth: true
MediaArtworkImage {
Layout.fillHeight: true
Layout.minimumWidth: parent.width / 2
Material.elevation: 2
padding: 0
contentItem: Rectangle {
color: app.foregroundColor
Image {
id: artworkImage
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: root.stateValue("artwork")
}
ColorIcon {
id: fallback
anchors.fill: parent
anchors.margins: app.margins * 2
name: root.stateValue("playerType") === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
color: app.primaryColor
}
}
Layout.preferredWidth: parent.width / parent.columns
device: root.device
}
ColumnLayout {
@ -110,62 +89,9 @@ DevicePageBase {
text: root.stateValue("collection")
}
RowLayout {
Layout.fillWidth: true
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
imageSource: "../images/media-skip-backward.svg"
longpressImageSource: "../images/media-seek-backward.svg"
repeat: true
onClicked: {
root.executeAction("skipBack")
}
onLongpressed: {
root.executeAction("fastRewind")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize * 2
Layout.preferredWidth: height
imageSource: root.playbackState.value === "Playing" ? "../images/media-playback-pause.svg" : "../images/media-playback-start.svg"
longpressImageSource: "../images/media-playback-stop.svg"
longpressEnabled: root.playbackState.value !== "Stopped"
onClicked: {
if (root.playbackState.value === "Playing") {
root.executeAction("pause")
} else {
root.executeAction("play")
}
}
onLongpressed: {
root.executeAction("stop")
}
}
Item { Layout.fillWidth: true }
ProgressButton {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
imageSource: "../images/media-skip-forward.svg"
longpressImageSource: "../images/media-seek-forward.svg"
repeat: true
onClicked: {
root.executeAction("skipNext")
}
onLongpressed: {
root.executeAction("fastForward")
}
}
Item { Layout.fillWidth: true }
MediaControls {
device: root.device
iconSize: app.iconSize * 2
}
}
}
@ -279,6 +205,7 @@ DevicePageBase {
Item {
Layout.fillWidth: true
Layout.preferredHeight: 2
visible: root.deviceClass.browsable
Rectangle {
height: parent.height
width: parent.width / 2

View File

@ -13,13 +13,7 @@ MainPageTile {
disconnected: devicesSubProxyConnectables.count > 0
batteryCritical: devicesSubProxyBattery.count > 0
backgroundImage: currentDevice.deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ?
currentDevice.states.getState(currentDevice.deviceClass.stateTypes.findByName("artwork").id).value : ""
property int currentDeviceIndex: 0
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
// readonly property State currentBackgroundState: currentDevice.state
backgroundImage: inlineControlLoader.item && inlineControlLoader.item.hasOwnProperty("backgroundImage") ? inlineControlLoader.item.backgroundImage : ""
onClicked: {
var page;
@ -55,6 +49,9 @@ MainPageTile {
case "powersocket":
page = "PowerSocketsDeviceListPage.qml";
break;
case "media":
page = "MediaDeviceListPage.qml";
break;
default:
page = "GenericDeviceListPage.qml"
}
@ -84,6 +81,9 @@ MainPageTile {
filterBatteryCritical: true
}
property int currentDeviceIndex: 0
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
contentItem: Loader {
id: inlineControlLoader
anchors {
@ -105,7 +105,6 @@ MainPageTile {
// return labelComponent;
case "light":
case "media":
case "garagegate":
case "blind":
case "extendedblind":
@ -115,12 +114,66 @@ MainPageTile {
case "extendedawning":
case "powersocket":
return buttonComponent
case "media":
return mediaControlComponent
default:
console.warn("DevicesPageDelegate, inlineControl: Unhandled interface", model.name)
}
}
}
Component {
id: mediaControlComponent
RowLayout {
id: inlineMediaControl
property string backgroundImage: artworkState ? artworkState.value : ""
property int currentDeviceIndex: 0
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
readonly property StateType playbackStateType: currentDevice.deviceClass.stateTypes.findByName("playbackStatus")
readonly property State playbackState: currentDevice.states.getState(playbackStateType.id)
readonly property StateType artworkStateType: currentDevice.deviceClass.stateTypes.findByName("artwork")
readonly property State artworkState: artworkStateType ? currentDevice.states.getState(artworkStateType.id) : null
Component.onCompleted: {
for (var i = 0; i < devicesProxy.count; i++) {
var d = devicesProxy.get(i);
var st = d.deviceClass.stateTypes.findByName("playbackStatus")
var s = d.states.getState(st.id)
s.valueChanged.connect(function() {updateTile()})
}
updateTile();
}
function updateTile() {
var playingIndex = -1;
var pausedIndex = -1;
for (var i = 0; i < devicesProxy.count; i++) {
var d = devicesProxy.get(i);
var st = d.deviceClass.stateTypes.findByName("playbackStatus");
if (!st) continue;
var s = d.states.getState(st.id);
if (playingIndex === -1 && s.value === "Playing") {
playingIndex = i;
} else if (pausedIndex === -1 && s.value === "Paused") {
pausedIndex = -i;
}
}
if (playingIndex !== -1) {
currentDeviceIndex = playingIndex;
} else if (pausedIndex !== -1) {
currentDeviceIndex = pausedIndex;
}
}
MediaControls {
iconSize: app.iconSize * 1.2
device: inlineMediaControl.currentDevice
}
}
}
Component {
id: buttonComponent
ColumnLayout {