Index: chrome/browser/resources/media_router/elements/route_details/route_details.js |
diff --git a/chrome/browser/resources/media_router/elements/route_details/route_details.js b/chrome/browser/resources/media_router/elements/route_details/route_details.js |
index ae7ebcca5e1d63a66b704f13ce420e12c896879c..e343d6aa72b9f826c9ebf0c94e2b01e51ec01641 100644 |
--- a/chrome/browser/resources/media_router/elements/route_details/route_details.js |
+++ b/chrome/browser/resources/media_router/elements/route_details/route_details.js |
@@ -24,7 +24,36 @@ Polymer({ |
changeRouteSourceAvailable_: { |
type: Boolean, |
computed: 'computeChangeRouteSourceAvailable_(route, sink,' + |
- 'isAnySinkCurrentlyLaunching, shownCastModeValue)', |
+ 'isAnySinkCurrentlyLaunching, shownCastModeValue)', |
+ }, |
+ |
+ /** |
+ * The current time displayed in milliseconds. |
+ * @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_: { |
+ 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_: { |
+ type: Boolean, |
+ value: false |
}, |
/** |
@@ -42,7 +71,17 @@ Polymer({ |
*/ |
route: { |
type: Object, |
- observer: 'maybeLoadCustomController_', |
+ }, |
+ |
+ |
+ /** |
+ * The status of the media route shown. |
+ * @type {?media_router.RouteStatus} |
+ */ |
+ routeStatus: { |
+ type: Object, |
+ observer: 'onRouteStatusChange_', |
+ value: null |
}, |
/** |
@@ -65,14 +104,21 @@ Polymer({ |
}, |
/** |
- * Whether the custom controller should be hidden. |
- * A custom controller is shown iff |route| specifies customControllerPath |
- * and the view can be loaded. |
- * @private {boolean} |
+ * The value of the time slider, between 0 and 100. |
+ * @type {number} |
*/ |
- isCustomControllerHidden_: { |
- type: Boolean, |
- value: true, |
+ timeSliderValue_: { |
+ type: Number, |
+ value: 0 |
+ }, |
+ |
+ /** |
+ * The value of the volume slider, between 0 and 100. |
+ * @type {number} |
+ */ |
+ volumeSliderValue_: { |
+ type: Number, |
+ value: 0 |
}, |
}, |
@@ -155,6 +201,209 @@ Polymer({ |
}, |
/** |
+ * @param {number} seekPosition The seek position in milliseconds. |
+ * @param {number} duration The duration in milliseconds. |
+ * @return {number} The seek position as a percentage between 0 and 100. |
+ * |
+ * @private |
+ */ |
+ getSeekPositionPercentage_: function(seekPosition, duration) { |
+ return duration ? (seekPosition / duration * 100) : 0; |
+ }, |
+ |
+ /** |
+ * @param {number} seekPositionPercentage Must be between 0 and 100. |
+ * @param {number} duration The duration in milliseconds. |
+ * @return {number} The seek position in milliseconds. |
+ * |
+ * @private |
+ */ |
+ getSeekPosition_: function(seekPositionPercentage, duration) { |
+ return duration ? (seekPositionPercentage * duration / 100) : 0; |
+ }, |
+ |
+ /** |
+ * 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 milliseconds to a string with |
+ * HH:MM:SS format. |
+ * @param {number} timeInMilliSec Must be positive. Intervals longer than 100 |
+ * hours get truncated silently. |
+ * @return {string} |
+ * |
+ * @private |
+ */ |
+ getFormattedTime_: function(timeInMilliSec) { |
+ if (timeInMilliSec < 0) { |
+ return ''; |
+ } |
+ var hours = Math.floor(timeInMilliSec / 3600 / 1000); |
+ var minutes = Math.floor(timeInMilliSec / 60 / 1000) % 60; |
+ var seconds = Math.floor(timeInMilliSec / 1000) % 60; |
+ var timeParts = [ |
+ ('0' + hours).substr(-2), ('0' + minutes).substr(-2), |
+ ('0' + seconds).substr(-2) |
+ ]; |
+ return timeParts.join(':'); |
+ }, |
+ |
+ /** |
+ * @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' : |
+ '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'); |
+ }, |
+ |
+ /** |
+ * Sends a play or pause command to the browser. |
+ * |
+ * @private |
+ */ |
+ onPlayPause_: function() { |
+ if (this.routeStatus.isPaused) { |
+ this.fire('play-route'); |
+ } else { |
+ this.fire('pause-route'); |
+ } |
+ }, |
+ |
+ /** |
+ * Sends a mute or unmute command to the browser. |
+ * |
+ * @private |
+ */ |
+ onMuteUnmute_: function() { |
+ this.fire('set-route-mute', {mute: !this.routeStatus.isMuted}); |
+ }, |
+ |
+ /** |
+ * Updates seek and volume sliders if the user is not currently dragging on |
+ * them. |
+ * @param {?media_router.RouteStatus} newRouteStatus |
+ * @param {?media_router.RouteStatus} oldRouteStatus |
+ * |
+ * @private |
+ */ |
+ onRouteStatusChange_: function(newRouteStatus, oldRouteStatus) { |
+ if (!newRouteStatus) |
+ return; |
+ |
+ if (!this.ignoreExternalTimeUpdates_) { |
+ this.displayedCurrentTime_ = newRouteStatus.currentTime; |
+ this.timeSliderValue_ = this.getSeekPositionPercentage_( |
+ newRouteStatus.currentTime, newRouteStatus.duration); |
+ } |
+ |
+ if (!this.ignoreExternalVolumeUpdates_) { |
+ this.volumeSliderValue_ = newRouteStatus.volume; |
+ } |
+ }, |
+ |
+ /** |
+ * Called when the user starts dragging the current-time slider. |
+ * @param {!Event} e |
+ * |
+ * @private |
+ */ |
+ onImmediateTimeSliderChange_: function(e) { |
+ 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 clicks on or stops dragging the current-time slider. |
+ * @param {!Event} e |
+ * |
+ * @private |
+ */ |
+ onTimeSliderChange_: function(e) { |
+ this.ignoreExternalTimeUpdates_ = false; |
+ /** @type {{value: number}} */ |
+ var target = e.target; |
+ this.timeSliderValue_ = target.value; |
+ this.displayedCurrentTime_ = this.getSeekPosition_( |
+ this.timeSliderValue_, this.routeStatus.duration); |
+ this.fire('seek-route', {time: this.displayedCurrentTime_}); |
+ }, |
+ |
+ /** |
+ * Called when the user starts dragging the volume slider. |
+ * @param {!Event} e |
+ * |
+ * @private |
+ */ |
+ onImmediateVolumeSliderChange_: function(e) { |
+ this.ignoreExternalVolumeUpdates_ = true; |
+ /** @type {{immediateValue: number}} */ |
+ var target = e.target; |
+ this.volumeSliderValue_ = target.immediateValue; |
+ }, |
+ |
+ /** |
+ * Called when the user clicks on or stops dragging the volume slider. |
+ * @param {!Event} e |
+ * |
+ * @private |
+ */ |
+ onVolumeSliderChange_: function(e) { |
+ this.ignoreExternalVolumeUpdates_ = false; |
+ /** @type {{value: number}} */ |
+ var target = e.target; |
+ this.volumeSliderValue_ = target.value; |
+ this.fire('set-route-volume', {volume: this.volumeSliderValue_}); |
+ }, |
+ |
+ /** |
* Fires a join-route-click event if the current route is joinable, otherwise |
* it fires a change-route-source-click event, which changes the source of the |
* current route. This may cause the current route to be closed and a new |
@@ -174,41 +423,4 @@ Polymer({ |
}); |
} |
}, |
- |
- /** |
- * Loads the custom controller if |route.customControllerPath| exists. |
- * Falls back to the default route details view otherwise, or if load fails. |
- * Updates |activityStatus_| for the default view. |
- * |
- * @private |
- */ |
- maybeLoadCustomController_: function() { |
- this.activityStatus_ = this.route ? |
- loadTimeData.getStringF('castingActivityStatus', |
- this.route.description) : |
- ''; |
- |
- if (!this.route || !this.route.customControllerPath) { |
- this.isCustomControllerHidden_ = true; |
- return; |
- } |
- |
- // Show custom controller |
- var extensionview = this.$['custom-controller']; |
- |
- // Do nothing if the url is the same and the view is not hidden. |
- if (this.route.customControllerPath == extensionview.src && |
- !this.isCustomControllerHidden_) |
- return; |
- |
- var that = this; |
- extensionview.load(this.route.customControllerPath) |
- .then(function() { |
- // Load was successful; show the custom controller. |
- that.isCustomControllerHidden_ = false; |
- }, function() { |
- // Load was unsuccessful; fall back to default view. |
- that.isCustomControllerHidden_ = true; |
- }); |
- }, |
}); |