Merge lp:~nik90/podbird/8.6-aboutpage into lp:podbird

Proposed by Nekhelesh Ramananthan
Status: Superseded
Proposed branch: lp:~nik90/podbird/8.6-aboutpage
Merge into: lp:podbird
Diff against target: 4725 lines (+3268/-686)
38 files modified
CMakeLists.txt (+2/-2)
Podbird.apparmor (+4/-2)
app/CMakeLists.txt (+3/-1)
app/graphics/CMakeLists.txt (+7/-0)
app/graphics/notFound.svg (+131/-0)
app/graphics/owlSearch.svg (+140/-0)
app/podbird.qml (+102/-8)
app/podcasts.js (+16/-3)
app/settings/About.qml (+151/-0)
app/settings/CMakeLists.txt (+7/-0)
app/settings/CleanSetting.qml (+65/-0)
app/settings/DownloadSetting.qml (+64/-0)
app/settings/ThemeSetting.qml (+63/-0)
app/themes/Dark.qml (+4/-4)
app/themes/Light.qml (+4/-4)
app/ui/ActionButton.qml (+2/-2)
app/ui/BlurredBackground.qml (+4/-21)
app/ui/Card.qml (+139/-0)
app/ui/CardView.qml (+55/-0)
app/ui/ColumnFlow.qml (+494/-0)
app/ui/EmptyState.qml (+6/-2)
app/ui/EpisodesPage.qml (+393/-232)
app/ui/ExpandableListItem.qml (+1/-0)
app/ui/NowPlayingPage.qml (+5/-0)
app/ui/PlayerControls.qml (+2/-2)
app/ui/PodcastsTab.qml (+168/-120)
app/ui/SearchPage.qml (+160/-101)
app/ui/SettingsPage.qml (+119/-86)
app/ui/Walkthrough.qml (+169/-0)
app/welcomewizard/CMakeLists.txt (+8/-0)
app/welcomewizard/Slide1.qml (+76/-0)
app/welcomewizard/Slide2.qml (+66/-0)
app/welcomewizard/Slide3.qml (+67/-0)
app/welcomewizard/Slide4.qml (+70/-0)
app/welcomewizard/Slide5.qml (+66/-0)
app/welcomewizard/Slide6.qml (+80/-0)
app/welcomewizard/WelcomeWizard.qml (+41/-0)
po/com.mikeasoft.podbird.pot (+314/-96)
To merge this branch: bzr merge lp:~nik90/podbird/8.6-aboutpage
Reviewer Review Type Date Requested Status
Michael Sheldon Pending
Review via email: mp+255909@code.launchpad.net

Commit message

Added about page to provide information about the licensing, version and also the contributors of Podbird.

Description of the change

Added about page to provide information about the licensing, version and also the contributors of Podbird.

To post a comment you must log in.
lp:~nik90/podbird/8.6-aboutpage updated
79. By Nekhelesh Ramananthan

merged prerequisite

80. By Nekhelesh Ramananthan

Split the credits and about page as otherwise it exposes a SDK bug

81. By Nekhelesh Ramananthan

Merged prerequisite

82. By Nekhelesh Ramananthan

merged prerequisite fix

83. By Nekhelesh Ramananthan

merged prerequisite

84. By Nekhelesh Ramananthan

Fixed version

85. By Nekhelesh Ramananthan

changed version to beta1

86. By Nekhelesh Ramananthan

merged prerequsiite

87. By Nekhelesh Ramananthan

merged prerequisite

88. By Nekhelesh Ramananthan

updated to v0.6.b2

89. By Nekhelesh Ramananthan

merged prerequisite lp:~nik90/podbird/8.5-usermetrics

90. By Nekhelesh Ramananthan

