Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #12314 from jimporter/music-notification-controls
Browse files Browse the repository at this point in the history
Bug 902981 - [Music] [User Story] Provide access to music controls in the notifications tray
  • Loading branch information
Jim Porter committed Oct 25, 2013
2 parents ed6b6eb + 08e4b5b commit 3604819
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 19 deletions.
5 changes: 5 additions & 0 deletions apps/system/index.html
Expand Up @@ -446,6 +446,11 @@
<div class="artist">
</div>
</div>
<div id="media-playback-controls">
<button class="previous"></button>
<button class="play-pause"></button>
<button class="next"></button>
</div>
</div>
<!-- App Install Manager -->
<div id="install-manager-notification-container">
Expand Down
48 changes: 45 additions & 3 deletions apps/system/js/media_playback.js
Expand Up @@ -4,18 +4,23 @@ var MediaPlayback = {
init: function mp_init() {
this.container = document.getElementById('media-playback-container');
this.nowPlaying = document.getElementById('media-playback-nowplaying');

this.icon = this.container.querySelector('.icon');
this.trackTitle = this.container.querySelector('.title');
this.trackArtist = this.container.querySelector('.artist');
this.albumArt = this.container.querySelector('.albumart');

this.previousButton = this.container.querySelector('.previous');
this.playPauseButton = this.container.querySelector('.play-pause');
this.nextButton = this.container.querySelector('.next');

var self = this;
window.navigator.mozSetMessageHandler('connection', function(request) {
if (request.keyword !== 'mediacomms')
return;

var port = request.port;
port.onmessage = function(event) {
self._port = request.port;
self._port.onmessage = function(event) {
var message = event.data;
switch (message.type) {
case 'appinfo':
Expand All @@ -32,6 +37,7 @@ var MediaPlayback = {
});

this.nowPlaying.addEventListener('click', this.openMediaApp.bind(this));
this.container.addEventListener('click', this);

// Listen for when the music app is terminated. We know which app to look
// for because we got it from the "appinfo" message. Then we hide the Now
Expand Down Expand Up @@ -75,7 +81,19 @@ var MediaPlayback = {
},

updatePlaybackStatus: function mp_updatePlaybackStatus(status) {
this.container.hidden = (status.playStatus === 'STOPPED');
switch (status.playStatus) {
case 'PLAYING':
this.container.hidden = false;
this.playPauseButton.classList.remove('is-paused');
break;
case 'PAUSED':
this.container.hidden = false;
this.playPauseButton.classList.add('is-paused');
break;
case 'STOPPED':
this.container.hidden = true;
break;
}
},

openMediaApp: function mp_openMediaApp(event) {
Expand All @@ -87,6 +105,30 @@ var MediaPlayback = {
});
window.dispatchEvent(evt);
}
},

handleEvent: function mp_handleEvent(event) {
if (!this._port)
return;

var command = null;
switch (event.target) {
case this.previousButton:
command = 'prevtrack';
break;
case this.playPauseButton:
// The play/pause indicator will get set once the music app replies with
// its "mode" message, but this will make us appear speedier.
this.playPauseButton.classList.toggle('is-paused');
command = 'playpause';
break;
case this.nextButton:
command = 'nexttrack';
break;
}

if (command)
this._port.postMessage({command: command});
}
};

Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 50 additions & 3 deletions apps/system/style/media_playback/media_playback.css
@@ -1,6 +1,10 @@
#media-playback-container {
height: auto;
}

#media-playback-nowplaying {
height: 4rem;
margin: 1rem 1.5rem 0rem 1rem;
margin: 1rem 1rem 0rem 1rem;
}

#media-playback-nowplaying > .icon {
Expand All @@ -16,8 +20,9 @@

#media-playback-nowplaying > .albumart {
float: right;
width: 4rem;
height: 4rem;
width: 3rem;
height: 3rem;
margin: 0.5rem 0;
background-size: 4rem;
}

Expand All @@ -42,3 +47,45 @@
font-weight: 400;
color: #bfbfbf;
}

#media-playback-controls {
height: 4.5rem;
overflow: hidden;
text-align: center;
font-size: 0; /* ensure there's no space between the buttons */
}

#media-playback-controls > button {
height: 100%;
padding: 0;
border-width: 0;
border-radius: 0;
overflow: hidden;
background-position: center center;
background-repeat: no-repeat;
background-size: 3rem;
background-color: transparent;
}

#media-playback-controls > button:active {
background-color: #00aacc;
}

#media-playback-controls > .previous {
width: 9.7rem;
background-image: url("./images/icon-previous.png");
}

#media-playback-controls > .play-pause {
width: 10.6rem;
background-image: url("./images/icon-pause.png");
}

#media-playback-controls > .play-pause.is-paused {
background-image: url("./images/icon-play.png");
}

