git.iko.soy/maciek134/podphoenix
on main help sign in
Add multi-select to Episodes page. Restore 'Add New Podcast' tab. Fix plural forms in Settings. Add support for reordering queue items. Add welcome wizard slide explaining ListItem actions and multi-select.
commit39ef064a7c49b8679990125adbd7cf3fcfa39753 authorNekhelesh Ramananthan <krnekhelesh@gmail.com> dateTue 29 Mar 2016, 00:30 (11 years ago) parent1f570cd2e1f85b
19 files changed+810−593
app/components/TabsList.qml+13−7 app/graphics/listitemactions.png app/podbird.qml+15−2 app/settings/CleanSetting.qml+9−2 app/settings/DownloadSetting.qml+9−2 app/settings/ThemeSetting.qml+9−2 app/themes/Dark.qml+3−3 app/themes/Light.qml+3−3 app/ui/EpisodesPage.qml+203−60 app/ui/EpisodesTab.qml+2 app/ui/FullPlayingView.qml+304 app/ui/NowPlayingPage.qml+16−309 app/ui/PodcastsTab.qml−7 app/ui/Queue.qml+65−50 app/ui/SearchPage.qml+11−2 app/welcomewizard/Slide5.qml+4−5 app/welcomewizard/Slide6.qml → app/welcomewizard/Slide7.qml+10−21 app/welcomewizard/WelcomeWizard.qml+2−1 po/com.mikeasoft.podbird.pot+132−117
app/components/TabsList.qml+13−7
@@ -22,31 +22,37 @@ import Ubuntu.Components 1.3
2222ActionList {
2323 id: tabsList
2424
25− property int currentTab: tabs.selectedTabIndex
26−
2725 children: [
2826 Action {
2927 text: i18n.tr("Episodes")
30− visible: currentTab !== 0
28+ visible: tabs.selectedTab !== episodesTab
3129 onTriggered: {
3230 tabs.selectedTabIndex = 0
3331 }
3432 },
3533
3634 Action {
37− text: i18n.tr("Podcasts")
38− visible: currentTab !== 1
35+ text: i18n.tr("Add New Podcasts")
36+ visible: tabs.selectedTab !== searchTab
3937 onTriggered: {
4038 tabs.selectedTabIndex = 1
4139 }
4240 },
4341
4442 Action {
45− text: i18n.tr("Settings")
46− visible: currentTab !== 2
43+ text: i18n.tr("Podcasts")
44+ visible: tabs.selectedTab !== podcastTab
4745 onTriggered: {
4846 tabs.selectedTabIndex = 2
4947 }
48+ },
49+
50+ Action {
51+ text: i18n.tr("Settings")
52+ visible: tabs.selectedTab !== settingsTab
53+ onTriggered: {
54+ tabs.selectedTabIndex = 3
55+ }
5056 }
5157 ]
5258}
app/graphics/listitemactions.pngnew file

Binary file not shown.

