From 9daba1c3bc1cb1ae68486a7aa34f734e54748403 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Thu, 15 Dec 2016 19:44:31 +0900 Subject: move MediaPlayer from CES2017 Change-Id: I302350f213b63d8f07e7020d95795d941228c86d Signed-off-by: Tasuku Suzuki --- .gitignore | 1 + app/MediaPlayer.qml | 241 +++++++++++++++++ app/app.pri | 12 + app/app.pro | 14 + app/config.tests/libhomescreen.cpp | 7 + app/config.tests/libhomescreen.pro | 5 + app/images/AGL_MediaPlayer_AlbumArtwork.svg | 71 +++++ app/images/AGL_MediaPlayer_BackArrow.svg | 56 ++++ app/images/AGL_MediaPlayer_Bluetooth_Active.svg | 84 ++++++ app/images/AGL_MediaPlayer_Bluetooth_Inactive.svg | 59 ++++ app/images/AGL_MediaPlayer_CD_Active.svg | 84 ++++++ app/images/AGL_MediaPlayer_CD_Inactive.svg | 59 ++++ app/images/AGL_MediaPlayer_DividingLine.svg | 60 +++++ app/images/AGL_MediaPlayer_ForwardArrow.svg | 56 ++++ app/images/AGL_MediaPlayer_Loop_Active.svg | 58 ++++ app/images/AGL_MediaPlayer_Loop_Inactive.svg | 58 ++++ app/images/AGL_MediaPlayer_Player_Pause.svg | 67 +++++ app/images/AGL_MediaPlayer_Player_Play.svg | 67 +++++ .../AGL_MediaPlayer_PlaylistToggle_Active.svg | 89 ++++++ .../AGL_MediaPlayer_PlaylistToggle_Inactive.svg | 89 ++++++ app/images/AGL_MediaPlayer_Playlist_Active.svg | 166 ++++++++++++ app/images/AGL_MediaPlayer_Playlist_Inactive.svg | 89 ++++++ app/images/AGL_MediaPlayer_Radio_Active.svg | 299 +++++++++++++++++++++ app/images/AGL_MediaPlayer_Radio_Inactive.svg | 131 +++++++++ app/images/AGL_MediaPlayer_Shuffle_Active.svg | 56 ++++ app/images/AGL_MediaPlayer_Shuffle_Inactive.svg | 56 ++++ app/images/Albums_Active.svg | 155 +++++++++++ app/images/Albums_Inactive.svg | 95 +++++++ app/images/DividingLine.svg | 58 ++++ app/images/GreenLine.svg | 59 ++++ app/images/Music_Active.svg | 63 +++++ app/images/Music_Inactive.svg | 63 +++++ app/images/Podcasts_Active.svg | 77 ++++++ app/images/Podcasts_Inactive.svg | 65 +++++ app/images/Popup_Highlight.svg | 67 +++++ app/images/Popup_PauseIcon.svg | 65 +++++ app/images/Popup_PlayIcon.svg | 65 +++++ app/images/Popup_VerticalLine.svg | 69 +++++ app/images/X.svg | 64 +++++ app/images/images.qrc | 37 +++ app/main.cpp | 76 ++++++ app/mediaplayer.qrc | 5 + app/playlistwithmetadata.cpp | 206 ++++++++++++++ app/playlistwithmetadata.h | 55 ++++ mediaplayer.pro | 3 + package/config.xml | 11 + package/icon.svg | 279 +++++++++++++++++++ package/package.pro | 19 ++ 48 files changed, 3690 insertions(+) create mode 100644 .gitignore create mode 100644 app/MediaPlayer.qml create mode 100644 app/app.pri create mode 100644 app/app.pro create mode 100644 app/config.tests/libhomescreen.cpp create mode 100644 app/config.tests/libhomescreen.pro create mode 100644 app/images/AGL_MediaPlayer_AlbumArtwork.svg create mode 100644 app/images/AGL_MediaPlayer_BackArrow.svg create mode 100644 app/images/AGL_MediaPlayer_Bluetooth_Active.svg create mode 100644 app/images/AGL_MediaPlayer_Bluetooth_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_CD_Active.svg create mode 100644 app/images/AGL_MediaPlayer_CD_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_DividingLine.svg create mode 100644 app/images/AGL_MediaPlayer_ForwardArrow.svg create mode 100644 app/images/AGL_MediaPlayer_Loop_Active.svg create mode 100644 app/images/AGL_MediaPlayer_Loop_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_Player_Pause.svg create mode 100644 app/images/AGL_MediaPlayer_Player_Play.svg create mode 100644 app/images/AGL_MediaPlayer_PlaylistToggle_Active.svg create mode 100644 app/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_Playlist_Active.svg create mode 100644 app/images/AGL_MediaPlayer_Playlist_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_Radio_Active.svg create mode 100644 app/images/AGL_MediaPlayer_Radio_Inactive.svg create mode 100644 app/images/AGL_MediaPlayer_Shuffle_Active.svg create mode 100644 app/images/AGL_MediaPlayer_Shuffle_Inactive.svg create mode 100644 app/images/Albums_Active.svg create mode 100644 app/images/Albums_Inactive.svg create mode 100644 app/images/DividingLine.svg create mode 100644 app/images/GreenLine.svg create mode 100644 app/images/Music_Active.svg create mode 100644 app/images/Music_Inactive.svg create mode 100644 app/images/Podcasts_Active.svg create mode 100644 app/images/Podcasts_Inactive.svg create mode 100644 app/images/Popup_Highlight.svg create mode 100644 app/images/Popup_PauseIcon.svg create mode 100644 app/images/Popup_PlayIcon.svg create mode 100644 app/images/Popup_VerticalLine.svg create mode 100644 app/images/X.svg create mode 100644 app/images/images.qrc create mode 100644 app/main.cpp create mode 100644 app/mediaplayer.qrc create mode 100644 app/playlistwithmetadata.cpp create mode 100644 app/playlistwithmetadata.h create mode 100644 mediaplayer.pro create mode 100644 package/config.xml create mode 100644 package/icon.svg create mode 100644 package/package.pro diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89f64c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pro.* diff --git a/app/MediaPlayer.qml b/app/MediaPlayer.qml new file mode 100644 index 0000000..fbb2c1f --- /dev/null +++ b/app/MediaPlayer.qml @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import QtMultimedia 5.6 +import AGL.Demo.Controls 1.0 +import MediaPlayer 1.0 + +ApplicationWindow { + id: root + + MediaPlayer { + id: player + audioRole: MediaPlayer.MusicRole + autoLoad: true + playlist: playlist + function time2str(value) { + return Qt.formatTime(new Date(value), 'mm:ss') + } + onPositionChanged: slider.value = player.position + Component.onCompleted: console.debug('player.supportedAudioRoles()', player.supportedAudioRoles()) + } + + Playlist { + id: playlist + playbackMode: random.checked ? Playlist.Random : loop.checked ? Playlist.Loop : Playlist.Sequential + + Component.onCompleted: { + playlist.addItems(mediaFiles) + } + } + + + ColumnLayout { + anchors.fill: parent + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredHeight: 3 + clip: true + Image { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: sourceSize.height * width / sourceSize.width + fillMode: Image.PreserveAspectCrop + source: player.metaData.coverArtImage ? player.metaData.coverArtImage : '' + } + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height :307 + Rectangle { + anchors.fill: parent + color: 'black' + opacity: 0.75 + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: root.width * 0.02 + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Row { + spacing: 20 + ToggleButton { + id: random + offImage: './images/AGL_MediaPlayer_Shuffle_Inactive.svg' + onImage: './images/AGL_MediaPlayer_Shuffle_Active.svg' + } + ToggleButton { + id: loop + offImage: './images/AGL_MediaPlayer_Loop_Inactive.svg' + onImage: './images/AGL_MediaPlayer_Loop_Active.svg' + } + } + ColumnLayout { + anchors.fill: parent + Label { + id: title + Layout.alignment: Layout.Center + text: player.metaData.title ? player.metaData.title : '' + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + } + Label { + id: artist + Layout.alignment: Layout.Center + text: player.metaData.author ? player.metaData.author : '' + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + font.pixelSize: title.font.pixelSize * 0.6 + } + } + } + Slider { + id: slider + Layout.fillWidth: true + to: player.duration + Label { + id: position + anchors.left: parent.left + anchors.bottom: parent.top + font.pixelSize: 32 + text: player.time2str(player.position) + } + Label { + id: duration + anchors.right: parent.right + anchors.bottom: parent.top + font.pixelSize: 32 + text: player.time2str(player.duration) + } + onPressedChanged: player.seek(value) + } + RowLayout { + Layout.fillHeight: true +// Image { +// source: './images/AGL_MediaPlayer_Playlist_Inactive.svg' +// } +// Image { +// source: './images/AGL_MediaPlayer_CD_Inactive.svg' +// } + Item { Layout.fillWidth: true } + ImageButton { + offImage: './images/AGL_MediaPlayer_BackArrow.svg' + onClicked: playlist.previous() + } + ImageButton { + id: play + offImage: './images/AGL_MediaPlayer_Player_Play.svg' + onClicked: player.play() + states: [ + State { + when: player.playbackState === MediaPlayer.PlayingState + PropertyChanges { + target: play + offImage: './images/AGL_MediaPlayer_Player_Pause.svg' + onClicked: player.pause() + } + } + ] + } + ImageButton { + offImage: './images/AGL_MediaPlayer_ForwardArrow.svg' + onClicked: playlist.next() + } + + Item { Layout.fillWidth: true } +// Image { +// source: './images/AGL_MediaPlayer_Bluetooth_Inactive.svg' +// } +// Image { +// source: './images/AGL_MediaPlayer_Radio_Inactive.svg' +// } + } + } + } + } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredHeight: 2 + ListView { + anchors.fill: parent + clip: true + header: Label { + x: 50 + text: 'PLAYLIST' + opacity: 0.5 + } + model: PlaylistWithMetadata { + source: playlist + } + currentIndex: playlist.currentIndex + + delegate: MouseArea { + id: delegate + width: ListView.view.width + height: ListView.view.height / 4 + RowLayout { + anchors.fill: parent + anchors.leftMargin: 50 + anchors.rightMargin: 50 + Image { + source: model.coverArt + fillMode: Image.PreserveAspectFit + Layout.preferredWidth: delegate.height + Layout.preferredHeight: delegate.height + } + ColumnLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + text: model.title + } + Label { + Layout.fillWidth: true + text: model.artist + color: '#66FF99' + font.pixelSize: 32 + } + } + Label { + text: player.time2str(model.duration) + color: '#66FF99' + font.pixelSize: 32 + } + } + onClicked: { + playlist.currentIndex = model.index + player.play() + } + } + + highlight: Rectangle { + color: 'white' + opacity: 0.25 + } + } + } + } +} diff --git a/app/app.pri b/app/app.pri new file mode 100644 index 0000000..014646f --- /dev/null +++ b/app/app.pri @@ -0,0 +1,12 @@ +TEMPLATE = app + +load(configure) +qtCompileTest(libhomescreen) + +config_libhomescreen { + CONFIG += link_pkgconfig + PKGCONFIG += homescreen + DEFINES += HAVE_LIBHOMESCREEN +} + +DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app/app.pro b/app/app.pro new file mode 100644 index 0000000..23ecfea --- /dev/null +++ b/app/app.pro @@ -0,0 +1,14 @@ +TARGET = mediaplayer +QT = quickcontrols2 multimedia + +HEADERS += \ + playlistwithmetadata.h + +SOURCES = main.cpp \ + playlistwithmetadata.cpp + +RESOURCES += \ + mediaplayer.qrc \ + images/images.qrc + +include(app.pri) diff --git a/app/config.tests/libhomescreen.cpp b/app/config.tests/libhomescreen.cpp new file mode 100644 index 0000000..e0fd460 --- /dev/null +++ b/app/config.tests/libhomescreen.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc,char **argv) +{ + LibHomeScreen libHomeScreen; + return 0; +} diff --git a/app/config.tests/libhomescreen.pro b/app/config.tests/libhomescreen.pro new file mode 100644 index 0000000..eb4e8f3 --- /dev/null +++ b/app/config.tests/libhomescreen.pro @@ -0,0 +1,5 @@ +SOURCES = libhomescreen.cpp + +CONFIG -= qt +CONFIG += link_pkgconfig +PKGCONFIG += homescreen diff --git a/app/images/AGL_MediaPlayer_AlbumArtwork.svg b/app/images/AGL_MediaPlayer_AlbumArtwork.svg new file mode 100644 index 0000000..b2578c0 --- /dev/null +++ b/app/images/AGL_MediaPlayer_AlbumArtwork.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_BackArrow.svg b/app/images/AGL_MediaPlayer_BackArrow.svg new file mode 100644 index 0000000..c49b519 --- /dev/null +++ b/app/images/AGL_MediaPlayer_BackArrow.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Bluetooth_Active.svg b/app/images/AGL_MediaPlayer_Bluetooth_Active.svg new file mode 100644 index 0000000..7386246 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Bluetooth_Active.svg @@ -0,0 +1,84 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Bluetooth_Inactive.svg b/app/images/AGL_MediaPlayer_Bluetooth_Inactive.svg new file mode 100644 index 0000000..24618b5 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Bluetooth_Inactive.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_CD_Active.svg b/app/images/AGL_MediaPlayer_CD_Active.svg new file mode 100644 index 0000000..a65f309 --- /dev/null +++ b/app/images/AGL_MediaPlayer_CD_Active.svg @@ -0,0 +1,84 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_CD_Inactive.svg b/app/images/AGL_MediaPlayer_CD_Inactive.svg new file mode 100644 index 0000000..adc9127 --- /dev/null +++ b/app/images/AGL_MediaPlayer_CD_Inactive.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_DividingLine.svg b/app/images/AGL_MediaPlayer_DividingLine.svg new file mode 100644 index 0000000..51ed1ee --- /dev/null +++ b/app/images/AGL_MediaPlayer_DividingLine.svg @@ -0,0 +1,60 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_ForwardArrow.svg b/app/images/AGL_MediaPlayer_ForwardArrow.svg new file mode 100644 index 0000000..56576ac --- /dev/null +++ b/app/images/AGL_MediaPlayer_ForwardArrow.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Loop_Active.svg b/app/images/AGL_MediaPlayer_Loop_Active.svg new file mode 100644 index 0000000..fed253d --- /dev/null +++ b/app/images/AGL_MediaPlayer_Loop_Active.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Loop_Inactive.svg b/app/images/AGL_MediaPlayer_Loop_Inactive.svg new file mode 100644 index 0000000..27ae317 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Loop_Inactive.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Player_Pause.svg b/app/images/AGL_MediaPlayer_Player_Pause.svg new file mode 100644 index 0000000..ee55213 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Player_Pause.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Player_Play.svg b/app/images/AGL_MediaPlayer_Player_Play.svg new file mode 100644 index 0000000..c296f8a --- /dev/null +++ b/app/images/AGL_MediaPlayer_Player_Play.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_PlaylistToggle_Active.svg b/app/images/AGL_MediaPlayer_PlaylistToggle_Active.svg new file mode 100644 index 0000000..6697335 --- /dev/null +++ b/app/images/AGL_MediaPlayer_PlaylistToggle_Active.svg @@ -0,0 +1,89 @@ + + + +image/svg+xmlPLAYLIST + \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg b/app/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg new file mode 100644 index 0000000..afbae1e --- /dev/null +++ b/app/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg @@ -0,0 +1,89 @@ + + + +image/svg+xmlPLAYLIST + \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Playlist_Active.svg b/app/images/AGL_MediaPlayer_Playlist_Active.svg new file mode 100644 index 0000000..d378e05 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Playlist_Active.svg @@ -0,0 +1,166 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Playlist_Inactive.svg b/app/images/AGL_MediaPlayer_Playlist_Inactive.svg new file mode 100644 index 0000000..65ee832 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Playlist_Inactive.svg @@ -0,0 +1,89 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Radio_Active.svg b/app/images/AGL_MediaPlayer_Radio_Active.svg new file mode 100644 index 0000000..77a9afa --- /dev/null +++ b/app/images/AGL_MediaPlayer_Radio_Active.svg @@ -0,0 +1,299 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Radio_Inactive.svg b/app/images/AGL_MediaPlayer_Radio_Inactive.svg new file mode 100644 index 0000000..fe00b77 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Radio_Inactive.svg @@ -0,0 +1,131 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Shuffle_Active.svg b/app/images/AGL_MediaPlayer_Shuffle_Active.svg new file mode 100644 index 0000000..c2c04f8 --- /dev/null +++ b/app/images/AGL_MediaPlayer_Shuffle_Active.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/AGL_MediaPlayer_Shuffle_Inactive.svg b/app/images/AGL_MediaPlayer_Shuffle_Inactive.svg new file mode 100644 index 0000000..068370a --- /dev/null +++ b/app/images/AGL_MediaPlayer_Shuffle_Inactive.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Albums_Active.svg b/app/images/Albums_Active.svg new file mode 100644 index 0000000..64086f3 --- /dev/null +++ b/app/images/Albums_Active.svg @@ -0,0 +1,155 @@ + + + +image/svg+xmlALBUMS + \ No newline at end of file diff --git a/app/images/Albums_Inactive.svg b/app/images/Albums_Inactive.svg new file mode 100644 index 0000000..7d6b1d8 --- /dev/null +++ b/app/images/Albums_Inactive.svg @@ -0,0 +1,95 @@ + + + +image/svg+xmlALBUMS + \ No newline at end of file diff --git a/app/images/DividingLine.svg b/app/images/DividingLine.svg new file mode 100644 index 0000000..40354c1 --- /dev/null +++ b/app/images/DividingLine.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/GreenLine.svg b/app/images/GreenLine.svg new file mode 100644 index 0000000..8d3a431 --- /dev/null +++ b/app/images/GreenLine.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Music_Active.svg b/app/images/Music_Active.svg new file mode 100644 index 0000000..22d50d0 --- /dev/null +++ b/app/images/Music_Active.svg @@ -0,0 +1,63 @@ + + + +image/svg+xmlMUSIC + \ No newline at end of file diff --git a/app/images/Music_Inactive.svg b/app/images/Music_Inactive.svg new file mode 100644 index 0000000..c009efa --- /dev/null +++ b/app/images/Music_Inactive.svg @@ -0,0 +1,63 @@ + + + +image/svg+xmlMUSIC + \ No newline at end of file diff --git a/app/images/Podcasts_Active.svg b/app/images/Podcasts_Active.svg new file mode 100644 index 0000000..ab68439 --- /dev/null +++ b/app/images/Podcasts_Active.svg @@ -0,0 +1,77 @@ + + + +image/svg+xmlPODCASTS + \ No newline at end of file diff --git a/app/images/Podcasts_Inactive.svg b/app/images/Podcasts_Inactive.svg new file mode 100644 index 0000000..43d6958 --- /dev/null +++ b/app/images/Podcasts_Inactive.svg @@ -0,0 +1,65 @@ + + + +image/svg+xmlPODCASTS + \ No newline at end of file diff --git a/app/images/Popup_Highlight.svg b/app/images/Popup_Highlight.svg new file mode 100644 index 0000000..24795a4 --- /dev/null +++ b/app/images/Popup_Highlight.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Popup_PauseIcon.svg b/app/images/Popup_PauseIcon.svg new file mode 100644 index 0000000..f1df1a7 --- /dev/null +++ b/app/images/Popup_PauseIcon.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Popup_PlayIcon.svg b/app/images/Popup_PlayIcon.svg new file mode 100644 index 0000000..5053ebb --- /dev/null +++ b/app/images/Popup_PlayIcon.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Popup_VerticalLine.svg b/app/images/Popup_VerticalLine.svg new file mode 100644 index 0000000..b8457ec --- /dev/null +++ b/app/images/Popup_VerticalLine.svg @@ -0,0 +1,69 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/X.svg b/app/images/X.svg new file mode 100644 index 0000000..3afe6f6 --- /dev/null +++ b/app/images/X.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/images.qrc b/app/images/images.qrc new file mode 100644 index 0000000..eb07c3b --- /dev/null +++ b/app/images/images.qrc @@ -0,0 +1,37 @@ + + + AGL_MediaPlayer_Bluetooth_Active.svg + AGL_MediaPlayer_AlbumArtwork.svg + AGL_MediaPlayer_BackArrow.svg + AGL_MediaPlayer_Bluetooth_Inactive.svg + AGL_MediaPlayer_CD_Active.svg + AGL_MediaPlayer_CD_Inactive.svg + AGL_MediaPlayer_DividingLine.svg + AGL_MediaPlayer_ForwardArrow.svg + AGL_MediaPlayer_Loop_Active.svg + AGL_MediaPlayer_Loop_Inactive.svg + AGL_MediaPlayer_Player_Pause.svg + AGL_MediaPlayer_Player_Play.svg + AGL_MediaPlayer_Playlist_Active.svg + AGL_MediaPlayer_Playlist_Inactive.svg + AGL_MediaPlayer_PlaylistToggle_Active.svg + AGL_MediaPlayer_PlaylistToggle_Inactive.svg + AGL_MediaPlayer_Radio_Active.svg + AGL_MediaPlayer_Radio_Inactive.svg + AGL_MediaPlayer_Shuffle_Active.svg + AGL_MediaPlayer_Shuffle_Inactive.svg + Albums_Active.svg + Albums_Inactive.svg + DividingLine.svg + GreenLine.svg + Music_Active.svg + Music_Inactive.svg + Podcasts_Active.svg + Podcasts_Inactive.svg + Popup_Highlight.svg + Popup_PauseIcon.svg + Popup_PlayIcon.svg + Popup_VerticalLine.svg + X.svg + + diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..12654ad --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBHOMESCREEN +#include +#endif + +#include "playlistwithmetadata.h" + +QVariantList readMusicFile(const QString &path) +{ + QVariantList ret; + QDir dir(path); + for (const auto &entry : dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) { + QFileInfo fileInfo(dir.absoluteFilePath(entry)); + if (fileInfo.isDir()) { + ret.append(readMusicFile(fileInfo.absoluteFilePath())); + } else if (fileInfo.isFile()) { + ret.append(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); + } + } + return ret; +} + +int main(int argc, char *argv[]) +{ +#ifdef HAVE_LIBHOMESCREEN + LibHomeScreen libHomeScreen; + + if (!libHomeScreen.renderAppToAreaAllowed(0, 1)) { + qWarning() << "renderAppToAreaAllowed is denied"; + return -1; + } +#endif + + QGuiApplication app(argc, argv); + + QQuickStyle::setStyle("AGL"); + + qmlRegisterType("MediaPlayer", 1, 0, "PlaylistWithMetadata"); + + QVariantList mediaFiles; + for (const auto &music : QStandardPaths::standardLocations(QStandardPaths::MusicLocation)) { + mediaFiles.append(readMusicFile(music)); + } + + QQmlApplicationEngine engine; + QQmlContext *context = engine.rootContext(); + context->setContextProperty("mediaFiles", mediaFiles); + engine.load(QUrl(QStringLiteral("qrc:/MediaPlayer.qml"))); + + return app.exec(); +} + diff --git a/app/mediaplayer.qrc b/app/mediaplayer.qrc new file mode 100644 index 0000000..d3fa02e --- /dev/null +++ b/app/mediaplayer.qrc @@ -0,0 +1,5 @@ + + + MediaPlayer.qml + + diff --git a/app/playlistwithmetadata.cpp b/app/playlistwithmetadata.cpp new file mode 100644 index 0000000..be6d9a1 --- /dev/null +++ b/app/playlistwithmetadata.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "playlistwithmetadata.h" + +#include +#include +#include +#include +#include +#include + +class PlaylistWithMetadata::Private +{ +public: + Private(PlaylistWithMetadata *parent); + + void disconnect(); + void connect(); + +private: + void loadMetadata(int row); + +private: + PlaylistWithMetadata *q; + +public: + QAbstractListModel *source; + QList connections; + QList urls; + QHash title; + QHash artist; + QHash coverArt; + QHash duration; + QHash players; +}; + +PlaylistWithMetadata::Private::Private(PlaylistWithMetadata *parent) + : q(parent) + , source(nullptr) +{ +} + +void PlaylistWithMetadata::Private::disconnect() +{ + if (source) { + for (const auto &connection : connections) + q->disconnect(connection); + connections.clear(); + } +} + +void PlaylistWithMetadata::Private::connect() +{ + if (source) { + connections.append(q->connect(source, &QAbstractListModel::rowsAboutToBeInserted, [&](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + q->beginInsertRows(QModelIndex(), first, last); + })); + connections.append(q->connect(source, &QAbstractListModel::rowsInserted, [&](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + for (int i = first; i <= last; i++) { + loadMetadata(i); + } + q->endInsertRows(); + })); + + int count = source->rowCount(); + if (count > 0) { + q->beginInsertRows(QModelIndex(), 0, count); + for (int i = 0; i < count; i++) { + loadMetadata(i); + } + q->endInsertRows(); + } + } +} + +void PlaylistWithMetadata::Private::loadMetadata(int row) +{ + QUrl url = source->data(source->index(row), Qt::UserRole + 1).toUrl(); + QMediaPlayer *player = new QMediaPlayer(q); + urls.append(url); + players.insert(url, player); + q->connect(player, &QMediaPlayer::mediaStatusChanged, [this, url](QMediaPlayer::MediaStatus mediaStatus) { + switch (mediaStatus) { + case QMediaPlayer::NoMedia: + case QMediaPlayer::LoadedMedia: { + QMediaPlayer *player = players.take(url); + title.insert(url, player->metaData(QMediaMetaData::Title).toString()); + artist.insert(url, player->metaData(QMediaMetaData::ContributingArtist).toString()); + QVariant coverArtImage = player->metaData(QMediaMetaData::CoverArtImage); + if (coverArtImage.type() == QVariant::Image) { + QImage image = coverArtImage.value(); + QByteArray data; + QBuffer buffer(&data); + buffer.open(QBuffer::WriteOnly); + QImageWriter png(&buffer, "png"); + if (png.write(image)) { + buffer.close(); + coverArt.insert(url, QUrl(QStringLiteral("data:image/png;base64,") + data.toBase64())); + } + } + duration.insert(url, player->duration()); + QModelIndex index = q->index(urls.indexOf(url)); + q->dataChanged(index, index, QVector() << TitleRole << ArtistRole << CoverArtRole << DurationRole); + player->deleteLater(); + break; } + default: + break; + } + + }); + player->setMedia(url); +} + +PlaylistWithMetadata::PlaylistWithMetadata(QObject *parent) + : QAbstractListModel(parent) + , d(new Private(this)) +{ +} + +PlaylistWithMetadata::~PlaylistWithMetadata() +{ + delete d; +} + +int PlaylistWithMetadata::rowCount(const QModelIndex &parent) const +{ + int ret = 0; + if (parent.isValid()) + return ret; + if (d->source) + ret = d->source->rowCount(QModelIndex()); + return ret; +} + +QVariant PlaylistWithMetadata::data(const QModelIndex &index, int role) const +{ + QVariant ret; + if (!index.isValid()) + return ret; + int row = index.row(); + if (row < 0 || rowCount() <= row) + return ret; + QUrl url = d->urls.at(row); + switch (role) { + case TitleRole: + ret = d->title.value(url); + break; + case ArtistRole: + ret = d->artist.value(url); + break; + case CoverArtRole: + ret = d->coverArt.value(url); + break; + case SourceRole: + ret = url; + break; + case DurationRole: + ret = d->duration.value(url); + break; + default: + qWarning() << role; + } + + return ret; +} + +QHash PlaylistWithMetadata::roleNames() const +{ + return { + {TitleRole, "title"}, + {ArtistRole, "artist"}, + {CoverArtRole, "coverArt"}, + {SourceRole, "source"}, + {DurationRole, "duration"} + }; +} + +QAbstractListModel *PlaylistWithMetadata::source() const +{ + return d->source; +} + +void PlaylistWithMetadata::setSource(QAbstractListModel *source) +{ + if (d->source == source) return; + d->disconnect(); + d->source = source; + d->connect(); + emit sourceChanged(source); +} diff --git a/app/playlistwithmetadata.h b/app/playlistwithmetadata.h new file mode 100644 index 0000000..74cf6f5 --- /dev/null +++ b/app/playlistwithmetadata.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLAYLISTWITHMETADATA_H +#define PLAYLISTWITHMETADATA_H + +#include + +class PlaylistWithMetadata : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QAbstractListModel *source READ source WRITE setSource NOTIFY sourceChanged) +public: + PlaylistWithMetadata(QObject *parent = nullptr); + ~PlaylistWithMetadata(); + + enum { + TitleRole = Qt::DisplayRole + , ArtistRole = Qt::UserRole + 1 + , CoverArtRole + , SourceRole + , DurationRole + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + QAbstractListModel *source() const; + +public slots: + void setSource(QAbstractListModel *source); + +signals: + void sourceChanged(QAbstractListModel *source); + +private: + class Private; + Private *d; +}; + +#endif // PLAYLISTWITHMETADATA_H diff --git a/mediaplayer.pro b/mediaplayer.pro new file mode 100644 index 0000000..579a952 --- /dev/null +++ b/mediaplayer.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = app package +package.depends += app diff --git a/package/config.xml b/package/config.xml new file mode 100644 index 0000000..736bb0d --- /dev/null +++ b/package/config.xml @@ -0,0 +1,11 @@ + + + MediaPlayer + + + This is a demo application for multimedia + Tasuku Suzuki <tasuku.suzuki@qt.io> + APL 2.0 + + + diff --git a/package/icon.svg b/package/icon.svg new file mode 100644 index 0000000..91661a7 --- /dev/null +++ b/package/icon.svg @@ -0,0 +1,279 @@ + + + +image/svg+xmlMULTIMEDIA + \ No newline at end of file diff --git a/package/package.pro b/package/package.pro new file mode 100644 index 0000000..47996f5 --- /dev/null +++ b/package/package.pro @@ -0,0 +1,19 @@ + +DISTFILES = icon.svg config.xml + +copy_icon.target = $$OUT_PWD/root/icon.svg +copy_icon.depends = $$_PRO_FILE_PWD_/icon.svg +copy_icon.commands = $(COPY_FILE) \"$$replace(copy_icon.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_icon.target, /, $$QMAKE_DIR_SEP)\" +QMAKE_EXTRA_TARGETS += copy_icon +PRE_TARGETDEPS += $$copy_icon.target + +copy_config.target = $$OUT_PWD/root/config.xml +copy_config.depends = $$_PRO_FILE_PWD_/config.xml +copy_config.commands = $(COPY_FILE) \"$$replace(copy_config.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_config.target, /, $$QMAKE_DIR_SEP)\" +QMAKE_EXTRA_TARGETS += copy_config +PRE_TARGETDEPS += $$copy_config.target + +wgt.target = package +wgt.commands = wgtpkg-pack -f -o mediaplayer.wgt root + +QMAKE_EXTRA_TARGETS += wgt -- cgit 1.2.3-korg