#media-playback-controls > .next {
width: 9.7rem;
background-image: url("./images/icon-next.png");
}
24 changes: 22 additions & 2 deletions apps/system/test/marionette/fakemusic/js/comms.js
Expand Up @@ -7,6 +7,20 @@ var FakeMusicComms = {
_ports: null,
_queuedMessages: [],

commands: {
playpause: function(event) {
FakeMusic.playpause();
},

prevtrack: function(event) {
FakeMusic.previous();
},

nexttrack: function(event) {
FakeMusic.next();
}
},

init: function() {
this._sendMessage('appinfo', {
origin: window.location.origin,
Expand All @@ -19,8 +33,14 @@ var FakeMusicComms = {

app.connect('mediacomms').then(function(ports) {
self._ports = ports;
self._queuedMessages.forEach(function(message) {
self._ports.forEach(function(port) {
self._ports.forEach(function(port) {
port.onmessage = function(event) {
var message = event.data;
if (message.command in self.commands)
self.commands[message.command](event);
};

self._queuedMessages.forEach(function(message) {
port.postMessage(message);
});
});
Expand Down
3 changes: 3 additions & 0 deletions apps/system/test/marionette/fakemusic/js/fakemusic.js
Expand Up @@ -30,11 +30,13 @@ var FakeMusic = {

play: function() {
this._mode = 'playing';
document.getElementById('play-pause').classList.remove('is-paused');
FakeMusicComms.notifyStatusChanged({playStatus: 'PLAYING'});
},

pause: function() {
this._mode = 'paused';
document.getElementById('play-pause').classList.add('is-paused');
FakeMusicComms.notifyStatusChanged({playStatus: 'PAUSED'});
},

Expand All @@ -47,6 +49,7 @@ var FakeMusic = {

stop: function() {
this._mode = 'stopped';
document.getElementById('play-pause').classList.remove('is-paused');
FakeMusicComms.notifyStatusChanged({playStatus: 'STOPPED'});
},

Expand Down
16 changes: 8 additions & 8 deletions apps/system/test/marionette/lib/fake_music.js
Expand Up @@ -10,8 +10,7 @@ module.exports = FakeMusic;
FakeMusic.Selector = Object.freeze({
albumOneElement: '#album-one',

playElement: '#play',
pauseElement: '#pause',
playPauseElement: '#play-pause',
stopElement: '#stop',
previousTrackElement: '#previous',
nextTrackElement: '#next'
Expand All @@ -24,12 +23,8 @@ FakeMusic.prototype = {
return this.client.findElement(FakeMusic.Selector.albumOneElement);
},

get playElement() {
return this.client.findElement(FakeMusic.Selector.playElement);
},

get pauseElement() {
return this.client.findElement(FakeMusic.Selector.pauseElement);
get playPauseElement() {
return this.client.findElement(FakeMusic.Selector.playPauseElement);
},

get stopElement() {
Expand All @@ -44,6 +39,11 @@ FakeMusic.prototype = {
return this.client.findElement(FakeMusic.Selector.nextTrackElement);
},

get isPlaying() {
var className = this.playPauseElement.getAttribute('class');
return !(/\bis-paused\b/.test(className));
},

launchInBackground: function() {
this.client.apps.launch(this.origin);
this.client.apps.switchToApp(this.origin);
Expand Down
49 changes: 48 additions & 1 deletion apps/system/test/marionette/lib/media_playback.js
@@ -1,5 +1,17 @@
'use strict';

function click(client, element) {
// Make sure the element is displayed first. This seems really unnecessary
// and is probably masking a bug in Marionette, since all the elements we're
// clicking on should be displayed thanks to waitForContainerShown.
if (!element.displayed()) {
this.client.waitFor(function() {
return element.displayed();
});
}
element.click();
}

function MediaPlaybackTest(client) {
this.client = client;
}
Expand All @@ -9,8 +21,13 @@ module.exports = MediaPlaybackTest;
MediaPlaybackTest.Selector = Object.freeze({
containerElement: '#media-playback-container',
nowPlayingElement: '#media-playback-nowplaying',

titleElement: '#media-playback-nowplaying > .title',
artistElement: '#media-playback-nowplaying > .artist'
artistElement: '#media-playback-nowplaying > .artist',

previousTrackElement: '#media-playback-controls > .previous',
playPauseElement: '#media-playback-controls > .play-pause',
nextTrackElement: '#media-playback-controls > .next'
});

MediaPlaybackTest.prototype = {
Expand All @@ -34,6 +51,19 @@ MediaPlaybackTest.prototype = {
return this.client.findElement(MediaPlaybackTest.Selector.artistElement);
},

get previousTrackElement() {
return this.client.findElement(
MediaPlaybackTest.Selector.previousTrackElement);
},

get playPauseElement() {
return this.client.findElement(MediaPlaybackTest.Selector.playPauseElement);
},

get nextTrackElement() {
return this.client.findElement(MediaPlaybackTest.Selector.nextTrackElement);
},

get titleText() {
return this.titleElement.getAttribute('textContent');
},
Expand Down Expand Up @@ -66,5 +96,22 @@ MediaPlaybackTest.prototype = {
return this.artistText === artist &&
this.titleText === title;
}.bind(this));
},

playPause: function() {
click(this.client, this.playPauseElement);
},

get isPlaying() {
var className = this.playPauseElement.getAttribute('class');
return !(/\bis-paused\b/.test(className));
},

previousTrack: function() {
click(this.client, this.previousTrackElement);
},

nextTrack: function() {
click(this.client, this.nextTrackElement);
}
};

0 comments on commit 3604819

Please sign in to comment.