Chromium Code Reviews| Index: chrome/browser/resources/media_router/elements/route_controls/route_controls.js |
| diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f53a41a5dcd7cf96ee0a7940c7015226e7847141 |
| --- /dev/null |
| +++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js |
| @@ -0,0 +1,345 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +// This Polymer element shows media controls for a route that is currently cast |
| +// to a device. |
| +Polymer({ |
| + is: 'route-controls', |
| + |
| + properties: { |
| + /** |
| + * The current time displayed in seconds. |
| + * @type {number} |
| + */ |
| + displayedCurrentTime_: { |
| + type: Number, |
| + value: 0 |
| + }, |
| + |
| + /** |
| + * Whether updates for the current time from the browser should be ignored. |
| + * Set to true when the user is dragging the time slider. |
| + * @private {boolean} |
| + */ |
| + ignoreExternalTimeUpdates_: { |
|
mark a. foltz
2017/05/03 21:27:00
This might be clearer as isSeeking_, but I don't f
takumif
2017/05/05 18:57:39
Done.
|
| + type: Boolean, |
| + value: false |
| + }, |
| + |
| + /** |
| + * Whether volume updates from the browser should be ignored. Set to true |
| + * when the user is dragging the volume slider. |
| + * @private {boolean} |
| + */ |
| + ignoreExternalVolumeUpdates_: { |
|
mark a. foltz
2017/05/03 21:27:00
isVolumeChanging_
takumif
2017/05/05 18:57:40
Done.
|
| + type: Boolean, |
| + value: false |
| + }, |
| + |
| + /** |
| + * Whether we have received status updates from the browser for the current |
| + * route. When this is false, we allow updating the media title based on the |
| + * route name. |
|
mark a. foltz
2017/05/03 21:26:59
I don't follow how a media status update is relate
takumif
2017/05/05 18:57:39
Removed this variable. I wanted to use route descr
|
| + * @private {boolean} |
| + */ |
| + receivedStatusUpdates_: { |
| + type: Boolean, |
| + value: false |
| + }, |
| + |
| + /** |
| + * The route to show. |
| + * @type {?media_router.Route} |
|
mark a. foltz
2017/05/03 21:26:59
Must this be null on construction?
takumif
2017/05/05 18:57:39
Removed.
|
| + */ |
| + route: { |
| + type: Object, |
| + observer: 'onRouteChange_', |
| + value: null |
| + }, |
| + |
| + /** |
| + * The status of the media route shown. External updates are done using |
| + * updateRouteStatus() to discern from internal updates. |
|
mark a. foltz
2017/05/03 21:27:00
s/status/media status/
Can you explain "internal"
takumif
2017/05/05 18:57:40
Removed. This was to discern updates by status obj
|
| + * @private {?media_router.RouteStatus} |
| + */ |
| + routeStatus_: { |
| + type: Object, |
| + observer: 'onRouteStatusChange_', |
| + value: null |
| + }, |
| + |
| + /** |
| + * The value of the time slider, between 0 and 1. |
|
mark a. foltz
2017/05/03 21:26:59
s/time slider/seek bar/
Shouldn't this be in secon
takumif
2017/05/05 18:57:40
Switched to seconds. Also merged with displayedCur
|
| + * @type {number} |
| + */ |
| + timeSliderValue_: { |
|
mark a. foltz
2017/05/03 21:26:59
currentTimeFraction_ ?
takumif
2017/05/05 18:57:39
Merged with displayedCurrentTime_
|
| + type: Number, |
| + value: 0 |
| + }, |
| + |
| + /** |
| + * The value of the volume slider, between 0 and 1. |
|
mark a. foltz
2017/05/03 21:27:00
s/slider/control/
takumif
2017/05/05 18:57:40
Done.
|
| + * @type {number} |
| + */ |
| + volumeSliderValue_: { |
| + type: Number, |
| + value: 0 |
| + }, |
| + }, |
| + |
| + behaviors: [ |
| + I18nBehavior, |
| + ], |
| + |
| + /** |
| + * Gets the duration formatted in HH:MM:SS format. |
| + * @param {?media_router.RouteStatus} routeStatus |
| + * @return {string} |
| + * |
| + * @private |
| + */ |
| + getDuration_: function(routeStatus) { |
| + return routeStatus ? this.getFormattedTime_(routeStatus.duration) : ''; |
| + }, |
| + |
| + /** |
| + * Converts a number representing an interval of seconds to a string with |
| + * HH:MM:SS format. |
| + * @param {number} timeInSec Must be non-negative. Intervals longer than 100 |
| + * hours get truncated silently. |
| + * @return {string} |
| + * |
| + * @private |
| + */ |
| + getFormattedTime_: function(timeInSec) { |
| + if (timeInSec < 0) { |
| + return ''; |
| + } |
| + var hours = Math.floor(timeInSec / 3600); |
|
mark a. foltz
2017/05/03 21:27:00
s/var/const/
takumif
2017/05/05 18:57:39
The presubmit check tells us to use var instead of
|
| + var minutes = Math.floor(timeInSec / 60) % 60; |
| + var seconds = Math.floor(timeInSec) % 60; |
| + var timeParts = [ |
| + ('0' + hours).substr(-2), ('0' + minutes).substr(-2), |
| + ('0' + seconds).substr(-2) |
| + ]; |
| + return timeParts.join(':'); |
|
mark a. foltz
2017/05/03 21:27:00
This can be combined with the previous line.
takumif
2017/05/05 18:57:39
Done.
|
| + }, |
| + |
| + /** |
| + * @param {?media_router.RouteStatus} routeStatus |
| + * @return {string} The value for the icon attribute of the mute/unmute |
| + * button. |
| + * |
| + * @private |
| + */ |
| + getMuteUnmuteIcon_: function(routeStatus) { |
| + return (routeStatus && routeStatus.isMuted) ? 'av:volume-off' : |
|
mark a. foltz
2017/05/03 21:27:00
Can we initialize the element with a routeStatus t
takumif
2017/05/05 18:57:39
Done.
|
| + 'av:volume-up'; |
| + }, |
| + |
| + /** |
| + * @param {?media_router.RouteStatus} routeStatus |
| + * @return {string} Localized title for the mute/unmute button. |
| + * |
| + * @private |
| + */ |
| + getMuteUnmuteTitle_: function(routeStatus) { |
| + return (routeStatus && routeStatus.isMuted) ? this.i18n('unmuteTitle') : |
| + this.i18n('muteTitle'); |
| + }, |
| + |
| + /** |
| + * @param {?media_router.RouteStatus} routeStatus |
| + * @return {string}The value for the icon attribute of the play/pause button. |
| + * |
| + * @private |
| + */ |
| + getPlayPauseIcon_: function(routeStatus) { |
| + return (routeStatus && routeStatus.isPaused) ? 'av:play-arrow' : 'av:pause'; |
| + }, |
| + |
| + /** |
| + * @param {?media_router.RouteStatus} routeStatus |
| + * @return {string} Localized title for the play/pause button. |
| + * |
| + * @private |
| + */ |
| + getPlayPauseTitle_: function(routeStatus) { |
| + return (routeStatus && routeStatus.isPaused) ? this.i18n('playTitle') : |
| + this.i18n('pauseTitle'); |
| + }, |
| + |
| + /** |
| + * @param {number} seekPositionRatio The ratio with the duration, which must |
| + * be between 0 and 1. |
| + * @param {number} duration The duration in seconds. |
| + * @return {number} The seek position in seconds. |
| + * |
| + * @private |
| + */ |
| + getSeekPosition_: function(seekPositionRatio, duration) { |
|
mark a. foltz
2017/05/03 21:27:00
getSeekTime_(seekPosition, duration) ?
takumif
2017/05/05 18:57:40
Using seconds in the slider, and removing this fun
|
| + return duration ? Math.floor(seekPositionRatio * duration) : 0; |
|
mark a. foltz
2017/05/03 21:27:00
I don't think this extra check is necessary; Math.
takumif
2017/05/05 18:57:39
Removing.
|
| + }, |
| + |
| + /** |
| + * @param {number} seekPosition The seek position in seconds. |
| + * @param {number} duration The duration in seconds. |
| + * @return {number} The seek position as a ratio with the duration, between 0 |
| + * and 1. |
| + * |
| + * @private |
| + */ |
| + getSeekPositionRatio_: function(seekPosition, duration) { |
|
mark a. foltz
2017/05/03 21:27:00
getSeekPosition_(seekTime, duration) ?
takumif
2017/05/05 18:57:39
Removing.
|
| + return duration ? (seekPosition / duration) : 0; |
| + }, |
| + |
| + /** |
| + * Called when the user starts dragging the current-time slider. |
| + * @param {!Event} e |
| + * |
| + * @private |
| + */ |
| + onImmediateTimeSliderChange_: function(e) { |
|
mark a. foltz
2017/05/03 21:26:59
onSeekStart_ ?
takumif
2017/05/05 18:57:39
Done.
|
| + this.ignoreExternalTimeUpdates_ = true; |
| + /** @type {{immediateValue: number}} */ |
| + var target = e.target; |
| + this.timeSliderValue_ = target.immediateValue; |
| + this.displayedCurrentTime_ = this.getSeekPosition_( |
| + this.timeSliderValue_, this.routeStatus_.duration); |
| + }, |
| + |
| + /** |
| + * Called when the user starts dragging the volume slider. |
| + * @param {!Event} e |
| + * |
| + * @private |
| + */ |
| + onImmediateVolumeSliderChange_: function(e) { |
|
mark a. foltz
2017/05/03 21:27:00
onVolumeChangeStart_?
takumif
2017/05/05 18:57:40
Done.
|
| + this.ignoreExternalVolumeUpdates_ = true; |
| + /** @type {{immediateValue: number}} */ |
| + var target = e.target; |
| + this.volumeSliderValue_ = target.immediateValue; |
| + }, |
| + |
| + /** |
| + * Sends a mute or unmute command to the browser. |
|
mark a. foltz
2017/05/03 21:27:00
Called when the user toggles the mute status of th
takumif
2017/05/05 18:57:39
Done.
|
| + * |
| + * @private |
| + */ |
| + onMuteUnmute_: function() { |
| + media_router.browserApi.setCurrentMediaMute(!this.routeStatus_.isMuted); |
| + }, |
| + |
| + /** |
| + * Sends a play or pause command to the browser. |
|
mark a. foltz
2017/05/03 21:27:00
Called when the user toggles between playing and p
takumif
2017/05/05 18:57:39
Done.
|
| + * |
| + * @private |
| + */ |
| + onPlayPause_: function() { |
| + if (this.routeStatus_.isPaused) { |
| + media_router.browserApi.playCurrentMedia(); |
| + } else { |
| + media_router.browserApi.pauseCurrentMedia(); |
| + } |
| + }, |
| + |
| + /** |
| + * Updates the route title shown, if no status updates have been received. |
| + * |
| + * @private |
| + */ |
| + onRouteChange_: function(newRoute) { |
| + if (!newRoute || this.receivedStatusUpdates_) |
| + return; |
| + |
| + // Hide all the elements except for the title showing the route description. |
| + this.routeStatus_ = new media_router.RouteStatus( |
|
mark a. foltz
2017/05/03 21:27:00
Would this be simpler as just a <div> with text co
takumif
2017/05/05 18:57:39
Creating a displayedDescription_ property to set i
|
| + loadTimeData.getStringF('castingActivityStatus', newRoute.description), |
| + '', false, false, false, false, false, false, 0, 0, 0); |
| + }, |
| + |
| + /** |
| + * Called when the route details view is closed. |
| + */ |
| + onRouteDetailsClosed: function() { |
| + this.receivedStatusUpdates_ = false; |
| + this.routeStatus_ = null; |
| + media_router.ui.setRouteControls(null); |
| + }, |
| + |
| + /** |
| + * Updates seek and volume sliders if the user is not currently dragging on |
| + * them. |
| + * @param {?media_router.RouteStatus} newRouteStatus |
| + * |
| + * @private |
| + */ |
| + onRouteStatusChange_: function(newRouteStatus) { |
| + if (!newRouteStatus) |
| + return; |
| + |
| + if (!this.ignoreExternalTimeUpdates_) { |
| + this.displayedCurrentTime_ = newRouteStatus.currentTime; |
| + this.timeSliderValue_ = this.getSeekPositionRatio_( |
| + newRouteStatus.currentTime, newRouteStatus.duration); |
| + } |
| + |
| + if (!this.ignoreExternalVolumeUpdates_) { |
| + this.volumeSliderValue_ = newRouteStatus.volume; |
| + } |
| + }, |
| + |
| + /** |
| + * Called when the user clicks on or stops dragging the current-time slider. |
| + * @param {!Event} e |
| + * |
| + * @private |
| + */ |
| + onTimeSliderChange_: function(e) { |
|
mark a. foltz
2017/05/03 21:27:00
onSeekComplete_
takumif
2017/05/05 18:57:40
Done.
|
| + if (!this.routeStatus_) |
| + return; |
| + |
| + this.ignoreExternalTimeUpdates_ = false; |
| + /** @type {{value: number}} */ |
| + var target = e.target; |
| + this.timeSliderValue_ = target.value; |
| + this.displayedCurrentTime_ = this.getSeekPosition_( |
| + this.timeSliderValue_, this.routeStatus_.duration); |
| + media_router.browserApi.seekCurrentMedia(this.displayedCurrentTime_); |
| + }, |
| + |
| + /** |
| + * Called when the user clicks on or stops dragging the volume slider. |
| + * @param {!Event} e |
| + * |
| + * @private |
| + */ |
| + onVolumeSliderChange_: function(e) { |
|
mark a. foltz
2017/05/03 21:27:00
onVolumeChangeComplete_
takumif
2017/05/05 18:57:39
Done.
|
| + if (!this.routeStatus_) |
| + return; |
| + |
| + this.ignoreExternalVolumeUpdates_ = false; |
| + /** @type {{value: number}} */ |
| + var target = e.target; |
| + this.volumeSliderValue_ = target.value; |
| + media_router.browserApi.setCurrentMediaVolume(this.volumeSliderValue_); |
| + }, |
| + |
| + /** |
| + * Called by Polymer on ready. |
| + */ |
| + ready: function() { |
| + media_router.ui.setRouteControls(this); |
| + }, |
| + |
| + /** |
| + * Updates the route status that is displayed on the controls. |
| + * |
| + * @param {!media_router.RouteStatus} status |
| + */ |
| + updateRouteStatus: function(status) { |
| + this.receivedStatusUpdates_ = true; |
|
mark a. foltz
2017/05/03 21:27:00
Is this the same as testing this.routeStatus_ ?
takumif
2017/05/05 18:57:39
Removed.
|
| + this.routeStatus_ = status; |
| + }, |
| +}); |