Unity 8
MediaServices.qml
1 /*
2  * Copyright (C) 2016 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import QtMultimedia 5.0
19 import Ubuntu.Components 1.3
20 
21 FocusScope {
22  id: root
23  property var sourceData
24  property string context: ""
25  property list<Action> actions
26  property Item rootItem: QuickUtils.rootItem(root)
27  property var maximumEmbeddedHeight
28 
29  property alias header: headerContent.item
30  property alias content: contentLoader.item
31  property alias footer: footerContent.item
32  property alias fullscreen: priv.fullscreen
33 
34  signal close();
35 
36  onFullscreenChanged: {
37  if (!fullscreen) rotationAction.checked = false;
38  }
39 
40  readonly property color backgroundColor: "#1B1B1B"
41  readonly property color iconColor: "#F3F3E7"
42 
43  Rectangle {
44  anchors.fill: parent
45  color: root.backgroundColor
46  opacity: 0.85
47  }
48 
49  OrientationHelper {
50  id: orientationHelper
51  automaticOrientation: fullscreen
52 
53  StateGroup {
54  id: orientationState
55 
56  states: [
57  State {
58  name: "portrait"
59  when: !rotationAction.checked
60  },
61  State {
62  name: "landscape"
63  when: rotationAction.checked
64  }
65  ]
66 
67  transitions: [
68  Transition {
69  to: "landscape"
70  SequentialAnimation {
71  PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
72  PropertyAction { target: orientationHelper; property: "orientationAngle"; value: 90 }
73  PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
74  }
75  },
76  Transition {
77  to: "portrait"
78  SequentialAnimation {
79  PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
80  PropertyAction { target: orientationHelper; property: "orientationAngle"; value: 0 }
81  PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: fullscreen }
82  }
83  }
84  ]
85  }
86 
87  Loader {
88  id: contentLoader
89 
90  sourceComponent: {
91  switch (context) {
92  case "video":
93  return videoComponent;
94  }
95  return undefined;
96  }
97 
98  Component {
99  id: videoComponent
100  VideoPlayer {
101  id: player
102  objectName: "videoPlayer"
103 
104  width: orientationHelper.width
105  height: orientationHelper.height
106  maximumEmbeddedHeight: root.maximumEmbeddedHeight
107  fixedHeight: fullscreen
108  orientation: orientationState.state === "landscape" ? Qt.LandscapeOrientation : Qt.PortraitOrientation
109 
110  playButtonBackgroundColor: root.backgroundColor
111  playButtonIconColor: root.iconColor
112 
113  screenshot: {
114  var screenshotData = root.sourceData["screenshot"];
115  if (screenshotData) return screenshotData;
116 
117  var source = root.sourceData["source"];
118  if (source) {
119  if (source.toString().indexOf("file://") === 0) {
120  return "image://thumbnailer/" + source.toString().substr(7);
121  }
122  }
123  return "";
124  }
125 
126  mediaPlayer: footer ? footer.mediaPlayer : null
127 
128  onClicked: {
129  if (mediaPlayer.availability !== MediaPlayer.Available) return;
130 
131  if (mediaPlayer.playbackState === MediaPlayer.StoppedState) {
132  mediaPlayer.play();
133  } else if (controlHideTimer.running) {
134  if (mediaPlayer.playbackState === MediaPlayer.PlayingState) {
135  mediaPlayer.pause();
136  } else {
137  mediaPlayer.play();
138  }
139  } else {
140  controlHideTimer.restart();
141  }
142  }
143  onPositionChanged: controlHideTimer.restart();
144  }
145  }
146  }
147  }
148 
149  Loader {
150  id: headerContent
151  anchors {
152  top: parent.top
153  left: parent.left
154  right: parent.right
155  topMargin: -headerContent.height
156  }
157  height: units.gu(6)
158  visible: false
159 
160  // eater
161  MouseArea {
162  anchors.fill: parent
163  }
164 
165  Rectangle {
166  anchors.fill: parent
167  color: root.backgroundColor
168  opacity: 0.85
169  visible: headerContent.status === Loader.Ready
170  }
171 
172  active: root.fullscreen
173  sourceComponent: headerComponent
174 
175  Component {
176  id: headerComponent
177 
178  MediaServicesHeader {
179  iconColor: root.iconColor
180  onGoPrevious: {
181  rotationAction.checked = false;
182  root.close();
183  }
184  }
185  }
186 
187  // If we interact with the bar, reset the hide timer.
188  MouseArea {
189  anchors.fill: parent
190  onPressed: {
191  mouse.accepted = false
192  if (controlHideTimer.running) controlHideTimer.restart()
193  }
194  }
195  }
196 
197  Loader {
198  id: footerContent
199  anchors {
200  left: parent.left
201  right: parent.right
202  bottom: parent.bottom
203  bottomMargin: -footerContent.height
204  }
205  height: units.gu(7)
206  visible: false
207 
208  sourceComponent: {
209  switch (context) {
210  case "video":
211  return videoControlsComponent;
212  }
213  return undefined;
214  }
215  // eater
216  MouseArea {
217  anchors.fill: parent
218  }
219 
220  Rectangle {
221  anchors.fill: parent
222  color: root.backgroundColor
223  opacity: 0.85
224  visible: footerContent.status === Loader.Ready
225  }
226 
227  Component {
228  id: videoControlsComponent
229  VideoPlayerControls {
230  id: controls
231  objectName: "videoControls"
232 
233  viewAction: rotationAction.enabled ? rotationAction : fullscreenAction
234  userActions: root.actions
235  iconColor: root.iconColor
236  backgroundColor: root.backgroundColor
237 
238  mediaPlayer.source: {
239  if (!root.sourceData) return "";
240 
241  var source = root.sourceData["source"];
242  if (source.toString().indexOf("video://") === 0) {
243  return source.toString().substr(6);
244  }
245  return source;
246  }
247  mediaPlayer.onPlaybackStateChanged: {
248  controlHideTimer.restart();
249  }
250 
251  Binding {
252  target: priv
253  property: "forceControlsShown"
254 
255  value: (fullscreen && mediaPlayer.playbackState === MediaPlayer.StoppedState) ||
256  mediaPlayer.playbackState === MediaPlayer.PausedState ||
257  interacting
258  }
259 
260  onInteractingChanged: {
261  controlHideTimer.restart();
262  }
263 
264  Binding {
265  target: header
266  property: "title"
267  value: controls.mediaPlayer.metaData.title !== undefined ?
268  controls.mediaPlayer.metaData.title :
269  controls.mediaPlayer.source.toString().replace(/^.*[\\\/]/, '')
270  when: header != null
271  }
272  }
273  }
274 
275  // If we interact with the bar, reset the hide timer.
276  MouseArea {
277  z: 1
278  anchors.fill: parent
279  onPressed: {
280  mouse.accepted = false
281  if (controlHideTimer.running) controlHideTimer.restart()
282  }
283  }
284  }
285 
286  StateGroup {
287  states: [
288  State {
289  name: "controlsShown"
290  when: priv.forceControlsShown || priv.controlTimerActive
291  PropertyChanges {
292  target: footerContent
293  anchors.bottomMargin: 0
294  visible: true
295  }
296  PropertyChanges {
297  target: headerContent
298  anchors.topMargin: 0
299  visible: true
300  }
301  }
302  ]
303 
304  transitions: [
305  Transition {
306  to: "controlsShown"
307  SequentialAnimation {
308  PropertyAction { target: root; property: "clip"; value: true }
309  PropertyAction { property: "visible" }
310  NumberAnimation {
311  properties: "anchors.bottomMargin,anchors.topMargin"
312  duration: UbuntuAnimation.FastDuration
313  }
314  PropertyAction { target: root; property: "clip"; value: false }
315  }
316  },
317  Transition {
318  from: "controlsShown"
319  SequentialAnimation {
320  PropertyAction { target: root; property: "clip"; value: true }
321  NumberAnimation {
322  properties: "anchors.bottomMargin,anchors.topMargin"
323  duration: UbuntuAnimation.FastDuration
324  }
325  PropertyAction { property: "visible" }
326  PropertyAction { target: root; property: "clip"; value: false }
327  }
328  }
329  ]
330  }
331 
332  StateGroup {
333  states: [
334  State {
335  name: "minimized"
336  when: !priv.fullscreen
337  PropertyChanges { target: root; implicitHeight: content ? content.implicitHeight : 0; }
338  },
339  State {
340  name: "fullscreen"
341  when: priv.fullscreen
342  ParentChange { target: root; parent: rootItem; x: 0; y: 0; width: parent.width; }
343  PropertyChanges { target: root; implicitHeight: root.parent ? root.parent.height : 0; }
344  PropertyChanges { target: fullscreenAction; iconName: "view-restore"; }
345  }
346  ]
347 
348  transitions: Transition {
349  ParentAnimation {
350  UbuntuNumberAnimation { properties: "x,y,width,implicitHeight"; duration: UbuntuAnimation.FastDuration }
351  }
352  }
353  }
354 
355  QtObject {
356  id: priv
357 
358  property bool fullscreen: false
359  property alias controlTimerActive: controlHideTimer.running
360  property bool forceControlsShown: false
361  }
362 
363  Timer {
364  id: controlHideTimer
365  objectName: "controlHideTimer"
366  interval: 4000
367  running: false
368  }
369 
370  Action {
371  id: fullscreenAction
372  enabled: rotationAction.enabled === false
373  iconName: "view-fullscreen"
374 
375  onTriggered: priv.fullscreen = !priv.fullscreen
376  }
377 
378  Action {
379  id: rotationAction
380  enabled: root.fullscreen === true
381  iconName: "view-rotate"
382 
383  property bool checked: false
384  onTriggered: checked = !checked
385  }
386 }