app/podbird.qml+15−2
@@ -54,9 +54,9 @@ MainView {
5454 // RefreshModel function to call refreshModel() function of the tab currently
5555 // visible on application start.
5656 function refreshModels() {
57− if (tabs.selectedTabIndex === 0) {
57+ if (tabs.selectedTab === episodesTab) {
5858 episodesTab.refreshModel()
59− } else if (tabs.selectedTabIndex === 1) {
59+ } else if (tabs.selectedTab === podcastTab) {
6060 podcastPage.item.refreshModel()
6161 }
6262 }
@@ -322,6 +322,19 @@ MainView {
322322 objectName: "episodesTab"
323323 }
324324
325+ Tab {
326+ id: searchTab
327+
328+ // Dynamically load/unload the search tab as required
329+ page: Loader {
330+ parent: searchTab
331+ anchors.left: parent.left
332+ anchors.right: parent.right
333+ anchors.bottom: parent.bottom
334+ source: (tabs.selectedTab === searchTab) ? Qt.resolvedUrl("ui/SearchPage.qml") : ""
335+ }
336+ }
337+
325338 Tab {
326339 id: podcastTab
327340
app/settings/CleanSetting.qml+9−2
@@ -24,7 +24,14 @@ Page {
2424 id: cleanSettingPage
2525
2626 visible: false
27− title: i18n.tr("Delete older than")
27+
28+ header: PageHeader {
29+ title: i18n.tr("Delete older than")
30+ flickable: cleanup
31+ StyleHints {
32+ backgroundColor: podbird.appTheme.background
33+ }
34+ }
2835
2936 ListModel {
3037 id: cleanupModel
@@ -39,7 +46,7 @@ Page {
3946 }
4047 }
4148
42− UbuntuListView {
49+ ListView {
4350 id: cleanup
4451
4552 currentIndex: -1
app/settings/DownloadSetting.qml+9−2
@@ -24,7 +24,14 @@ Page {
2424 id: downloadSetting
2525
2626 visible: false
27− title: i18n.tr("Download at most")
27+
28+ header: PageHeader {
29+ title: i18n.tr("Download at most")
30+ flickable: download
31+ StyleHints {
32+ backgroundColor: podbird.appTheme.background
33+ }
34+ }
2835
2936 ListModel {
3037 id: episodeDownloadNumber
@@ -38,7 +45,7 @@ Page {
3845 }
3946 }
4047
41− UbuntuListView {
48+ ListView {
4249 id: download
4350
4451 currentIndex: -1
app/settings/ThemeSetting.qml+9−2
@@ -24,7 +24,14 @@ Page {
2424 id: themeSettingPage
2525
2626 visible: false
27− title: i18n.tr("Theme")
27+
28+ header: PageHeader {
29+ title: i18n.tr("Theme")
30+ flickable: themes
31+ StyleHints {
32+ backgroundColor: podbird.appTheme.background
33+ }
34+ }
2835
2936 ListModel {
3037 id: themeModel
@@ -37,7 +44,7 @@ Page {
3744 }
3845 }
3946
40− UbuntuListView {
47+ ListView {
4148 id: themes
4249
4350 currentIndex: -1
app/themes/Dark.qml+3−3
@@ -33,9 +33,9 @@ QtObject {
3333 property color baseIcon: "White"
3434
3535 // Button Colors
36− property color positiveActionButton: "#3EB34F" // UbuntuColors.green
37− property color negativeActionButton: "#ED3146" // UbuntuColors.red
38− property color neutralActionButton: "#5D5D5D" // UbuntuColors.coolGrey
36+ property color positiveActionButton: UbuntuColors.green
37+ property color negativeActionButton: UbuntuColors.red
38+ property color neutralActionButton: UbuntuColors.coolGrey
3939
4040 // Bottom Player Bar Colors
4141 property color bottomBarBackground: "#15141A"
app/themes/Light.qml+3−3
@@ -33,9 +33,9 @@ QtObject {
3333 property color baseIcon: UbuntuColors.darkGrey
3434
3535 // Button Colors
36− property color positiveActionButton: "#3EB34F" // UbuntuColors.green
37− property color negativeActionButton: "#ED3146" // UbuntuColors.red
38− property color neutralActionButton: "#5D5D5D" // UbuntuColors.coolGrey
36+ property color positiveActionButton: UbuntuColors.green
37+ property color negativeActionButton: UbuntuColors.red
38+ property color neutralActionButton: UbuntuColors.coolGrey
3939
4040 // Bottom Player Bar Colors
4141 property color bottomBarBackground: "#323435"
app/ui/EpisodesPage.qml+203−60
@@ -29,8 +29,6 @@ Page {
2929 id: episodesPage
3030
3131 visible: false
32− title: i18n.tr("Podcast")
33− flickable: null
3432
3533 property string episodeName
3634 property string episodeId
@@ -47,81 +45,205 @@ Page {
4745 tempGuid = downloader.downloadingGuid
4846 }
4947
50− head.contents: Label {
51− text: title
52− anchors.fill: parent
53− anchors.margins: units.gu(0.5)
54− verticalAlignment: Text.AlignVCenter
48+ header: standardHeader
5549
56− textSize: Label.XLarge
57− fontSizeMode: Text.Fit
50+ PageHeader {
51+ id: standardHeader
52+ title: i18n.tr("Podcast")
53+ flickable: null
5854
59− maximumLineCount: 3
60− minimumPointSize: 8
61− elide: Text.Right
62− wrapMode: Text.WordWrap
55+ StyleHints {
56+ backgroundColor: podbird.appTheme.background
57+ }
58+
59+ trailingActionBar.actions: [
60+ Action {
61+ iconName: "search"
62+ text: i18n.tr("Search Episode")
63+ onTriggered: {
64+ episodesPage.header = searchHeader
65+ searchField.item.forceActiveFocus()
66+ }
67+ },
68+
69+ Action {
70+ text: i18n.tr("Unsubscribe")
71+ iconName: "delete"
72+ onTriggered: {
73+ PopupUtils.open(confirmDeleteDialog, episodesPage);
74+ }
75+ }
76+ ]
6377 }
6478
65− state: "default"
66− states: [
67− PageHeadState {
68− name: "default"
69− head: episodesPage.head
79+ PageHeader {
80+ id: searchHeader
81+ visible: episodesPage.header === searchHeader
82+ flickable: null
83+
84+ leadingActionBar.actions: Action {
85+ iconName: "back"
86+ onTriggered: {
87+ episodeList.forceActiveFocus()
88+ episodesPage.header = standardHeader
89+ episodeList.positionViewAtBeginning()
90+ }
91+ }
92+
93+ StyleHints {
94+ backgroundColor: podbird.appTheme.background
95+ }
96+
97+ contents: Loader {
98+ id: searchField
99+ sourceComponent: episodesPage.header === searchHeader ? searchFieldComponent : undefined
100+ anchors.left: parent ? parent.left : undefined
101+ anchors.right: parent ? parent.right : undefined
102+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
103+ }
104+ }
105+
106+ PageHeader {
107+ id: selectionHeader
108+ visible: episodeList.ViewItems.selectMode
109+ // TRANSLATORS: This is the page title. Keep it short. Otherwise it will just be elided.
110+ title: i18n.tr("%1 item selected", "%1 items selected", episodeList.ViewItems.selectedIndices.length).arg(episodeList.ViewItems.selectedIndices.length)
111+
112+ onVisibleChanged: {
113+ if (visible) {
114+ episodesPage.header = selectionHeader
115+ }
116+ }
117+
118+ StyleHints {
119+ backgroundColor: podbird.appTheme.background
120+ }
121+
122+ leadingActionBar.actions: [
123+ Action {
124+ iconName: "back"
125+ text: i18n.tr("Back")
126+ onTriggered: {
127+ episodeList.closeSelection()
128+ }
129+ }
130+ ]
131+
132+ trailingActionBar {
133+ numberOfSlots: 6
70134 actions: [
71135 Action {
72− iconName: "search"
73− text: i18n.tr("Search Episode")
136+ iconName: "select"
137+ text: i18n.tr("Mark Listened")
138+ enabled: episodeList.ViewItems.selectedIndices.length !== 0
139+ visible: episodesPage.mode !== "listened"
74140 onTriggered: {
75− episodesPage.state = "search"
76− searchField.item.forceActiveFocus()
141+ var db = Podcasts.init();
142+ db.transaction(function (tx) {
143+ for (var i=0; i<episodeList.ViewItems.selectedIndices.length; i++) {
144+ var index = episodeList.ViewItems.selectedIndices[i]
145+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [episodeModel.get(index).guid]);
146+ }
147+ });
148+
149+ refreshModel();
150+ episodeList.closeSelection()
77151 }
78152 },
79153
80154 Action {
81− iconName: "select"
82− text: i18n.tr("Mark all listened")
155+ iconName: "save"
156+ text: i18n.tr("Download episode(s)")
157+ enabled: episodeList.ViewItems.selectedIndices.length !== 0
158+ visible: episodesPage.mode !== "downloaded"
159+
83160 onTriggered: {
84161 var db = Podcasts.init();
85162 db.transaction(function (tx) {
86− tx.executeSql("UPDATE Episode SET listened=1 WHERE podcast=?", [episodeId]);
87− refreshModel();
163+ for (var i=0; i<episodeList.ViewItems.selectedIndices.length; i++) {
164+ var index = episodeList.ViewItems.selectedIndices[i]
165+ if (!episodeModel.get(index).downloadedfile) {
166+ episodeModel.setProperty(index, "queued", 1)
167+ tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [episodeModel.get(index).guid]);
168+ if (episodeModel.get(index).audiourl) {
169+ podbird.downloadEpisode(episodeModel.get(index).image, episodeModel.get(index).name, episodeModel.get(index).guid, episodeModel.get(index).audiourl)
170+ } else {
171+ console.log("[ERROR]: Invalid download url: " + episodeModel.get(index).audiourl)
172+ }
173+ }
174+ }
88175 });
176+
177+ refreshModel();
178+ episodeList.closeSelection()
89179 }
90180 },
91181
92182 Action {
93− text: i18n.tr("Unsubscribe")
94183 iconName: "delete"
184+ text: i18n.tr("Delete episode(s)")
185+ enabled: episodeList.ViewItems.selectedIndices.length !== 0
186+
187+ onTriggered: {
188+ var db = Podcasts.init();
189+ db.transaction(function (tx) {
190+ for (var i=0; i<episodeList.ViewItems.selectedIndices.length; i++) {
191+ var index = episodeList.ViewItems.selectedIndices[i]
192+ if (episodeModel.get(index).downloadedfile) {
193+ fileManager.deleteFile(episodeModel.get(index).downloadedfile);
194+ tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [episodeModel.get(index).guid]);
195+ episodeModel.setProperty(index, "downloadedfile", "")
196+ }
197+ }
198+ });
199+
200+ refreshModel();
201+ episodeList.closeSelection()
202+ }
203+ },
204+
205+ Action {
206+ iconName: "like"
207+ text: i18n.tr("Favourite episode(s)")
208+ enabled: episodeList.ViewItems.selectedIndices.length !== 0
209+
95210 onTriggered: {
96− PopupUtils.open(confirmDeleteDialog, episodesPage);
211+ var db = Podcasts.init();
212+ db.transaction(function (tx) {
213+ for (var i=0; i<episodeList.ViewItems.selectedIndices.length; i++) {
214+ var index = episodeList.ViewItems.selectedIndices[i]
215+ if (!episodeModel.get(index).favourited) {
216+ tx.executeSql("UPDATE Episode SET favourited=1 WHERE guid=?", [episodeModel.get(index).guid])
217+ episodeModel.setProperty(index, "favourited", 1)
218+ }
219+ }
220+ });
221+
222+ refreshModel();
223+ episodeList.closeSelection()
97224 }
98− }
225+ },
99226
100− ]
101− },
227+ Action {
228+ iconName: "add-to-playlist"
229+ text: i18n.tr("Add to queue")
230+ enabled: episodeList.ViewItems.selectedIndices.length !== 0
102231
103− PageHeadState {
104− name: "search"
105− head: episodesPage.head
232+ onTriggered: {
233+ for (var i=0; i<episodeList.ViewItems.selectedIndices.length; i++) {
234+ var index = episodeList.ViewItems.selectedIndices[i]
235+ if (episodeModel.get(index).audiourl) {
236+ var url = episodeModel.get(index).downloadedfile ? "file://" + episodeModel.get(index).downloadedfile : episodeModel.get(index).audiourl
237+ player.addEpisodeToQueue(episodeModel.get(index).guid, episodeModel.get(index).image, episodeModel.get(index).name, episodeModel.get(index).artist, url)
238+ }
239+ }
106240
107− backAction: Action {
108− iconName: "back"
109− onTriggered: {
110− episodeList.forceActiveFocus()
111− episodesPage.state = "default"
112− episodeList.positionViewAtBeginning()
241+ episodeList.closeSelection()
242+ }
113243 }
114− }
115−
116− contents: Loader {
117− id: searchField
118− sourceComponent: episodesPage.state === "search" ? searchFieldComponent : undefined
119− anchors.left: parent ? parent.left : undefined
120− anchors.right: parent ? parent.right : undefined
121− anchors.rightMargin: units.gu(2)
122− }
244+ ]
123245 }
124− ]
246+ }
125247
126248 onVisibleChanged: {
127249 if (!visible) {
@@ -251,8 +373,8 @@ Page {
251373 verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
252374 }
253375
254− sourceComponent: (episodesPage.state === "search" && sortedEpisodeModel.count === 0) ? emptyStateComponent
255− : undefined
376+ sourceComponent: (episodesPage.header === searchHeader && sortedEpisodeModel.count === 0) ? emptyStateComponent
377+ : undefined
256378 }
257379
258380 Component {
@@ -272,13 +394,16 @@ Page {
272394 id: sortedEpisodeModel
273395 model: episodeModel
274396 filter.property: "name"
275− filter.pattern: episodesPage.state === "search" && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
276− : RegExp("", "gi")
397+ filter.pattern: episodesPage.header === searchHeader && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
398+ : RegExp("", "gi")
277399 }
278400
279401 ListView {
280402 id: episodeList
281403
404+ signal clearSelection()
405+ signal closeSelection()
406+
282407 Component.onCompleted: {
283408 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
284409 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
@@ -287,7 +412,7 @@ Page {
287412 flickDeceleration = flickDeceleration * scaleFactor;
288413 }
289414
290− anchors.fill: parent
415+ anchors { fill: parent; topMargin: episodesPage.header.height }
291416 model: sortedEpisodeModel
292417 clip: true
293418
@@ -297,8 +422,8 @@ Page {
297422 id: coverArtContainer
298423
299424 width: episodesPage.width
300− visible: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0
301− height: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0 ? cover.height + units.gu(6) : 0
425+ visible: episodesPage.header !== searchHeader && sortedEpisodeModel.count !== 0
426+ height: episodesPage.header !== searchHeader && sortedEpisodeModel.count !== 0 ? cover.height + units.gu(6) : 0
302427
303428 Image {
304429 id:cover
@@ -548,11 +673,29 @@ Page {
548673
549674 onClicked: {
550675 Haptics.play()
551− if (currentGuid !== model.guid) {
552− currentUrl = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl;
553− player.playEpisode(model.guid, model.image, model.name, model.artist, currentUrl)
676+ if (selectMode) {
677+ selected = !selected
678+ } else {
679+ if (currentGuid !== model.guid) {
680+ currentUrl = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl;
681+ player.playEpisode(model.guid, model.image, model.name, model.artist, currentUrl)
682+ }
554683 }
555684 }
685+
686+ onPressAndHold: {
687+ ListView.view.ViewItems.selectMode = !ListView.view.ViewItems.selectMode
688+ }
689+ }
690+
691+ onClearSelection: {
692+ ViewItems.selectedIndices = []
693+ }
694+
695+ onCloseSelection: {
696+ clearSelection()
697+ ViewItems.selectMode = false
698+ episodesPage.header = standardHeader
556699 }
557700
558701 PullToRefresh {
app/ui/EpisodesTab.qml+2
@@ -124,6 +124,8 @@ Tab {
124124 PageHeader {
125125 id: selectionHeader
126126 visible: episodeList.ViewItems.selectMode
127+ // TRANSLATORS: This is the page title. Keep it short. Otherwise it will just be elided.
128+ title: i18n.tr("%1 item selected", "%1 items selected", episodeList.ViewItems.selectedIndices.length).arg(episodeList.ViewItems.selectedIndices.length)
127129
128130 onVisibleChanged: {
129131 if (visible) {
app/ui/FullPlayingView.qmlnew file+304
@@ -0,0 +1,304 @@
1+/*
2+ * Copyright 2016 Podbird Team
3+ *
4+ * This file is part of Podbird.
5+ *
6+ * Podbird is free software; you can redistribute it and/or modify
7+ * it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation; version 3.
9+ *
10+ * Podbird is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+import QtQuick 2.4
20+import QtMultimedia 5.6
21+import Ubuntu.Components 1.3
22+import "../podcasts.js" as Podcasts
23+import "../components"
24+
25+Item {
26+ id: nowPlayingItem
27+
28+ property bool isLandscapeMode: width > height
29+
30+ // Landscape rule
31+ states: [
32+ State {
33+ name: "landscape"
34+ when: nowPlayingItem.isLandscapeMode
35+
36+ PropertyChanges {
37+ target: blurredBackground
38+ width: nowPlayingItem.width/2.2
39+ height: nowPlayingItem.height
40+ }
41+
42+ AnchorChanges {
43+ target: blurredBackground
44+ anchors {
45+ top: nowPlayingItem.top
46+ left: parent.left
47+ right: undefined
48+ }
49+ }
50+
51+ AnchorChanges {
52+ target: dataContainer
53+ anchors {
54+ top: nowPlayingItem.top
55+ left: blurredBackground.right
56+ right: parent.right
57+ bottom: parent.bottom
58+ }
59+ }
60+ }
61+ ]
62+
63+ BlurredBackground {
64+ id: blurredBackground
65+
66+ anchors.left: parent.left
67+ anchors.top: nowPlayingItem.top
68+ anchors.right: parent.right
69+ height: title.lineCount === 1 ? nowPlayingItem.height/2.3 + units.gu(3)
70+ : nowPlayingItem.height/2.3
71+ art: currentImage
72+
73+ Image {
74+ width: Math.min(nowPlayingItem.width/2, nowPlayingItem.height/2)
75+ height: width
76+ sourceSize.height: width
77+ sourceSize.width: width
78+ source: currentImage
79+ asynchronous: true
80+ anchors.centerIn: parent
81+ }
82+ }
83+
84+ Item {
85+ id: dataContainer
86+
87+ anchors {
88+ top: blurredBackground.bottom
89+ left: parent.left
90+ right: parent.right
91+ bottom: parent.bottom
92+ margins: units.gu(2)
93+ bottomMargin: nowPlayingItem.isLandscapeMode ? units.gu(4) : units.gu(2)
94+ }
95+
96+ Label {
97+ id: title
98+ anchors.left: parent.left
99+ anchors.right: parent.right
100+ anchors.top: parent.top
101+ text: currentName
102+ elide: Text.ElideRight
103+ textSize: Label.Large
104+ maximumLineCount: 2
105+ wrapMode: Text.WordWrap
106+ color: podbird.appTheme.baseText
107+ }
108+
109+ Label {
110+ id: artist
111+ anchors.left: title.left
112+ anchors.right: title.right
113+ anchors.top: title.bottom
114+ anchors.topMargin: units.gu(1)
115+ text: currentArtist
116+ elide: Text.ElideRight
117+ textSize: Label.Small
118+ color: podbird.appTheme.baseSubText
119+ }
120+
121+ Slider {
122+ id: scrubber
123+
124+ anchors {
125+ left: parent.left
126+ right: parent.right
127+ bottom: controls.top
128+ bottomMargin: nowPlayingItem.isLandscapeMode && title.lineCount < 2 ? units.gu(4) : units.gu(2)
129+ }
130+
131+ live: true
132+ minimumValue: 0
133+ maximumValue: player.duration
134+ value: player.position
135+ height: units.gu(2)
136+
137+ onValueChanged: {
138+ if (pressed) {
139+ player.seek(value);
140+ }
141+ }
142+
143+ function formatValue(v) { return Podcasts.formatTime(v/1000); }
144+ StyleHints { foregroundColor: podbird.appTheme.focusText }
145+ }
146+
147+ Connections {
148+ target: player
149+ onPositionChanged: scrubber.value = player.position
150+ }
151+
152+ Label {
153+ id: startTime
154+ textSize: Label.Small
155+ anchors.left: scrubber.left
156+ anchors.top: scrubber.bottom
157+ color: podbird.appTheme.baseText
158+ text: Podcasts.formatTime(player.position / 1000)
159+ }
160+
161+ Label {
162+ id: endTime
163+ textSize: Label.Small
164+ anchors.right: scrubber.right
165+ anchors.top: scrubber.bottom
166+ color: podbird.appTheme.baseText
167+ text: Podcasts.formatTime(player.duration / 1000)
168+ }
169+
170+ Row {
171+ id: controls
172+
173+ anchors.bottom: parent.bottom
174+ anchors.horizontalCenter: parent.horizontalCenter
175+ spacing: units.gu(1)
176+
177+ AbstractButton {
178+ id: mediaBackwardButton
179+ width: units.gu(6)
180+ height: width
181+ anchors.verticalCenter: parent.verticalCenter
182+ enabled: player.playlist.canGoPrevious
183+ opacity: enabled ? 1.0 : 0.4
184+ onClicked: player.playlist.previous()
185+
186+ Icon {
187+ id: mediaBackwardIcon
188+ width: units.gu(3)
189+ height: width
190+ anchors.centerIn: parent
191+ color: podbird.appTheme.baseIcon
192+ name: "media-skip-backward"
193+ }
194+ }
195+
196+ AbstractButton {
197+ id: skipBackwardButton
198+ width: units.gu(6)
199+ height: width
200+ anchors.verticalCenter: parent.verticalCenter
201+ opacity: player.position === 0 ? 0.4 : 1.0
202+ onClicked: {
203+ if (player.position > 0) {
204+ player.seek(player.position - podbird.settings.skipBack * 1000);
205+ }
206+ }
207+
208+ Row {
209+ spacing: units.gu(1)
210+ anchors.centerIn: parent
211+
212+ Label {
213+ // TRANSLATORS: The string shown in the UI is -15s to denote the number of seconds that the podcast playback will skip backward.
214+ // xgettext: no-c-format
215+ text: i18n.tr("-%1s").arg(podbird.settings.skipBack)
216+ textSize: Label.XxSmall
217+ color: podbird.appTheme.baseText
218+ anchors.verticalCenter: skipBackwardIcon.verticalCenter
219+ }
220+
221+ Icon {
222+ id: skipBackwardIcon
223+ width: units.gu(3)
224+ height: width
225+ name: "media-seek-backward"
226+ color: podbird.appTheme.baseIcon
227+ }
228+ }
229+ }
230+
231+ AbstractButton {
232+ id: playButton
233+ width: units.gu(10)
234+ height: width
235+ opacity: playButton.pressed ? 0.4 : 1.0
236+ onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play()
237+
238+ Icon {
239+ id: playIcon
240+ width: units.gu(6)
241+ height: width
242+ anchors.centerIn: parent
243+ color: podbird.appTheme.baseIcon
244+ name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause"
245+ : "media-playback-start"
246+ }
247+ }
248+
249+ AbstractButton {
250+ id: skipForwardButton
251+ width: units.gu(6)
252+ height: width
253+ anchors.verticalCenter: parent.verticalCenter
254+ opacity: player.position === 0 ? 0.4 : 1.0
255+ onClicked: {
256+ if (player.position > 0) {
257+ player.seek(player.position + podbird.settings.skipForward * 1000);
258+ }
259+ }
260+
261+ Row {
262+ spacing: units.gu(1)
263+ anchors.centerIn: parent
264+
265+ Icon {
266+ id: skipForwardIcon
267+ width: units.gu(3)
268+ height: width
269+ name: "media-seek-forward"
270+ color: podbird.appTheme.baseIcon
271+ }
272+
273+ Label {
274+ // TRANSLATORS: The string shown in the UI is +15s to denote the number of seconds that the podcast playback will skip forward.
275+ // xgettext: no-c-format
276+ text: i18n.tr("+%1s").arg(podbird.settings.skipForward)
277+ textSize: Label.XxSmall
278+ color: podbird.appTheme.baseText
279+ anchors.verticalCenter: skipForwardIcon.verticalCenter
280+ }
281+ }
282+ }
283+
284+ AbstractButton {
285+ id: mediaForwardButton
286+ width: units.gu(6)
287+ height: width
288+ anchors.verticalCenter: parent.verticalCenter
289+ enabled: player.playlist.canGoNext
290+ opacity: enabled ? 1.0 : 0.4
291+ onClicked: player.playlist.next()
292+
293+ Icon {
294+ id: mediaForwardIcon
295+ width: units.gu(3)
296+ height: width
297+ anchors.centerIn: parent
298+ color: podbird.appTheme.baseIcon
299+ name: "media-skip-forward"
300+ }
301+ }
302+ }
303+ }
304+}
app/ui/NowPlayingPage.qml+16−309
@@ -37,6 +37,18 @@ Page {
3737 backgroundColor: podbird.appTheme.background
3838 }
3939
40+ leadingActionBar.actions: Action {
41+ iconName: "back"
42+ text: i18n.tr("Back")
43+ onTriggered: {
44+ if (nowPlayingPageSections.selectedIndex === 1 && currentViewLoader.item.ViewItems.dragMode) {
45+ currentViewLoader.item.ViewItems.dragMode = !currentViewLoader.item.ViewItems.dragMode
46+ } else {
47+ mainStack.pop()
48+ }
49+ }
50+ }
51+
4052 trailingActionBar.actions: Action {
4153 iconName: "delete"
4254 visible: nowPlayingPageSections.selectedIndex === 1
@@ -62,314 +74,9 @@ Page {
6274 }
6375 }
6476
65− VisualItemModel {
66− id: tabs
67−
68− Item {
69− id: nowPlayingItem
70−
71− width: tabView.width
72− height: tabView.height
73−
74− property bool isLandscapeMode: nowPlayingPage.width > nowPlayingPage.height
75−
76− // Landscape rule
77− states: [
78− State {
79− name: "landscape"
80− when: nowPlayingItem.isLandscapeMode
81−
82− PropertyChanges {
83− target: blurredBackground
84− width: nowPlayingPage.width/2.2
85− height: nowPlayingPage.height
86− }
87−
88− AnchorChanges {
89− target: blurredBackground
90− anchors {
91− top: nowPlayingItem.top
92− left: parent.left
93− right: undefined
94− }
95− }
96−
97− AnchorChanges {
98− target: dataContainer
99− anchors {
100− top: nowPlayingItem.top
101− left: blurredBackground.right
102− right: parent.right
103− bottom: parent.bottom
104− }
105− }
106− }
107− ]
108−
109− BlurredBackground {
110− id: blurredBackground
111−
112− anchors.left: parent.left
113− anchors.top: nowPlayingItem.top
114− anchors.right: parent.right
115− height: title.lineCount === 1 ? nowPlayingPage.height/2.3 + units.gu(3)
116− : nowPlayingPage.height/2.3
117− art: currentImage
118−
119− Image {
120− width: Math.min(nowPlayingPage.width/2, nowPlayingPage.height/2)
121− height: width
122− sourceSize.height: width
123− sourceSize.width: width
124− source: currentImage
125− asynchronous: true
126− anchors.centerIn: parent
127− }
128− }
129−
130− Item {
131− id: dataContainer
132−
133− anchors {
134− top: blurredBackground.bottom
135− left: parent.left
136− right: parent.right
137− bottom: parent.bottom
138− margins: units.gu(2)
139− bottomMargin: nowPlayingItem.isLandscapeMode ? units.gu(4) : units.gu(2)
140− }
141−
142− Label {
143− id: title
144− anchors.left: parent.left
145− anchors.right: parent.right
146− anchors.top: parent.top
147− text: currentName
148− elide: Text.ElideRight
149− textSize: Label.Large
150− maximumLineCount: 2
151− wrapMode: Text.WordWrap
152− color: podbird.appTheme.baseText
153− }
154−
155− Label {
156− id: artist
157− anchors.left: title.left
158− anchors.right: title.right
159− anchors.top: title.bottom
160− anchors.topMargin: units.gu(1)
161− text: currentArtist
162− elide: Text.ElideRight
163− textSize: Label.Small
164− color: podbird.appTheme.baseSubText
165− }
166−
167− Slider {
168− id: scrubber
169−
170− anchors {
171− left: parent.left
172− right: parent.right
173− bottom: controls.top
174− bottomMargin: nowPlayingItem.isLandscapeMode && title.lineCount < 2 ? units.gu(4) : units.gu(2)
175− }
176−
177− live: true
178− minimumValue: 0
179− maximumValue: player.duration
180− value: player.position
181− height: units.gu(2)
182−
183− onValueChanged: {
184− if (pressed) {
185− player.seek(value);
186− }
187− }
188−
189− function formatValue(v) { return Podcasts.formatTime(v/1000); }
190− StyleHints { foregroundColor: podbird.appTheme.focusText }
191− }
192−
193− Connections {
194− target: player
195− onPositionChanged: scrubber.value = player.position
196− }
197−
198− Label {
199− id: startTime
200− textSize: Label.Small
201− anchors.left: scrubber.left
202− anchors.top: scrubber.bottom
203− color: podbird.appTheme.baseText
204− text: Podcasts.formatTime(player.position / 1000)
205− }
206−
207− Label {
208− id: endTime
209− textSize: Label.Small
210− anchors.right: scrubber.right
211− anchors.top: scrubber.bottom
212− color: podbird.appTheme.baseText
213− text: Podcasts.formatTime(player.duration / 1000)
214− }
215−
216− Row {
217− id: controls
218−
219− anchors.bottom: parent.bottom
220− anchors.horizontalCenter: parent.horizontalCenter
221− spacing: units.gu(1)
222−
223− AbstractButton {
224− id: mediaBackwardButton
225− width: units.gu(6)
226− height: width
227− anchors.verticalCenter: parent.verticalCenter
228− enabled: player.playlist.canGoPrevious
229− opacity: enabled ? 1.0 : 0.4
230− onClicked: player.playlist.previous()
231−
232− Icon {
233− id: mediaBackwardIcon
234− width: units.gu(3)
235− height: width
236− anchors.centerIn: parent
237− color: podbird.appTheme.baseIcon
238− name: "media-skip-backward"
239− }
240− }
241−
242− AbstractButton {
243− id: skipBackwardButton
244− width: units.gu(6)
245− height: width
246− anchors.verticalCenter: parent.verticalCenter
247− opacity: player.position === 0 ? 0.4 : 1.0
248− onClicked: {
249− if (player.position > 0) {
250− player.seek(player.position - podbird.settings.skipBack * 1000);
251− }
252− }
253−
254− Row {
255− spacing: units.gu(1)
256− anchors.centerIn: parent
257−
258− Label {
259− // TRANSLATORS: The string shown in the UI is -15s to denote the number of seconds that the podcast playback will skip backward.
260− // xgettext: no-c-format
261− text: i18n.tr("-%1s").arg(podbird.settings.skipBack)
262− textSize: Label.XxSmall
263− color: podbird.appTheme.baseText
264− anchors.verticalCenter: skipBackwardIcon.verticalCenter
265− }
266−
267− Icon {
268− id: skipBackwardIcon
269− width: units.gu(3)
270− height: width
271− name: "media-seek-backward"
272− color: podbird.appTheme.baseIcon
273− }
274− }
275− }
276−
277− AbstractButton {
278− id: playButton
279− width: units.gu(10)
280− height: width
281− opacity: playButton.pressed ? 0.4 : 1.0
282− onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play()
283−
284− Icon {
285− id: playIcon
286− width: units.gu(6)
287− height: width
288− anchors.centerIn: parent
289− color: podbird.appTheme.baseIcon
290− name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause"
291− : "media-playback-start"
292− }
293− }
294−
295− AbstractButton {
296− id: skipForwardButton
297− width: units.gu(6)
298− height: width
299− anchors.verticalCenter: parent.verticalCenter
300− opacity: player.position === 0 ? 0.4 : 1.0
301− onClicked: {
302− if (player.position > 0) {
303− player.seek(player.position + podbird.settings.skipForward * 1000);
304− }
305− }
306−
307− Row {
308− spacing: units.gu(1)
309− anchors.centerIn: parent
310−
311− Icon {
312− id: skipForwardIcon
313− width: units.gu(3)
314− height: width
315− name: "media-seek-forward"
316− color: podbird.appTheme.baseIcon
317− }
318−
319− Label {
320− // TRANSLATORS: The string shown in the UI is +15s to denote the number of seconds that the podcast playback will skip forward.
321− // xgettext: no-c-format
322− text: i18n.tr("+%1s").arg(podbird.settings.skipForward)
323− textSize: Label.XxSmall
324− color: podbird.appTheme.baseText
325− anchors.verticalCenter: skipForwardIcon.verticalCenter
326− }
327− }
328− }
329−
330− AbstractButton {
331− id: mediaForwardButton
332− width: units.gu(6)
333− height: width
334− anchors.verticalCenter: parent.verticalCenter
335− enabled: player.playlist.canGoNext
336− opacity: enabled ? 1.0 : 0.4
337− onClicked: player.playlist.next()
338−
339− Icon {
340− id: mediaForwardIcon
341− width: units.gu(3)
342− height: width
343− anchors.centerIn: parent
344− color: podbird.appTheme.baseIcon
345− name: "media-skip-forward"
346− }
347− }
348− }
349− }
350− }
351−
352− Queue {
353− width: tabView.width
354− height: tabView.height
355− }
356− }
357−
358− ListView {
359− id: tabView
360− model: tabs
361− interactive: false
362−
363− anchors {
364− top: nowPlayingPage.header.bottom
365− left: parent.left
366− right: parent.right
367− bottom: parent.bottom
368− }
369−
370− orientation: Qt.Horizontal
371− snapMode: ListView.SnapOneItem
372− currentIndex: nowPlayingPageSections.selectedIndex
373− highlightMoveDuration: UbuntuAnimation.SlowDuration
77+ Loader {
78+ id: currentViewLoader
79+ anchors { fill: parent; topMargin: nowPlayingPage.header.height }
80+ source: nowPlayingPageSections.selectedIndex === 0 ? Qt.resolvedUrl("FullPlayingView.qml") : Qt.resolvedUrl("Queue.qml")
37481 }
37582}
app/ui/PodcastsTab.qml−7
@@ -59,13 +59,6 @@ Page {
5959 podcastPage.header = searchHeader
6060 searchField.item.forceActiveFocus()
6161 }
62− },
63− Action {
64− iconName: "add"
65− text: i18n.tr("Add New Podcasts")
66− onTriggered: {
67− mainStack.push(Qt.resolvedUrl("SearchPage.qml"))
68− }
6962 }
7063 ]
7164 }
app/ui/Queue.qml+65−50
@@ -19,70 +19,85 @@
1919import QtQuick 2.4
2020import Ubuntu.Components 1.3
2121import "../podcasts.js" as Podcasts
22−import "../components"
2322
24−Item {
25− id: queuePage
23+ListView {
24+ id: queueList
2625
27− ListView {
28− id: queueList
26+ Component.onCompleted: {
27+ // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
28+ // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
29+ var scaleFactor = units.gridUnit / 8;
30+ maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
31+ flickDeceleration = flickDeceleration * scaleFactor;
32+ }
2933
30− anchors.fill: parent
31− model: player.playlist
34+ model: player.playlist
3235
33− delegate: ListItem {
34− id: listItem
36+ delegate: ListItem {
37+ id: listItem
3538
36− height: layout.height
37− divider.visible: false
39+ height: layout.height
40+ divider.visible: false
3841
39− ListItemLayout {
40− id: layout
42+ ListItemLayout {
43+ id: layout
4144
42− // Grab the metaData for the current index using its unique source url
43− property var metaModel: player.metaForSource(model.source)
45+ // Grab the metaData for the current index using its unique source url
46+ property var metaModel: player.metaForSource(model.source)
4447
45− Image {
46− id: imgFrame
47− width: units.gu(6)
48− height: width
49− source: Qt.resolvedUrl(layout.metaModel.image)
50− sourceSize.height: width
51− sourceSize.width: width
52− SlotsLayout.position: SlotsLayout.First
53− }
48+ Image {
49+ id: imgFrame
50+ width: units.gu(6)
51+ height: width
52+ source: Qt.resolvedUrl(layout.metaModel.image)
53+ sourceSize.height: width
54+ sourceSize.width: width
55+ SlotsLayout.position: SlotsLayout.First
56+ }
5457
55− title.text: layout.metaModel.name
56− // #FIXME: Change this 2 to prevent title eliding when UITK is updated to rev > 1800
57− title.maximumLineCount: 1
58− title.color: player.playlist.currentIndex === index ? podbird.appTheme.focusText
59− : podbird.appTheme.baseText
58+ title.text: layout.metaModel.name
59+ // #FIXME: Change this 2 to prevent title eliding when UITK is updated to rev > 1800
60+ title.maximumLineCount: 1
61+ title.color: player.playlist.currentIndex === index ? podbird.appTheme.focusText
62+ : podbird.appTheme.baseText
6063
61− subtitle.text: layout.metaModel.artist
62− subtitle.color: podbird.appTheme.baseSubText
63− }
64+ subtitle.text: layout.metaModel.artist
65+ subtitle.color: podbird.appTheme.baseSubText
66+ }
6467
65− leadingActions: ListItemActions {
66− actions: [
67− Action {
68− iconName: "delete"
69− onTriggered: {
70− player.playlist.removeItem(index)
71− var source = model.source
72− source = source.toString()
73− Podcasts.removeItemFromQueue(source)
74− }
68+ leadingActions: ListItemActions {
69+ actions: [
70+ Action {
71+ iconName: "delete"
72+ onTriggered: {
73+ player.playlist.removeItem(index)
74+ var source = model.source
75+ source = source.toString()
76+ Podcasts.removeItemFromQueue(source)
7577 }
76− ]
77− }
78−
79− onClicked: {
80− if (player.playlist.currentIndex === index) {
81− player.toggle()
82− } else {
83− player.playlist.currentIndex = index
8478 }
79+ ]
80+ }
81+
82+ onClicked: {
83+ if (player.playlist.currentIndex === index) {
84+ player.toggle()
85+ } else {
86+ player.playlist.currentIndex = index
8587 }
8688 }
89+
90+ onPressAndHold: {
91+ ListView.view.ViewItems.dragMode = !ListView.view.ViewItems.dragMode
92+ }
93+ }
94+
95+ ViewItems.onDragUpdated: {
96+ // Only update the model when the listitem is dropped, not 'live'
97+ if (event.status === ListItemDrag.Moving) {
98+ event.accept = false
99+ } else if (event.status === ListItemDrag.Dropped) {
100+ player.playlist.moveItem(event.from, event.to)
101+ }
87102 }
88103}
app/ui/SearchPage.qml+11−2
@@ -28,6 +28,10 @@ Page {
2828
2929 property var xhr: new XMLHttpRequest;
3030
31+ TabsList {
32+ id: tabsList
33+ }
34+
3135 header: standardHeader
3236
3337 PageHeader {
@@ -40,6 +44,11 @@ Page {
4044 backgroundColor: podbird.appTheme.background
4145 }
4246
47+ leadingActionBar {
48+ numberOfSlots: 0
49+ actions: tabsList.actions
50+ }
51+
4352 trailingActionBar.actions: [
4453 Action {
4554 iconName: "search"
@@ -284,7 +293,7 @@ Page {
284293 }
285294 });
286295 }
287− mainStack.pop();
296+ tabs.selectedTabIndex = 2;
288297 }
289298 }
290299 }
@@ -433,7 +442,7 @@ Page {
433442 Podcasts.subscribe(artist, name, feed, image);
434443 imageDownloader.feed = feed;
435444 imageDownloader.download(image);
436− mainStack.pop();
445+ tabs.selectedTabIndex = 2;
437446 } else {
438447 PopupUtils.open(subscribeFailedDialog);
439448 searchPage.header = addHeader
app/welcomewizard/Slide5.qml+4−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2015 Podbird Team
2+ * Copyright 2016 Podbird Team
33 *
44 * This file is part of Podbird.
55 *
@@ -33,7 +33,7 @@ Component {
3333 horizontalCenter: parent.horizontalCenter
3434 }
3535 fillMode: Image.PreserveAspectFit
36− source: Qt.resolvedUrl("../graphics/support.png")
36+ source: Qt.resolvedUrl("../graphics/listitemactions.png")
3737 }
3838
3939 Label {
@@ -42,7 +42,7 @@ Component {
4242 elide: Text.ElideRight
4343 textSize: Label.XLarge
4444 maximumLineCount: 2
45− text: i18n.tr("Support")
45+ text: i18n.tr("Touch Gestures")
4646 horizontalAlignment: Text.AlignHCenter
4747 width: parent.width
4848 wrapMode: Text.WordWrap
@@ -61,8 +61,7 @@ Component {
6161 wrapMode: Text.WordWrap
6262 horizontalAlignment: Text.AlignHCenter
6363 linkColor: podbird.appTheme.linkText
64− // TRANSLATORS: The %1 points to a url defined in html format <a href=\link\>Link</a>.
65− text: i18n.tr("If you find any bugs or have any feature requests, let us know on our project page at %1").arg("<a href=\"https://launchpad.net/podbird\">launchpad.net</a>")
64+ text: i18n.tr("Episodes can be swiped left to reveal more actions. You can also multi-select them by long-pressing on an episode.")
6665 onLinkActivated: Qt.openUrlExternally(link)
6766 }
6867 }
app/welcomewizard/Slide6.qml → app/welcomewizard/Slide7.qmlrenamed+10−21
@@ -19,9 +19,9 @@
1919import QtQuick 2.4
2020import Ubuntu.Components 1.3
2121
22−// Slide 6
22+// Slide 5
2323Component {
24− id: slide6
24+ id: slide5
2525 Item {
2626 id: slide1Container
2727
@@ -33,7 +33,7 @@ Component {
3333 horizontalCenter: parent.horizontalCenter
3434 }
3535 fillMode: Image.PreserveAspectFit
36− source: Qt.resolvedUrl("../graphics/gift.png")
36+ source: Qt.resolvedUrl("../graphics/support.png")
3737 }
3838
3939 Label {
@@ -42,7 +42,7 @@ Component {
4242 elide: Text.ElideRight
4343 textSize: Label.XLarge
4444 maximumLineCount: 2
45− text: i18n.tr("Enjoy")
45+ text: i18n.tr("Support")
4646 horizontalAlignment: Text.AlignHCenter
4747 width: parent.width
4848 wrapMode: Text.WordWrap
@@ -52,29 +52,18 @@ Component {
5252 id: finalMessage
5353 anchors {
5454 top: introductionText.bottom
55− bottom: continueButton.top
55+ bottom: parent.bottom
5656 left: parent.left
5757 right: parent.right
5858 margins: units.gu(1)
5959 topMargin: units.gu(4)
6060 }
61− horizontalAlignment: Text.AlignHCenter
62− text: i18n.tr("We hope you enjoy using Podbird!")
6361 wrapMode: Text.WordWrap
64− }
65−
66− Button {
67− id: continueButton
68− anchors {
69− bottom: parent.bottom
70− bottomMargin: units.gu(3)
71− horizontalCenter: parent.horizontalCenter
72− }
73− height: units.gu(6)
74− width: parent.width/1.3
75− color: UbuntuColors.green
76− text: i18n.tr("Finish")
77− onClicked: finished()
62+ horizontalAlignment: Text.AlignHCenter
63+ linkColor: podbird.appTheme.linkText
64+ // TRANSLATORS: The %1 points to a url defined in html format <a href=\link\>Link</a>.
65+ text: i18n.tr("If you find any bugs or have any feature requests, let us know on our project page at %1").arg("<a href=\"https://launchpad.net/podbird\">launchpad.net</a>")
66+ onLinkActivated: Qt.openUrlExternally(link)
7867 }
7968 }
8069}
app/welcomewizard/WelcomeWizard.qml+2−1
@@ -36,6 +36,7 @@ Walkthrough {
3636 Slide3{},
3737 Slide4{},
3838 Slide5{},
39− Slide6{}
39+ Slide6{},
40+ Slide7{}
4041 ]
4142}
po/com.mikeasoft.podbird.pot+132−117
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: \n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-03-19 19:13+0530\n"
11+"POT-Creation-Date: 2016-03-19 19:22+0530\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,15 +18,19 @@ msgstr ""
1818"Content-Transfer-Encoding: 8bit\n"
1919"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21−#: ../app/components/TabsList.qml:29 ../app/ui/EpisodesTab.qml:48
21+#: ../app/components/TabsList.qml:27 ../app/ui/EpisodesTab.qml:48
2222msgid "Episodes"
2323msgstr ""
2424
25−#: ../app/components/TabsList.qml:37 ../app/ui/PodcastsTab.qml:42
25+#: ../app/components/TabsList.qml:35 ../app/ui/SearchPage.qml:41
26+msgid "Add New Podcasts"
27+msgstr ""
28+
29+#: ../app/components/TabsList.qml:43 ../app/ui/PodcastsTab.qml:42
2630msgid "Podcasts"
2731msgstr ""
2832
29−#: ../app/components/TabsList.qml:45 ../app/ui/SettingsPage.qml:32
33+#: ../app/components/TabsList.qml:51 ../app/ui/SettingsPage.qml:32
3034msgid "Settings"
3135msgstr ""
3236
@@ -74,30 +78,30 @@ msgstr ""
7478msgid "Source code available on %1"
7579msgstr ""
7680
77−#: ../app/settings/CleanSetting.qml:27
81+#: ../app/settings/CleanSetting.qml:29
7882msgid "Delete older than"
7983msgstr ""
8084
81−#: ../app/settings/CleanSetting.qml:33 ../app/settings/DownloadSetting.qml:33
85+#: ../app/settings/CleanSetting.qml:40 ../app/settings/DownloadSetting.qml:40
8286msgid "Never"
8387msgstr ""
8488
85−#: ../app/settings/CleanSetting.qml:34
89+#: ../app/settings/CleanSetting.qml:41
8690#, qt-format
8791msgid "%1 day"
8892msgid_plural "%1 days"
8993msgstr[0] ""
9094msgstr[1] ""
9195
92−#: ../app/settings/CleanSetting.qml:35 ../app/settings/CleanSetting.qml:36
93−#: ../app/settings/CleanSetting.qml:37
96+#: ../app/settings/CleanSetting.qml:42 ../app/settings/CleanSetting.qml:43
97+#: ../app/settings/CleanSetting.qml:44
9498#, qt-format
9599msgid "%1 month"
96100msgid_plural "%1 months"
97101msgstr[0] ""
98102msgstr[1] ""
99103
100−#: ../app/settings/CleanSetting.qml:38
104+#: ../app/settings/CleanSetting.qml:45
101105#, qt-format
102106msgid "%1 year"
103107msgid_plural "%1 years"
@@ -122,95 +126,124 @@ msgstr ""
122126msgid "Translators"
123127msgstr ""
124128
125−#: ../app/settings/DownloadSetting.qml:27
129+#: ../app/settings/DownloadSetting.qml:29
126130msgid "Download at most"
127131msgstr ""
128132
129−#: ../app/settings/DownloadSetting.qml:34
130−#: ../app/settings/DownloadSetting.qml:35
131−#: ../app/settings/DownloadSetting.qml:36
132−#: ../app/settings/DownloadSetting.qml:37
133+#: ../app/settings/DownloadSetting.qml:41
134+#: ../app/settings/DownloadSetting.qml:42
135+#: ../app/settings/DownloadSetting.qml:43
136+#: ../app/settings/DownloadSetting.qml:44
133137#, qt-format
134138msgid "%1 episode"
135139msgid_plural "%1 episodes"
136140msgstr[0] ""
137141msgstr[1] ""
138142
139−#: ../app/settings/ThemeSetting.qml:27 ../app/ui/SettingsPage.qml:144
143+#: ../app/settings/ThemeSetting.qml:29 ../app/ui/SettingsPage.qml:144
140144msgid "Theme"
141145msgstr ""
142146
143147#. TRANSLATORS: Light Theme
144−#: ../app/settings/ThemeSetting.qml:34 ../app/ui/SettingsPage.qml:145
148+#: ../app/settings/ThemeSetting.qml:41 ../app/ui/SettingsPage.qml:145
145149msgid "Light"
146150msgstr ""
147151
148152#. TRANSLATORS: Dark Theme
149−#: ../app/settings/ThemeSetting.qml:36 ../app/ui/SettingsPage.qml:145
153+#: ../app/settings/ThemeSetting.qml:43 ../app/ui/SettingsPage.qml:145
150154msgid "Dark"
151155msgstr ""
152156
153−#: ../app/ui/EpisodesPage.qml:32
157+#: ../app/ui/EpisodesPage.qml:52
154158msgid "Podcast"
155159msgstr ""
156160
157−#: ../app/ui/EpisodesPage.qml:73 ../app/ui/EpisodesTab.qml:62
161+#: ../app/ui/EpisodesPage.qml:62 ../app/ui/EpisodesTab.qml:62
158162msgid "Search Episode"
159163msgstr ""
160164
161−#: ../app/ui/EpisodesPage.qml:82
162−msgid "Mark all listened"
165+#: ../app/ui/EpisodesPage.qml:70 ../app/ui/EpisodesPage.qml:306
166+#: ../app/ui/SearchPage.qml:275
167+msgid "Unsubscribe"
163168msgstr ""
164169
165−#: ../app/ui/EpisodesPage.qml:93 ../app/ui/EpisodesPage.qml:184
166−#: ../app/ui/SearchPage.qml:266
167−msgid "Unsubscribe"
170+#. TRANSLATORS: This is the page title. Keep it short. Otherwise it will just be elided.
171+#: ../app/ui/EpisodesPage.qml:110 ../app/ui/EpisodesTab.qml:128
172+#, qt-format
173+msgid "%1 item selected"
174+msgid_plural "%1 items selected"
175+msgstr[0] ""
176+msgstr[1] ""
177+
178+#: ../app/ui/EpisodesPage.qml:125 ../app/ui/EpisodesTab.qml:143
179+#: ../app/ui/NowPlayingPage.qml:42
180+msgid "Back"
181+msgstr ""
182+
183+#: ../app/ui/EpisodesPage.qml:137 ../app/ui/EpisodesTab.qml:155
184+msgid "Mark Listened"
185+msgstr ""
186+
187+#: ../app/ui/EpisodesPage.qml:156 ../app/ui/EpisodesTab.qml:173
188+msgid "Download episode(s)"
189+msgstr ""
190+
191+#: ../app/ui/EpisodesPage.qml:184 ../app/ui/EpisodesTab.qml:201
192+msgid "Delete episode(s)"
193+msgstr ""
194+
195+#: ../app/ui/EpisodesPage.qml:207 ../app/ui/EpisodesTab.qml:224
196+msgid "Favourite episode(s)"
197+msgstr ""
198+
199+#: ../app/ui/EpisodesPage.qml:229 ../app/ui/EpisodesTab.qml:270
200+msgid "Add to queue"
168201msgstr ""
169202
170−#: ../app/ui/EpisodesPage.qml:136 ../app/ui/EpisodesTab.qml:120
203+#: ../app/ui/EpisodesPage.qml:258 ../app/ui/EpisodesTab.qml:120
171204msgid "Search episode"
172205msgstr ""
173206
174−#: ../app/ui/EpisodesPage.qml:181
207+#: ../app/ui/EpisodesPage.qml:303
175208msgid "Unsubscribe Confirmation"
176209msgstr ""
177210
178−#: ../app/ui/EpisodesPage.qml:182
211+#: ../app/ui/EpisodesPage.qml:304
179212#, qt-format
180213msgid "Are you sure you want to unsubscribe from <b>%1</b>?"
181214msgstr ""
182215
183−#: ../app/ui/EpisodesPage.qml:201 ../app/ui/SearchPage.qml:121
216+#: ../app/ui/EpisodesPage.qml:323 ../app/ui/SearchPage.qml:130
184217#: ../app/ui/SettingsPage.qml:83 ../app/ui/SettingsPage.qml:118
185218msgid "Cancel"
186219msgstr ""
187220
188−#: ../app/ui/EpisodesPage.qml:223 ../app/ui/EpisodesTab.qml:405
221+#: ../app/ui/EpisodesPage.qml:345 ../app/ui/EpisodesTab.qml:407
189222msgid "Episode Description"
190223msgstr ""
191224
192−#: ../app/ui/EpisodesPage.qml:234 ../app/ui/EpisodesTab.qml:416
193−#: ../app/ui/SearchPage.qml:170
225+#: ../app/ui/EpisodesPage.qml:356 ../app/ui/EpisodesTab.qml:418
226+#: ../app/ui/SearchPage.qml:179
194227msgid "Close"
195228msgstr ""
196229
197−#: ../app/ui/EpisodesPage.qml:262
230+#: ../app/ui/EpisodesPage.qml:384
198231msgid "No episodes found"
199232msgstr ""
200233
201−#: ../app/ui/EpisodesPage.qml:263
234+#: ../app/ui/EpisodesPage.qml:385
202235msgid "No episodes found matching the search term."
203236msgstr ""
204237
205−#: ../app/ui/EpisodesPage.qml:377
238+#: ../app/ui/EpisodesPage.qml:502
206239msgid "Unheard"
207240msgstr ""
208241
209−#: ../app/ui/EpisodesPage.qml:394
242+#: ../app/ui/EpisodesPage.qml:519
210243msgid "Listened"
211244msgstr ""
212245
213−#: ../app/ui/EpisodesPage.qml:413
246+#: ../app/ui/EpisodesPage.qml:538
214247msgid "Downloaded"
215248msgstr ""
216249
@@ -226,195 +259,167 @@ msgstr ""
226259msgid "Favourites"
227260msgstr ""
228261
229−#: ../app/ui/EpisodesTab.qml:141
230−msgid "Back"
231−msgstr ""
232−
233−#: ../app/ui/EpisodesTab.qml:153
234−msgid "Mark Listened"
235−msgstr ""
236−
237−#: ../app/ui/EpisodesTab.qml:171
238−msgid "Download episode(s)"
239−msgstr ""
240−
241−#: ../app/ui/EpisodesTab.qml:199
242−msgid "Delete episode(s)"
243−msgstr ""
244−
245−#: ../app/ui/EpisodesTab.qml:222
246−msgid "Favourite episode(s)"
247−msgstr ""
248−
249−#: ../app/ui/EpisodesTab.qml:245
262+#: ../app/ui/EpisodesTab.qml:247
250263msgid "Unfavourite episode(s)"
251264msgstr ""
252265
253−#: ../app/ui/EpisodesTab.qml:268
254−msgid "Add to queue"
255−msgstr ""
256−
257−#: ../app/ui/EpisodesTab.qml:308
266+#: ../app/ui/EpisodesTab.qml:310
258267msgid "No New Episodes"
259268msgstr ""
260269
261−#: ../app/ui/EpisodesTab.qml:310
270+#: ../app/ui/EpisodesTab.qml:312
262271msgid "No Downloaded Episodes"
263272msgstr ""
264273
265−#: ../app/ui/EpisodesTab.qml:312
274+#: ../app/ui/EpisodesTab.qml:314
266275msgid "No Favourited Episodes"
267276msgstr ""
268277
269−#: ../app/ui/EpisodesTab.qml:314
278+#: ../app/ui/EpisodesTab.qml:316
270279msgid "No Episodes Found"
271280msgstr ""
272281
273−#: ../app/ui/EpisodesTab.qml:320
282+#: ../app/ui/EpisodesTab.qml:322
274283msgid "No more episodes to listen to!"
275284msgstr ""
276285
277−#: ../app/ui/EpisodesTab.qml:322
286+#: ../app/ui/EpisodesTab.qml:324
278287msgid "No episodes have been downloaded for offline listening"
279288msgstr ""
280289
281−#: ../app/ui/EpisodesTab.qml:324
290+#: ../app/ui/EpisodesTab.qml:326
282291msgid "No episodes have been favourited."
283292msgstr ""
284293
285−#: ../app/ui/EpisodesTab.qml:326
294+#: ../app/ui/EpisodesTab.qml:328
286295msgid "No Episodes found matching the search term."
287296msgstr ""
288297
289−#: ../app/ui/EpisodesTab.qml:455
298+#: ../app/ui/EpisodesTab.qml:457
290299msgid "Downloads in progress"
291300msgstr ""
292301
293−#: ../app/ui/EpisodesTab.qml:495
302+#: ../app/ui/EpisodesTab.qml:497
294303msgid "Downloaded episodes"
295304msgstr ""
296305
297−#: ../app/ui/EpisodesTab.qml:505
306+#: ../app/ui/EpisodesTab.qml:507
298307msgid "Today"
299308msgstr ""
300309
301−#: ../app/ui/EpisodesTab.qml:509
310+#: ../app/ui/EpisodesTab.qml:511
302311msgid "Yesterday"
303312msgstr ""
304313
305−#: ../app/ui/EpisodesTab.qml:513
314+#: ../app/ui/EpisodesTab.qml:515
306315msgid "Older"
307316msgstr ""
308317
309−#: ../app/ui/NowPlayingPage.qml:34
310−msgid "Now Playing"
311−msgstr ""
312−
313−#: ../app/ui/NowPlayingPage.qml:61
314−msgid "Full view"
315−msgstr ""
316−
317−#: ../app/ui/NowPlayingPage.qml:61
318−msgid "Queue"
319−msgstr ""
320−
321318#. TRANSLATORS: The string shown in the UI is -15s to denote the number of seconds that the podcast playback will skip backward.
322−#: ../app/ui/NowPlayingPage.qml:261
319+#: ../app/ui/FullPlayingView.qml:215
323320#, no-c-format, qt-format
324321msgid "-%1s"
325322msgstr ""
326323
327324#. TRANSLATORS: The string shown in the UI is +15s to denote the number of seconds that the podcast playback will skip forward.
328−#: ../app/ui/NowPlayingPage.qml:322
325+#: ../app/ui/FullPlayingView.qml:276
329326#, no-c-format, qt-format
330327msgid "+%1s"
331328msgstr ""
332329
333−#: ../app/ui/PodcastsTab.qml:57 ../app/ui/SearchPage.qml:46
334−#: ../app/ui/SearchPage.qml:152
335−msgid "Search Podcast"
330+#: ../app/ui/NowPlayingPage.qml:34
331+msgid "Now Playing"
336332msgstr ""
337333
338−#: ../app/ui/PodcastsTab.qml:65 ../app/ui/SearchPage.qml:37
339−msgid "Add New Podcasts"
334+#: ../app/ui/NowPlayingPage.qml:73
335+msgid "Full view"
336+msgstr ""
337+
338+#: ../app/ui/NowPlayingPage.qml:73
339+msgid "Queue"
340340msgstr ""
341341
342−#: ../app/ui/PodcastsTab.qml:104
342+#: ../app/ui/PodcastsTab.qml:57 ../app/ui/SearchPage.qml:55
343+#: ../app/ui/SearchPage.qml:161
344+msgid "Search Podcast"
345+msgstr ""
346+
347+#: ../app/ui/PodcastsTab.qml:97
343348msgid "Search podcast"
344349msgstr ""
345350
346−#: ../app/ui/PodcastsTab.qml:134
351+#: ../app/ui/PodcastsTab.qml:127
347352msgid "No Podcast Subscriptions"
348353msgstr ""
349354
350−#: ../app/ui/PodcastsTab.qml:134
355+#: ../app/ui/PodcastsTab.qml:127
351356msgid "No Podcasts Found"
352357msgstr ""
353358
354−#: ../app/ui/PodcastsTab.qml:135
359+#: ../app/ui/PodcastsTab.qml:128
355360msgid ""
356361"You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' "
357362"page to add some."
358363msgstr ""
359364
360−#: ../app/ui/PodcastsTab.qml:136 ../app/ui/SearchPage.qml:200
365+#: ../app/ui/PodcastsTab.qml:129 ../app/ui/SearchPage.qml:209
361366msgid "No podcasts found matching the search term."
362367msgstr ""
363368
364−#: ../app/ui/PodcastsTab.qml:222
369+#: ../app/ui/PodcastsTab.qml:215
365370#, qt-format
366371msgid "%1 unheard episode"
367372msgid_plural "%1 unheard episodes"
368373msgstr[0] ""
369374msgstr[1] ""
370375
371−#: ../app/ui/SearchPage.qml:54
376+#: ../app/ui/SearchPage.qml:63
372377msgid "Add Podcast"
373378msgstr ""
374379
375−#: ../app/ui/SearchPage.qml:113
380+#: ../app/ui/SearchPage.qml:122
376381msgid "Save Podcast"
377382msgstr ""
378383
379−#: ../app/ui/SearchPage.qml:140
384+#: ../app/ui/SearchPage.qml:149
380385msgid "Feed URL"
381386msgstr ""
382387
383−#: ../app/ui/SearchPage.qml:167
388+#: ../app/ui/SearchPage.qml:176
384389msgid "Unable to subscribe"
385390msgstr ""
386391
387−#: ../app/ui/SearchPage.qml:168
392+#: ../app/ui/SearchPage.qml:177
388393msgid "Please check the URL and try again"
389394msgstr ""
390395
391−#: ../app/ui/SearchPage.qml:198
396+#: ../app/ui/SearchPage.qml:207
392397msgid "Looking to add a new Podcast?"
393398msgstr ""
394399
395−#: ../app/ui/SearchPage.qml:198
400+#: ../app/ui/SearchPage.qml:207
396401msgid "No Podcasts found"
397402msgstr ""
398403
399−#: ../app/ui/SearchPage.qml:199
404+#: ../app/ui/SearchPage.qml:208
400405msgid ""
401406"Click the 'magnifier' at the top to search or the 'plus' button to add by URL"
402407msgstr ""
403408
404−#: ../app/ui/SearchPage.qml:266
409+#: ../app/ui/SearchPage.qml:275
405410msgid "Subscribe"
406411msgstr ""
407412
408413#. TRANSLATORS: The first argument here is the date of when the podcast was last updated followed by
409414#. the podcast description.
410−#: ../app/ui/SearchPage.qml:305
415+#: ../app/ui/SearchPage.qml:314
411416#, qt-format
412417msgid ""
413418"Last Updated: %1\n"
414419"%2"
415420msgstr ""
416421
417−#: ../app/ui/SearchPage.qml:362
422+#: ../app/ui/SearchPage.qml:371
418423msgid "Not Available"
419424msgstr ""
420425
@@ -536,33 +541,43 @@ msgid ""
536541msgstr ""
537542
538543#: ../app/welcomewizard/Slide5.qml:45
544+msgid "Touch Gestures"
545+msgstr ""
546+
547+#: ../app/welcomewizard/Slide5.qml:64
548+msgid ""
549+"Episodes can be swiped left to reveal more actions. You can also multi-"
550+"select them by long-pressing on an episode."
551+msgstr ""
552+
553+#: ../app/welcomewizard/Slide6.qml:45
539554msgid "Support"
540555msgstr ""
541556
542557#. TRANSLATORS: The %1 points to a url defined in html format <a href=\link\>Link</a>.
543−#: ../app/welcomewizard/Slide5.qml:65
558+#: ../app/welcomewizard/Slide6.qml:65
544559#, qt-format
545560msgid ""
546561"If you find any bugs or have any feature requests, let us know on our "
547562"project page at %1"
548563msgstr ""
549564
550−#: ../app/welcomewizard/Slide6.qml:45
565+#: ../app/welcomewizard/Slide7.qml:45
551566msgid "Enjoy"
552567msgstr ""
553568
554−#: ../app/welcomewizard/Slide6.qml:62
569+#: ../app/welcomewizard/Slide7.qml:62
555570msgid "We hope you enjoy using Podbird!"
556571msgstr ""
557572
558−#: ../app/welcomewizard/Slide6.qml:76
573+#: ../app/welcomewizard/Slide7.qml:76
559574msgid "Finish"
560575msgstr ""
561576
562−#: /home/krnekhelesh/Development/devel-branch-sync-2-build/po/Podbird.desktop.in.h:1
577+#: /home/krnekhelesh/Development/devel-branch-sync-3-build/po/Podbird.desktop.in.h:1
563578msgid "The chirpiest podcast manager for Ubuntu"
564579msgstr ""
565580
566−#: /home/krnekhelesh/Development/devel-branch-sync-2-build/po/Podbird.desktop.in.h:2
581+#: /home/krnekhelesh/Development/devel-branch-sync-3-build/po/Podbird.desktop.in.h:2
567582msgid "podcast;audio;itunes;broadcast;digital;stream;podcatcher;video;vodcast;"
568583msgstr ""