@@ -136,6 +136,43 @@ var kArtShapeHolderCode = 'Item { \n\
139+// %1 is anchors.fill
142+var kAudioButtonCode = 'AbstractButton { \n\
143+ id: audioButton; \n\
144+ anchors.fill: %1; \n\
147+ readonly property url source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
149+ anchors.fill: parent; \n\
150+ visible: parent.pressed; \n\
151+ radius: "medium"; \n\
154+ anchors.fill: parent; \n\
155+ anchors.margins: parent.height > units.gu(5) ? units.gu(2) : 0; \n\
157+ name: DashAudioPlayer.playing && AudioUrlComparer.compare(parent.source, DashAudioPlayer.currentSource) ? "media-playback-pause" : "media-playback-start"; \n\
160+ if (AudioUrlComparer.compare(source, DashAudioPlayer.currentSource)) { \n\
161+ if (DashAudioPlayer.playing) { \n\
162+ DashAudioPlayer.pause(); \n\
164+ DashAudioPlayer.play(); \n\
167+ var playlist = (cardData["quickPreviewData"] && cardData["quickPreviewData"]["playlist"]) || null; \n\
168+ DashAudioPlayer.playSource(source, playlist); \n\
171+ onPressAndHold: { \n\
172+ root.pressAndHold(); \n\
139var kOverlayLoaderCode = 'Loader { \n\
176var kOverlayLoaderCode = 'Loader { \n\
140 id: overlayLoader; \n\
177 id: overlayLoader; \n\
141 readonly property real overlayHeight: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
178 readonly property real overlayHeight: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
@@ -316,6 +353,22 @@ var kSummaryLabelCode = 'Label { \n\
316 color: %3; \n\
353 color: %3; \n\
356+// %1 is used as bottom anchor of audio progress bar
357+// %2 is used as left anchor of audio progress bar
358+// %3 is used as text color
359+var kAudioProgressBarCode = 'CardAudioProgress { \n\
360+ id: audioProgressBar; \n\
361+ duration: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["duration"]) || 0; \n\
362+ source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
366+ right: parent.right; \n\
367+ margins: units.gu(1); \n\
319function cardString(template, components) {
372function cardString(template, components) {
320 var code;
373 var code;
@@ -351,7 +404,20 @@ function cardString(template, components) {
351 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
404 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
352 var hasSubtitle = hasTitle && components["subtitle"] || false;
405 var hasSubtitle = hasTitle && components["subtitle"] || false;
353 var hasHeaderRow = hasMascot && hasTitle;
406 var hasHeaderRow = hasMascot && hasTitle;
354− var hasAttributes = hasTitle && components["attributes"]["field"] || false;
407+ var hasAttributes = hasTitle && components["attributes"] && components["attributes"]["field"] || false;
408+ var isAudio = template["quick-preview-type"] === "audio";
411+ // For now we only support audio cards with [optional] art, title, subtitle
412+ // in horizontal mode
413+ // Anything else makes it behave not like an audio card
414+ if (hasSummary) isAudio = false;
415+ if (!isHorizontal) isAudio = false;
416+ if (hasMascot) isAudio = false;
417+ if (hasEmblem) isAudio = false;
418+ if (headerAsOverlay) isAudio = false;
419+ if (hasAttributes) isAudio = false;
356 if (hasBackground) {
422 if (hasBackground) {
357 var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : "";
423 var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : "";
@@ -421,17 +487,22 @@ function cardString(template, components) {
421 topMargin: units.gu(1);\n';
487 topMargin: units.gu(1);\n';
424 var headerLeftAnchor;
491 var headerLeftAnchor;
425 var headerLeftAnchorHasMargin = false;
492 var headerLeftAnchorHasMargin = false;
426 if (isHorizontal && hasArt) {
493 if (isHorizontal && hasArt) {
427 headerLeftAnchor = 'left: artShapeHolder.right; \n\
494 headerLeftAnchor = 'left: artShapeHolder.right; \n\
428 leftMargin: units.gu(1);\n';
495 leftMargin: units.gu(1);\n';
429 headerLeftAnchorHasMargin = true;
496 headerLeftAnchorHasMargin = true;
497+ } else if (isHorizontal && isAudio) {
498+ headerLeftAnchor = 'left: audioButton.right; \n\
499+ leftMargin: units.gu(1);\n';
500+ headerLeftAnchorHasMargin = true;
431 headerLeftAnchor = 'left: parent.left;\n';
502 headerLeftAnchor = 'left: parent.left;\n';
434− var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary;
505+ var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary && !isAudio;
436 if (hasHeaderRow) {
507 if (hasHeaderRow) {
437 code += 'readonly property int headerHeight: row.height;\n'
508 code += 'readonly property int headerHeight: row.height;\n'
@@ -445,6 +516,14 @@ function cardString(template, components) {
446 code += 'readonly property int headerHeight: attributesRow.height;\n'
517 code += 'readonly property int headerHeight: attributesRow.height;\n'
519+ } else if (isAudio) {
521+ code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
522+ } else if (hasTitle) {
523+ code += 'readonly property int headerHeight: titleLabel.height + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
525+ code += 'readonly property int headerHeight: audioProgressBar.height;\n'
448 } else if (hasSubtitle) {
527 } else if (hasSubtitle) {
449 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
528 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
450 } else if (hasTitle) {
529 } else if (hasTitle) {
@@ -632,6 +711,30 @@ function cardString(template, components) {
632 code += mascotShapeCode + mascotCode + titleSubtitleCode;
711 code += mascotShapeCode + mascotCode + titleSubtitleCode;
715+ var audioProgressBarLeftAnchor = 'audioButton.right';
716+ var audioProgressBarBottomAnchor = 'audioButton.bottom';
717+ var audioProgressBarTextColor = 'root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText';
719+ code += kAudioProgressBarCode.arg(audioProgressBarBottomAnchor)
720+ .arg(audioProgressBarLeftAnchor)
721+ .arg(audioProgressBarTextColor);
723+ var audioButtonAnchorsFill;
724+ var audioButtonWidth;
725+ var audioButtonHeight;
727+ audioButtonAnchorsFill = 'artShapeHolder';
728+ audioButtonWidth = 'undefined';
729+ audioButtonHeight = 'undefined';
731+ audioButtonAnchorsFill = 'undefined';
732+ audioButtonWidth = 'height';
733+ audioButtonHeight = '(root.fixedHeaderHeight > 0 ? root.fixedHeaderHeight : headerHeight) + 2 * units.gu(1)';
735+ code += kAudioButtonCode.arg(audioButtonAnchorsFill).arg(audioButtonWidth).arg(audioButtonHeight);
635 if (hasSummary) {
738 if (hasSummary) {
636 var summaryTopAnchor;
739 var summaryTopAnchor;
637 if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
740 if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
@@ -670,6 +773,8 @@ function cardString(template, components) {
670 var implicitHeight = 'implicitHeight: ';
773 var implicitHeight = 'implicitHeight: ';
671 if (hasSummary) {
774 if (hasSummary) {
672 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
775 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
776+ } else if (isAudio) {
777+ implicitHeight += 'audioButton.height;\n';
673 } else if (headerAsOverlay) {
778 } else if (headerAsOverlay) {
674 implicitHeight += 'artShapeHolder.height;\n';
779 implicitHeight += 'artShapeHolder.height;\n';
675 } else if (hasHeaderRow) {
780 } else if (hasHeaderRow) {