Adjusted link color to be more visible in the dark theme throughout the app

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-01-04 10:20:41 +0000
+++ CMakeLists.txt 2015-04-11 21:39:04 +0000
@@ -28,7 +28,7 @@
28set(APP_ID "com.mikeasoft.podbird")28set(APP_ID "com.mikeasoft.podbird")
29set(PODBIRD_DIR "share/qml/podbird")29set(PODBIRD_DIR "share/qml/podbird")
30set(MAIN_QML "podbird.qml")30set(MAIN_QML "podbird.qml")
31set(ICON "graphics/podbird.png")31set(ICON "podbird.png")
3232
33# Set install paths33# Set install paths
34set(CMAKE_INSTALL_PREFIX /)34set(CMAKE_INSTALL_PREFIX /)
@@ -49,7 +49,7 @@
49install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json49install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json
50 DESTINATION ${CMAKE_INSTALL_PREFIX})50 DESTINATION ${CMAKE_INSTALL_PREFIX})
5151
52install(DIRECTORY "app/graphics" DESTINATION ${DATA_DIR})52install(FILES "podbird.png" DESTINATION ${DATA_DIR})
53install(FILES "Podbird.apparmor" DESTINATION ${DATA_DIR})53install(FILES "Podbird.apparmor" DESTINATION ${DATA_DIR})
5454
55add_subdirectory(app)55add_subdirectory(app)
5656
=== modified file 'Podbird.apparmor'
--- Podbird.apparmor 2015-01-30 00:16:10 +0000
+++ Podbird.apparmor 2015-04-11 21:39:04 +0000
@@ -1,7 +1,9 @@
1{1{
2 "policy_groups": [2 "policy_groups": [
3 "networking",3 "networking",
4 "audio"4 "audio",
5 "connectivity",
6 "usermetrics"
5 ],7 ],
6 "policy_version": 1.28 "policy_version": 1.2
7}
8\ No newline at end of file9\ No newline at end of file
10}
911
=== modified file 'app/CMakeLists.txt'
--- app/CMakeLists.txt 2015-03-03 22:44:29 +0000
+++ app/CMakeLists.txt 2015-04-11 21:39:04 +0000
@@ -11,4 +11,6 @@
1111
12add_subdirectory(ui)12add_subdirectory(ui)
13add_subdirectory(themes)13add_subdirectory(themes)
1414add_subdirectory(welcomewizard)
15add_subdirectory(graphics)
16add_subdirectory(settings)
1517
=== added file 'app/graphics/CMakeLists.txt'
--- app/graphics/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/graphics/CMakeLists.txt 2015-04-11 21:39:04 +0000
@@ -0,0 +1,7 @@
1file(GLOB GRAPHIC_FILES *.png *.jpg *.svg)
2
3# Make the files visible in the qtcreator tree
4add_custom_target(podbird_GRAPHICFiles ALL SOURCES ${GRAPHIC_FILES})
5
6install(FILES ${GRAPHIC_FILES} DESTINATION ${PODBIRD_DIR}/graphics)
7
08
=== added file 'app/graphics/discover.png'
1Binary files app/graphics/discover.png 1970-01-01 00:00:00 +0000 and app/graphics/discover.png 2015-04-11 21:39:04 +0000 differ9Binary files app/graphics/discover.png 1970-01-01 00:00:00 +0000 and app/graphics/discover.png 2015-04-11 21:39:04 +0000 differ
=== added file 'app/graphics/gift.png'
2Binary files app/graphics/gift.png 1970-01-01 00:00:00 +0000 and app/graphics/gift.png 2015-04-11 21:39:04 +0000 differ10Binary files app/graphics/gift.png 1970-01-01 00:00:00 +0000 and app/graphics/gift.png 2015-04-11 21:39:04 +0000 differ
=== added file 'app/graphics/language.png'
3Binary files app/graphics/language.png 1970-01-01 00:00:00 +0000 and app/graphics/language.png 2015-04-11 21:39:04 +0000 differ11Binary files app/graphics/language.png 1970-01-01 00:00:00 +0000 and app/graphics/language.png 2015-04-11 21:39:04 +0000 differ
=== added file 'app/graphics/notFound.svg'
--- app/graphics/notFound.svg 1970-01-01 00:00:00 +0000
+++ app/graphics/notFound.svg 2015-04-11 21:39:04 +0000
@@ -0,0 +1,131 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="383.80545"
13 height="214.75534"
14 id="svg12819"
15 version="1.1"
16 inkscape:version="0.48.4 r9939"
17 sodipodi:docname="notFound.svg"
18 inkscape:export-filename="/home/kevin/Documents/Development/app screenshots/owlSearch.png"
19 inkscape:export-xdpi="90"
20 inkscape:export-ydpi="90">
21 <defs
22 id="defs12821" />
23 <sodipodi:namedview
24 id="base"
25 pagecolor="#ffffff"
26 bordercolor="#666666"
27 borderopacity="1.0"
28 inkscape:pageopacity="0.0"
29 inkscape:pageshadow="2"
30 inkscape:zoom="1.41521"
31 inkscape:cx="388.73869"
32 inkscape:cy="44.241833"
33 inkscape:document-units="px"
34 inkscape:current-layer="g15020"
35 showgrid="false"
36 fit-margin-top="10"
37 fit-margin-left="0"
38 fit-margin-right="0"
39 fit-margin-bottom="10"
40 inkscape:window-width="1920"
41 inkscape:window-height="1056"
42 inkscape:window-x="0"
43 inkscape:window-y="24"
44 inkscape:window-maximized="1" />
45 <metadata
46 id="metadata12824">
47 <rdf:RDF>
48 <cc:Work
49 rdf:about="">
50 <dc:format>image/svg+xml</dc:format>
51 <dc:type
52 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
53 <dc:title />
54 </cc:Work>
55 </rdf:RDF>
56 </metadata>
57 <g
58 inkscape:label="Layer 1"
59 inkscape:groupmode="layer"
60 id="layer1"
61 transform="translate(-174.89943,-164.11232)">
62 <g
63 style="display:inline"
64 id="g14777"
65 transform="matrix(0.69639553,0,0,0.69639553,-479.44734,119.33528)">
66 <g
67 id="g15020"
68 transform="matrix(0.63322219,0,0,0.63322219,-295.2311,987.63072)">
69 <g
70 id="g15082"
71 transform="translate(0,82.828838)">
72 <path
73 sodipodi:type="arc"
74 style="fill:none;stroke:#808080;stroke-width:46.75191879;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
75 id="path14966-8"
76 sodipodi:cx="1962.8733"
77 sodipodi:cy="-378.12149"
78 sodipodi:rx="136.36804"
79 sodipodi:ry="136.36804"
80 d="m 2099.2413,-378.12149 c 0,75.31399 -61.054,136.36804 -136.368,136.36804 -75.314,0 -136.3681,-61.05405 -136.3681,-136.36804 0,-75.31399 61.0541,-136.36804 136.3681,-136.36804 75.314,0 136.368,61.05405 136.368,136.36804 z"
81 transform="matrix(1.2126303,0,0,1.2126303,246.51638,-866.06892)" />
82 <path
83 sodipodi:type="arc"
84 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none"
85 id="path14988"
86 sodipodi:cx="2108.5391"
87 sodipodi:cy="-525.33698"
88 sodipodi:rx="41.840195"
89 sodipodi:ry="41.840195"
90 d="m 2150.3793,-525.33698 c 0,23.10771 -18.7325,41.8402 -41.8402,41.8402 -23.1077,0 -41.8402,-18.73249 -41.8402,-41.8402 0,-23.1077 18.7325,-41.84019 41.8402,-41.84019 23.1077,0 41.8402,18.73249 41.8402,41.84019 z"
91 transform="matrix(1.4359656,0,0,1.4359656,-645.94974,-626.80514)" />
92 <path
93 inkscape:connector-curvature="0"
94 style="fill:#808080;fill-opacity:1;stroke:none"
95 d="m 2089.4375,-438.03125 0,103.3125 17.9063,0 c 7.3889,-17.02228 24.323,-28.9375 44.0624,-28.9375 19.7395,0 36.7049,11.91522 44.0938,28.9375 l 17.9062,0 0,-103.3125 -123.9687,0 z"
96 transform="matrix(1.4359656,0,0,1.4359656,-707.51452,-787.38294)"
97 id="rect14986" />
98 <path
99 sodipodi:type="arc"
100 style="fill:none;stroke:#808080;stroke-width:46.75191879;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
101 id="path14966-8-2"
102 sodipodi:cx="1962.8733"
103 sodipodi:cy="-378.12149"
104 sodipodi:rx="136.36804"
105 sodipodi:ry="136.36804"
106 d="m 2099.2413,-378.12149 c 0,75.31399 -61.054,136.36804 -136.368,136.36804 -75.314,0 -136.3681,-61.05405 -136.3681,-136.36804 0,-75.31399 61.0541,-136.36804 136.3681,-136.36804 75.314,0 136.368,61.05405 136.368,136.36804 z"
107 transform="matrix(1.2126303,0,0,1.2126303,-236.42299,-866.06892)" />
108 </g>
109 <rect
110 style="fill:#808080;fill-opacity:1;stroke:none"
111 id="rect15091"
112 width="129.21298"
113 height="129.21298"
114 x="856.36871"
115 y="-2520.2727"
116 transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
117 <path
118 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none"
119 d="m 103.71875,171.6875 -33.5625,16.21875 c 2.149792,8.13106 9.568126,14.125 18.375,14.125 10.489627,0 18.96875,-8.51037 18.96875,-19 0,-4.26924 -1.40349,-8.17188 -3.78125,-11.34375 z"
120 transform="matrix(2.267712,0,0,2.267712,1943.0825,-1656.8514)"
121 id="path15093"
122 inkscape:connector-curvature="0" />
123 <path
124 inkscape:connector-curvature="0"
125 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
126 d="m 2592.9883,-1268.3362 76.1101,36.7795 c -4.8751,18.4389 -21.6977,32.0314 -41.6692,32.0314 -23.7874,0 -43.0156,-19.2991 -43.0156,-43.0865 0,-9.6814 3.1827,-18.5315 8.5747,-25.7244 z"
127 id="path15093-2" />
128 </g>
129 </g>
130 </g>
131</svg>
0132
=== added file 'app/graphics/owlSearch.svg'
--- app/graphics/owlSearch.svg 1970-01-01 00:00:00 +0000
+++ app/graphics/owlSearch.svg 2015-04-11 21:39:04 +0000
@@ -0,0 +1,140 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="383.80545"
13 height="214.75534"
14 id="svg12819"
15 version="1.1"
16 inkscape:version="0.48.4 r9939"
17 sodipodi:docname="owlSearch.svg"
18 inkscape:export-filename="/home/kevin/Documents/Development/app screenshots/owlSearch.png"
19 inkscape:export-xdpi="90"
20 inkscape:export-ydpi="90">
21 <defs
22 id="defs12821" />
23 <sodipodi:namedview
24 id="base"
25 pagecolor="#ffffff"
26 bordercolor="#666666"
27 borderopacity="1.0"
28 inkscape:pageopacity="0.0"
29 inkscape:pageshadow="2"
30 inkscape:zoom="1.990625"
31 inkscape:cx="153.4653"
32 inkscape:cy="131.99307"
33 inkscape:document-units="px"
34 inkscape:current-layer="layer1"
35 showgrid="false"
36 fit-margin-top="10"
37 fit-margin-left="0"
38 fit-margin-right="0"
39 fit-margin-bottom="10"
40 inkscape:window-width="1920"
41 inkscape:window-height="1056"
42 inkscape:window-x="0"
43 inkscape:window-y="24"
44 inkscape:window-maximized="1" />
45 <metadata
46 id="metadata12824">
47 <rdf:RDF>
48 <cc:Work
49 rdf:about="">
50 <dc:format>image/svg+xml</dc:format>
51 <dc:type
52 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
53 <dc:title />
54 </cc:Work>
55 </rdf:RDF>
56 </metadata>
57 <g
58 inkscape:label="Layer 1"
59 inkscape:groupmode="layer"
60 id="layer1"
61 transform="translate(-174.89943,-164.11232)">
62 <g
63 style="display:inline"
64 id="g14777"
65 transform="matrix(0.69639553,0,0,0.69639553,-479.44734,119.33528)">
66 <g
67 id="g15020"
68 transform="matrix(0.63322219,0,0,0.63322219,-295.2311,987.63072)">
69 <g
70 id="g15082"
71 transform="translate(0,82.828838)">
72 <path
73 sodipodi:type="arc"
74 style="fill:none;stroke:#808080;stroke-width:46.75191879;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
75 id="path14966-8"
76 sodipodi:cx="1962.8733"
77 sodipodi:cy="-378.12149"
78 sodipodi:rx="136.36804"
79 sodipodi:ry="136.36804"
80 d="m 2099.2413,-378.12149 c 0,75.31399 -61.054,136.36804 -136.368,136.36804 -75.314,0 -136.3681,-61.05405 -136.3681,-136.36804 0,-75.31399 61.0541,-136.36804 136.3681,-136.36804 75.314,0 136.368,61.05405 136.368,136.36804 z"
81 transform="matrix(1.2126303,0,0,1.2126303,246.51638,-866.06892)" />
82 <path
83 sodipodi:type="arc"
84 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none"
85 id="path14988"
86 sodipodi:cx="2108.5391"
87 sodipodi:cy="-525.33698"
88 sodipodi:rx="41.840195"
89 sodipodi:ry="41.840195"
90 d="m 2150.3793,-525.33698 c 0,23.10771 -18.7325,41.8402 -41.8402,41.8402 -23.1077,0 -41.8402,-18.73249 -41.8402,-41.8402 0,-23.1077 18.7325,-41.84019 41.8402,-41.84019 23.1077,0 41.8402,18.73249 41.8402,41.84019 z"
91 transform="matrix(1.4359656,0,0,1.4359656,-645.94974,-626.80514)" />
92 <path
93 inkscape:connector-curvature="0"
94 style="fill:#808080;fill-opacity:1;stroke:none"
95 d="m 2089.4375,-438.03125 0,103.3125 17.9063,0 c 7.3889,-17.02228 24.323,-28.9375 44.0624,-28.9375 19.7395,0 36.7049,11.91522 44.0938,28.9375 l 17.9062,0 0,-103.3125 -123.9687,0 z"
96 transform="matrix(1.4359656,0,0,1.4359656,-707.51452,-787.38294)"
97 id="rect14986" />
98 <path
99 sodipodi:type="arc"
100 style="fill:none;stroke:#808080;stroke-width:46.75191879;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
101 id="path14966-8-2"
102 sodipodi:cx="1962.8733"
103 sodipodi:cy="-378.12149"
104 sodipodi:rx="136.36804"
105 sodipodi:ry="136.36804"
106 d="m 2099.2413,-378.12149 c 0,75.31399 -61.054,136.36804 -136.368,136.36804 -75.314,0 -136.3681,-61.05405 -136.3681,-136.36804 0,-75.31399 61.0541,-136.36804 136.3681,-136.36804 75.314,0 136.368,61.05405 136.368,136.36804 z"
107 transform="matrix(1.2126303,0,0,1.2126303,-236.42299,-866.06892)" />
108 </g>
109 <rect
110 style="fill:#808080;fill-opacity:1;stroke:none"
111 id="rect15091"
112 width="129.21298"
113 height="129.21298"
114 x="856.36871"
115 y="-2520.2727"
116 transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
117 <path
118 sodipodi:type="arc"
119 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none"
120 id="path15093"
121 sodipodi:cx="1206.7955"
122 sodipodi:cy="673.53888"
123 sodipodi:rx="18.993151"
124 sodipodi:ry="18.993151"
125 d="m 1225.7887,673.53888 c 0,10.48963 -8.5035,18.99315 -18.9932,18.99315 -10.4896,0 -18.9931,-8.50352 -18.9931,-18.99315 0,-10.48963 8.5035,-18.99315 18.9931,-18.99315 10.4897,0 18.9932,8.50352 18.9932,18.99315 z"
126 transform="matrix(2.267712,0,0,2.267712,-592.84807,-2769.1539)" />
127 <path
128 sodipodi:type="arc"
129 style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
130 id="path15093-0"
131 sodipodi:cx="1206.7955"
132 sodipodi:cy="673.53888"
133 sodipodi:rx="18.993151"
134 sodipodi:ry="18.993151"
135 d="m 1225.7887,673.53888 c 0,10.48963 -8.5035,18.99315 -18.9932,18.99315 -10.4896,0 -18.9931,-8.50352 -18.9931,-18.99315 0,-10.48963 8.5035,-18.99315 18.9931,-18.99315 10.4897,0 18.9932,8.50352 18.9932,18.99315 z"
136 transform="matrix(2.267712,0,0,2.267712,-109.9087,-2769.1539)" />
137 </g>
138 </g>
139 </g>
140</svg>
0141
=== modified file 'app/graphics/podbird.png'
1Binary files app/graphics/podbird.png 2015-01-04 10:20:41 +0000 and app/graphics/podbird.png 2015-04-11 21:39:04 +0000 differ142Binary files app/graphics/podbird.png 2015-01-04 10:20:41 +0000 and app/graphics/podbird.png 2015-04-11 21:39:04 +0000 differ
=== added file 'app/graphics/smart.png'
2Binary files app/graphics/smart.png 1970-01-01 00:00:00 +0000 and app/graphics/smart.png 2015-04-11 21:39:04 +0000 differ143Binary files app/graphics/smart.png 1970-01-01 00:00:00 +0000 and app/graphics/smart.png 2015-04-11 21:39:04 +0000 differ
=== added file 'app/graphics/support.png'
3Binary files app/graphics/support.png 1970-01-01 00:00:00 +0000 and app/graphics/support.png 2015-04-11 21:39:04 +0000 differ144Binary files app/graphics/support.png 1970-01-01 00:00:00 +0000 and app/graphics/support.png 2015-04-11 21:39:04 +0000 differ
=== modified file 'app/podbird.qml'
--- app/podbird.qml 2015-03-27 15:38:43 +0000
+++ app/podbird.qml 2015-04-11 21:39:04 +0000
@@ -18,7 +18,9 @@
1818
19import QtQuick 2.319import QtQuick 2.3
20import Podbird 1.020import Podbird 1.0
21import UserMetrics 0.1
21import QtMultimedia 5.022import QtMultimedia 5.0
23import Ubuntu.Connectivity 1.0
22import Qt.labs.settings 1.024import Qt.labs.settings 1.0
23import Ubuntu.Components 1.125import Ubuntu.Components 1.1
24import QtQuick.LocalStorage 2.026import QtQuick.LocalStorage 2.0
@@ -41,18 +43,35 @@
41 backgroundColor: theme.background43 backgroundColor: theme.background
4244
43 Component.onDestruction: {45 Component.onDestruction: {
44 console.log("Download cancelled");46 console.log("[LOG]: Download cancelled");
45 downloader.cancel();47 downloader.cancel();
48 var db = Podcasts.init()
49 db.transaction(function (tx) {
50 tx.executeSql('UPDATE Episode SET queued=0 WHERE queued=1');
51 })
46 }52 }
4753
48 Component.onCompleted: {54 Component.onCompleted: {
55 var db = Podcasts.init()
56 db.transaction(function (tx) {
57 tx.executeSql('UPDATE Episode SET queued=0 WHERE queued=1');
58 })
59
49 var today = new Date()60 var today = new Date()
50 // Only perform cleanup of old episodes once a day61 // Only perform cleanup of old episodes once a day
51 if (Math.floor((today - settings.lastCheck)/86400000) >= 1 && settings.retentionDays !== -1) {62 if (Math.floor((today - settings.lastCheck)/86400000) >= 1 && settings.retentionDays !== -1) {
52 console.log("[LOG]: Starting cleanup of old episodes..")
53 cleanUp(today, settings.retentionDays)63 cleanUp(today, settings.retentionDays)
54 settings.lastCheck = today64 settings.lastCheck = today
55 }65 }
66
67 if (NetworkingStatus.limitedBandwith && settings.onlyWifiDownload || !NetworkingStatus.online || settings.maxEpisodeDownload === -1) {
68 console.log("[LOG]: Skipped autodownloading due to missing wifi connectivity and only download on wifi preference.")
69 console.log("[LOG]: Detecting limited bandwidth: " + NetworkingStatus.limitedBandwith)
70 console.log("[LOG]: Detecting online connectivity: " + NetworkingStatus.online)
71 console.log("[LOG]: User settings (onlywifidownload & maxEpisodeDownload): " + settings.onlyWifiDownload + ", " + settings.maxEpisodeDownload)
72 } else {
73 autoDownloadEpisodes(settings.maxEpisodeDownload)
74 }
56 }75 }
5776
58 property string currentName77 property string currentName
@@ -73,6 +92,11 @@
73 property string themeName: "Light.qml"92 property string themeName: "Light.qml"
74 property int retentionDays: -193 property int retentionDays: -1
75 property var lastCheck: new Date()94 property var lastCheck: new Date()
95 property bool firstRun: true
96 property bool onlyWifiDownload: true
97 property int maxEpisodeDownload: -1
98 property bool hideListened: false
99 property bool showListView: true
76 }100 }
77101
78 FileManager {102 FileManager {
@@ -100,7 +124,7 @@
100 var db = Podcasts.init();124 var db = Podcasts.init();
101 var finalLocation = fileManager.saveDownload(path);125 var finalLocation = fileManager.saveDownload(path);
102 db.transaction(function (tx) {126 db.transaction(function (tx) {
103 tx.executeSql("UPDATE Episode SET downloadedfile=? WHERE guid=?", [finalLocation, downloadingGuid]);127 tx.executeSql("UPDATE Episode SET downloadedfile=?, queued=0 WHERE guid=?", [finalLocation, downloadingGuid]);
104 queue.shift();128 queue.shift();
105 if (queue.length > 0) {129 if (queue.length > 0) {
106 downloadingGuid = queue[0][0];130 downloadingGuid = queue[0][0];
@@ -120,13 +144,36 @@
120 }144 }
121 }145 }
122146
147 // UserMetrics to show Podbird stats on welcome screen
148 Metric {
149 id: podcastsMetric
150 name: "podcast-metrics"
151 // TRANSLATORS: this refers to a number of songs greater than one. The actual number will be prepended to the string automatically (plural forms are not yet fully supported in usermetrics, the library that displays that string)
152 format: "<b>%1</b> " + i18n.tr("podcasts listened today")
153 emptyFormat: i18n.tr("No podcasts listened today")
154 domain: "com.mikeasoft.podbird"
155 }
156
123 MediaPlayer {157 MediaPlayer {
124 id: player158 id: player
159
160 property bool podcastCounted: false
161
162 onSourceChanged: {
163 podcastCounted = false
164 }
165
125 onPositionChanged: {166 onPositionChanged: {
126 if (currentGuid == "" || duration <= 0) {167 if (currentGuid == "" || duration <= 0) {
127 return;168 return;
128 }169 }
129170
171 if (position > 10000 && !podcastCounted) {
172 podcastCounted = true
173 podcastsMetric.increment()
174 console.log("[LOG]: Podcast User metric incremented")
175 }
176
130 var db = Podcasts.init();177 var db = Podcasts.init();
131 db.transaction(function (tx) {178 db.transaction(function (tx) {
132 tx.executeSql("UPDATE Episode SET position=? WHERE guid=?", [position >= duration ? 120 : position, currentGuid]);179 tx.executeSql("UPDATE Episode SET position=? WHERE guid=?", [position >= duration ? 120 : position, currentGuid]);
@@ -139,7 +186,16 @@
139186
140 PageStack {187 PageStack {
141 id: mainStack188 id: mainStack
142 Component.onCompleted: push(tabs)189 Component.onCompleted: {
190 // Show the welcome wizard only when running the app for the first time
191 if (settings.firstRun) {
192 console.log("[LOG]: Detecting first time run by user. Starting welcome wizard.")
193 push(Qt.resolvedUrl("welcomewizard/WelcomeWizard.qml"))
194 } else {
195 push(tabs)
196 }
197 }
198
143 Tabs {199 Tabs {
144 id: tabs200 id: tabs
145201
@@ -148,14 +204,32 @@
148 objectName: "podcastsTab"204 objectName: "podcastsTab"
149 }205 }
150206
151 SearchTab {207 Tab {
152 id: searchTab208 id: searchTab
153 objectName: "searchTab"209
210 title: i18n.tr("Find New Podcasts")
211
212 page: Loader {
213 parent: searchTab
214 anchors.left: parent.left
215 anchors.right: parent.right
216 anchors.bottom: parent.bottom
217 source: (tabs.selectedTab === searchTab) ? Qt.resolvedUrl("ui/SearchPage.qml") : ""
218 }
154 }219 }
155220
156 SettingsTab {221 Tab {
157 id: settingsTab222 id: settingsTab
158 objectName: "settingsTab"223
224 title: i18n.tr("Settings")
225
226 page: Loader {
227 parent: settingsTab
228 anchors.left: parent.left
229 anchors.right: parent.right
230 anchors.bottom: parent.bottom
231 source: (tabs.selectedTab === settingsTab) ? Qt.resolvedUrl("ui/SettingsPage.qml") : ""
232 }
159 }233 }
160 }234 }
161 }235 }
@@ -189,6 +263,7 @@
189 }263 }
190264
191 function cleanUp(today, retentionDays) {265 function cleanUp(today, retentionDays) {
266 console.log("[LOG]: Cleaning up old episodes")
192 var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000267 var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000
193 var db = Podcasts.init()268 var db = Podcasts.init()
194 db.transaction(function (tx) {269 db.transaction(function (tx) {
@@ -206,4 +281,23 @@
206 }281 }
207 });282 });
208 }283 }
284
285 function autoDownloadEpisodes(maxEpisodeDownload) {
286 console.log("[LOG]: Auto-downloading new episodes")
287 var db = Podcasts.init()
288 db.transaction(function (tx) {
289 var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
290 for (var i=0; i < rs.rows.length; i++) {
291 var podcast = rs.rows.item(i);
292 var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
293 var loopCount = maxEpisodeDownload > rs2.rows.length ? rs2.rows.length : maxEpisodeDownload
294 for (var j=0; j < loopCount; j++) {
295 if (!rs2.rows.item(j).downloadedfile && !rs2.rows.item(j).listened) {
296 downloader.addDownload(rs2.rows.item(j).guid, rs2.rows.item(j).audiourl)
297 tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [rs2.rows.item(j).guid]);
298 }
299 }
300 }
301 });
302 }
209}303}
210304
=== modified file 'app/podcasts.js'
--- app/podcasts.js 2015-02-28 09:46:48 +0000
+++ app/podcasts.js 2015-04-11 21:39:04 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright 2015 Michael Sheldon <mike@mikeasoft.com>2 * Copyright 2015 Podbird Team
3 *3 *
4 * This file is part of Podbird.4 * This file is part of Podbird.
5 *5 *
@@ -17,11 +17,24 @@
17 */17 */
1818
19function init() {19function init() {
20 var db = LocalStorage.openDatabaseSync("Podbird", "1.0", "Database of subscribed podcasts and their episodes", 1000000);20 var db = LocalStorage.openDatabaseSync("Podbird", "", "Database of subscribed podcasts and their episodes", 1000000);
21
21 db.transaction(function(tx) {22 db.transaction(function(tx) {
22 tx.executeSql('CREATE TABLE IF NOT EXISTS Podcast(artist TEXT, name TEXT, description TEXT, feed TEXT, image TEXT, lastupdate TIMESTAMP)');23 tx.executeSql('CREATE TABLE IF NOT EXISTS Podcast(artist TEXT, name TEXT, description TEXT, feed TEXT, image TEXT, lastupdate TIMESTAMP)');
23 tx.executeSql('CREATE TABLE IF NOT EXISTS Episode(guid TEXT, podcast INTEGER, name TEXT, subtitle TEXT, description TEXT, duration INTEGER, audiourl TEXT, downloadedfile TEXT, published TIMESTAMP, listened BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');24 tx.executeSql('CREATE TABLE IF NOT EXISTS Episode(guid TEXT, podcast INTEGER, name TEXT, subtitle TEXT, description TEXT, duration INTEGER, audiourl TEXT, downloadedfile TEXT, published TIMESTAMP, queued BOOLEAN, listened BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');
24 });25 });
26
27 /*
28 Schema Upgrade to v1.1 which adds a new queued boolean variable which is needed to track the queued status
29 of a episode properly.
30 */
31 if (db.version == "1.0") {
32 db.changeVersion("1.0", "1.1", function(tx) {
33 tx.executeSql('ALTER TABLE Episode ADD queued BOOLEAN');
34 tx.executeSql('UPDATE Episode SET queued=0');
35 });
36 }
37
25 return db;38 return db;
26}39}
2740
2841
=== added directory 'app/settings'
=== added file 'app/settings/About.qml'
--- app/settings/About.qml 1970-01-01 00:00:00 +0000
+++ app/settings/About.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,151 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23Page {
24 id: aboutPage
25
26 title: i18n.tr("About")
27 visible: false
28
29 head.sections.model: [i18n.tr("About"), i18n.tr("Credits")]
30
31 VisualItemModel {
32 id: tabs
33
34 Item {
35 width: tabView.width
36 height: tabView.height
37
38 Column {
39 spacing: units.gu(4)
40 anchors.centerIn: parent
41 width: parent.width > units.gu(50) ? units.gu(50) : parent.width
42
43 Image {
44 height: width
45 width: parent.width/2
46 source: "../graphics/podbird.png"
47 anchors.horizontalCenter: parent.horizontalCenter
48 }
49
50 Column {
51 width: parent.width
52 Label {
53 width: parent.width
54 fontSize: "x-large"
55 font.weight: Font.DemiBold
56 horizontalAlignment: Text.AlignHCenter
57 text: "Podbird"
58 }
59 Label {
60 width: parent.width
61 horizontalAlignment: Text.AlignHCenter
62 text: qsTr("Version %1").arg(0.6)
63 }
64 }
65
66 Column {
67 anchors {
68 left: parent.left
69 right: parent.right
70 margins: units.gu(2)
71 }
72 Label {
73 width: parent.width
74 wrapMode: Text.WordWrap
75 horizontalAlignment: Text.AlignHCenter
76 text: "(C) 2015 Podbird Team"
77 }
78 Label {
79 fontSize: "small"
80 width: parent.width
81 wrapMode: Text.WordWrap
82 horizontalAlignment: Text.AlignHCenter
83 text: i18n.tr("Released under the terms of the GNU GPL v3")
84 }
85 }
86
87 Label {
88 width: parent.width
89 wrapMode: Text.WordWrap
90 fontSize: "small"
91 horizontalAlignment: Text.AlignHCenter
92 text: i18n.tr("Source code available on %1").arg("<a href=\"https://launchpad.net/podbird\">launchpad.net</a>")
93 onLinkActivated: Qt.openUrlExternally(link)
94 }
95 }
96 }
97
98 Item {
99 width: tabView.width
100 height: tabView.height
101
102 Column {
103 anchors.topMargin: units.gu(1)
104 anchors.fill: parent
105
106 ListItem.Header {
107 text: i18n.tr("Developers")
108 }
109
110 ListItem.Standard {
111 showDivider: false
112 text: "Michael Sheldon (Creator)"
113 }
114
115 ListItem.Standard {
116 showDivider: false
117 text: "Nekhelesh Ramananthan"
118 }
119
120 ListItem.Header {
121 text: i18n.tr("Designer")
122 }
123
124 ListItem.Standard {
125 showDivider: false
126 text: "Kevin Feyder"
127 }
128
129 ListItem.Header {
130 text: i18n.tr("Translators")
131 }
132
133 ListItem.Standard {
134 showDivider: false
135 text: "Ubuntu Translators Team"
136 }
137 }
138 }
139 }
140
141 ListView {
142 id: tabView
143 model: tabs
144 interactive: false
145 anchors.fill: parent
146 orientation: Qt.Horizontal
147 snapMode: ListView.SnapOneItem
148 currentIndex: aboutPage.head.sections.selectedIndex
149 highlightMoveDuration: UbuntuAnimation.SlowDuration
150 }
151}
0152
=== added file 'app/settings/CMakeLists.txt'
--- app/settings/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/settings/CMakeLists.txt 2015-04-11 21:39:04 +0000
@@ -0,0 +1,7 @@
1file(GLOB SETTING_FILES *.qml)
2
3# Make the files visible in the qtcreator tree
4add_custom_target(podbird_SETTINGFiles ALL SOURCES ${SETTING_FILES})
5
6install(FILES ${SETTING_FILES} DESTINATION ${PODBIRD_DIR}/settings)
7
08
=== added file 'app/settings/CleanSetting.qml'
--- app/settings/CleanSetting.qml 1970-01-01 00:00:00 +0000
+++ app/settings/CleanSetting.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,65 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23Page {
24 id: cleanSettingPage
25
26 visible: false
27 title: i18n.tr("Delete older than")
28
29 ListModel {
30 id: cleanupModel
31 Component.onCompleted: initialize()
32 function initialize() {
33 cleanupModel.append({ name: i18n.tr("Never"), value: -1 })
34 cleanupModel.append({ name: i18n.tr("%1 day", "%1 days", 7).arg(7), value: 7 })
35 cleanupModel.append({ name: i18n.tr("%1 month", "%1 months", 1).arg(1), value: 31 })
36 cleanupModel.append({ name: i18n.tr("%1 month", "%1 months", 3).arg(3), value: 90 })
37 cleanupModel.append({ name: i18n.tr("%1 month", "%1 months", 6).arg(6), value: 180 })
38 cleanupModel.append({ name: i18n.tr("%1 year", "%1 years", 1).arg(1), value: 360 })
39 }
40 }
41
42 UbuntuListView {
43 id: cleanup
44
45 model: cleanupModel
46 anchors.fill: parent
47
48 delegate: ListItem.Standard {
49 text: model.name
50 onClicked: {
51 podbird.settings.retentionDays = model.value
52 }
53
54 Icon {
55 width: units.gu(2)
56 height: width
57 name: "ok"
58 visible: podbird.settings.retentionDays === model.value
59 anchors.right: parent.right
60 anchors.rightMargin: units.gu(3)
61 anchors.verticalCenter: parent.verticalCenter
62 }
63 }
64 }
65}
066
=== added file 'app/settings/DownloadSetting.qml'
--- app/settings/DownloadSetting.qml 1970-01-01 00:00:00 +0000
+++ app/settings/DownloadSetting.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,64 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23Page {
24 id: downloadSetting
25
26 visible: false
27 title: i18n.tr("Download at most")
28
29 ListModel {
30 id: episodeDownloadNumber
31 Component.onCompleted: initialize()
32 function initialize() {
33 episodeDownloadNumber.append({ name: i18n.tr("Never"), value: -1 })
34 episodeDownloadNumber.append({ name: i18n.tr("%1 episode", "%1 episodes", 1).arg(1), value: 1 })
35 episodeDownloadNumber.append({ name: i18n.tr("%1 episode", "%1 episodes", 3).arg(3), value: 3 })
36 episodeDownloadNumber.append({ name: i18n.tr("%1 episode", "%1 episodes", 5).arg(5), value: 5 })
37 episodeDownloadNumber.append({ name: i18n.tr("%1 episode", "%1 episodes", 10).arg(10), value: 10 })
38 }
39 }
40
41 UbuntuListView {
42 id: download
43
44 model: episodeDownloadNumber
45 anchors.fill: parent
46
47 delegate: ListItem.Standard {
48 text: model.name
49 onClicked: {
50 podbird.settings.maxEpisodeDownload = model.value
51 }
52
53 Icon {
54 width: units.gu(2)
55 height: width
56 name: "ok"
57 visible: podbird.settings.maxEpisodeDownload === model.value
58 anchors.right: parent.right
59 anchors.rightMargin: units.gu(3)
60 anchors.verticalCenter: parent.verticalCenter
61 }
62 }
63 }
64}
065
=== added file 'app/settings/ThemeSetting.qml'
--- app/settings/ThemeSetting.qml 1970-01-01 00:00:00 +0000
+++ app/settings/ThemeSetting.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,63 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23Page {
24 id: themeSettingPage
25
26 visible: false
27 title: i18n.tr("Theme")
28
29 ListModel {
30 id: themeModel
31 Component.onCompleted: initialize()
32 function initialize() {
33 themeModel.append({ name: i18n.tr("Light"), file: "Light.qml" })
34 themeModel.append({ name: i18n.tr("Dark"), file: "Dark.qml" })
35 }
36 }
37
38 UbuntuListView {
39 id: themes
40
41 model: themeModel
42 anchors.fill: parent
43
44 delegate: ListItem.Standard {
45 text: model.name
46 onClicked: {
47 var themeElement = model.file
48 podbird.settings.themeName = themeElement
49 podbird.themeManager.source = themeElement
50 }
51
52 Icon {
53 width: units.gu(2)
54 height: width
55 name: "ok"
56 visible: podbird.settings.themeName === model.file
57 anchors.right: parent.right
58 anchors.rightMargin: units.gu(3)
59 anchors.verticalCenter: parent.verticalCenter
60 }
61 }
62 }
63}
064
=== modified file 'app/themes/Dark.qml'
--- app/themes/Dark.qml 2015-03-04 02:58:36 +0000
+++ app/themes/Dark.qml 2015-04-11 21:39:04 +0000
@@ -21,12 +21,12 @@
2121
22QtObject {22QtObject {
23 // MainView23 // MainView
24 property color background: "#1E1E23"24 property color background: "#242423"
2525
26 // Main Text Colors26 // Main Text Colors
27 property color baseText: "White"27 property color baseText: "White"
28 property color baseSubText: "#999999"28 property color baseSubText: "#999999"
29 property color focusText: "#FF9900"29 property color focusText: "#35AF44"
3030
31 // Icon Colors31 // Icon Colors
32 property color baseIcon: "White"32 property color baseIcon: "White"
@@ -37,8 +37,8 @@
37 property color neutralActionButton: UbuntuColors.coolGrey37 property color neutralActionButton: UbuntuColors.coolGrey
3838
39 // Bottom Player Bar Colors39 // Bottom Player Bar Colors
40 property color bottomBarBackground: "#0F0F0F"40 property color bottomBarBackground: "#15141A"
4141
42 // Highlight Color42 // Highlight Color
43 property color hightlightListView: "#2C2C34"43 property color hightlightListView: "#333533"
44}44}
4545
=== modified file 'app/themes/Light.qml'
--- app/themes/Light.qml 2015-03-04 02:58:36 +0000
+++ app/themes/Light.qml 2015-04-11 21:39:04 +0000
@@ -21,12 +21,12 @@
2121
22QtObject {22QtObject {
23 // MainView23 // MainView
24 property color background: "#EEEEEE"24 property color background: "#ECECEC"
2525
26 // Main Text Colors26 // Main Text Colors
27 property color baseText: UbuntuColors.darkGrey27 property color baseText: UbuntuColors.darkGrey
28 property color baseSubText: "#999999"28 property color baseSubText: "#999999"
29 property color focusText: UbuntuColors.orange29 property color focusText: "#35AF44"
3030
31 // Icon Colors31 // Icon Colors
32 property color baseIcon: UbuntuColors.darkGrey32 property color baseIcon: UbuntuColors.darkGrey
@@ -37,8 +37,8 @@
37 property color neutralActionButton: UbuntuColors.coolGrey37 property color neutralActionButton: UbuntuColors.coolGrey
3838
39 // Bottom Player Bar Colors39 // Bottom Player Bar Colors
40 property color bottomBarBackground: "#0F0F0F"40 property color bottomBarBackground: "#323435"
4141
42 // Highlight Color42 // Highlight Color
43 property color hightlightListView: "#D8D8D8"43 property color hightlightListView: "#F5F5F5"
44}44}
4545
=== modified file 'app/ui/ActionButton.qml'
--- app/ui/ActionButton.qml 2015-03-08 11:10:07 +0000
+++ app/ui/ActionButton.qml 2015-04-11 21:39:04 +0000
@@ -22,7 +22,8 @@
22AbstractButton {22AbstractButton {
23 id: abstractButton23 id: abstractButton
2424
25 property string iconName25 property alias iconName: _icon.name
26 property alias color: _icon.color
2627
27 Rectangle {28 Rectangle {
28 visible: abstractButton.pressed29 visible: abstractButton.pressed
@@ -35,7 +36,6 @@
35 width: units.gu(2.5)36 width: units.gu(2.5)
36 height: width37 height: width
37 anchors.centerIn: parent38 anchors.centerIn: parent
38 name: abstractButton.iconName
39 color: podbird.theme.baseIcon39 color: podbird.theme.baseIcon
40 }40 }
41}41}
4242
=== modified file 'app/ui/BlurredBackground.qml'
--- app/ui/BlurredBackground.qml 2015-02-10 06:57:54 +0000
+++ app/ui/BlurredBackground.qml 2015-04-11 21:39:04 +0000
@@ -25,15 +25,8 @@
25Item {25Item {
26 width: parent.width26 width: parent.width
2727
28 property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : player.currentMetaArt28 property string art
2929 property real backgroundStrength: 0.4
30 // dark layer
31 Rectangle {
32 anchors {
33 fill: parent
34 }
35 color: "black"
36 }
3730
38 // the album art31 // the album art
39 Image {32 Image {
@@ -44,16 +37,6 @@
44 fillMode: Image.PreserveAspectCrop37 fillMode: Image.PreserveAspectCrop
45 height: parent.height38 height: parent.height
46 source: art // this has to be fixed for the default cover art to work - cant find in this dir39 source: art // this has to be fixed for the default cover art to work - cant find in this dir
47
48 // TODO: This should be investigated once http://pad.lv/1391368
49 // is resolved. Once it is, these can either be set to
50 // "height" and "width" or a property exposed via the
51 // SDK or Thumbnailer to avoid a regression caused by
52 // these hardcoded values changing in the Thumbnailer.
53 // 512 is size of the "xlarge" thumbnails in pixels.
54 sourceSize.height: 512
55 sourceSize.width: 512
56
57 visible: false40 visible: false
58 width: Math.max(parent.height, parent.width)41 width: Math.max(parent.height, parent.width)
59 }42 }
@@ -63,8 +46,8 @@
63 id: backgroundBlur46 id: backgroundBlur
64 anchors.fill: backgroundImage47 anchors.fill: backgroundImage
65 source: backgroundImage48 source: backgroundImage
66 radius: units.dp(42)49 radius: units.dp(30)
67 opacity: 0.250 opacity: backgroundStrength
68 }51 }
69 onArtChanged: {52 onArtChanged: {
70 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,53 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,
7154
=== added file 'app/ui/Card.qml'
--- app/ui/Card.qml 1970-01-01 00:00:00 +0000
+++ app/ui/Card.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,139 @@
1/*
2 * Copyright (C) 2014
3 * Andrew Hayzen <ahayzen@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18import QtQuick 2.3
19import Ubuntu.Components 1.1
20
21Item {
22 id: card
23
24 /* Required by ColumnFlow */
25 property int index
26 property var model
27
28 property alias coverArt: imgFrame.source
29 property alias primaryText: primaryLabel.text
30 property alias secondaryText: secondaryLabel.text
31 property alias secondaryTextVisible: secondaryLabel.visible
32
33 signal clicked(var mouse)
34 signal pressAndHold(var mouse)
35
36 height: cardColumn.childrenRect.height + 2 * bg.anchors.margins
37
38 /* Background for card */
39 Rectangle {
40 id: bg
41 anchors.fill: parent
42 anchors.margins: units.gu(1)
43 color: podbird.theme.hightlightListView
44 }
45
46 /* Column containing image and labels */
47 Column {
48 id: cardColumn
49
50 anchors.fill: bg
51 spacing: units.gu(0.5)
52
53 Image {
54 id: imgFrame
55 width: parent.width
56 height: width
57 sourceSize.height: width
58 sourceSize.width: width
59 }
60
61 Item {
62 height: units.gu(1)
63 width: units.gu(1)
64 }
65
66 Label {
67 id: primaryLabel
68 anchors {
69 left: parent.left
70 right: parent.right
71 margins: units.gu(1)
72 }
73 color: podbird.theme.baseText
74 elide: Text.ElideRight
75 fontSize: "small"
76 opacity: 1.0
77 wrapMode: Text.WordWrap
78 horizontalAlignment: Text.AlignHCenter
79 }
80
81 Label {
82 id: secondaryLabel
83 anchors {
84 left: parent.left
85 leftMargin: units.gu(1)
86 right: parent.right
87 rightMargin: units.gu(1)
88 }
89 color: podbird.theme.baseSubText
90 elide: Text.ElideRight
91 fontSize: "small"
92 opacity: 1.0
93 wrapMode: Text.WordWrap
94 horizontalAlignment: Text.AlignHCenter
95 }
96
97 Item {
98 height: units.gu(1.5)
99 width: units.gu(1)
100 }
101 }
102
103 /* Overlay for when card is pressed */
104 Rectangle {
105 id: overlay
106 anchors.fill: bg
107 color: "#000"
108 opacity: 0
109
110 Behavior on opacity {
111 UbuntuNumberAnimation {}
112 }
113 }
114
115 /* Capture mouse events */
116 MouseArea {
117 anchors.fill: parent
118 onClicked: card.clicked(mouse)
119 onPressAndHold: card.pressAndHold(mouse)
120 onPressedChanged: overlay.opacity = pressed ? 0.3 : 0
121 }
122
123 /* Animations */
124 Behavior on height {
125 UbuntuNumberAnimation {}
126 }
127
128 Behavior on width {
129 UbuntuNumberAnimation {}
130 }
131
132 Behavior on x {
133 UbuntuNumberAnimation {}
134 }
135
136 Behavior on y {
137 UbuntuNumberAnimation {}
138 }
139}
0140
=== added file 'app/ui/CardView.qml'
--- app/ui/CardView.qml 1970-01-01 00:00:00 +0000
+++ app/ui/CardView.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,55 @@
1/*
2 * Copyright (C) 2014
3 * Andrew Hayzen <ahayzen@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18import QtQuick 2.3
19import Ubuntu.Components 1.1
20
21Flickable {
22 id: cardViewFlickable
23 anchors {
24 fill: parent
25 margins: units.gu(1)
26 }
27
28 // dont use flow.contentHeight as it is inaccurate due to height of labels
29 // changing as they load
30 contentHeight: flow.contentHeight + flow.anchors.margins * 2 + units.gu(8)
31 contentWidth: width
32
33 property alias count: flow.count
34 property alias delegate: flow.delegate
35 property var getter
36 property alias model: flow.model
37 property real itemWidth: units.gu(15)
38
39 onGetterChanged: flow.getter = getter // cannot use alias to set a function (must be var)
40
41 ColumnFlow {
42 id: flow
43 anchors.fill: parent
44 columns: parseInt(cardViewFlickable.width / itemWidth) || 1 // never drop to 0
45 flickable: cardViewFlickable
46 }
47
48 Component.onCompleted: {
49 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
50 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
51 var scaleFactor = units.gridUnit / 8;
52 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
53 flickDeceleration = flickDeceleration * scaleFactor;
54 }
55}
056
=== added file 'app/ui/ColumnFlow.qml'
--- app/ui/ColumnFlow.qml 1970-01-01 00:00:00 +0000
+++ app/ui/ColumnFlow.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,494 @@
1/*
2 * Copyright (C) 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18import QtQuick 2.3
19
20Item {
21 id: columnFlow
22 property int columns: 1
23 property Flickable flickable
24 property var model
25 property Component delegate
26
27 property var getter: function (i) { return model.get(i); } // optional getter override (useful for music-app ms2 models)
28
29 property int buffer: units.gu(20)
30 property var columnHeights: []
31 property var columnHeightsMax: []
32 property int columnWidth: parent.width / columns
33 property int contentHeight: 0
34 property int count: model === undefined ? 0 : model.count
35 property int delayRebuildIndex: -1
36 property var incubating: ({}) // incubating objects
37 property var items: ({})
38 property var itemToColumn: ({}) // cache of the columns of indexes
39 property int lastIndex: 0 // the furtherest index loaded
40 property bool removing: false
41 property bool restoring: false // is the view restoring?
42 property var restoreItems: ({}) // when rebuilding items are stored here temporarily
43
44 onColumnWidthChanged: {
45 if (restoring) {
46 return;
47 } else if (columns != columnHeights.length && visible) {
48 // number of columns has changed so rebuild the columns
49 rebuildColumns()
50 } else { // column width has changed update visible items properties linked to columnWidth
51 for (var column=0; column < columnHeights.length; column++) {
52 for (var i in columnHeights[column]) {
53 if (columnHeights[column].hasOwnProperty(i) && items.hasOwnProperty(i)) {
54 items[i].width = columnWidth;
55 items[i].x = column * columnWidth;
56 }
57 }
58 }
59
60 ensureItemsVisible()
61 }
62 }
63
64 onVisibleChanged: {
65 if (visible && delayRebuildIndex !== -1) { // restore from count change
66 if (delayRebuildIndex === 0) {
67 reset()
68 } else {
69 removeIndex(delayRebuildIndex)
70 }
71
72 delayRebuildIndex = -1
73 append(true)
74 }
75
76 // number of columns has changed while invisible so reset if not already restoring
77 if (visible && !restoring && columns != columnHeights.length) {
78 rebuildColumns()
79 }
80 }
81
82 ListModel { // fakemodel for connections to link to when there is no model
83 id: fakeModel
84 }
85
86 Connections {
87 target: model === undefined ? fakeModel : model
88 onModelReset: {
89 if (!visible && lastIndex > 0) {
90 delayRebuildIndex = 0
91 } else {
92 reset()
93 append()
94 }
95 }
96 onRowsInserted: {
97 if (!visible && lastIndex > 0) {
98 setDelayRebuildIndex(first)
99 } else {
100 if (first <= lastIndex) {
101 if (first === 0) {
102 reset()
103 } else {
104 removeIndex(first) // remove earliest index and all items after
105 }
106 }
107
108 // Supply last index if larger as count is not updated until after insertion
109 append(true, last > count ? last : count)
110 }
111 }
112 onRowsRemoved: {
113 if (!visible) {
114 setDelayRebuildIndex(first)
115 } else {
116 if (first <= lastIndex) {
117 if (first === 0) {
118 reset()
119 } else {
120 removeIndex(first) // remove earliest index and all items after
121 }
122
123 // count is not updated until after removal, so send insertMax
124 // insertMax is count - removal region inclusive - 1 (lastIndex is 1 infront)
125
126 append(true, count - (1 + last - first) - 1) // rebuild any items on screen or before
127 }
128 }
129 }
130 }
131
132
133 Connections {
134 target: flickable
135 onContentYChanged: {
136 append() // Append any new items (scrolling down)
137
138 ensureItemsVisible()
139 }
140 }
141
142 // Append a new row of items if possible
143 function append(loadBefore, insertMax)
144 {
145 // Do not allow append to run if incubating
146 if (isIncubating() || restoring || removing) {
147 return;
148 }
149
150 // get the columns in order
151 var columnsByHeight = getColumnsByHeight();
152 var workDone = false;
153
154 // check if a new item in each column is possible
155 for (var i=0; i < columnsByHeight.length; i++) {
156 var y = columnHeightsMax[columnsByHeight[i]];
157
158 // build new object in column if possible
159 // if insertMax is undefined then allow if there is work todo (from the count in the model)
160 // otherwise use the insertMax as the count to compare with the lastIndex added to the columnFlow
161 // and
162 // allow if the y position is within the viewport
163 // or if loadBefore is true then allow if the y position is before the viewport
164 if (((count > 0 && lastIndex < count && insertMax === undefined) || (insertMax !== undefined && lastIndex <= insertMax)) && (inViewport(y, 0) || (loadBefore === true && beforeViewport(y)))) {
165 incubateObject(lastIndex++, columnsByHeight[i], getMaxInColumn(columnsByHeight[i]), append);
166 workDone = true
167 } else {
168 break;
169 }
170 }
171
172 if (!workDone) { // last iteration over append so visible ensure items are correct
173 ensureItemsVisible();
174 }
175 }
176
177 // Detect if a loaded object is before the viewport with a buffer
178 function beforeViewport(y)
179 {
180 return y <= flickable.contentY - buffer;
181 }
182
183 // Cache the size of the columns for use later
184 function cacheColumnHeights()
185 {
186 columnHeightsMax = [];
187
188 for (var i=0; i < columnHeights.length; i++) {
189 var sum = 0;
190
191 for (var j in columnHeights[i]) {
192 sum += columnHeights[i][j];
193 }
194
195 columnHeightsMax.push(sum);
196 }
197
198 if (!restoring) { // when not restoring otherwise user will be pushed to the top of the view
199 // set the height of columnFlow to max column (for flickable contentHeight)
200 contentHeight = Math.max.apply(null, columnHeightsMax);
201 }
202 }
203
204 // Recache the visible items heights (due to a change in their height)
205 function cacheVisibleItemsHeights()
206 {
207 for (var i in items) {
208 if (items.hasOwnProperty(i)) {
209 columnHeights[itemToColumn[i]][i] = items[i].height;
210 }
211 }
212
213 cacheColumnHeights();
214 }
215
216 // Ensures that the correct items are visible
217 function ensureItemsVisible()
218 {
219 for (var i in items) {
220 if (items.hasOwnProperty(i)) {
221 items[i].visible = inViewport(items[i].y, items[i].height)
222 }
223 }
224 }
225
226 // Return if there are incubating objects
227 function isIncubating()
228 {
229 for (var i in incubating) {
230 if (incubating.hasOwnProperty(i)) {
231 return true;
232 }
233 }
234
235 return false;
236 }
237
238 // Run after incubation to store new column height and call any further append/restores
239 function finishIncubation(index, callback)
240 {
241 var obj = incubating[index].object;
242 delete incubating[index];
243
244 obj.heightChanged.connect(cacheVisibleItemsHeights) // if the height changes recache
245
246 // Ensure properties linked to columnWidth are correct (as width may still be changing)
247 obj.x = itemToColumn[index] * columnWidth;
248 obj.width = columnWidth;
249
250 items[index] = obj;
251
252 columnHeights[itemToColumn[index]][index] = obj.height; // ensure height is the latest
253
254 if (!isIncubating()) {
255 cacheColumnHeights();
256
257 // Check if there is any more work to be done (append or restore)
258 callback();
259 }
260 }
261
262 // Force any incubation to finish
263 function forceIncubationCompletion()
264 {
265 for (var i in incubating) {
266 if (incubating.hasOwnProperty(i)) {
267 incubating[i].forceCompletion()
268 }
269 }
270 }
271
272 // Get the column index in order of height
273 function getColumnsByHeight()
274 {
275 var columnsByHeight = [];
276
277 for (var i=0; i < columnHeightsMax.length; i++) {
278 var min = undefined;
279 var index = -1;
280
281 // Find the smallest column that has not been found yet
282 for (var j=0; j < columnHeightsMax.length; j++) {
283 if (columnsByHeight.indexOf(j) === -1 && (min === undefined || columnHeightsMax[j] < min)) {
284 min = columnHeightsMax[j];
285 index = j;
286 }
287 }
288
289 columnsByHeight.push(index);
290 }
291
292 return columnsByHeight;
293 }
294
295 // Get the highest index for a column
296 function getMaxInColumn(column)
297 {
298 var max;
299
300 for (var i in columnHeights[column]) {
301 if (columnHeights[column].hasOwnProperty(i)) {
302 i = parseInt(i);
303
304 if (items.hasOwnProperty(i)) {
305 if (i > max || max === undefined) {
306 max = i;
307 }
308 }
309 }
310 }
311
312 return max;
313 }
314
315 // Incubate an object for creation
316 function incubateObject(index, column, anchorIndex, callback)
317 {
318 // Load parameters to send to the object on creation
319 var params = {
320 "anchors.top": anchorIndex === undefined ? parent.top : items[anchorIndex].bottom,
321 index: index,
322 model: getter(index),
323 width: columnWidth,
324 x: column * columnWidth
325 };
326
327 // Start incubating and cache the column
328 incubating[index] = delegate.incubateObject(parent, params);
329 itemToColumn[index] = column;
330
331 if (incubating[index].status != Component.Ready) {
332 incubating[index].onStatusChanged = function(status) {
333 if (status == Component.Ready) {
334 finishIncubation(index, callback)
335 }
336 }
337 } else {
338 finishIncubation(index, callback)
339 }
340 }
341
342 // Detect if a loaded object is in the viewport with a buffer
343 function inViewport(y, height)
344 {
345 return flickable.contentY - buffer < y + height && y < flickable.contentY + flickable.height + buffer;
346 }
347
348 // Number of columns has changed rebuild with live items
349 function rebuildColumns()
350 {
351 restoring = true;
352 var i;
353
354 forceIncubationCompletion()
355
356 columnHeights = []
357 columnHeightsMax = []
358
359 for (i=0; i < columns; i++) {
360 columnHeights.push({});
361 columnHeightsMax.push(0);
362 }
363
364 lastIndex = 0;
365
366 restoreItems = items;
367 items = {};
368
369 restoreExisting()
370
371 restoring = false;
372
373 cacheColumnHeights(); // rebuilds contentHeight
374
375 // If the columns have changed while the view was locked rerun
376 if (columns != columnHeights.length && visible) {
377 rebuildColumns()
378 } else {
379 append() // check if any new items can be added
380 }
381 }
382
383 // Remove an index from the model (invalidating anything after)
384 function removeIndex(index)
385 {
386 removing = true
387
388 forceIncubationCompletion()
389
390 for (var i in items) {
391 if (i >= index && items.hasOwnProperty(i)) {
392 delete columnHeights[itemToColumn[i]][i]
393 delete itemToColumn[i]
394
395 items[i].destroy()
396 delete items[i]
397 }
398 }
399
400 lastIndex = index
401 removing = false
402
403 cacheColumnHeights()
404 }
405
406 // Restores existing items into potentially new positions
407 function restoreExisting()
408 {
409 var i;
410
411 // get the columns in order
412 var columnsByHeight = getColumnsByHeight();
413 var workDone = false;
414
415 // check if a new item in each column is possible
416 for (i=0; i < columnsByHeight.length; i++) {
417 var column = columnsByHeight[i];
418
419 // build new object in column if possible
420 if (count > 0 && lastIndex < count) {
421 if (restoreItems.hasOwnProperty(lastIndex)) {
422 var item = restoreItems[lastIndex];
423 var maxInColumn = getMaxInColumn(column); // get lowest item in column
424
425 itemToColumn[lastIndex] = column;
426 columnHeights[column][lastIndex] = item.height; // ensure height is the latest
427
428 // Rebuild item properties
429 item.anchors.bottom = undefined
430 item.anchors.top = maxInColumn === undefined ? parent.top : items[maxInColumn].bottom;
431 item.x = column * columnWidth;
432 item.visible = inViewport(item.y, item.height);
433
434 // Migrate item from restoreItems to items
435 items[lastIndex] = item;
436 delete restoreItems[lastIndex];
437
438 // set after restore as height will likely change causing cacheVisibleItemsHeights to be run
439 item.width = columnWidth;
440
441 cacheColumnHeights(); // ensure column heights are up to date
442
443 lastIndex++;
444 workDone = true;
445 }
446 } else {
447 break;
448 }
449 }
450
451 if (workDone) {
452 restoreExisting() // if work done then check if any more is needed
453 } else {
454 restoreItems = {}; // ensure restoreItems is empty
455 }
456 }
457
458 // Reset the column flow
459 function reset()
460 {
461 forceIncubationCompletion()
462
463 // Destroy any old items
464 for (var j in items) {
465 if (items.hasOwnProperty(j)) {
466 items[j].destroy()
467 }
468 }
469
470 // Reset and rebuild the variables
471 items = ({})
472 itemToColumn = ({})
473 lastIndex = 0
474
475 columnHeights = []
476
477 for (var k=0; k < columns; k++) {
478 columnHeights.push({})
479 }
480
481 cacheColumnHeights()
482
483 contentHeight = 0
484 }
485
486 function setDelayRebuildIndex(index)
487 {
488 if (delayRebuildIndex === -1 || index < lastIndex) {
489 delayRebuildIndex = index
490 }
491 }
492
493 Component.onCompleted: append(true)
494}
0495
=== modified file 'app/ui/EmptyState.qml'
--- app/ui/EmptyState.qml 2015-03-04 03:27:26 +0000
+++ app/ui/EmptyState.qml 2015-04-11 21:39:04 +0000
@@ -31,14 +31,18 @@
31 property alias iconName: emptyIcon.name31 property alias iconName: emptyIcon.name
32 property alias title: emptyLabel.text32 property alias title: emptyLabel.text
33 property alias subTitle: emptySublabel.text33 property alias subTitle: emptySublabel.text
34 property alias iconSource: emptyIcon.source
35
36 property real iconHeight: units.gu(10)
37 property real iconWidth: units.gu(10)
3438
35 height: childrenRect.height39 height: childrenRect.height
36 width: parent.width40 width: parent.width
3741
38 Icon {42 Icon {
39 id: emptyIcon43 id: emptyIcon
40 width: height44 width: parent.iconWidth
41 height: units.gu(10)45 height: parent.iconHeight
42 color: podbird.theme.baseIcon46 color: podbird.theme.baseIcon
43 anchors.horizontalCenter: parent.horizontalCenter47 anchors.horizontalCenter: parent.horizontalCenter
44 }48 }
4549
=== modified file 'app/ui/EpisodesPage.qml'
--- app/ui/EpisodesPage.qml 2015-03-29 15:33:26 +0000
+++ app/ui/EpisodesPage.qml 2015-04-11 21:39:04 +0000
@@ -30,17 +30,21 @@
30 id: episodesPage30 id: episodesPage
3131
32 visible: false32 visible: false
33 title: episodeName33 title: i18n.tr("Podcast")
34 flickable: null
3435
35 property string episodeName36 property string episodeName
36 property string episodeId37 property string episodeId
37 property string episodeArtist38 property string episodeArtist
38 property string episodeImage39 property string episodeImage
40 property string tempGuid: "NULL"
3941
40 property bool episodesUpdating: false;42 property bool episodesUpdating: false;
4143
42 Component.onCompleted: {44 Component.onCompleted: {
43 loadEpisodes(episodeId, episodeArtist, episodeImage)45 loadEpisodes(episodeId, episodeArtist, episodeImage)
46 if (downloader.downloadingGuid != "")
47 tempGuid = downloader.downloadingGuid
44 }48 }
4549
46 /*50 /*
@@ -95,7 +99,7 @@
95 onTriggered: {99 onTriggered: {
96 var db = Podcasts.init();100 var db = Podcasts.init();
97 db.transaction(function (tx) {101 db.transaction(function (tx) {
98 tx.executeSql("UPDATE Episode SET listened=1 WHERE podcast=?", [episodeModel.pid]);102 tx.executeSql("UPDATE Episode SET listened=1 WHERE podcast=?", [episodeId]);
99 refreshModel();103 refreshModel();
100 });104 });
101 }105 }
@@ -122,6 +126,7 @@
122 episodeList.forceActiveFocus()126 episodeList.forceActiveFocus()
123 searchField.text = ""127 searchField.text = ""
124 episodesPage.state = "default"128 episodesPage.state = "default"
129 episodeList.positionViewAtBeginning()
125 }130 }
126 }131 }
127132
@@ -139,7 +144,36 @@
139 Connections {144 Connections {
140 target: downloader145 target: downloader
141 onDownloadingGuidChanged: {146 onDownloadingGuidChanged: {
142 loadEpisodes(episodeId, episodeArtist, episodeImage);147 var db = Podcasts.init();
148 db.transaction(function (tx) {
149 /*
150 If tempGuid is NULL, then the episode currently being downloaded is not found within
151 this podcast. On the other hand, if it is within this podcast, then update the episodeModel
152 with the downloadedfile location we just received from the downloader.
153 */
154 if (tempGuid != "NULL") {
155 var rs2 = tx.executeSql("SELECT downloadedfile, podcast FROM Episode WHERE guid=?", [tempGuid]);
156 for (var i=0; i<episodeModel.count; i++) {
157 if (episodeModel.get(i).guid == tempGuid) {
158 console.log("[LOG]: Setting episode download URL to " + rs2.rows.item(0).downloadedfile)
159 episodeModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
160 break
161 }
162 }
163 tempGuid = "NULL"
164 }
165
166 /*
167 Here it is checked if the currently downloaded episode belongs to the podcast
168 page being currently displayed. If it is, then the downloaded episode guid is
169 stored in the tempGuid variable to track it.
170 */
171 var rs = tx.executeSql("SELECT podcast FROM Episode WHERE guid=?", [downloader.downloadingGuid]);
172
173 if (downloader.downloadingGuid != "" && rs.rows.item(0).podcast == episodeId && tempGuid == "NULL") {
174 tempGuid = downloader.downloadingGuid
175 }
176 });
143 }177 }
144 }178 }
145179
@@ -155,12 +189,12 @@
155 onClicked: {189 onClicked: {
156 var db = Podcasts.init();190 var db = Podcasts.init();
157 db.transaction(function (tx) {191 db.transaction(function (tx) {
158 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeModel.pid]);192 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeId]);
159 for(var i = 0; i < rs.rows.length; i++) {193 for(var i = 0; i < rs.rows.length; i++) {
160 fileManager.deleteFile(rs.rows.item(i).downloadedfile);194 fileManager.deleteFile(rs.rows.item(i).downloadedfile);
161 }195 }
162 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeModel.pid]);196 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeId]);
163 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeModel.pid]);197 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeId]);
164 mainStack.pop()198 mainStack.pop()
165 PopupUtils.close(dialogInternal)199 PopupUtils.close(dialogInternal)
166 });200 });
@@ -176,20 +210,135 @@
176 }210 }
177 }211 }
178212
213 Component {
214 id: popoverComponent
215
216 Popover {
217 id: popover
218
219 property bool queued: false
220 property bool listened: false
221 property string downloadedfile: ""
222 property string guid: ""
223 property string audiourl: ""
224 property int index: -1
225
226 contentWidth: mainColumn.width
227
228 Column {
229 id: mainColumn
230
231 width: Math.max(download.width, listen.width)
232 anchors.top: parent.top
233
234 ListItem.Empty {
235 id: download
236
237 width: Math.max(row.width, row2.width)
238
239 Row {
240 id: row
241
242 spacing: units.gu(3)
243 anchors.left: parent.left
244 anchors.leftMargin: units.gu(2)
245 anchors.verticalCenter: parent.verticalCenter
246 width: downloadIcon.width + downloadText.implicitWidth + row.spacing + units.gu(4)
247
248 Icon {
249 id: downloadIcon
250 width: height
251 height: downloadText.height
252 name: popover.downloadedfile ? "delete" : (popover.queued && downloader.downloadingGuid !== popover.guid ? "history" : "save")
253 }
254
255 Label {
256 id: downloadText
257 color: UbuntuColors.darkGrey
258 text: popover.downloadedfile ? i18n.tr("Delete local file")
259 : (popover.queued && downloader.downloadingGuid !== popover.guid ? i18n.tr("Episode queued for download")
260 : i18n.tr("Download episode"))
261 }
262 }
263
264 enabled: downloader.downloadingGuid !== popover.guid
265 onClicked: {
266 var db = Podcasts.init();
267 if (popover.downloadedfile) {
268 fileManager.deleteFile(popover.downloadedfile);
269 db.transaction(function (tx) {
270 tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [popover.guid]);
271 });
272 episodeModel.setProperty(popover.index, "downloadedfile", "")
273 } else {
274 db.transaction(function (tx) {
275 tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [popover.guid]);
276 });
277 episodeModel.setProperty(popover.index, "queued", 1)
278 downloader.addDownload(popover.guid, popover.audiourl);
279 }
280 PopupUtils.close(popover)
281 }
282 }
283
284 ListItem.Empty {
285 id: listen
286
287 showDivider: false
288 width: Math.max(row.width, row2.width)
289
290 Row {
291 id: row2
292
293 spacing: units.gu(3)
294 anchors.left: parent.left
295 anchors.leftMargin: units.gu(2)
296 anchors.verticalCenter: parent.verticalCenter
297 width: listenIcon.width + listenText.implicitWidth + row2.spacing + units.gu(4)
298
299 Icon {
300 id: listenIcon
301 width: height
302 height: listenText.height
303 name: popover.listened ? "view-collapse" : "select"
304 }
305
306 Label {
307 id: listenText
308 color: UbuntuColors.darkGrey
309 text: popover.listened ? "Mark episode unlistened" : "Mark episode listened"
310 }
311 }
312
313 onClicked: {
314 var db = Podcasts.init();
315 db.transaction(function (tx) {
316 if (popover.listened)
317 tx.executeSql("UPDATE Episode SET listened=0 WHERE guid=?", [popover.guid])
318 else
319 tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [popover.guid])
320 refreshModel();
321 });
322 PopupUtils.close(popover)
323 }
324 }
325 }
326 }
327 }
328
179 EmptyState {329 EmptyState {
180 anchors.centerIn: parent330 anchors.centerIn: parent
181 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0331 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
182 visible: episodesPage.state === "search" && sortedEpisodeModel.count === 0332 visible: (episodesPage.state === "search" && sortedEpisodeModel.count === 0) || (episodeModel.count === 0 && podbird.settings.hideListened)
183 iconName: "music-app-symbolic"333 iconHeight: units.gu(12)
184 title: i18n.tr("No Episodes found")334 iconWidth: iconHeight + units.gu(10)
185 subTitle: i18n.tr("No episodes found matching the search term.")335 iconSource: Qt.resolvedUrl("../graphics/notFound.svg")
336 title: podbird.settings.hideListened ? i18n.tr("No more episodes") : i18n.tr("No Episodes found")
337 subTitle: podbird.settings.hideListened ? i18n.tr("All episodes have been listened to.") : i18n.tr("No episodes found matching the search term.")
186 }338 }
187339
188 ListModel {340 ListModel {
189 id: episodeModel341 id: episodeModel
190 property string pid;
191 property string artist;
192 property string image;
193 }342 }
194343
195 SortFilterModel {344 SortFilterModel {
@@ -199,12 +348,126 @@
199 filter.pattern: RegExp(searchField.text, "gi")348 filter.pattern: RegExp(searchField.text, "gi")
200 }349 }
201350
202 ListView {351 function formatTime(seconds) {
352 var time = Podcasts.getTimeDiff(seconds)
353 var hour = time[0]
354 var minute = time[1]
355 // TRANSLATORS: the first argument is the number of hours,
356 // followed by minute (eg. 20h 3m)
357 if(hour > 0 && minute > 0) {
358 // xgettext: no-c-format
359 return (i18n.tr("%1 hr %2 min"))
360 .arg(hour)
361 .arg(minute)
362 }
363
364 // TRANSLATORS: this string indicates the number of hours
365 // eg. 20h (no plural state required)
366 else if(hour > 0 && minute === 0) {
367 // xgettext: no-c-format
368 return (i18n.tr("%1 hr"))
369 .arg(hour)
370 }
371
372 // TRANSLATORS: this string indicates the number of minutes
373 // eg. 15m (no plural state required)
374 else if(hour === 0 && minute > 0) {
375 // xgettext: no-c-format
376 return (i18n.tr("%1 min"))
377 .arg(minute)
378 }
379
380 else {
381 return Podcasts.formatTime(seconds)
382 }
383 }
384
385 UbuntuListView {
203 id: episodeList386 id: episodeList
204387
388 Component.onCompleted: {
389 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
390 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
391 var scaleFactor = units.gridUnit / 8;
392 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
393 flickDeceleration = flickDeceleration * scaleFactor;
394 }
395
396 anchors.fill: parent
397 model: sortedEpisodeModel
398
205 clip: true399 clip: true
206 anchors.fill: parent400 section.property: "listened"
207 model: sortedEpisodeModel401 section.labelPositioning: ViewSection.InlineLabels
402
403 section.delegate: Rectangle {
404 width: parent.width
405 color: section === "0" ? podbird.theme.hightlightListView : "Transparent"
406 height: header.implicitHeight + units.gu(2)
407 Label {
408 id: header
409 anchors {
410 left: parent.left
411 right: parent.right
412 margins: units.gu(2)
413 verticalCenter: parent.verticalCenter
414 }
415 fontSize: "x-large"
416 text: section === "0" ? i18n.tr("Unheard") : i18n.tr("Listened")
417 }
418 }
419
420 header: BlurredBackground {
421 id: blurredBackground
422
423 art: episodeImage
424 width: parent.width
425 visible: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0
426 height: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0 ? cover.height + units.gu(4) : 0
427 backgroundStrength: podbird.settings.themeName === "Light.qml" ? 0.3 : 0.6
428
429 Image {
430 id:cover
431 width: units.gu(12)
432 height: width
433 sourceSize.height: width
434 sourceSize.width: width
435 source: episodeImage
436 anchors {
437 left: parent.left
438 top: parent.top
439 margins: units.gu(2)
440 }
441 }
442
443 Column {
444 id: podcastTitle
445
446 anchors {
447 left: cover.right
448 right: parent.right
449 bottom: parent.bottom
450 margins: units.gu(2)
451 }
452
453 Label {
454 text: episodeName
455 width: parent.width
456 wrapMode: Text.WordWrap
457 maximumLineCount: 2
458 elide: Text.ElideRight
459 color: podbird.theme.baseText
460 }
461
462 Label {
463 text: i18n.tr("%1 episode", "%1 episodes", episodeList.count).arg(episodeList.count)
464 width: parent.width
465 elide: Text.ElideRight
466 fontSize: "x-small"
467 color: podbird.theme.baseText
468 }
469 }
470 }
208471
209 footer: Item {472 footer: Item {
210 width: parent.width473 width: parent.width
@@ -214,51 +477,41 @@
214 delegate: ListItem.Empty {477 delegate: ListItem.Empty {
215 id: listItem478 id: listItem
216479
217 property bool expanded: false480 property bool expanded
218481
219 width: parent.width482 height: dataColumn.height + units.gu(2)
220 height: mainColumn.height
221 highlightWhenPressed: false483 highlightWhenPressed: false
484 showDivider: false
222485
223 onClicked: listItem.expanded = !listItem.expanded486 onClicked: {
487 expanded = !expanded;
488 }
224489
225 Rectangle {490 Rectangle {
226 anchors.fill: parent491 visible: !model.listened
227 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"492 width: parent.width
493 height: dataColumn.height + units.gu(2)
494 color: podbird.theme.hightlightListView
228 }495 }
229496
230 Column {497 Column {
231 id: mainColumn498 id: dataColumn
232
233 anchors {
234 top: parent.top
235 left: parent.left
236 right: parent.right
237 margins: units.gu(2)
238 topMargin: units.gu(1)
239 }
240499
241 spacing: units.gu(1)500 spacing: units.gu(1)
501 anchors.left: parent.left
502 anchors.right: parent.right
503 anchors.margins: units.gu(2)
504 anchors.top: parent.top
505 anchors.topMargin: units.gu(0.5)
242506
243 RowLayout {507 RowLayout {
244 id: titleRow508 id: rowlayout
245509
246 width: parent.width510 width: parent.width
247 spacing: units.gu(2)511 height: titleColumn.height
248
249 Image {
250 id: imgFrame
251 width: units.gu(6)
252 height: width
253 sourceSize.height: width
254 sourceSize.width: width
255 source: model.image
256 }
257512
258 Column {513 Column {
259 id: detailColumn514 id: titleColumn
260
261 anchors.verticalCenter: imgFrame.verticalCenter
262 Layout.fillWidth: true515 Layout.fillWidth: true
263516
264 Label {517 Label {
@@ -267,19 +520,88 @@
267 maximumLineCount: 2520 maximumLineCount: 2
268 wrapMode: Text.WordWrap521 wrapMode: Text.WordWrap
269 elide: Text.ElideRight522 elide: Text.ElideRight
270 color: currentGuid === model.guid ? podbird.theme.focusText523 color: listItem.expanded || currentGuid === model.guid || downloader.downloadingGuid === model.guid ? podbird.theme.focusText
271 : podbird.theme.baseText524 : podbird.theme.baseText
272 }525 }
273526
274 Label {527 Label {
275 id: episodePublishDate528 id: episodePublishDate
276 width: parent.width529 width: parent.width
277 text: Qt.formatDate(new Date(model.published), "MMM d, yyyy")530 text: formatTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
278 fontSize: "x-small"531 fontSize: "x-small"
279 color: currentGuid === model.guid ? podbird.theme.focusText
280 : podbird.theme.baseText
281 elide: Text.ElideRight532 elide: Text.ElideRight
282 }533 color: podbird.theme.baseSubText
534 }
535 }
536
537 ActionButton {
538 id: contextualMenu
539
540 width: units.gu(5)
541 height: units.gu(4)
542
543 iconName: "contextual-menu"
544 color: progressBar.visible || listItem.expanded ? podbird.theme.focusText
545 : podbird.theme.baseIcon
546 onClicked: {
547 var popover = PopupUtils.open(popoverComponent, contextualMenu)
548 popover.queued = Qt.binding(function() { return model.queued })
549 popover.listened = Qt.binding(function() { return model.listened })
550 popover.guid = Qt.binding(function() { return model.guid })
551 popover.audiourl = Qt.binding(function() { return model.audiourl })
552 popover.downloadedfile = Qt.binding(function() { return episodeModel.get(index).downloadedfile })
553 popover.index = Qt.binding(function() { return index })
554 }
555 }
556
557 ActionButton {
558 width: units.gu(4)
559 height: units.gu(4)
560
561 iconName: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause"
562 : "media-playback-start"
563 color: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? podbird.theme.focusText
564 : podbird.theme.baseIcon
565
566 onClicked: {
567 var db = Podcasts.init();
568 db.transaction(function (tx) {
569 if (currentGuid === model.guid) {
570 if (player.playbackState === MediaPlayer.PlayingState) {
571 player.pause()
572 } else {
573 player.play()
574 }
575 } else {
576 currentGuid = "";
577 player.source = model.downloadedfile ? model.downloadedfile : model.audiourl;
578 var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
579 player.play();
580 player.seek(rs.rows.item(0).position);
581 currentName = model.name;
582 currentArtist = model.artist;
583 currentImage = model.image;
584 currentGuid = model.guid;
585 }
586 });
587 }
588 }
589 }
590
591 Rectangle {
592 id: progressBar
593 radius: width/3
594 width: parent.width
595 height: units.dp(5)
596 color: Theme.palette.normal.base
597 visible: downloader.downloadingGuid === model.guid
598 Rectangle {
599 height: parent.height
600 radius: parent.radius
601 anchors.left: parent.left
602 anchors.top: parent.top
603 color: podbird.theme.focusText
604 width: downloader.progress > 0 ? Math.min((downloader.progress / 100) * parent.width, parent.width) : 0
283 }605 }
284 }606 }
285607
@@ -288,184 +610,15 @@
288 text: model.description610 text: model.description
289 textFormat: Text.RichText611 textFormat: Text.RichText
290 clip: true612 clip: true
291 height: listItem.expanded ? contentHeight : units.gu(4)613 height: listItem.expanded ? contentHeight : 0
292 wrapMode: Text.WordWrap614 wrapMode: Text.WordWrap
293 width: parent.width615 width: parent.width
294 elide: Text.ElideRight
295 fontSize: "small"616 fontSize: "small"
296 color: podbird.theme.baseSubText617 color: podbird.theme.baseSubText
297 Behavior on height {618 Behavior on height {
298 UbuntuNumberAnimation {619 UbuntuNumberAnimation {
299 duration: UbuntuAnimation.SlowDuration620 duration: UbuntuAnimation.BriskDuration
300 }621 }
301 }
302
303 }
304
305 Item {
306 id: statusBox
307
308 width: parent.width
309 height: units.gu(6)
310
311 function formatTime(seconds) {
312 var time = Podcasts.getTimeDiff(seconds)
313 var hour = time[0]
314 var minute = time[1]
315 // TRANSLATORS: the first argument is the number of hours,
316 // followed by minute (eg. 20h 3m)
317 if(hour > 0 && minute > 0) {
318 // xgettext: no-c-format
319 return (i18n.tr("%1h %2m"))
320 .arg(hour)
321 .arg(minute)
322 }
323
324 // TRANSLATORS: this string indicates the number of hours
325 // eg. 20h (no plural state required)
326 else if(hour > 0 && minute === 0) {
327 // xgettext: no-c-format
328 return (i18n.tr("%1h"))
329 .arg(hour)
330 }
331
332 // TRANSLATORS: this string indicates the number of minutes
333 // eg. 15m (no plural state required)
334 else if(hour === 0 && minute > 0) {
335 // xgettext: no-c-format
336 return (i18n.tr("%1m"))
337 .arg(minute)
338 }
339
340 else {
341 return Podcasts.formatTime(model.duration)
342 }
343 }
344
345 Rectangle {
346 id: listened
347 border.color: UbuntuColors.lightGrey
348 height: units.gu(2.5)
349 width: height
350 radius: width / 2
351 anchors.right: durationIcon.left
352 anchors.rightMargin: units.gu(2)
353 anchors.verticalCenter: actionRow.verticalCenter
354 visible: model.listened
355 Icon {
356 id: tick
357 name: "tick"
358 anchors.centerIn: parent
359 anchors.verticalCenterOffset: units.gu(0.1)
360 height: units.gu(1.4)
361 width: height
362 }
363 }
364
365 Icon {
366 id: durationIcon
367 width: units.gu(2.5)
368 height: width
369 name: "alarm-clock"
370 visible: duration.text !== ""
371 anchors.right: duration.left
372 anchors.rightMargin: units.gu(0.5)
373 anchors.verticalCenter: actionRow.verticalCenter
374 color: podbird.theme.baseIcon
375 }
376
377 Label {
378 id: duration
379 color: podbird.theme.baseText
380 anchors.right: parent.right
381 anchors.verticalCenter: durationIcon.verticalCenter
382 fontSize: "small"
383 text: !isNaN(model.duration) && model.duration !== 0 ? statusBox.formatTime(model.duration) : ""
384 }
385
386 Row {
387 id: actionRow
388
389 anchors.left: parent.left
390 anchors.leftMargin: units.gu(-1.5)
391
392 ActionButton {
393 width: units.gu(5)
394 height: units.gu(4)
395
396 iconName: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause"
397 : "media-playback-start"
398
399 onClicked: {
400 var db = Podcasts.init();
401 db.transaction(function (tx) {
402 if (currentGuid === model.guid) {
403 if (player.playbackState === MediaPlayer.PlayingState) {
404 player.pause()
405 } else {
406 player.play()
407 }
408 } else {
409 currentGuid = "";
410 player.source = model.downloadedfile ? model.downloadedfile : model.audiourl;
411 var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
412 player.play();
413 player.seek(rs.rows.item(0).position);
414 currentName = model.name;
415 currentArtist = model.artist;
416 currentImage = model.image;
417 currentGuid = model.guid;
418 }
419 });
420 }
421 }
422
423 ActionButton {
424 id: downloadButton
425
426 width: units.gu(5)
427 height: units.gu(4)
428
429 property bool queued: false
430
431 iconName: model.downloadedfile ? "delete" : (queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
432 opacity: downloader.downloadingGuid === model.guid ? 0.4 : 1.0
433 enabled: downloader.downloadingGuid !== model.guid
434
435 ActivityIndicator {
436 anchors.centerIn: parent
437 visible: downloader.downloadingGuid === model.guid
438 running: visible
439 }
440
441 onClicked: {
442 if (model.downloadedfile) {
443 fileManager.deleteFile(model.downloadedfile);
444 var db = Podcasts.init();
445 db.transaction(function (tx) {
446 tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
447 });
448 loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image);
449 } else {
450 downloadButton.queued = true;
451 downloader.addDownload(model.guid, model.audiourl);
452 }
453 }
454 }
455 }
456
457
458 ProgressBar {
459 visible: downloader.downloadingGuid === model.guid
460 minimumValue: 0
461 maximumValue: 100
462 anchors.left: actionRow.right
463 anchors.right: model.listened ? listened.left : durationIcon.left
464 anchors.leftMargin: units.gu(2)
465 anchors.rightMargin: units.gu(2)
466 anchors.verticalCenter: actionRow.verticalCenter
467 height: units.gu(2.6)
468 value: downloader.progress
469 }622 }
470 }623 }
471 }624 }
@@ -483,21 +636,28 @@
483636
484 function refreshModel() {637 function refreshModel() {
485 var db = Podcasts.init();638 var db = Podcasts.init();
486 loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image);639 loadEpisodes(episodeId, episodeArtist, episodeImage);
487 episodesUpdating = false;640 episodesUpdating = false;
488 }641 }
489642
490 function loadEpisodes(pid, artist, img) {643 function loadEpisodes(pid, artist, img) {
644 var i, episode;
645 var newCount = 0;
646
647 episodeModel.clear();
648
491 var db = Podcasts.init();649 var db = Podcasts.init();
492 db.transaction(function (tx) {650 db.transaction(function (tx) {
493 episodeModel.clear();
494 var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]);651 var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]);
495 for(var i = 0; i < rs.rows.length; i++) {652 for(i = 0; i < rs.rows.length; i++) {
496 var episode = rs.rows.item(i);653 episode = rs.rows.item(i);
497 episodeModel.pid = pid;654 //console.log(episode.queued)
498 episodeModel.artist = artist;655 if (!episode.listened) {
499 episodeModel.image = img;656 episodeModel.insert(newCount, {"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued});
500 episodeModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : img, "artist" : artist, "audiourl" : episode.audiourl});657 newCount++;
658 } else if (!podbird.settings.hideListened) {
659 episodeModel.insert(i,{"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued});
660 }
501 }661 }
502 });662 });
503 }663 }
@@ -558,12 +718,13 @@
558 db.transaction(function(tx2) {718 db.transaction(function(tx2) {
559 var ers = tx2.executeSql("SELECT rowid FROM Episode WHERE guid=?", [track.guid]);719 var ers = tx2.executeSql("SELECT rowid FROM Episode WHERE guid=?", [track.guid]);
560 if (ers.rows.length === 0) {720 if (ers.rows.length === 0) {
561 tx2.executeSql("INSERT INTO Episode(podcast, name, description, audiourl, guid, listened, duration, published) VALUES(?, ?, ? , ?, ?, ?, ?, ?)", [pid,721 tx2.executeSql("INSERT INTO Episode(podcast, name, description, audiourl, guid, listened, queued, duration, published) VALUES(?, ?, ? , ?, ?, ?, ?, ?)", [pid,
562 track.name,722 track.name,
563 track.description,723 track.description,
564 track.audiourl,724 track.audiourl,
565 track.guid,725 track.guid,
566 false,726 false,
727 false,
567 track.duration,728 track.duration,
568 track.published]);729 track.published]);
569 }730 }
570731
=== modified file 'app/ui/ExpandableListItem.qml'
--- app/ui/ExpandableListItem.qml 2015-03-27 15:04:39 +0000
+++ app/ui/ExpandableListItem.qml 2015-04-11 21:39:04 +0000
@@ -61,6 +61,7 @@
61 width: units.gu(2)61 width: units.gu(2)
62 height: width62 height: width
63 anchors.right: parent.right63 anchors.right: parent.right
64 anchors.rightMargin: units.gu(1)
64 anchors.verticalCenter: parent.verticalCenter65 anchors.verticalCenter: parent.verticalCenter
6566
66 name: "go-down"67 name: "go-down"
6768
=== modified file 'app/ui/NowPlayingPage.qml'
--- app/ui/NowPlayingPage.qml 2015-03-08 11:10:07 +0000
+++ app/ui/NowPlayingPage.qml 2015-04-11 21:39:04 +0000
@@ -101,6 +101,11 @@
101 function formatValue(v) { return Podcasts.formatTime(v/1000); }101 function formatValue(v) { return Podcasts.formatTime(v/1000); }
102 }102 }
103103
104 Connections {
105 target: player
106 onPositionChanged: scrubber.value = player.position
107 }
108
104 Label {109 Label {
105 id: startTime110 id: startTime
106 fontSize: "small"111 fontSize: "small"
107112
=== modified file 'app/ui/PlayerControls.qml'
--- app/ui/PlayerControls.qml 2015-03-08 11:10:07 +0000
+++ app/ui/PlayerControls.qml 2015-04-11 21:39:04 +0000
@@ -53,7 +53,7 @@
53 id: progressBarHint53 id: progressBarHint
54 anchors.left: parent.left54 anchors.left: parent.left
55 anchors.top: cover.bottom55 anchors.top: cover.bottom
56 color: UbuntuColors.orange56 color: podbird.theme.focusText
57 height: units.gu(0.25)57 height: units.gu(0.25)
58 width: player.duration > 0 ? (player.position / player.duration) * parent.width : 058 width: player.duration > 0 ? (player.position / player.duration) * parent.width : 0
59 }59 }
@@ -72,7 +72,7 @@
72 color: "white"72 color: "white"
73 elide: Text.ElideRight73 elide: Text.ElideRight
74 maximumLineCount: 274 maximumLineCount: 2
75 wrapMode: Text.WrapAnywhere75 wrapMode: Text.WordWrap
76 text: currentName76 text: currentName
77 }77 }
7878
7979
=== modified file 'app/ui/PodcastsTab.qml'
--- app/ui/PodcastsTab.qml 2015-03-29 15:33:26 +0000
+++ app/ui/PodcastsTab.qml 2015-04-11 21:39:04 +0000
@@ -36,6 +36,8 @@
36 page: Page {36 page: Page {
37 id: podcastPage37 id: podcastPage
3838
39 flickable: viewLoader.item
40
39 /*41 /*
40 #FIXME: The following lines of code is necessary due to a upstream bug42 #FIXME: The following lines of code is necessary due to a upstream bug
41 in the SDK http://pad.lv/1400297. This bug is still present in the rtm.43 in the SDK http://pad.lv/1400297. This bug is still present in the rtm.
@@ -74,6 +76,14 @@
74 podcastPage.state = "search"76 podcastPage.state = "search"
75 searchField.forceActiveFocus()77 searchField.forceActiveFocus()
76 }78 }
79 },
80
81 Action {
82 iconName: podbird.settings.showListView ? "view-grid-symbolic" : "view-list-symbolic"
83 text: podbird.settings.showListView ? i18n.tr("Grid View") : i18n.tr("List View")
84 onTriggered: {
85 podbird.settings.showListView = !podbird.settings.showListView
86 }
77 }87 }
78 ]88 ]
79 },89 },
@@ -85,7 +95,7 @@
85 iconName: "back"95 iconName: "back"
86 text: i18n.tr("Back")96 text: i18n.tr("Back")
87 onTriggered: {97 onTriggered: {
88 view.forceActiveFocus()98 viewLoader.item.forceActiveFocus()
89 searchField.text = ""99 searchField.text = ""
90 podcastPage.state = "default"100 podcastPage.state = "default"
91 }101 }
@@ -108,7 +118,7 @@
108 iconName: "back"118 iconName: "back"
109 text: i18n.tr("Back")119 text: i18n.tr("Back")
110 onTriggered: {120 onTriggered: {
111 view.forceActiveFocus()121 viewLoader.item.forceActiveFocus()
112 feedUrlField.text = ""122 feedUrlField.text = ""
113 podcastPage.state = "default"123 podcastPage.state = "default"
114 }124 }
@@ -119,7 +129,7 @@
119 iconName: "ok"129 iconName: "ok"
120 text: i18n.tr("Save Podcast")130 text: i18n.tr("Save Podcast")
121 onTriggered: {131 onTriggered: {
122 view.forceActiveFocus()132 viewLoader.item.forceActiveFocus()
123 subscribeFromFeed(feedUrlField.text);133 subscribeFromFeed(feedUrlField.text);
124 feedUrlField.text = ""134 feedUrlField.text = ""
125 podcastPage.state = "default"135 podcastPage.state = "default"
@@ -134,7 +144,7 @@
134 anchors.left: parent ? parent.left : undefined144 anchors.left: parent ? parent.left : undefined
135 anchors.right: parent ? parent.right : undefined145 anchors.right: parent ? parent.right : undefined
136 onAccepted: {146 onAccepted: {
137 view.forceActiveFocus()147 viewLoader.item.forceActiveFocus()
138 subscribeFromFeed(feedUrlField.text);148 subscribeFromFeed(feedUrlField.text);
139 feedUrlField.text = ""149 feedUrlField.text = ""
140 podcastPage.state = "default"150 podcastPage.state = "default"
@@ -167,8 +177,10 @@
167 EmptyState {177 EmptyState {
168 anchors.centerIn: parent178 anchors.centerIn: parent
169 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0179 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
180 iconHeight: units.gu(12)
181 iconWidth: iconHeight + units.gu(10)
182 iconSource: Qt.resolvedUrl("../graphics/notFound.svg")
170 visible: podcastModel.count === 0 || sortedPodcastModel.count === 0183 visible: podcastModel.count === 0 || sortedPodcastModel.count === 0
171 iconName: "music-app-symbolic"
172 title: podcastModel.count === 0 ? i18n.tr("No Podcast Subscriptions")184 title: podcastModel.count === 0 ? i18n.tr("No Podcast Subscriptions")
173 : i18n.tr("No Podcasts Found")185 : i18n.tr("No Podcasts Found")
174 subTitle: podcastModel.count === 0 ? i18n.tr("You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' page to add some.")186 subTitle: podcastModel.count === 0 ? i18n.tr("You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' page to add some.")
@@ -186,122 +198,158 @@
186 filter.pattern: RegExp(searchField.text, "gi")198 filter.pattern: RegExp(searchField.text, "gi")
187 }199 }
188200
189 ListModel {201 Loader {
190 id: episodeModel202 id: viewLoader
191 property string pid;
192 property string artist;
193 property string image;
194 }
195
196 ListView {
197 id: view
198
199 clip: true
200 model: sortedPodcastModel
201 anchors.fill: parent203 anchors.fill: parent
202204 sourceComponent: podbird.settings.showListView ? listviewComponent : cardviewComponent
203 footer: Item {205 }
204 width: parent.width206
205 height: units.gu(8)207 Component {
206 }208 id: cardviewComponent
207209
208 delegate: ListItem.Empty {210 CardView {
209 id: listItem211 id: cardView
210212 model: sortedPodcastModel
211 property bool expanded: false213 delegate: Card {
212214 id: albumCard
213 height: units.gu(8)215 coverArt: model.image
214 removable: true216 primaryText: model.name.trim()
215 confirmRemoval: true217 secondaryText: i18n.tr("%1 unheard episode", "%1 unheard episodes", model.episodeCount).arg(model.episodeCount)
216 highlightWhenPressed: false218 onClicked: {
217219 if(podcastPage.state === "search") {
218 onItemRemoved: {220 cardView.forceActiveFocus()
219 var db = Podcasts.init();221 searchField.text = ""
220 db.transaction(function (tx) {222 podcastPage.state = "default"
221 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [model.id]);223 }
222 for(var i = 0; i < rs.rows.length; i++) {224 mainStack.push(Qt.resolvedUrl("EpisodesPage.qml"), {"episodeName": model.name, "episodeId": model.id, "episodeArtist": model.artist, "episodeImage": model.image})
223 fileManager.deleteFile(rs.rows.item(i).downloadedfile);225 }
224 }226 }
225 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [model.id]);227 }
226 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [model.id]);228 }
227 refreshModel()229
228 });230 Component {
229 }231 id: listviewComponent
230232
231 Rectangle {233 ListView {
232 anchors.fill: parent234 id: listView
233 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"235
234 }236 Component.onCompleted: {
235237 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
236 onClicked: {238 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
237 if(podcastPage.state === "search") {239 var scaleFactor = units.gridUnit / 8;
238 view.forceActiveFocus()240 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
239 searchField.text = ""241 flickDeceleration = flickDeceleration * scaleFactor;
240 podcastPage.state = "default"242 }
241 }243
242 mainStack.push(Qt.resolvedUrl("EpisodesPage.qml"), {"episodeName": model.name, "episodeId": model.id, "episodeArtist": model.artist, "episodeImage": model.image})244 model: sortedPodcastModel
243 }245 anchors.fill: parent
244246
245 Column {247 footer: Item {
246 id: mainColumn248 width: parent.width
247249 height: units.gu(8)
248 anchors.left: parent.left250 }
249 anchors.right: parent.right251
250 anchors.margins: units.gu(2)252 delegate: ListItem.Empty {
251 anchors.verticalCenter: parent.verticalCenter253 id: listItem
252 spacing: units.gu(1)254
253255 property bool expanded: false
254 RowLayout {256
255 id: titleRow257 height: units.gu(8)
256258 removable: true
257 width: parent.width259 confirmRemoval: true
258 spacing: units.gu(2)260 showDivider: false
259261 highlightWhenPressed: false
260 Image {262
261 id: imgFrame263 onItemRemoved: {
262 width: units.gu(6)264 var db = Podcasts.init();
263 height: width265 db.transaction(function (tx) {
264 sourceSize.height: width266 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [model.id]);
265 sourceSize.width: width267 for(var i = 0; i < rs.rows.length; i++) {
266 source: model.image268 fileManager.deleteFile(rs.rows.item(i).downloadedfile);
267 }269 }
268270 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [model.id]);
269 Column {271 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [model.id]);
270 id: detailColumn272 podcastModel.remove(index, 1)
271273 });
272 anchors.verticalCenter: imgFrame.verticalCenter274 }
273 Layout.fillWidth: true275
274276 Rectangle {
275 Label {277 anchors.fill: parent
276 id: podcastTitle278 opacity: 0.3
277 textFormat: Text.PlainText279 color: index % 2 === 0 ? podbird.theme.hightlightListView : "Transparent"
278 text: model.name.trim()280 }
279 width: parent.width281
280 fontSize: "small"282 onClicked: {
281 elide: Text.ElideRight283 if(podcastPage.state === "search") {
282 color: podbird.theme.baseText284 listView.forceActiveFocus()
283 }285 searchField.text = ""
284286 podcastPage.state = "default"
285 Label {287 }
286 id: episodeCount288 mainStack.push(Qt.resolvedUrl("EpisodesPage.qml"), {"episodeName": model.name, "episodeId": model.id, "episodeArtist": model.artist, "episodeImage": model.image})
287 width: parent.width289 }
288 fontSize: "x-small"290
289 color: podbird.theme.baseSubText291 Column {
290 visible: model.episodeCount > 0292 id: mainColumn
291 text: i18n.tr("%1 unheard episode", "%1 unheard episodes", model.episodeCount).arg(model.episodeCount)293
292 }294 anchors.left: parent.left
293 }295 anchors.right: parent.right
294 }296 anchors.margins: units.gu(2)
295 }297 anchors.verticalCenter: parent.verticalCenter
296 }298 spacing: units.gu(1)
297299
298 PullToRefresh {300 RowLayout {
299 refreshing: episodesUpdating301 id: titleRow
300 onRefresh: updateEpisodes();302
301 }303 width: parent.width
302 }304 spacing: units.gu(2)
303 Scrollbar {305
304 flickableItem: view306 Image {
307 id: imgFrame
308 width: units.gu(6)
309 height: width
310 sourceSize.height: width
311 sourceSize.width: width
312 source: model.image
313 }
314
315 Column {
316 id: detailColumn
317
318 anchors.verticalCenter: imgFrame.verticalCenter
319 Layout.fillWidth: true
320
321 Label {
322 id: podcastTitle
323 textFormat: Text.PlainText
324 text: model.name.trim()
325 width: parent.width
326 fontSize: "small"
327 elide: Text.ElideRight
328 color: podbird.theme.baseText
329 }
330
331 Label {
332 id: episodeCount
333 width: parent.width
334 fontSize: "x-small"
335 color: podbird.theme.baseSubText
336 visible: model.episodeCount > 0
337 text: i18n.tr("%1 unheard episode", "%1 unheard episodes", model.episodeCount).arg(model.episodeCount)
338 }
339 }
340 }
341 }
342 }
343
344 Scrollbar {
345 flickableItem: listView
346 }
347
348 PullToRefresh {
349 refreshing: episodesUpdating
350 onRefresh: updateEpisodes();
351 }
352 }
305 }353 }
306 }354 }
307355
308356
=== renamed file 'app/ui/SearchTab.qml' => 'app/ui/SearchPage.qml'
--- app/ui/SearchTab.qml 2015-03-29 15:33:26 +0000
+++ app/ui/SearchPage.qml 2015-04-11 21:39:04 +0000
@@ -23,24 +23,64 @@
23import Ubuntu.Components.ListItems 1.0 as ListItem23import Ubuntu.Components.ListItems 1.0 as ListItem
24import "../podcasts.js" as Podcasts24import "../podcasts.js" as Podcasts
2525
26Tab {26Page {
27 title: i18n.tr("Find New Podcasts")27 id: searchPage
2828
29 property var xhr: new XMLHttpRequest;29 property var xhr: new XMLHttpRequest;
3030
31 page: Page {31 /*
32 Column {32 #FIXME: The following lines of code is necessary due to a upstream bug
33 spacing: units.gu(2)33 in the SDK http://pad.lv/1400297. This bug is still present in the rtm.
34 anchors.fill: parent34 Once it is fixed, this following property and connection can be remvoed.
35 anchors.topMargin: units.gu(2)35 */
3636 property Item __oldContents: null
37 TextField {37 Connections {
38 target: searchPage.head
39 onContentsChanged: {
40 if (searchPage.__oldContents) {
41 searchPage.__oldContents.parent = null;
42 }
43 searchPage.__oldContents = searchPage.head.contents;
44 }
45 }
46
47 state: "default"
48 states: [
49 PageHeadState {
50 name: "default"
51 head: searchPage.head
52 actions: [
53 Action {
54 iconName: "search"
55 text: i18n.tr("Search Episode")
56 onTriggered: {
57 searchPage.state = "search"
58 searchField.forceActiveFocus()
59 }
60 }
61 ]
62 },
63
64 PageHeadState {
65 name: "search"
66 head: searchPage.head
67 backAction: Action {
68 iconName: "back"
69 text: i18n.tr("Back")
70 onTriggered: {
71 resultsView.forceActiveFocus()
72 searchField.text = ""
73 searchPage.state = "default"
74 }
75 }
76
77 contents: TextField {
38 id: searchField78 id: searchField
39 anchors.left: parent.left79 inputMethodHints: Qt.ImhNoPredictiveText
40 anchors.right: parent.right
41 anchors.margins: units.gu(2)
42 placeholderText: i18n.tr("Search...")80 placeholderText: i18n.tr("Search...")
43 inputMethodHints: Qt.ImhNoPredictiveText;81 anchors.left: parent ? parent.left : undefined
82 anchors.right: parent ? parent.right : undefined
83 anchors.rightMargin: units.gu(2)
44 onTextChanged: {84 onTextChanged: {
45 if (text.length > 2) {85 if (text.length > 2) {
46 search(text)86 search(text)
@@ -49,94 +89,113 @@
49 }89 }
50 }90 }
51 }91 }
5292 }
53 ListView {93 ]
54 id: resultsView94
55 clip: true95 EmptyState {
56 width: parent.width96 anchors.centerIn: parent
57 model: searchResults97 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
58 height: parent.height - searchField.height - units.gu(2)98 iconHeight: units.gu(12)
5999 iconWidth: iconHeight + units.gu(10)
60 footer: Item {100 visible: searchPage.state !== "search" ? true : searchResults.count === 0 && searchField.text.length > 2
61 width: parent.width101 iconSource: searchPage.state !== "search" ? Qt.resolvedUrl("../graphics/owlSearch.svg") : Qt.resolvedUrl("../graphics/notFound.svg")
62 height: units.gu(7)102 title: searchPage.state !== "search" ? i18n.tr("Looking for a new Podcast?") : i18n.tr("No Podcasts found")
63 }103 subTitle: searchPage.state !== "search" ? i18n.tr("Click the 'magnifier' at the top to search.") : i18n.tr("No podcasts found matching the search term.")
64104 }
65 delegate: ListItem.Empty {105
66 id: listItem106 ListView {
67107 id: resultsView
68 height: units.gu(8)108
69 highlightWhenPressed: false109 Component.onCompleted: {
70110 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
71 Rectangle {111 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
72 anchors.fill: parent112 var scaleFactor = units.gridUnit / 8;
73 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"113 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
74 }114 flickDeceleration = flickDeceleration * scaleFactor;
75115 }
76 RowLayout {116
77 id: titleRow117 model: searchResults
78118 anchors.fill: parent
79 anchors.left: parent.left119
80 anchors.right: parent.right120 footer: Item {
81 anchors.margins: units.gu(2)121 width: parent.width
82 anchors.verticalCenter: parent.verticalCenter122 height: units.gu(7)
83123 }
84 spacing: units.gu(2)124
85125 delegate: ListItem.Empty {
86 Image {126 id: listItem
87 id: imgFrame127
88 width: units.gu(6)128 height: units.gu(8)
89 height: width129 showDivider: false
90 sourceSize.height: width130 highlightWhenPressed: false
91 sourceSize.width: width131
92 source: model.image132 Rectangle {
93 }133 anchors.fill: parent
94134 opacity: 0.3
95 Column {135 color: index % 2 === 0 ? podbird.theme.hightlightListView : "Transparent"
96 id: detailColumn136 }
97137
98 anchors.verticalCenter: imgFrame.verticalCenter138 RowLayout {
99 Layout.fillWidth: true139 id: titleRow
100140
101 Label {141 anchors.left: parent.left
102 id: podcastTitle142 anchors.right: parent.right
103 textFormat: Text.PlainText143 anchors.margins: units.gu(2)
104 text: model.name144 anchors.verticalCenter: parent.verticalCenter
105 width: parent.width145
106 fontSize: "small"146 spacing: units.gu(2)
107 elide: Text.ElideRight147
108 }148 Image {
109149 id: imgFrame
110 Label {150 width: units.gu(6)
111 id: episodeCount151 height: width
112 width: parent.width152 sourceSize.height: width
113 color: "#999999"153 sourceSize.width: width
114 text: model.artist154 source: model.image
115 fontSize: "x-small"155 }
116 elide: Text.ElideRight156
117 }157 Column {
118 }158 id: detailColumn
119159
120 Button {160 anchors.verticalCenter: imgFrame.verticalCenter
121 anchors.right: parent.right161 Layout.fillWidth: true
122 text: i18n.tr("Subscribe")162
123 color: UbuntuColors.green163 Label {
124 onClicked: {164 id: podcastTitle
125 Podcasts.subscribe(model.artist, model.name, model.feed, model.image);165 textFormat: Text.PlainText
126 imageDownloader.feed = model.feed;166 text: model.name
127 imageDownloader.download(model.image);167 width: parent.width
128 tabs.selectedTabIndex = 0;168 fontSize: "medium"
129 searchField.text = ""169 elide: Text.ElideRight
130 }170 }
131 }171
132 }172 Label {
133 }173 id: episodeCount
134174 width: parent.width
135 Scrollbar {175 color: "#999999"
136 flickableItem: resultsView176 text: model.artist
137 }177 fontSize: "x-small"
138178 elide: Text.ElideRight
139 }179 }
180 }
181
182 Button {
183 anchors.right: parent.right
184 text: i18n.tr("Subscribe")
185 color: UbuntuColors.green
186 onClicked: {
187 Podcasts.subscribe(model.artist, model.name, model.feed, model.image);
188 imageDownloader.feed = model.feed;
189 imageDownloader.download(model.image);
190 tabs.selectedTabIndex = 0;
191 searchField.text = ""
192 }
193 }
194 }
195 }
196
197 Scrollbar {
198 flickableItem: resultsView
140 }199 }
141 }200 }
142201
143202
=== renamed file 'app/ui/SettingsTab.qml' => 'app/ui/SettingsPage.qml'
--- app/ui/SettingsTab.qml 2015-03-27 15:38:43 +0000
+++ app/ui/SettingsPage.qml 2015-04-11 21:39:04 +0000
@@ -20,98 +20,131 @@
20import Ubuntu.Components 1.120import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem21import Ubuntu.Components.ListItems 1.0 as ListItem
2222
23Tab {23Page {
24 id: tab24 id: settingsPage
2525
26 title: i18n.tr("Settings")26 Flickable {
2727 id: flickable
28 page: Page {28
29 id: settingsPage29 anchors.fill: parent
3030 contentHeight: settingsColumn.height + units.gu(8)
31 ListModel {31 contentWidth: parent.width
32 id: themeModel
33 Component.onCompleted: initialize()
34 function initialize() {
35 themeModel.append({ name: i18n.tr("Light"), file: "Light.qml" })
36 themeModel.append({ name: i18n.tr("Dark"), file: "Dark.qml" })
37 }
38 }
39
40 ListModel {
41 id: cleanupModel
42 Component.onCompleted: initialize()
43 function initialize() {
44 cleanupModel.append({ name: i18n.tr("Never"), value: -1 })
45 cleanupModel.append({ name: i18n.tr("7 days"), value: 7 })
46 cleanupModel.append({ name: i18n.tr("31 days"), value: 31 })
47 cleanupModel.append({ name: i18n.tr("90 days"), value: 90 })
48 cleanupModel.append({ name: i18n.tr("180 days"), value: 180 })
49 cleanupModel.append({ name: i18n.tr("360 days"), value: 360 })
50 }
51 }
5232
53 Column {33 Column {
54 id: settingsColumn34 id: settingsColumn
5535
56 anchors.fill: parent36 anchors {
5737 top: parent.top
58 ExpandableListItem {38 left: parent.left
59 id: themeSetting39 right: parent.right
6040 }
61 model: themeModel41
42 ListItem.Header {
43 text: i18n.tr("General Settings")
44 }
45
46 ListItem.SingleValue {
47 progression: true
48 showDivider: false
62 text: i18n.tr("Theme")49 text: i18n.tr("Theme")
63 subText: podbird.settings.themeName.split(".qml")[0]50 value: podbird.settings.themeName.split(".qml")[0]
6451 onClicked: mainStack.push(Qt.resolvedUrl("../settings/ThemeSetting.qml"))
65 delegate: ListItem.Standard {52 }
66 text: model.name53
6754 ListItem.Header {
68 onClicked: {55 text: i18n.tr("Podcast Episode Settings")
69 var themeElement = model.file56 }
70 podbird.settings.themeName = themeElement57
71 podbird.themeManager.source = themeElement58 ListItem.Standard {
72 themeSetting.expanded = false59 showDivider: false
73 }60 text: i18n.tr("Hide listened episodes")
7461 control: Switch {
75 Icon {62 checked: podbird.settings.hideListened
76 width: units.gu(2)63 onClicked: podbird.settings.hideListened = checked
77 height: width64 }
78 name: "ok"65 }
79 visible: podbird.settings.themeName === model.file66
80 anchors.right: parent.right67 ListItem.Base {
81 anchors.rightMargin: units.gu(2)68 height: units.gu(10)
82 anchors.verticalCenter: parent.verticalCenter69 progression: true
83 }70 showDivider: false
84 }71 onClicked: mainStack.push(Qt.resolvedUrl("../settings/CleanSetting.qml"))
85 }72 Column {
8673 anchors.verticalCenter: parent.verticalCenter
87 ExpandableListItem {74 anchors.right: parent.right
88 id: cleanupSetting75 anchors.left: parent.left
8976 Label {
90 listViewHeight: units.gu(36)77 width: parent.width
91 model: cleanupModel78 wrapMode: Text.WordWrap
92 text: i18n.tr("Remove episodes older than")79 text: i18n.tr("Automatically delete old episodes")
93 subText: podbird.settings.retentionDays === -1 ? i18n.tr("Never")80 }
94 : i18n.tr("%1 days").arg(podbird.settings.retentionDays)81
9582 Label {
96 delegate: ListItem.Standard {83 fontSize: "small"
97 text: model.name84 color: podbird.theme.baseSubText
9885 width: parent.width
99 onClicked: {86 wrapMode: Text.WordWrap
100 podbird.settings.retentionDays = model.value87 text: i18n.tr("Delete episodes that are older than a given number of days for each podcast")
101 cleanupSetting.expanded = false88 }
102 }89 }
10390 }
104 Icon {91
105 width: units.gu(2)92 ListItem.Base {
106 height: width93 height: units.gu(10)
107 name: "ok"94 progression: true
108 visible: podbird.settings.retentionDays === model.value95 showDivider: false
109 anchors.right: parent.right96 onClicked: mainStack.push(Qt.resolvedUrl("../settings/DownloadSetting.qml"))
110 anchors.rightMargin: units.gu(2)97 Column {
111 anchors.verticalCenter: parent.verticalCenter98 anchors.verticalCenter: parent.verticalCenter
112 }99 anchors.right: parent.right
113 }100 anchors.left: parent.left
101 Label {
102 width: parent.width
103 wrapMode: Text.WordWrap
104 text: i18n.tr("Automatically download new episodes")
105 }
106
107 Label {
108 fontSize: "small"
109 color: podbird.theme.baseSubText
110 width: parent.width
111 wrapMode: Text.WordWrap
112 text: i18n.tr("Default number of new episodes to download for each podcast")
113 }
114 }
115 }
116
117 ListItem.Standard {
118 showDivider: false
119 text: i18n.tr("Auto download on WiFi only")
120 enabled: podbird.settings.maxEpisodeDownload !== -1
121 control: Switch {
122 checked: podbird.settings.onlyWifiDownload
123 onClicked: {
124 podbird.settings.onlyWifiDownload = checked
125 }
126 }
127 }
128
129 ListItem.Header {
130 text: i18n.tr("Misc.")
131 }
132
133 ListItem.Standard {
134 progression: true
135 showDivider: false
136 text: i18n.tr("About")
137 onClicked: mainStack.push(Qt.resolvedUrl("../settings/About.qml"))
138 }
139
140 ListItem.Standard {
141 progression: true
142 showDivider: false
143 text: i18n.tr("Report Bug")
144 onClicked: Qt.openUrlExternally("https://bugs.launchpad.net/podbird/+filebug")
114 }145 }
115 }146 }
116 }147 }
117}148}
149
150
118151
=== added file 'app/ui/Walkthrough.qml'
--- app/ui/Walkthrough.qml 1970-01-01 00:00:00 +0000
+++ app/ui/Walkthrough.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,169 @@
1/*
2 * Copyright 2015 Nekhelesh Ramananthan (UCS)
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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23Page {
24 id: walkthrough
25
26 // Property to set the app name used in the walkthrough
27 property string appName
28
29 // Property to check if this is the first run or not
30 property bool isFirstRun: true
31
32 // Property to store the slides shown in the walkthrough (Each slide is a component defined in a separate file for simplicity)
33 property list<Component> model
34
35 // Property to set the color of bottom cirle to indicate the user's progress
36 property color completeColor: "green"
37
38 // Property to set the color of the bottom circle to indicate the slide still left to cover
39 property color inCompleteColor: "lightgrey"
40
41 // Property to set the color of the skip welcome wizard text
42 property color skipTextColor: "grey"
43
44 // Property to signal walkthrough completion
45 signal finished
46
47 // ListView to show the slides
48 ListView {
49 id: listView
50 anchors {
51 left: parent.left
52 right: parent.right
53 top: skipLabel.bottom
54 bottom: slideIndicator.top
55 }
56
57 model: walkthrough.model
58 snapMode: ListView.SnapOneItem
59 orientation: Qt.Horizontal
60 highlightMoveDuration: UbuntuAnimation.FastDuration
61 highlightRangeMode: ListView.StrictlyEnforceRange
62 highlightFollowsCurrentItem: true
63
64 delegate: Item {
65 width: listView.width
66 height: listView.height
67 clip: true
68
69 Loader {
70 anchors {
71 fill: parent
72 margins: units.gu(2)
73 }
74
75 sourceComponent: modelData
76 }
77 }
78 }
79
80 // Label to skip the walkthrough. Only visible on the first slide
81 Label {
82 id: skipLabel
83
84 color: skipTextColor
85 fontSize: "small"
86 wrapMode: Text.WordWrap
87 text: i18n.tr("Skip")
88 horizontalAlignment: Text.AlignRight
89
90 anchors {
91 top: parent.top
92 left: parent.left
93 right: parent.right
94 margins: units.gu(2)
95 }
96
97 MouseArea {
98 anchors.fill: parent
99 onClicked: walkthrough.finished()
100 }
101 }
102
103 // Indicator element to represent the current slide of the walkthrough
104 Row {
105 id: slideIndicator
106 height: units.gu(6)
107 spacing: units.gu(2)
108 anchors {
109 bottom: parent.bottom
110 horizontalCenter: parent.horizontalCenter
111 }
112
113 Repeater {
114 model: walkthrough.model.length
115 delegate: Rectangle {
116 height: width
117 radius: width/2
118 width: units.gu(2)
119 antialiasing: true
120 border.width: listView.currentIndex == index ? units.gu(0.2) : units.gu(0)
121 border.color: completeColor
122 anchors.verticalCenter: parent.verticalCenter
123 color: listView.currentIndex == index ? "White"
124 : listView.currentIndex >= index ? completeColor
125 : inCompleteColor
126 Behavior on color {
127 ColorAnimation {
128 duration: UbuntuAnimation.FastDuration
129 }
130 }
131 }
132 }
133 }
134
135 ActionButton {
136 id: rightchevron
137
138 width: units.gu(6)
139 height: units.gu(6)
140
141 anchors {
142 bottom: parent.bottom
143 right: parent.right
144 }
145
146 iconName: "chevron"
147 visible: enabled
148 enabled: listView.currentIndex !== listView.count-1
149 onClicked: listView.currentIndex++
150 }
151
152 ActionButton {
153 id: leftchevron
154
155 width: units.gu(6)
156 height: units.gu(6)
157
158 anchors {
159 bottom: parent.bottom
160 left: parent.left
161 }
162
163 iconName: "chevron"
164 rotation: 180
165 visible: enabled
166 enabled: listView.currentIndex !== 0
167 onClicked: listView.currentIndex--
168 }
169}
0170
=== added directory 'app/welcomewizard'
=== added file 'app/welcomewizard/CMakeLists.txt'
--- app/welcomewizard/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/CMakeLists.txt 2015-04-11 21:39:04 +0000
@@ -0,0 +1,8 @@
1file(GLOB WELCOMEWIZARD_QML_JS_FILES *.qml *.js)
2
3# Make the files visible in the qtcreator tree
4add_custom_target(podbird_welcomewizard_QMlFiles ALL SOURCES ${WELCOMEWIZARD_QML_JS_FILES})
5
6install(FILES ${WELCOMEWIZARD_QML_JS_FILES} DESTINATION ${PODBIRD_DIR}/welcomewizard)
7
8
09
=== added file 'app/welcomewizard/Slide1.qml'
--- app/welcomewizard/Slide1.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide1.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 1
23Component {
24 id: slide1
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 source: Qt.resolvedUrl("../graphics/podbird.png")
36 fillMode: Image.PreserveAspectFit
37 antialiasing: true
38 }
39
40 Label {
41 id: introductionText
42 text: i18n.tr("Welcome to Podbird")
43 fontSize: "x-large"
44 height: contentHeight
45 anchors.centerIn: parent
46 }
47
48 Label {
49 id: bodyText
50 text: i18n.tr("Enjoy your favourite podcasts with Podbird, the one and only podcast \
51manager for Ubuntu Touch.\n\nChirp Chirp..")
52 anchors.left: parent.left
53 anchors.right: parent.right
54 anchors.margins: units.gu(1)
55 anchors.top: introductionText.bottom
56 anchors.topMargin: units.gu(4)
57 anchors.bottom: swipeText.top
58 wrapMode: Text.WordWrap
59 horizontalAlignment: Text.AlignHCenter
60 }
61
62 Label {
63 id: swipeText
64 anchors.left: parent.left
65 anchors.right: parent.right
66 anchors.margins: units.gu(1)
67 anchors.bottom: parent.bottom
68 color: "grey"
69 fontSize: "small"
70 wrapMode: Text.WordWrap
71 horizontalAlignment: Text.AlignHCenter
72 text: i18n.tr("Swipe to move between Pages")
73
74 }
75 }
76}
077
=== added file 'app/welcomewizard/Slide2.qml'
--- app/welcomewizard/Slide2.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide2.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,66 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 2
23Component {
24 id: slide2
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 fillMode: Image.PreserveAspectFit
36 source: Qt.resolvedUrl("../graphics/discover.png")
37 }
38
39 Label {
40 id: introductionText
41 anchors.centerIn: parent
42 elide: Text.ElideRight
43 fontSize: "x-large"
44 maximumLineCount: 2
45 text: i18n.tr("Discover New Podcasts")
46 horizontalAlignment: Text.AlignHCenter
47 width: parent.width
48 wrapMode: Text.WordWrap
49 }
50
51 Label {
52 id: finalMessage
53 anchors {
54 top: introductionText.bottom
55 bottom: parent.bottom
56 left: parent.left
57 right: parent.right
58 margins: units.gu(1)
59 topMargin: units.gu(4)
60 }
61 horizontalAlignment: Text.AlignHCenter
62 text: i18n.tr("Podbird uses the Itunes database to provide access to a huge collections of podcasts. You can also add podcasts by URL.")
63 wrapMode: Text.WordWrap
64 }
65 }
66}
067
=== added file 'app/welcomewizard/Slide3.qml'
--- app/welcomewizard/Slide3.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide3.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,67 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 3
23Component {
24 id: slide3
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 fillMode: Image.PreserveAspectFit
36 source: Qt.resolvedUrl("../graphics/smart.png")
37 }
38
39 Label {
40 id: introductionText
41 anchors.centerIn: parent
42 elide: Text.ElideRight
43 fontSize: "x-large"
44 maximumLineCount: 2
45 text: i18n.tr("Smart Settings")
46 horizontalAlignment: Text.AlignHCenter
47 width: parent.width
48 wrapMode: Text.WordWrap
49 }
50
51 Label {
52 id: finalMessage
53 anchors {
54 top: introductionText.bottom
55 bottom: parent.bottom
56 left: parent.left
57 right: parent.right
58 margins: units.gu(1)
59 topMargin: units.gu(4)
60 }
61 horizontalAlignment: Text.AlignHCenter
62 text: i18n.tr("Podbird automatically downloads new episodes and cleans up old episodes. \
63As a power user you can also tweak these settings to suit your needs.")
64 wrapMode: Text.WordWrap
65 }
66 }
67}
068
=== added file 'app/welcomewizard/Slide4.qml'
--- app/welcomewizard/Slide4.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide4.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,70 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 4
23Component {
24 id: slide4
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 fillMode: Image.PreserveAspectFit
36 source: Qt.resolvedUrl("../graphics/language.png")
37 }
38
39 Label {
40 id: introductionText
41 anchors.centerIn: parent
42 elide: Text.ElideRight
43 fontSize: "x-large"
44 maximumLineCount: 2
45 // TRANSLATORS: This text should be in a language different from the language set by the user.
46 // For instance, if the app was in english, then it is appropriate to set this string as
47 // Hallo or Bonjour etc to symbolize the internationalization feature in podbird.
48 text: i18n.tr("Hallo! Bonjour!")
49 horizontalAlignment: Text.AlignHCenter
50 width: parent.width
51 wrapMode: Text.WordWrap
52 }
53
54 Label {
55 id: finalMessage
56 anchors {
57 top: introductionText.bottom
58 bottom: parent.bottom
59 left: parent.left
60 right: parent.right
61 margins: units.gu(1)
62 topMargin: units.gu(4)
63 }
64 horizontalAlignment: Text.AlignHCenter
65 text: i18n.tr("Podbird is available in over 15 languages and is translated by the \
66Ubuntu Translators community.")
67 wrapMode: Text.WordWrap
68 }
69 }
70}
071
=== added file 'app/welcomewizard/Slide5.qml'
--- app/welcomewizard/Slide5.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide5.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,66 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 5
23Component {
24 id: slide5
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 fillMode: Image.PreserveAspectFit
36 source: Qt.resolvedUrl("../graphics/support.png")
37 }
38
39 Label {
40 id: introductionText
41 anchors.centerIn: parent
42 elide: Text.ElideRight
43 fontSize: "x-large"
44 maximumLineCount: 2
45 text: i18n.tr("Support")
46 horizontalAlignment: Text.AlignHCenter
47 width: parent.width
48 wrapMode: Text.WordWrap
49 }
50
51 Label {
52 id: finalMessage
53 anchors {
54 top: introductionText.bottom
55 bottom: parent.bottom
56 left: parent.left
57 right: parent.right
58 margins: units.gu(1)
59 topMargin: units.gu(4)
60 }
61 horizontalAlignment: Text.AlignHCenter
62 text: i18n.tr("If you find any bugs or have any feature requests, let us know at our project page https://launchpad.net/podbird.")
63 wrapMode: Text.WordWrap
64 }
65 }
66}
067
=== added file 'app/welcomewizard/Slide6.qml'
--- app/welcomewizard/Slide6.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/Slide6.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,80 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22// Slide 6
23Component {
24 id: slide6
25 Item {
26 id: slide1Container
27
28 Image {
29 anchors {
30 top: parent.top
31 bottom: introductionText.top
32 bottomMargin: units.gu(6)
33 horizontalCenter: parent.horizontalCenter
34 }
35 fillMode: Image.PreserveAspectFit
36 source: Qt.resolvedUrl("../graphics/gift.png")
37 }
38
39 Label {
40 id: introductionText
41 anchors.centerIn: parent
42 elide: Text.ElideRight
43 fontSize: "x-large"
44 maximumLineCount: 2
45 text: i18n.tr("Enjoy")
46 horizontalAlignment: Text.AlignHCenter
47 width: parent.width
48 wrapMode: Text.WordWrap
49 }
50
51 Label {
52 id: finalMessage
53 anchors {
54 top: introductionText.bottom
55 bottom: continueButton.top
56 left: parent.left
57 right: parent.right
58 margins: units.gu(1)
59 topMargin: units.gu(4)
60 }
61 horizontalAlignment: Text.AlignHCenter
62 text: i18n.tr("We hope you will enjoy using Podbird!")
63 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()
78 }
79 }
80}
081
=== added file 'app/welcomewizard/WelcomeWizard.qml'
--- app/welcomewizard/WelcomeWizard.qml 1970-01-01 00:00:00 +0000
+++ app/welcomewizard/WelcomeWizard.qml 2015-04-11 21:39:04 +0000
@@ -0,0 +1,41 @@
1/*
2 * Copyright 2015 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
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import "../ui"
22
23// Initial Walkthrough tutorial
24Walkthrough {
25 id: walkthrough
26 appName: "Podbird"
27 onFinished: {
28 console.log("[LOG]: Welcome tour complete")
29 settings.firstRun = false
30 mainStack.pop()
31 mainStack.push(tabs)
32 }
33 model: [
34 Slide1{},
35 Slide2{},
36 Slide3{},
37 Slide4{},
38 Slide5{},
39 Slide6{}
40 ]
41}
042
=== modified file 'po/com.mikeasoft.podbird.pot'
--- po/com.mikeasoft.podbird.pot 2015-03-29 16:40:52 +0000
+++ po/com.mikeasoft.podbird.pot 2015-04-11 21:39:04 +0000
@@ -8,7 +8,7 @@
8msgstr ""8msgstr ""
9"Project-Id-Version: \n"9"Project-Id-Version: \n"
10"Report-Msgid-Bugs-To: \n"10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2015-03-29 16:48+0100\n"11"POT-Creation-Date: 2015-04-11 23:32+0200\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,72 +18,207 @@
18"Content-Transfer-Encoding: 8bit\n"18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21#: ../app/ui/EpisodesPage.qml:8521#. TRANSLATORS: this refers to a number of songs greater than one. The actual number will be prepended to the string automatically (plural forms are not yet fully supported in usermetrics, the library that displays that string)
22#: ../app/podbird.qml:152
23msgid "podcasts listened today"
24msgstr ""
25
26#: ../app/podbird.qml:153
27msgid "No podcasts listened today"
28msgstr ""
29
30#: ../app/podbird.qml:210
31msgid "Find New Podcasts"
32msgstr ""
33
34#: ../app/podbird.qml:224
35msgid "Settings"
36msgstr ""
37
38#: ../app/settings/About.qml:8 ../app/settings/About.qml:11
39#: ../app/ui/SettingsPage.qml:136
40msgid "About"
41msgstr ""
42
43#: ../app/settings/About.qml:11
44msgid "Credits"
45msgstr ""
46
47#: ../app/settings/About.qml:65
48msgid "Released under the terms of the GNU GPL v3"
49msgstr ""
50
51#: ../app/settings/About.qml:74
52#, qt-format
53msgid "Source code available on %1"
54msgstr ""
55
56#: ../app/settings/About.qml:89
57msgid "Developers"
58msgstr ""
59
60#: ../app/settings/About.qml:103
61msgid "Designer"
62msgstr ""
63
64#: ../app/settings/About.qml:112
65msgid "Translators"
66msgstr ""
67
68#: ../app/settings/CleanSetting.qml:27
69msgid "Delete older than"
70msgstr ""
71
72#: ../app/settings/CleanSetting.qml:33 ../app/settings/DownloadSetting.qml:33
73msgid "Never"
74msgstr ""
75
76#: ../app/settings/CleanSetting.qml:34
77#, qt-format
78msgid "%1 day"
79msgid_plural "%1 days"
80msgstr[0] ""
81msgstr[1] ""
82
83#: ../app/settings/CleanSetting.qml:35 ../app/settings/CleanSetting.qml:36
84#: ../app/settings/CleanSetting.qml:37
85#, qt-format
86msgid "%1 month"
87msgid_plural "%1 months"
88msgstr[0] ""
89msgstr[1] ""
90
91#: ../app/settings/CleanSetting.qml:38
92#, qt-format
93msgid "%1 year"
94msgid_plural "%1 years"
95msgstr[0] ""
96msgstr[1] ""
97
98#: ../app/settings/DownloadSetting.qml:27
99msgid "Download at most"
100msgstr ""
101
102#: ../app/settings/DownloadSetting.qml:34
103#: ../app/settings/DownloadSetting.qml:35
104#: ../app/settings/DownloadSetting.qml:36
105#: ../app/settings/DownloadSetting.qml:37 ../app/ui/EpisodesPage.qml:463
106#, qt-format
107msgid "%1 episode"
108msgid_plural "%1 episodes"
109msgstr[0] ""
110msgstr[1] ""
111
112#: ../app/settings/ThemeSetting.qml:27 ../app/ui/SettingsPage.qml:49
113msgid "Theme"
114msgstr ""
115
116#: ../app/settings/ThemeSetting.qml:33
117msgid "Light"
118msgstr ""
119
120#: ../app/settings/ThemeSetting.qml:34
121msgid "Dark"
122msgstr ""
123
124#: ../app/ui/EpisodesPage.qml:33
125msgid "Podcast"
126msgstr ""
127
128#: ../app/ui/EpisodesPage.qml:89 ../app/ui/SearchPage.qml:55
22msgid "Search Episode"129msgid "Search Episode"
23msgstr ""130msgstr ""
24131
25#: ../app/ui/EpisodesPage.qml:94132#: ../app/ui/EpisodesPage.qml:98
26msgid "Mark all listened"133msgid "Mark all listened"
27msgstr ""134msgstr ""
28135
29#: ../app/ui/EpisodesPage.qml:105 ../app/ui/EpisodesPage.qml:153136#: ../app/ui/EpisodesPage.qml:109 ../app/ui/EpisodesPage.qml:187
30msgid "Unsubscribe"137msgid "Unsubscribe"
31msgstr ""138msgstr ""
32139
33#: ../app/ui/EpisodesPage.qml:120 ../app/ui/PodcastsTab.qml:86140#: ../app/ui/EpisodesPage.qml:124 ../app/ui/PodcastsTab.qml:96
34#: ../app/ui/PodcastsTab.qml:109141#: ../app/ui/PodcastsTab.qml:119 ../app/ui/SearchPage.qml:69
35msgid "Back"142msgid "Back"
36msgstr ""143msgstr ""
37144
38#: ../app/ui/EpisodesPage.qml:131145#: ../app/ui/EpisodesPage.qml:136
39msgid "Search episode"146msgid "Search episode"
40msgstr ""147msgstr ""
41148
42#: ../app/ui/EpisodesPage.qml:150149#: ../app/ui/EpisodesPage.qml:184
43msgid "Unsubscribe Confirmation"150msgid "Unsubscribe Confirmation"
44msgstr ""151msgstr ""
45152
46#: ../app/ui/EpisodesPage.qml:151153#: ../app/ui/EpisodesPage.qml:185
47#, qt-format154#, qt-format
48msgid "Are you sure you want to unsubscribe from <b>%1</b>?"155msgid "Are you sure you want to unsubscribe from <b>%1</b>?"
49msgstr ""156msgstr ""
50157
51#: ../app/ui/EpisodesPage.qml:170158#: ../app/ui/EpisodesPage.qml:204
52msgid "Cancel"159msgid "Cancel"
53msgstr ""160msgstr ""
54161
55#: ../app/ui/EpisodesPage.qml:184162#: ../app/ui/EpisodesPage.qml:258
163msgid "Delete local file"
164msgstr ""
165
166#: ../app/ui/EpisodesPage.qml:259
167msgid "Episode queued for download"
168msgstr ""
169
170#: ../app/ui/EpisodesPage.qml:260
171msgid "Download episode"
172msgstr ""
173
174#: ../app/ui/EpisodesPage.qml:336
175msgid "No more episodes"
176msgstr ""
177
178#: ../app/ui/EpisodesPage.qml:336
56msgid "No Episodes found"179msgid "No Episodes found"
57msgstr ""180msgstr ""
58181
59#: ../app/ui/EpisodesPage.qml:185182#: ../app/ui/EpisodesPage.qml:337
183msgid "All episodes have been listened to."
184msgstr ""
185
186#: ../app/ui/EpisodesPage.qml:337
60msgid "No episodes found matching the search term."187msgid "No episodes found matching the search term."
61msgstr ""188msgstr ""
62189
63#: ../app/ui/EpisodesPage.qml:319190#: ../app/ui/EpisodesPage.qml:359
64#, no-c-format, qt-format191#, no-c-format, qt-format
65msgid "%1h %2m"192msgid "%1 hr %2 min"
66msgstr ""193msgstr ""
67194
68#: ../app/ui/EpisodesPage.qml:328195#: ../app/ui/EpisodesPage.qml:368
69#, no-c-format, qt-format196#, no-c-format, qt-format
70msgid "%1h"197msgid "%1 hr"
71msgstr ""198msgstr ""
72199
73#: ../app/ui/EpisodesPage.qml:336200#: ../app/ui/EpisodesPage.qml:376
74#, no-c-format, qt-format201#, no-c-format, qt-format
75msgid "%1m"202msgid "%1 min"
203msgstr ""
204
205#: ../app/ui/EpisodesPage.qml:416
206msgid "Unheard"
207msgstr ""
208
209#: ../app/ui/EpisodesPage.qml:416
210msgid "Listened"
76msgstr ""211msgstr ""
77212
78#: ../app/ui/NowPlayingPage.qml:28213#: ../app/ui/NowPlayingPage.qml:28
79msgid "Now Playing"214msgid "Now Playing"
80msgstr ""215msgstr ""
81216
82#: ../app/ui/NowPlayingPage.qml:145217#: ../app/ui/NowPlayingPage.qml:150
83msgid "-15s"218msgid "-15s"
84msgstr ""219msgstr ""
85220
86#: ../app/ui/NowPlayingPage.qml:204221#: ../app/ui/NowPlayingPage.qml:209
87msgid "+15s"222msgid "+15s"
88msgstr ""223msgstr ""
89224
@@ -91,124 +226,207 @@
91msgid "Podcasts"226msgid "Podcasts"
92msgstr ""227msgstr ""
93228
94#: ../app/ui/PodcastsTab.qml:62229#: ../app/ui/PodcastsTab.qml:64
95msgid "Add Podcast"230msgid "Add Podcast"
96msgstr ""231msgstr ""
97232
98#: ../app/ui/PodcastsTab.qml:72233#: ../app/ui/PodcastsTab.qml:74
99msgid "Search Podcast"234msgid "Search Podcast"
100msgstr ""235msgstr ""
101236
102#: ../app/ui/PodcastsTab.qml:97237#: ../app/ui/PodcastsTab.qml:83
238msgid "Grid View"
239msgstr ""
240
241#: ../app/ui/PodcastsTab.qml:83
242msgid "List View"
243msgstr ""
244
245#: ../app/ui/PodcastsTab.qml:107
103msgid "Search podcast"246msgid "Search podcast"
104msgstr ""247msgstr ""
105248
106#: ../app/ui/PodcastsTab.qml:120249#: ../app/ui/PodcastsTab.qml:130
107msgid "Save Podcast"250msgid "Save Podcast"
108msgstr ""251msgstr ""
109252
110#: ../app/ui/PodcastsTab.qml:133253#: ../app/ui/PodcastsTab.qml:143
111msgid "Feed URL"254msgid "Feed URL"
112msgstr ""255msgstr ""
113256
114#: ../app/ui/PodcastsTab.qml:157257#: ../app/ui/PodcastsTab.qml:167
115msgid "Unable to subscribe"258msgid "Unable to subscribe"
116msgstr ""259msgstr ""
117260
118#: ../app/ui/PodcastsTab.qml:158261#: ../app/ui/PodcastsTab.qml:168
119msgid "Please check the URL and try again"262msgid "Please check the URL and try again"
120msgstr ""263msgstr ""
121264
122#: ../app/ui/PodcastsTab.qml:160265#: ../app/ui/PodcastsTab.qml:170
123msgid "Close"266msgid "Close"
124msgstr ""267msgstr ""
125268
126#: ../app/ui/PodcastsTab.qml:172269#: ../app/ui/PodcastsTab.qml:184
127msgid "No Podcast Subscriptions"270msgid "No Podcast Subscriptions"
128msgstr ""271msgstr ""
129272
130#: ../app/ui/PodcastsTab.qml:173273#: ../app/ui/PodcastsTab.qml:185
131msgid "No Podcasts Found"274msgid "No Podcasts Found"
132msgstr ""275msgstr ""
133276
134#: ../app/ui/PodcastsTab.qml:174277#: ../app/ui/PodcastsTab.qml:186
135msgid ""278msgid ""
136"You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' "279"You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' "
137"page to add some."280"page to add some."
138msgstr ""281msgstr ""
139282
140#: ../app/ui/PodcastsTab.qml:175283#: ../app/ui/PodcastsTab.qml:187 ../app/ui/SearchPage.qml:103
141msgid "No podcasts found matching the search term."284msgid "No podcasts found matching the search term."
142msgstr ""285msgstr ""
143286
144#: ../app/ui/PodcastsTab.qml:291287#: ../app/ui/PodcastsTab.qml:217 ../app/ui/PodcastsTab.qml:337
145#, qt-format288#, qt-format
146msgid "%1 unheard episode"289msgid "%1 unheard episode"
147msgid_plural "%1 unheard episodes"290msgid_plural "%1 unheard episodes"
148msgstr[0] ""291msgstr[0] ""
149msgstr[1] ""292msgstr[1] ""
150293
151#: ../app/ui/SearchTab.qml:27294#: ../app/ui/SearchPage.qml:80
152msgid "Find New Podcasts"
153msgstr ""
154
155#: ../app/ui/SearchTab.qml:42
156msgid "Search..."295msgid "Search..."
157msgstr ""296msgstr ""
158297
159#: ../app/ui/SearchTab.qml:122298#: ../app/ui/SearchPage.qml:102
299msgid "Looking for a new Podcast?"
300msgstr ""
301
302#: ../app/ui/SearchPage.qml:102
303msgid "No Podcasts found"
304msgstr ""
305
306#: ../app/ui/SearchPage.qml:103
307msgid "Click the 'magnifier' at the top to search."
308msgstr ""
309
310#: ../app/ui/SearchPage.qml:184
160msgid "Subscribe"311msgid "Subscribe"
161msgstr ""312msgstr ""
162313
163#: ../app/ui/SettingsTab.qml:26314#: ../app/ui/SettingsPage.qml:43
164msgid "Settings"315msgid "General Settings"
165msgstr ""316msgstr ""
166317
167#: ../app/ui/SettingsTab.qml:35318#: ../app/ui/SettingsPage.qml:55
168msgid "Light"319msgid "Podcast Episode Settings"
169msgstr ""320msgstr ""
170321
171#: ../app/ui/SettingsTab.qml:36322#: ../app/ui/SettingsPage.qml:60
172msgid "Dark"323msgid "Hide listened episodes"
173msgstr ""324msgstr ""
174325
175#: ../app/ui/SettingsTab.qml:44 ../app/ui/SettingsTab.qml:93326#: ../app/ui/SettingsPage.qml:79
176msgid "Never"327msgid "Automatically delete old episodes"
177msgstr ""328msgstr ""
178329
179#: ../app/ui/SettingsTab.qml:45330#: ../app/ui/SettingsPage.qml:87
180msgid "7 days"331msgid ""
181msgstr ""332"Delete episodes that are older than a given number of days for each podcast"
182333msgstr ""
183#: ../app/ui/SettingsTab.qml:46334
184msgid "31 days"335#: ../app/ui/SettingsPage.qml:104
185msgstr ""336msgid "Automatically download new episodes"
186337msgstr ""
187#: ../app/ui/SettingsTab.qml:47338
188msgid "90 days"339#: ../app/ui/SettingsPage.qml:112
189msgstr ""340msgid "Default number of new episodes to download for each podcast"
190341msgstr ""
191#: ../app/ui/SettingsTab.qml:48342
192msgid "180 days"343#: ../app/ui/SettingsPage.qml:119
193msgstr ""344msgid "Auto download on WiFi only"
194345msgstr ""
195#: ../app/ui/SettingsTab.qml:49346
196msgid "360 days"347#: ../app/ui/SettingsPage.qml:130
197msgstr ""348msgid "Misc."
198349msgstr ""
199#: ../app/ui/SettingsTab.qml:62350
200msgid "Theme"351#: ../app/ui/SettingsPage.qml:143
201msgstr ""352msgid "Report Bug"
202353msgstr ""
203#: ../app/ui/SettingsTab.qml:92354
204msgid "Remove episodes older than"355#: ../app/ui/Walkthrough.qml:87
205msgstr ""356msgid "Skip"
206357msgstr ""
207#: ../app/ui/SettingsTab.qml:94358
208#, qt-format359#: ../app/welcomewizard/Slide1.qml:42
209msgid "%1 days"360msgid "Welcome to Podbird"
210msgstr ""361msgstr ""
211362
212#: /home/mike/src/build-podbird-Ubuntu_Device_GCC_armhf_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1363#: ../app/welcomewizard/Slide1.qml:50
364msgid ""
365"Enjoy your favourite podcasts with Podbird, the one and only podcast manager "
366"for Ubuntu Touch.\n"
367"\n"
368"Chirp Chirp.."
369msgstr ""
370
371#: ../app/welcomewizard/Slide1.qml:72
372msgid "Swipe to move between Pages"
373msgstr ""
374
375#: ../app/welcomewizard/Slide2.qml:45
376msgid "Discover New Podcasts"
377msgstr ""
378
379#: ../app/welcomewizard/Slide2.qml:62
380msgid ""
381"Podbird uses the Itunes database to provide access to a huge collections of "
382"podcasts. You can also add podcasts by URL."
383msgstr ""
384
385#: ../app/welcomewizard/Slide3.qml:45
386msgid "Smart Settings"
387msgstr ""
388
389#: ../app/welcomewizard/Slide3.qml:62
390msgid ""
391"Podbird automatically downloads new episodes and cleans up old episodes. As "
392"a power user you can also tweak these settings to suit your needs."
393msgstr ""
394
395#. TRANSLATORS: This text should be in a language different from the language set by the user.
396#. For instance, if the app was in english, then it is appropriate to set this string as
397#. Hallo or Bonjour etc to symbolize the internationalization feature in podbird.
398#: ../app/welcomewizard/Slide4.qml:48
399msgid "Hallo! Bonjour!"
400msgstr ""
401
402#: ../app/welcomewizard/Slide4.qml:65
403msgid ""
404"Podbird is available in over 15 languages and is translated by the Ubuntu "
405"Translators community."
406msgstr ""
407
408#: ../app/welcomewizard/Slide5.qml:45
409msgid "Support"
410msgstr ""
411
412#: ../app/welcomewizard/Slide5.qml:62
413msgid ""
414"If you find any bugs or have any feature requests, let us know at our "
415"project page https://launchpad.net/podbird."
416msgstr ""
417
418#: ../app/welcomewizard/Slide6.qml:45
419msgid "Enjoy"
420msgstr ""
421
422#: ../app/welcomewizard/Slide6.qml:62
423msgid "We hope you will enjoy using Podbird!"
424msgstr ""
425
426#: ../app/welcomewizard/Slide6.qml:76
427msgid "Finish"
428msgstr ""
429
430#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-8.6-aboutpage-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1
213msgid "Podbird"431msgid "Podbird"
214msgstr ""432msgstr ""
215433
=== added file 'podbird.png'
216Binary files podbird.png 1970-01-01 00:00:00 +0000 and podbird.png 2015-04-11 21:39:04 +0000 differ434Binary files podbird.png 1970-01-01 00:00:00 +0000 and podbird.png 2015-04-11 21:39:04 +0000 differ

Subscribers

People subscribed via source and target branches