Merge lp:~urbanape/ubuntuone-ios-client/downloader into lp:ubuntuone-ios-client

Proposed by Zachery Bir
Status: Merged
Merged at revision: 192
Proposed branch: lp:~urbanape/ubuntuone-ios-client/downloader
Merge into: lp:ubuntuone-ios-client
Diff against target: 7121 lines (+2521/-2219)
88 files modified
musicstreaming/Other Sources/iSubAppDelegate.h (+2/-2)
musicstreaming/Other Sources/iSubAppDelegate.m (+1/-6)
musicstreaming/about/about.html (+178/-115)
musicstreaming/categories/NSString+Extras.h (+26/-0)
musicstreaming/categories/NSString+Extras.m (+46/-0)
musicstreaming/categories/UIActionSheet+Blocks.h (+16/-0)
musicstreaming/categories/UIActionSheet+Blocks.m (+71/-0)
musicstreaming/categories/UIAlertView+Blocks.h (+16/-0)
musicstreaming/categories/UIAlertView+Blocks.m (+61/-0)
musicstreaming/controls/AlbumUITableViewCell.h (+2/-4)
musicstreaming/controls/AlbumUITableViewCell.m (+1/-2)
musicstreaming/controls/AsynchronousImageView.h (+0/-23)
musicstreaming/controls/AsynchronousImageView.m (+0/-78)
musicstreaming/controls/AsynchronousImageViewCached.h (+0/-26)
musicstreaming/controls/AsynchronousImageViewCached.m (+0/-120)
musicstreaming/controls/MBProgressHUD.h (+0/-273)
musicstreaming/controls/MBProgressHUD.m (+0/-529)
musicstreaming/controls/RIButtonItem.h (+23/-0)
musicstreaming/controls/RIButtonItem.m (+28/-0)
musicstreaming/controls/SongUITableViewCell.h (+4/-0)
musicstreaming/controls/SongUITableViewCell.m (+20/-8)
musicstreaming/iSub-Info.plist (+9/-1)
musicstreaming/iSub.xcodeproj/project.pbxproj (+162/-58)
musicstreaming/iSub_Prefix.pch (+1/-1)
musicstreaming/models/Album.h (+3/-1)
musicstreaming/models/Album.m (+34/-40)
musicstreaming/models/Artist.h (+3/-1)
musicstreaming/models/Artist.m (+35/-17)
musicstreaming/models/MOC.m (+1/-6)
musicstreaming/models/Playlist.h (+17/-17)
musicstreaming/models/Playlist.m (+64/-38)
musicstreaming/models/Song.h (+14/-3)
musicstreaming/models/Song.m (+11/-33)
musicstreaming/utilities/AlbumListParser.m (+1/-27)
musicstreaming/utilities/AlbumParser.m (+7/-11)
musicstreaming/utilities/ArtistListParser.h (+0/-10)
musicstreaming/utilities/ArtistListParser.m (+4/-56)
musicstreaming/utilities/ArtistParser.m (+4/-7)
musicstreaming/utilities/AudioStreamer.h (+6/-1)
musicstreaming/utilities/AudioStreamer.m (+97/-62)
musicstreaming/utilities/DataCache.h (+0/-29)
musicstreaming/utilities/DataCache.m (+0/-56)
musicstreaming/utilities/Globals.h (+21/-0)
musicstreaming/utilities/Globals.m (+51/-0)
musicstreaming/utilities/PlaylistListParser.h (+0/-2)
musicstreaming/utilities/PlaylistListParser.m (+9/-27)
musicstreaming/utilities/PlaylistParser.h (+7/-8)
musicstreaming/utilities/PlaylistParser.m (+52/-24)
musicstreaming/utilities/StreamingPlayer.m (+11/-1)
musicstreaming/utilities/Subsonic.m (+5/-1)
musicstreaming/utilities/operations/AbstractNetworkOperation.h (+35/-0)
musicstreaming/utilities/operations/AbstractNetworkOperation.m (+116/-0)
musicstreaming/utilities/operations/AlbumArtLoader.h (+10/-7)
musicstreaming/utilities/operations/AlbumArtLoader.m (+61/-12)
musicstreaming/utilities/operations/AlbumArtLoadingOperation.h (+13/-27)
musicstreaming/utilities/operations/AlbumArtLoadingOperation.m (+24/-87)
musicstreaming/utilities/operations/DownloadOperation.h (+32/-0)
musicstreaming/utilities/operations/DownloadOperation.m (+79/-0)
musicstreaming/utilities/operations/Downloader.h (+29/-0)
musicstreaming/utilities/operations/Downloader.m (+188/-0)
musicstreaming/view_controllers/AlbumListViewController.h (+3/-3)
musicstreaming/view_controllers/AlbumListViewController.m (+16/-28)
musicstreaming/view_controllers/AlbumViewController.h (+12/-5)
musicstreaming/view_controllers/AlbumViewController.m (+114/-25)
musicstreaming/view_controllers/ArtistListViewController.m (+3/-2)
musicstreaming/view_controllers/ArtistViewController.h (+3/-1)
musicstreaming/view_controllers/ArtistViewController.m (+68/-22)
musicstreaming/view_controllers/PlaylistEditAlbumListViewController.m (+1/-0)
musicstreaming/view_controllers/PlaylistEditAlbumViewController.m (+3/-3)
musicstreaming/view_controllers/PlaylistEditArtistListViewController.m (+1/-0)
musicstreaming/view_controllers/PlaylistEditArtistViewController.m (+1/-0)
musicstreaming/view_controllers/PlaylistEditSongListViewController.m (+4/-3)
musicstreaming/view_controllers/PlaylistEditViewController.m (+2/-3)
musicstreaming/view_controllers/PlaylistListViewController.m (+36/-42)
musicstreaming/view_controllers/PlaylistViewController.h (+2/-0)
musicstreaming/view_controllers/PlaylistViewController.m (+50/-55)
musicstreaming/view_controllers/SettingsViewController.h (+8/-2)
musicstreaming/view_controllers/SettingsViewController.m (+217/-28)
musicstreaming/view_controllers/SongListViewController.m (+3/-2)
musicstreaming/view_controllers/SongViewController.h (+8/-6)
musicstreaming/view_controllers/SongViewController.m (+44/-14)
musicstreaming/view_controllers/SubsonicIndexedTableViewController.m (+8/-0)
musicstreaming/view_controllers/SubsonicTableViewController.h (+7/-1)
musicstreaming/view_controllers/SubsonicTableViewController.m (+30/-17)
musicstreaming/view_controllers/SubsonicViewController.m (+13/-8)
musicstreaming/xibs/MainWindow.xib (+73/-32)
musicstreaming/xibs/PlaylistEditViewController.xib (+58/-31)
musicstreaming/xibs/SongViewController.xib (+22/-29)
To merge this branch: bzr merge lp:~urbanape/ubuntuone-ios-client/downloader
Reviewer Review Type Date Requested Status
Ubuntu One iOS Client Team Pending
Review via email: mp+63031@code.launchpad.net

Description of the change

Ridiculously large branch that does a lot:

  - Default album art behavior internalized to the AlbumArtLoader
  - AlbumArtLoader uses a persistent filesystem cache for album art
  - Music cache buttons on Album view allow for downloading tracks for off-line listening
  - Cache controls in Settings screen allow for clearing music/album art cache

Known issues (to be resolved in subsequent branches):

  - Playlists don't seem to be getting wired up correctly, though they no longer crash the app
  - Playlist editing isn't wired up properly at all
  - Playlist song ordering isn't wired up properly at all

To post a comment you must log in.
236. By Zachery Bir

2.0 (6)

- Fix background streaming progress (or at least make it less broken - Matt Griffin and Aaron Brethorst)
- Album art properly shows on playback screen when first playing (Matt Griffin)
- Cache report values properly update when downloading/deleting tracks
- Tapping button for cached songs deletes cached file
- Tapping button while downloading is a no-op
- Tapping a playing track while in album view goes to Now Playing view instead of restarting the track
- Settings now shows number of Artists, Albums, Songs, and Playlists
- Bumped up the buffering to hopefully hit the sweet spot of reliability/responsiveness.
- Cleaned up a bunch of small bits (Jason Foreman)

237. By Zachery Bir

- little bit of debugging

- turn off idle timer when downloading, turn it back on when download queue is empty

- delete playlists locally as well as remotely

- ditch the annoying error message

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'musicstreaming/Default.png'
2Binary files musicstreaming/Default.png 2011-05-02 02:22:54 +0000 and musicstreaming/Default.png 2011-06-13 16:58:33 +0000 differ
3=== modified file 'musicstreaming/Default@2x.png'
4Binary files musicstreaming/Default@2x.png 2011-05-02 02:22:54 +0000 and musicstreaming/Default@2x.png 2011-06-13 16:58:33 +0000 differ
5=== modified file 'musicstreaming/Other Sources/iSubAppDelegate.h'
6--- musicstreaming/Other Sources/iSubAppDelegate.h 2011-03-28 18:18:01 +0000
7+++ musicstreaming/Other Sources/iSubAppDelegate.h 2011-06-13 16:58:33 +0000
8@@ -34,10 +34,10 @@
9
10 UIWindow *window;
11 UITabBarController *tabBarController;
12- NSMutableDictionary *coverArtCache;
13 }
14 @property (nonatomic, retain) IBOutlet UIWindow *window;
15 @property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
16-@property (nonatomic, retain) NSMutableDictionary *coverArtCache;
17+
18+extern NSString * const NOTIF_reloadAccountCredentials;
19 @end
20
21
22=== modified file 'musicstreaming/Other Sources/iSubAppDelegate.m'
23--- musicstreaming/Other Sources/iSubAppDelegate.m 2011-05-05 08:09:46 +0000
24+++ musicstreaming/Other Sources/iSubAppDelegate.m 2011-06-13 16:58:33 +0000
25@@ -49,7 +49,6 @@
26
27 @synthesize window;
28 @synthesize tabBarController;
29-@synthesize coverArtCache;
30
31 #pragma mark -
32 #pragma mark Application lifecycle
33@@ -66,8 +65,6 @@
34 }
35 }
36
37- coverArtCache = [[NSMutableDictionary alloc] init];
38-
39 [self configureUsernamePassword];
40
41 [self recoverState];
42@@ -87,7 +84,7 @@
43 if ([[url scheme] isEqual:@"x-ubuntuone-music"])
44 {
45 [self parseQueryCredentials:url];
46- [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadAccountCredentials" object:nil];
47+ [[NSNotificationCenter defaultCenter] postNotificationName:NOTIF_reloadAccountCredentials object:nil];
48
49 return YES;
50 }
51@@ -164,8 +161,6 @@
52
53 - (void)dealloc
54 {
55- [coverArtCache release];
56-
57 [tabBarController release];
58
59 [window release];
60
61=== modified file 'musicstreaming/about/about.html'
62--- musicstreaming/about/about.html 2010-09-30 21:09:34 +0000
63+++ musicstreaming/about/about.html 2011-06-13 16:58:33 +0000
64@@ -1,119 +1,182 @@
65 <!DOCTYPE HTML>
66
67 <html>
68- <head>
69- <meta name="viewport" content="width=294, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
70- <link rel="stylesheet" href="about.css" type="text/css" media="screen" title="no title" charset="utf-8">
71- </head>
72- <body>
73- <div id="container">
74-
75- <div id="logo">
76- <img src="music_512.png">
77- </div>
78-
79- <h1>Ubuntu One Music</h1>
80-
81- <p class="center">Version 1.0</p>
82- <p class="center">&copy;2010 Canonical Limited. All rights reserved.</p>
83- <p class="center">Support: <a href="https://one.ubuntu.com/support/">one.ubuntu.com/support</a>
84-
85- <div id="acknowledgements">
86- <h2>Acknowledgements</h2>
87- <p>
88- Special thanks to Ben Baron, developer of iSub (<a href="http://einsteinx2.com/projects/isub/">http://einsteinx2.com/projects/isub/</a>)
89- </p>
90-
91- <div id="source">
92- <div class="item">
93- <p>
94- iSub Music Streamer<br>
95- Copyright 2010 Ben Baron. All rights reserved.
96- </p>
97- <p>
98- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
99- </p>
100- <p>
101- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
102- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.<br>
103- * Neither the my name nor the names of my contributors may be used to endorse or promote products derived from this software without specific prior written permission.
104- </p>
105- <p>
106- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
107- </p>
108- </div>
109-
110- <div class="item">
111- <p>
112- Icons by Joseph Wain / glyphish.com
113- </p>
114- </div>
115-
116- <div class="item">
117- <p>
118- SynthesizeSingleton
119- </p>
120- <p>
121- Created by Matt Gallagher on 20/10/08.<br>
122- Copyright 2009 Matt Gallagher. All rights reserved.
123- </p>
124- <p>
125- Permission is given to use this source code file without charge in any project, commercial or otherwise, entirely at your risk, with the condition that any redistribution (in part or whole) of source code must retain this copyright and permission notice. Attribution in compiled projects is appreciated but not required.</p>
126- </div>
127-
128- <div class="item">
129- <p>
130- UIDevice+Hardware<br>
131- Erica Sadun, http://ericasadun.com<br>
132- iPhone Developer's Cookbook, 3.0 Edition<br>
133- Thanks to Emanuele Vulcano, Kevin Ballard/Eridius, Ryandjohnson, Matt Brown, etc.
134- </p>
135- </div>
136- <div class="item">
137- <p>
138- MBProgressHUD<br>
139- This code is distributed under the terms and conditions of the MIT license.
140- </p>
141-
142- <p>
143- Copyright (c) 2009 Matej Bukovinski
144- </p>
145-
146- <p>
147- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
148- </p>
149-
150- <p>
151- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
152- </p>
153-
154- <p>
155- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
156- </p>
157- </div>
158-
159- <div class="item">
160- <p>
161- AsynchronousImageView<br>
162- Copyright 2009 Slava Bushtruk. All rights reserved.
163- </p>
164- </div>
165-
166- <div class="item">
167- <p>
168- Copyright (c) 2010 Leah Culver
169- </p>
170- <p>
171- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
172- </p>
173- <p>
174- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
175- </p>
176- <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
177- </p>
178- </div>
179- </div> <!-- #source -->
180- </div>
181+ <head>
182+ <meta name="viewport" content="width=294, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
183+ <link rel="stylesheet" href="about.css" type="text/css" media="screen" title="no title" charset="utf-8">
184+ </head>
185+ <body>
186+ <div id="container">
187+
188+ <div id="logo">
189+ <img src="music_512.png">
190+ </div>
191+
192+ <h1>Ubuntu One Music</h1>
193+
194+ <p class="center">
195+ Version 2.0
196+ </p>
197+ <p class="center">
198+ &copy;2010-2011 Canonical Limited. All rights reserved.
199+ </p>
200+ <p class="center">
201+ Support: <a href="https://one.ubuntu.com/support/">one.ubuntu.com/support</a>
202+ </p>
203+
204+ <div id="acknowledgements">
205+ <h2>Acknowledgements</h2>
206+ <p>
207+ Special thanks to Ben Baron, developer of iSub
208+ (<a href="http://einsteinx2.com/projects/isub/">http://einsteinx2.com/projects/isub/</a>)
209+ </p>
210+
211+ <div id="source">
212+ <div class="item">
213+ <p>
214+ iSub Music Streamer<br> Copyright 2010 Ben Baron. All
215+ rights reserved.
216+ </p>
217+ <p>
218+ Redistribution and use in source and binary forms, with
219+ or without modification, are permitted provided that the
220+ following conditions are met:
221+ </p>
222+ <p>
223+ * Redistributions of source code must retain the above
224+ copyright notice, this list of conditions and the
225+ following disclaimer.<br>
226+
227+ * Redistributions in binary form must reproduce the
228+ above copyright notice, this list of conditions and the
229+ following disclaimer in the documentation and/or other
230+ materials provided with the distribution.<br>
231+
232+ * Neither the my name nor the names of my contributors
233+ may be used to endorse or promote products derived from
234+ this software without specific prior written permission.
235+ </p>
236+ <p>
237+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
238+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
239+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
240+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
241+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
242+ COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
243+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
244+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
245+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
246+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
247+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
248+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
249+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
250+ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
251+ OF SUCH DAMAGE.
252+ </p>
253+ </div>
254+
255+ <div class="item">
256+ <p>
257+ Icons by Joseph Wain / glyphish.com
258+ </p>
259+ </div>
260+
261+ <div class="item">
262+ <p>
263+ SynthesizeSingleton
264+ </p>
265+ <p>
266+ Created by Matt Gallagher on 20/10/08.<br> Copyright
267+ 2009 Matt Gallagher. All rights reserved.
268+ </p>
269+ <p>
270+ Permission is given to use this source code file without
271+ charge in any project, commercial or otherwise, entirely
272+ at your risk, with the condition that any redistribution
273+ (in part or whole) of source code must retain this
274+ copyright and permission notice. Attribution in compiled
275+ projects is appreciated but not required.</p>
276+ </div>
277+
278+ <div class="item">
279+ <p>
280+ UIDevice+Hardware<br> Erica Sadun,
281+ http://ericasadun.com<br> iPhone Developer's Cookbook,
282+ 3.0 Edition<br> Thanks to Emanuele Vulcano, Kevin
283+ Ballard/Eridius, Ryandjohnson, Matt Brown, etc.
284+ </p>
285+ </div>
286+
287+ <div class="item">
288+ <p>
289+ Copyright (c) 2010 Leah Culver
290+ </p>
291+ <p>
292+ Permission is hereby granted, free of charge, to any
293+ person obtaining a copy of this software and associated
294+ documentation files (the "Software"), to deal in the
295+ Software without restriction, including without
296+ limitation the rights to use, copy, modify, merge,
297+ publish, distribute, sublicense, and/or sell copies of
298+ the Software, and to permit persons to whom the Software
299+ is furnished to do so, subject to the following
300+ conditions:
301+ </p>
302+ <p>
303+ The above copyright notice and this permission notice
304+ shall be included in all copies or substantial portions
305+ of the Software.
306+ </p>
307+ <p>
308+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
309+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
310+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
311+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
312+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
313+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
314+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
315+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
316+ DEALINGS IN THE SOFTWARE.
317+ </p>
318+
319+ <div class="item">
320+ <p>
321+ Copyright (C) 2011 by Random Ideas, LLC
322+ </p>
323+
324+ <p>
325+ Permission is hereby granted, free of charge, to any
326+ person obtaining a copy of this software and
327+ associated documentation files (the "Software"), to
328+ deal in the Software without restriction, including
329+ without limitation the rights to use, copy, modify,
330+ merge, publish, distribute, sublicense, and/or sell
331+ copies of the Software, and to permit persons to whom
332+ the Software is furnished to do so, subject to the
333+ following conditions:
334+ </p>
335+
336+ <p>
337+ The above copyright notice and this permission notice
338+ shall be included in all copies or substantial
339+ portions of the Software.
340+ </p>
341+
342+ <p>
343+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
344+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
345+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
346+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
347+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
348+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
349+ AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
350+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
351+ USE OR OTHER DEALINGS IN THE SOFTWARE.
352+ </p>
353+ </div>
354+ </div> <!-- #source -->
355 </div>
356- </body>
357-</html>
358\ No newline at end of file
359+ </div>
360+ </div>
361+ </body>
362+</html>
363
364=== added file 'musicstreaming/albums.png'
365Binary files musicstreaming/albums.png 1970-01-01 00:00:00 +0000 and musicstreaming/albums.png 2011-06-13 16:58:33 +0000 differ
366=== added file 'musicstreaming/albums@2x.png'
367Binary files musicstreaming/albums@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/albums@2x.png 2011-06-13 16:58:33 +0000 differ
368=== added file 'musicstreaming/artists.png'
369Binary files musicstreaming/artists.png 1970-01-01 00:00:00 +0000 and musicstreaming/artists.png 2011-06-13 16:58:33 +0000 differ
370=== added file 'musicstreaming/artists@2x.png'
371Binary files musicstreaming/artists@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/artists@2x.png 2011-06-13 16:58:33 +0000 differ
372=== added file 'musicstreaming/cached.png'
373Binary files musicstreaming/cached.png 1970-01-01 00:00:00 +0000 and musicstreaming/cached.png 2011-06-13 16:58:33 +0000 differ
374=== added file 'musicstreaming/cached@2x.png'
375Binary files musicstreaming/cached@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/cached@2x.png 2011-06-13 16:58:33 +0000 differ
376=== added file 'musicstreaming/categories/NSString+Extras.h'
377--- musicstreaming/categories/NSString+Extras.h 1970-01-01 00:00:00 +0000
378+++ musicstreaming/categories/NSString+Extras.h 2011-06-13 16:58:33 +0000
379@@ -0,0 +1,26 @@
380+//
381+// NSString+Extras.h
382+// iSub
383+//
384+// Created by Zachery Bir on 5/23/11.
385+// Copyright 2011 Canonical Ltd.
386+//
387+// This program is free software: you can redistribute it and/or modify it
388+// under the terms of the GNU Affero General Public License version 3,
389+// as published by the Free Software Foundation.
390+//
391+// This program is distributed in the hope that it will be useful, but
392+// WITHOUT ANY WARRANTY; without even the implied warranties of
393+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
394+// PURPOSE. See the GNU Affero General Public License for more details.
395+//
396+// You should have received a copy of the GNU Affero General Public License
397+// along with this program. If not, see <http://www.gnu.org/licenses/>.
398+
399+#import <Foundation/Foundation.h>
400+
401+
402+@interface NSString (NSString_Extras)
403+- (NSString *)dearticlizedString;
404+- (NSString *)dearticlizedIndex;
405+@end
406
407=== added file 'musicstreaming/categories/NSString+Extras.m'
408--- musicstreaming/categories/NSString+Extras.m 1970-01-01 00:00:00 +0000
409+++ musicstreaming/categories/NSString+Extras.m 2011-06-13 16:58:33 +0000
410@@ -0,0 +1,46 @@
411+//
412+// NSString+Extras.m
413+// iSub
414+//
415+// Created by Zachery Bir on 5/23/11.
416+// Copyright 2011 Canonical Ltd.
417+//
418+// This program is free software: you can redistribute it and/or modify it
419+// under the terms of the GNU Affero General Public License version 3,
420+// as published by the Free Software Foundation.
421+//
422+// This program is distributed in the hope that it will be useful, but
423+// WITHOUT ANY WARRANTY; without even the implied warranties of
424+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
425+// PURPOSE. See the GNU Affero General Public License for more details.
426+//
427+// You should have received a copy of the GNU Affero General Public License
428+// along with this program. If not, see <http://www.gnu.org/licenses/>.
429+
430+#import "NSString+Extras.h"
431+
432+
433+@implementation NSString (NSString_Extras)
434+
435+- (NSString *)dearticlizedString
436+{
437+ NSError *error = NULL;
438+ NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:@"^(the|a|an|le|les|la|el|las|los)\\s(.*)$"
439+ options:NSRegularExpressionCaseInsensitive
440+ error:&error];
441+
442+ NSString *dearticlized = [expression stringByReplacingMatchesInString:self
443+ options:NSMatchingWithoutAnchoringBounds
444+ range:NSMakeRange(0, [self length])
445+ withTemplate:@"$2, $1"];
446+
447+ return dearticlized;
448+}
449+
450+- (NSString *)dearticlizedIndex
451+{
452+ NSString *index = [[self dearticlizedString] substringToIndex:1];
453+
454+ return index;
455+}
456+@end
457
458=== added file 'musicstreaming/categories/UIActionSheet+Blocks.h'
459--- musicstreaming/categories/UIActionSheet+Blocks.h 1970-01-01 00:00:00 +0000
460+++ musicstreaming/categories/UIActionSheet+Blocks.h 2011-06-13 16:58:33 +0000
461@@ -0,0 +1,16 @@
462+//
463+// UIActionSheet+Blocks.h
464+// Shibui
465+//
466+// Created by Jiva DeVoe on 1/5/11.
467+// Copyright 2011 Random Ideas, LLC. All rights reserved.
468+//
469+
470+#import <Foundation/Foundation.h>
471+#import "RIButtonItem.h"
472+
473+@interface UIActionSheet (Blocks) <UIActionSheetDelegate>
474+
475+-(id)initWithTitle:(NSString *)inTitle cancelButtonItem:(RIButtonItem *)inCancelButtonItem destructiveButtonItem:(RIButtonItem *)inDestructiveItem otherButtonItems:(RIButtonItem *)inOtherButtonItems, ... NS_REQUIRES_NIL_TERMINATION;
476+
477+@end
478
479=== added file 'musicstreaming/categories/UIActionSheet+Blocks.m'
480--- musicstreaming/categories/UIActionSheet+Blocks.m 1970-01-01 00:00:00 +0000
481+++ musicstreaming/categories/UIActionSheet+Blocks.m 2011-06-13 16:58:33 +0000
482@@ -0,0 +1,71 @@
483+//
484+// UIActionSheet+Blocks.m
485+// Shibui
486+//
487+// Created by Jiva DeVoe on 1/5/11.
488+// Copyright 2011 Random Ideas, LLC. All rights reserved.
489+//
490+
491+#import "UIActionSheet+Blocks.h"
492+#import <objc/runtime.h>
493+
494+static NSString *RI_BUTTON_ASS_KEY = @"com.random-ideas.BUTTONS";
495+
496+@implementation UIActionSheet (Blocks)
497+
498+-(id)initWithTitle:(NSString *)inTitle cancelButtonItem:(RIButtonItem *)inCancelButtonItem destructiveButtonItem:(RIButtonItem *)inDestructiveItem otherButtonItems:(RIButtonItem *)inOtherButtonItems, ...
499+{
500+ if((self = [self initWithTitle:inTitle delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]))
501+ {
502+ NSMutableArray *buttonsArray = [NSMutableArray array];
503+
504+ RIButtonItem *eachItem;
505+ va_list argumentList;
506+ if (inOtherButtonItems)
507+ {
508+ [buttonsArray addObject: inOtherButtonItems];
509+ va_start(argumentList, inOtherButtonItems);
510+ while((eachItem = va_arg(argumentList, RIButtonItem *)))
511+ {
512+ [buttonsArray addObject: eachItem];
513+ }
514+ va_end(argumentList);
515+ }
516+
517+ for(RIButtonItem *item in buttonsArray)
518+ {
519+ [self addButtonWithTitle:item.label];
520+ }
521+
522+ if(inDestructiveItem)
523+ {
524+ [buttonsArray addObject:inDestructiveItem];
525+ NSInteger destIndex = [self addButtonWithTitle:inDestructiveItem.label];
526+ [self setDestructiveButtonIndex:destIndex];
527+ }
528+ if(inCancelButtonItem)
529+ {
530+ [buttonsArray addObject:inCancelButtonItem];
531+ NSInteger cancelIndex = [self addButtonWithTitle:inCancelButtonItem.label];
532+ [self setCancelButtonIndex:cancelIndex];
533+ }
534+
535+ objc_setAssociatedObject(self, RI_BUTTON_ASS_KEY, buttonsArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
536+
537+ [self retain]; // keep yourself around!
538+ }
539+ return self;
540+}
541+
542+- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
543+{
544+ NSArray *buttonsArray = objc_getAssociatedObject(self, RI_BUTTON_ASS_KEY);
545+ RIButtonItem *item = [buttonsArray objectAtIndex:buttonIndex];
546+ if(item.action)
547+ item.action();
548+ objc_setAssociatedObject(self, RI_BUTTON_ASS_KEY, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
549+ [self release]; // and release yourself!
550+}
551+
552+
553+@end
554
555=== added file 'musicstreaming/categories/UIAlertView+Blocks.h'
556--- musicstreaming/categories/UIAlertView+Blocks.h 1970-01-01 00:00:00 +0000
557+++ musicstreaming/categories/UIAlertView+Blocks.h 2011-06-13 16:58:33 +0000
558@@ -0,0 +1,16 @@
559+//
560+// UIAlertView+Blocks.h
561+// Shibui
562+//
563+// Created by Jiva DeVoe on 12/28/10.
564+// Copyright 2010 Random Ideas, LLC. All rights reserved.
565+//
566+
567+#import <Foundation/Foundation.h>
568+#import "RIButtonItem.h"
569+
570+@interface UIAlertView (Blocks)
571+
572+-(id)initWithTitle:(NSString *)inTitle message:(NSString *)inMessage cancelButtonItem:(RIButtonItem *)inCancelButtonItem otherButtonItems:(RIButtonItem *)inOtherButtonItems, ... NS_REQUIRES_NIL_TERMINATION;
573+
574+@end
575
576=== added file 'musicstreaming/categories/UIAlertView+Blocks.m'
577--- musicstreaming/categories/UIAlertView+Blocks.m 1970-01-01 00:00:00 +0000
578+++ musicstreaming/categories/UIAlertView+Blocks.m 2011-06-13 16:58:33 +0000
579@@ -0,0 +1,61 @@
580+//
581+// UIAlertView+Blocks.m
582+// Shibui
583+//
584+// Created by Jiva DeVoe on 12/28/10.
585+// Copyright 2010 Random Ideas, LLC. All rights reserved.
586+//
587+
588+#import "UIAlertView+Blocks.h"
589+#import <objc/runtime.h>
590+
591+static NSString *RI_BUTTON_ASS_KEY = @"com.random-ideas.BUTTONS";
592+
593+@implementation UIAlertView (Blocks)
594+
595+-(id)initWithTitle:(NSString *)inTitle message:(NSString *)inMessage cancelButtonItem:(RIButtonItem *)inCancelButtonItem otherButtonItems:(RIButtonItem *)inOtherButtonItems, ...
596+{
597+ if((self = [self initWithTitle:inTitle message:inMessage delegate:self cancelButtonTitle:inCancelButtonItem.label otherButtonTitles:nil]))
598+ {
599+ NSMutableArray *buttonsArray = [NSMutableArray array];
600+
601+ RIButtonItem *eachItem;
602+ va_list argumentList;
603+ if (inOtherButtonItems)
604+ {
605+ [buttonsArray addObject: inOtherButtonItems];
606+ va_start(argumentList, inOtherButtonItems);
607+ while((eachItem = va_arg(argumentList, RIButtonItem *)))
608+ {
609+ [buttonsArray addObject: eachItem];
610+ }
611+ va_end(argumentList);
612+ }
613+
614+ for(RIButtonItem *item in buttonsArray)
615+ {
616+ [self addButtonWithTitle:item.label];
617+ }
618+
619+ if(inCancelButtonItem)
620+ [buttonsArray insertObject:inCancelButtonItem atIndex:0];
621+
622+ objc_setAssociatedObject(self, RI_BUTTON_ASS_KEY, buttonsArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
623+
624+ [self setDelegate:self];
625+ [self retain]; // keep yourself around!
626+ }
627+ return self;
628+}
629+
630+- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
631+{
632+ NSArray *buttonsArray = objc_getAssociatedObject(self, RI_BUTTON_ASS_KEY);
633+ RIButtonItem *item = [buttonsArray objectAtIndex:buttonIndex];
634+ if(item.action)
635+ item.action();
636+ objc_setAssociatedObject(self, RI_BUTTON_ASS_KEY, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
637+ [self release]; // and release yourself!
638+}
639+
640+@end
641
642=== modified file 'musicstreaming/controls/AlbumUITableViewCell.h'
643--- musicstreaming/controls/AlbumUITableViewCell.h 2010-09-20 20:32:15 +0000
644+++ musicstreaming/controls/AlbumUITableViewCell.h 2011-06-13 16:58:33 +0000
645@@ -30,14 +30,12 @@
646
647 #import <UIKit/UIKit.h>
648
649-@class AsynchronousImageViewCached;
650-
651 @interface AlbumUITableViewCell : UITableViewCell {
652- AsynchronousImageViewCached *coverArtView;
653+ UIImageView *coverArtView;
654 UILabel *albumNameLabel;
655 }
656
657-@property (nonatomic, retain) AsynchronousImageViewCached *coverArtView;
658+@property (nonatomic, retain) UIImageView *coverArtView;
659 @property (nonatomic, retain) UILabel *albumNameLabel;
660
661 @end
662
663=== modified file 'musicstreaming/controls/AlbumUITableViewCell.m'
664--- musicstreaming/controls/AlbumUITableViewCell.m 2011-04-14 04:24:43 +0000
665+++ musicstreaming/controls/AlbumUITableViewCell.m 2011-06-13 16:58:33 +0000
666@@ -29,7 +29,6 @@
667 // DAMAGE.
668
669 #import "AlbumUITableViewCell.h"
670-#import "AsynchronousImageViewCached.h"
671
672 @implementation AlbumUITableViewCell
673
674@@ -37,7 +36,7 @@
675
676 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
677 if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
678- self.coverArtView = [[[AsynchronousImageViewCached alloc] init] autorelease];
679+ self.coverArtView = [[[UIImageView alloc] init] autorelease];
680 [self.contentView addSubview:self.coverArtView];
681
682 self.albumNameLabel = [[[UILabel alloc] init] autorelease];
683
684=== removed file 'musicstreaming/controls/AsynchronousImageView.h'
685--- musicstreaming/controls/AsynchronousImageView.h 2010-09-20 20:32:15 +0000
686+++ musicstreaming/controls/AsynchronousImageView.h 1970-01-01 00:00:00 +0000
687@@ -1,23 +0,0 @@
688-//
689-// AsynchronousImageView.h
690-// GLOSS
691-//
692-// Created by Слава on 22.10.09.
693-// Copyright 2009 Slava Bushtruk. All rights reserved.
694-// ---------------------------------------------------
695-//
696-// Modified by Ben Baron for the iSub project.
697-//
698-
699-#import <UIKit/UIKit.h>
700-
701-
702-@interface AsynchronousImageView : UIImageView
703-{
704- NSURLConnection *connection;
705- NSMutableData *data;
706-}
707-
708-- (void)loadImageFromURLString:(NSString *)theUrlString;
709-
710-@end
711
712=== removed file 'musicstreaming/controls/AsynchronousImageView.m'
713--- musicstreaming/controls/AsynchronousImageView.m 2011-04-15 20:03:25 +0000
714+++ musicstreaming/controls/AsynchronousImageView.m 1970-01-01 00:00:00 +0000
715@@ -1,78 +0,0 @@
716-//
717-// AsynchronousImageView.m
718-// GLOSS
719-//
720-// Created by Слава on 22.10.09.
721-// Copyright 2009 Slava Bushtruk. All rights reserved.
722-// ---------------------------------------------------
723-//
724-// Modified by Ben Baron for the iSub project.
725-//
726-
727-#import "AsynchronousImageView.h"
728-#import "iSubAppDelegate.h"
729-#import "StreamingPlayer.h"
730-
731-@implementation AsynchronousImageView
732-
733-- (void)loadImageFromURLString:(NSString *)theUrlString
734-{
735- [self.image release], self.image = nil;
736- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:theUrlString]
737- cachePolicy:NSURLRequestReturnCacheDataElseLoad
738- timeoutInterval:30.0];
739-
740- connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
741-}
742-
743-
744-- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData
745-{
746- if (data == nil)
747- data = [[NSMutableData alloc] initWithCapacity:2048];
748-
749- [data appendData:incrementalData];
750-}
751-
752-
753-- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
754-{
755- NSLog(@"Connection to album art failed");
756-
757- [[StreamingPlayer sharedStreamingPlayer] setCurrentCoverArt:[UIImage imageNamed:@"default-album-art.png"]];
758- self.image = [StreamingPlayer sharedStreamingPlayer].currentCoverArt;
759-
760- RELEASE_SAFELY(data);
761- RELEASE_SAFELY(connection);
762-}
763-
764-
765-- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
766-{
767- UIImage *image = [UIImage imageWithData:data];
768-
769- if (image)
770- {
771- [StreamingPlayer sharedStreamingPlayer].currentCoverArt = image;
772- }
773- else
774- {
775- [StreamingPlayer sharedStreamingPlayer].currentCoverArt = [UIImage imageNamed:@"default-album-art.png"];
776- }
777-
778- self.image = [StreamingPlayer sharedStreamingPlayer].currentCoverArt;
779-
780- RELEASE_SAFELY(data);
781- RELEASE_SAFELY(connection);
782-}
783-
784-
785-- (void)dealloc
786-{
787- RELEASE_SAFELY(data);
788- RELEASE_SAFELY(connection);
789-
790- [super dealloc];
791-}
792-
793-@end
794
795=== removed file 'musicstreaming/controls/AsynchronousImageViewCached.h'
796--- musicstreaming/controls/AsynchronousImageViewCached.h 2011-04-14 19:06:11 +0000
797+++ musicstreaming/controls/AsynchronousImageViewCached.h 1970-01-01 00:00:00 +0000
798@@ -1,26 +0,0 @@
799-//
800-// AsynchronousImageView.h
801-// GLOSS
802-//
803-// Created by Слава on 22.10.09.
804-// Copyright 2009 Slava Bushtruk. All rights reserved.
805-// ---------------------------------------------------
806-//
807-// Modified by Ben Baron for the iSub project.
808-//
809-
810-#import <UIKit/UIKit.h>
811-
812-
813-@interface AsynchronousImageViewCached : UIImageView
814-{
815- NSURLConnection *connection;
816- NSMutableData *data;
817- NSString *coverArtId;
818- int displaySize;
819-}
820-- (void)loadImageId:(NSString *)artId forSize:(int)aSize;
821-- (void)loadImageFromURLString:(NSString *)theUrlString;
822-- (void)loadCachedImageId:(NSString *)artId;
823-- (void)loadDefaultImage;
824-@end
825
826=== removed file 'musicstreaming/controls/AsynchronousImageViewCached.m'
827--- musicstreaming/controls/AsynchronousImageViewCached.m 2011-04-15 20:03:25 +0000
828+++ musicstreaming/controls/AsynchronousImageViewCached.m 1970-01-01 00:00:00 +0000
829@@ -1,120 +0,0 @@
830-//
831-// AsynchronousImageView.m
832-// GLOSS
833-//
834-// Created by Слава on 22.10.09.
835-// Copyright 2009 Slava Bushtruk. All rights reserved.
836-// ---------------------------------------------------
837-//
838-// Modified by Ben Baron for the iSub project.
839-//
840-
841-#import "AsynchronousImageViewCached.h"
842-#import "iSubAppDelegate.h"
843-#import "UIImage+Resize.h"
844-#import "NSMutableSet+Extras.h"
845-#import "Subsonic.h"
846-
847-@implementation AsynchronousImageViewCached
848-
849-- (void)loadImageId:(NSString *)artId forSize:(int)aSize
850-{
851- coverArtId = [artId retain];
852- displaySize = aSize;
853- if (nil != artId)
854- {
855- [self loadCachedImageId:artId];
856- }
857- else
858- {
859- [self loadDefaultImage];
860- }
861-}
862-
863-- (void)loadImageFromURLString:(NSString *)theUrlString
864-{
865- self.image = nil;
866- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:theUrlString]
867- cachePolicy:NSURLRequestReturnCacheDataElseLoad
868- timeoutInterval:30.0];
869- connection = [[NSURLConnection alloc] initWithRequest:request
870- delegate:self
871- startImmediately:YES];
872-}
873-
874-- (void)loadCachedImageId:(NSString *)artId
875-{
876- iSubAppDelegate *appDelegate = (iSubAppDelegate *)[[UIApplication sharedApplication] delegate];
877-
878- if ([appDelegate.coverArtCache objectForKey:artId])
879- {
880- // If the image is already in the cache dictionary, load it
881- self.image = [[appDelegate.coverArtCache objectForKey:artId] resizedImage:CGSizeMake(displaySize, displaySize)
882- interpolationQuality:kCGInterpolationHigh];
883- }
884- else
885- {
886- // If not, grab it from the url and cache it
887- NSMutableSet *parameters = [NSMutableSet set];
888- [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", artId, nil]];
889- // Always get the largest we can handle
890- [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"size", [NSString stringWithFormat:@"%d", 640], nil]];
891- NSURL *url = [[Subsonic sharedSubsonic] getBaseURL:@"getCoverArt.view" parameters:parameters];
892- [self loadImageFromURLString:[url absoluteString]];
893- }
894-}
895-
896-- (void)loadDefaultImage
897-{
898- self.image = [[UIImage imageNamed:@"default-album-art.png"] resizedImage:CGSizeMake(displaySize, displaySize)
899- interpolationQuality:kCGInterpolationHigh];
900-}
901-
902-- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData
903-{
904- if (data == nil)
905- data = [[NSMutableData alloc] initWithCapacity:2048];
906-
907- [data appendData:incrementalData];
908-}
909-
910-
911-- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
912-{
913- NSLog(@"Connection to album art failed");
914- [data release], data = nil;
915- [connection release], connection = nil;
916-}
917-
918-
919-- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
920-{
921- iSubAppDelegate *appDelegate = (iSubAppDelegate *)[[UIApplication sharedApplication] delegate];
922-
923- // Check to see if the data is a valid image. If so, use it; if not, use the default image.
924- if([UIImage imageWithData:data])
925- {
926- [appDelegate.coverArtCache setObject:[UIImage imageWithData:data] forKey:coverArtId];
927- self.image = [[UIImage imageWithData:data] resizedImage:CGSizeMake(displaySize, displaySize)
928- interpolationQuality:kCGInterpolationHigh];
929- }
930- else
931- {
932- [self loadDefaultImage];
933- }
934-
935- [coverArtId release];
936- [data release], data = nil;
937- [connection release], connection = nil;
938-}
939-
940-
941-- (void)dealloc {
942- [data release];
943- [connection cancel];
944- [connection release];
945- connection = nil;
946- [super dealloc];
947-}
948-
949-@end
950
951=== removed file 'musicstreaming/controls/MBProgressHUD.h'
952--- musicstreaming/controls/MBProgressHUD.h 2010-08-26 22:06:31 +0000
953+++ musicstreaming/controls/MBProgressHUD.h 1970-01-01 00:00:00 +0000
954@@ -1,273 +0,0 @@
955-//
956-// MBProgressHUD.h
957-// Version 0.33
958-// Created by Matej Bukovinski on 2.4.09.
959-//
960-
961-// This code is distributed under the terms and conditions of the MIT license.
962-
963-// Copyright (c) 2009 Matej Bukovinski
964-//
965-// Permission is hereby granted, free of charge, to any person obtaining a copy
966-// of this software and associated documentation files (the "Software"), to deal
967-// in the Software without restriction, including without limitation the rights
968-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
969-// copies of the Software, and to permit persons to whom the Software is
970-// furnished to do so, subject to the following conditions:
971-//
972-// The above copyright notice and this permission notice shall be included in
973-// all copies or substantial portions of the Software.
974-//
975-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
976-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
977-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
978-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
979-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
980-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
981-// THE SOFTWARE.
982-
983-#import <UIKit/UIKit.h>
984-
985-/**
986- * MBProgressHUD operation modes.
987- */
988-typedef enum {
989- /** Progress is shown using an UIActivityIndicatorView. This is the default. */
990- MBProgressHUDModeIndeterminate,
991- /** Progress is shown using a MBRoundProgressView. */
992- MBProgressHUDModeDeterminate,
993- /** Shows a custom view */
994- MBProgressHUDModeCustomView
995-} MBProgressHUDMode;
996-
997-
998-/**
999- * Defines callback methods for MBProgressHUD delegates.
1000- */
1001-@protocol MBProgressHUDDelegate <NSObject>
1002-
1003-@required
1004-/**
1005- * A callback function that is called after the HUD was fully hidden from the screen.
1006- */
1007-- (void)hudWasHidden;
1008-
1009-@end
1010-
1011-
1012-/**
1013- * A progress view for showing definite progress by filling up a circle (similar to the indicator for building in xcode).
1014- */
1015-@interface MBRoundProgressView : UIProgressView {
1016-
1017-}
1018-
1019-/**
1020- * Create a 37 by 37 pixel indicator.
1021- * This is the same size as used by the larger UIActivityIndicatorView.
1022- */
1023-- (id)initWithDefaultSize;
1024-
1025-@end
1026-
1027-/**
1028- * Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
1029- *
1030- * This is a simple drop-in class for displaying a progress HUD view similar to Apples private UIProgressHUD class.
1031- * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all
1032- * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is
1033- * drawn centered as a rounded semi-transparent view witch resizes depending on the user specified content.
1034- *
1035- * This view supports three modes of operation:
1036- * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView
1037- * - MBProgressHUDModeDeterminate - shows a custom round progress indicator (MBRoundProgressView)
1038- * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView)
1039- *
1040- * All three modes can have optional labels assigned:
1041- * - If the labelText property is set and non-empty then a label containing the provided content is placed below the
1042- * indicator view.
1043- * - If also the detailsLabelText property is set then another label is placed below the first label.
1044- */
1045-@interface MBProgressHUD : UIView {
1046-
1047- MBProgressHUDMode mode;
1048-
1049- SEL methodForExecution;
1050- id targetForExecution;
1051- id objectForExecution;
1052- BOOL useAnimation;
1053-
1054- float yOffset;
1055- float xOffset;
1056-
1057- float width;
1058- float height;
1059-
1060- BOOL taskInProgress;
1061- float graceTime;
1062- float minShowTime;
1063- NSTimer *graceTimer;
1064- NSTimer *minShowTimer;
1065- NSDate *showStarted;
1066-
1067- UIView *indicator;
1068- UILabel *label;
1069- UILabel *detailsLabel;
1070-
1071- float progress;
1072-
1073- id<MBProgressHUDDelegate> delegate;
1074- NSString *labelText;
1075- NSString *detailsLabelText;
1076- float opacity;
1077- UIFont *labelFont;
1078- UIFont *detailsLabelFont;
1079-
1080- BOOL isFinished;
1081-
1082- UIView *customView;
1083-}
1084-
1085-/**
1086- * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with
1087- * window.bounds as the parameter.
1088- *
1089- * @param window The window instance that will provide the bounds for the HUD. Should probably be the same instance as
1090- * the HUD's superview (i.e., the window that the HUD will be added to).
1091- */
1092-- (id)initWithWindow:(UIWindow *)window;
1093-
1094-/**
1095- * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
1096- * view.bounds as the parameter
1097- *
1098- * @param view The view instance that will provide the bounds for the HUD. Should probably be the same instance as
1099- * the HUD's superview (i.e., the view that the HUD will be added to).
1100- */
1101-- (id)initWithView:(UIView *)view;
1102-
1103-/**
1104- * The UIView (i.g., a UIIMageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
1105- * For best results use a 37 by 37 pixel view (so the bounds match the build in indicator bounds).
1106- */
1107-@property (retain) UIView *customView;
1108-
1109-/**
1110- * MBProgressHUD operation mode. Switches between indeterminate (MBProgressHUDModeIndeterminate) and determinate
1111- * progress (MBProgressHUDModeDeterminate). The default is MBProgressHUDModeIndeterminate.
1112- */
1113-@property (assign) MBProgressHUDMode mode;
1114-
1115-/**
1116- * The HUD delegate object. If set the delegate will receive hudWasHidden callbacks when the HUD was hidden. The
1117- * delegate should conform to the MBProgressHUDDelegate protocol and implement the hudWasHidden method. The delegate
1118- * object will not be retained.
1119- */
1120-@property (assign) id<MBProgressHUDDelegate> delegate;
1121-
1122-/**
1123- * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
1124- * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or
1125- * set to @"", then no message is displayed.
1126- */
1127-@property (copy) NSString *labelText;
1128-
1129-/**
1130- * An optional details message displayed below the labelText message. This message is displayed only if the labelText
1131- * property is also set and is different from an empty string (@"").
1132- */
1133-@property (copy) NSString *detailsLabelText;
1134-
1135-/**
1136- * The opacity of the HUD window. Defaults to 0.9 (90% opacity).
1137- */
1138-@property (assign) float opacity;
1139-
1140-/**
1141- * The x-axis offset of the HUD relative to the centre of the superview.
1142- */
1143-@property (assign) float xOffset;
1144-
1145-/**
1146- *The y-ayis offset of the HUD relative to the centre of the superview.
1147- */
1148-@property (assign) float yOffset;
1149-
1150-/*
1151- * Grace period is the time (in seconds) that the invoked method may be run without
1152- * showing the HUD. If the task finishes befor the grace time runs out, the HUD will
1153- * not be shown at all.
1154- * This may be used to prevent HUD display for very short tasks.
1155- * Defaults to 0 (no grace time).
1156- * Grace time functionality is only supported when the task status is known!
1157- * @see taskInProgress
1158- */
1159-@property (assign) float graceTime;
1160-
1161-
1162-/**
1163- * The minimum time (in seconds) that the HUD is shown.
1164- * This avoids the problem of the HUD being shown and than instantly hidden.
1165- * Defaults to 0 (no minimum show time).
1166- */
1167-@property (assign) float minShowTime;
1168-
1169-/**
1170- * Indicates that the executed operation is in progress. Needed for correct graceTime operation.
1171- * If you don't set a graceTime (different than 0.0) this does nothing.
1172- * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:.
1173- * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly),
1174- * you need to set this property when your task starts and completes in order to have normal graceTime
1175- * functunality.
1176- */
1177-@property (assign) BOOL taskInProgress;
1178-
1179-/**
1180- * Font to be used for the main label. Set this property if the default is not adequate.
1181- */
1182-@property (retain) UIFont* labelFont;
1183-
1184-/**
1185- * Font to be used for the details label. Set this property if the default is not adequate.
1186- */
1187-@property (retain) UIFont* detailsLabelFont;
1188-
1189-/**
1190- * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
1191- */
1192-@property (assign) float progress;
1193-
1194-/**
1195- * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so
1196- * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread
1197- * (e.g., when using something like NSOperation or calling an asynchronous call like NSUrlRequest).
1198- *
1199- * @param animated If set to YES the HUD will appear using a fade animation. If set to NO the HUD will not use
1200- * animations while appearing.
1201- */
1202-- (void)show:(BOOL)animated;
1203-
1204-/**
1205- * Hide the HUD, this still calls the hudWasHidden delegate. This is the counterpart of the hide: method. Use it to
1206- * hide the HUD when your task completes.
1207- *
1208- * @param animated If set to YES the HUD will disappear using a fade animation. If set to NO the HUD will not use
1209- * animations while disappearing.
1210- */
1211-- (void)hide:(BOOL)animated;
1212-
1213-/**
1214- * Shows the HUD while a background task is executing in a new thread, then hides the HUD.
1215- *
1216- * This method also takes care of NSAutoreleasePools so your method does not have to be concerned with setting up a
1217- * pool.
1218- *
1219- * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
1220- * @param target The object that the target method belongs to.
1221- * @param object An optional object to be passed to the method.
1222- * @param animated If set to YES the HUD will appear and disappear using a fade animation. If set to NO the HUD will
1223- * not use animations while appearing and disappearing.
1224- */
1225-- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
1226-
1227-@end
1228
1229=== removed file 'musicstreaming/controls/MBProgressHUD.m'
1230--- musicstreaming/controls/MBProgressHUD.m 2010-08-26 22:06:31 +0000
1231+++ musicstreaming/controls/MBProgressHUD.m 1970-01-01 00:00:00 +0000
1232@@ -1,529 +0,0 @@
1233-//
1234-// MBProgressHUD.m
1235-// Version 0.33
1236-// Created by Matej Bukovinski on 2.4.09.
1237-//
1238-
1239-#import "MBProgressHUD.h"
1240-
1241-@interface MBProgressHUD ()
1242-
1243-- (void)hideUsingAnimation:(BOOL)animated;
1244-- (void)showUsingAnimation:(BOOL)animated;
1245-
1246-- (void)fillRoundedRect:(CGRect)rect inContext:(CGContextRef)context;
1247-
1248-- (void)done;
1249-
1250-- (void)updateLabelText:(NSString *)newText;
1251-- (void)updateDetailsLabelText:(NSString *)newText;
1252-- (void)updateProgress;
1253-- (void)updateIndicators;
1254-
1255-- (void)handleGraceTimer:(NSTimer *)theTimer;
1256-- (void)handleMinShowTimer:(NSTimer *)theTimer;
1257-
1258-@property (retain) UIView *indicator;
1259-
1260-@property (assign) float width;
1261-@property (assign) float height;
1262-
1263-@property (retain) NSTimer *graceTimer;
1264-@property (retain) NSTimer *minShowTimer;
1265-
1266-@property (retain) NSDate *showStarted;
1267-
1268-@end
1269-
1270-
1271-@implementation MBProgressHUD
1272-
1273-#pragma mark -
1274-#pragma mark Accessors
1275-
1276-@synthesize mode;
1277-
1278-@synthesize delegate;
1279-@synthesize labelText;
1280-@synthesize detailsLabelText;
1281-@synthesize opacity;
1282-@synthesize labelFont;
1283-@synthesize detailsLabelFont;
1284-@synthesize progress;
1285-
1286-@synthesize indicator;
1287-
1288-@synthesize width;
1289-@synthesize height;
1290-@synthesize xOffset;
1291-@synthesize yOffset;
1292-
1293-@synthesize graceTime;
1294-@synthesize minShowTime;
1295-@synthesize graceTimer;
1296-@synthesize minShowTimer;
1297-@synthesize taskInProgress;
1298-
1299-@synthesize customView;
1300-
1301-@synthesize showStarted;
1302-
1303-- (void)setMode:(MBProgressHUDMode)newMode {
1304- // Dont change mode if it wasn't actually changed to prevent flickering
1305- if (mode && (mode == newMode)) {
1306- return;
1307- }
1308-
1309- mode = newMode;
1310-
1311- [self performSelectorOnMainThread:@selector(updateIndicators) withObject:nil waitUntilDone:NO];
1312- [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO];
1313- [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
1314-}
1315-
1316-- (void)setLabelText:(NSString *)newText {
1317- [self performSelectorOnMainThread:@selector(updateLabelText:) withObject:newText waitUntilDone:NO];
1318- [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO];
1319- [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
1320-}
1321-
1322-- (void)setDetailsLabelText:(NSString *)newText {
1323- [self performSelectorOnMainThread:@selector(updateDetailsLabelText:) withObject:newText waitUntilDone:NO];
1324- [self performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO];
1325- [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
1326-}
1327-
1328-- (void)setProgress:(float)newProgress {
1329- progress = newProgress;
1330-
1331- // Update display ony if showing the determinate progress view
1332- if (mode == MBProgressHUDModeDeterminate) {
1333- [self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];
1334- [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
1335- }
1336-}
1337-
1338-#pragma mark -
1339-#pragma mark Accessor helpers
1340-
1341-- (void)updateLabelText:(NSString *)newText {
1342- if (labelText != newText) {
1343- [labelText release];
1344- labelText = [newText copy];
1345- }
1346-}
1347-
1348-- (void)updateDetailsLabelText:(NSString *)newText {
1349- if (detailsLabelText != newText) {
1350- [detailsLabelText release];
1351- detailsLabelText = [newText copy];
1352- }
1353-}
1354-
1355-- (void)updateProgress {
1356- [(MBRoundProgressView *)indicator setProgress:progress];
1357-}
1358-
1359-- (void)updateIndicators {
1360- if (indicator) {
1361- [indicator removeFromSuperview];
1362- }
1363-
1364- if (mode == MBProgressHUDModeDeterminate) {
1365- self.indicator = [[[MBRoundProgressView alloc] initWithDefaultSize] autorelease];
1366- }
1367- else if (mode == MBProgressHUDModeCustomView && self.customView != nil){
1368- self.indicator = self.customView;
1369- } else {
1370- self.indicator = [[[UIActivityIndicatorView alloc]
1371- initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
1372- [(UIActivityIndicatorView *)indicator startAnimating];
1373- }
1374-
1375-
1376- [self addSubview:indicator];
1377-}
1378-
1379-#pragma mark -
1380-#pragma mark Constants
1381-
1382-#define MARGIN 20.0
1383-#define PADDING 4.0
1384-
1385-#define LABELFONTSIZE 22.0
1386-#define LABELDETAILSFONTSIZE 16.0
1387-
1388-#define PI 3.14159265358979323846
1389-
1390-#pragma mark -
1391-#pragma mark Lifecycle methods
1392-
1393-- (id)initWithWindow:(UIWindow *)window {
1394- return [self initWithView:window];
1395-}
1396-
1397-- (id)initWithView:(UIView *)view {
1398- // Let's check if the view is nil (this is a common error when using the windw initializer above)
1399- if (!view) {
1400- [NSException raise:@"MBProgressHUDViewIsNillException"
1401- format:@"The view used in the MBProgressHUD initializer is nil."];
1402- }
1403- return [self initWithFrame:view.bounds];
1404-}
1405-
1406-- (id)initWithFrame:(CGRect)frame {
1407- if (self = [super initWithFrame:frame]) {
1408- // Set default values for properties
1409- self.mode = MBProgressHUDModeIndeterminate;
1410- self.labelText = nil;
1411- self.detailsLabelText = nil;
1412- self.opacity = 0.9;
1413- self.labelFont = [UIFont boldSystemFontOfSize:LABELFONTSIZE];
1414- self.detailsLabelFont = [UIFont boldSystemFontOfSize:LABELDETAILSFONTSIZE];
1415- self.xOffset = 0.0;
1416- self.yOffset = 0.0;
1417- self.graceTime = 0.0;
1418- self.minShowTime = 0.0;
1419-
1420- self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
1421-
1422- // Transparent background
1423- self.opaque = NO;
1424- self.backgroundColor = [UIColor clearColor];
1425-
1426- // Make invisible for now
1427- self.alpha = 0.0;
1428-
1429- // Add label
1430- label = [[UILabel alloc] initWithFrame:self.bounds];
1431-
1432- // Add details label
1433- detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
1434-
1435- taskInProgress = NO;
1436- }
1437- return self;
1438-}
1439-
1440-- (void)dealloc {
1441- [indicator release];
1442- [label release];
1443- [detailsLabel release];
1444- [labelText release];
1445- [detailsLabelText release];
1446- [graceTimer release];
1447- [minShowTimer release];
1448- [showStarted release];
1449- [customView release];
1450- [super dealloc];
1451-}
1452-
1453-#pragma mark -
1454-#pragma mark Layout
1455-
1456-- (void)layoutSubviews {
1457- CGRect frame = self.bounds;
1458-
1459- // Compute HUD dimensions based on indicator size (add margin to HUD border)
1460- CGRect indFrame = indicator.bounds;
1461- self.width = indFrame.size.width + 2 * MARGIN;
1462- self.height = indFrame.size.height + 2 * MARGIN;
1463-
1464- // Position the indicator
1465- indFrame.origin.x = floor((frame.size.width - indFrame.size.width) / 2) + self.xOffset;
1466- indFrame.origin.y = floor((frame.size.height - indFrame.size.height) / 2) + self.yOffset;
1467- indicator.frame = indFrame;
1468-
1469- // Add label if label text was set
1470- if (nil != self.labelText) {
1471- // Get size of label text
1472- CGSize dims = [self.labelText sizeWithFont:self.labelFont];
1473-
1474- // Compute label dimensions based on font metrics if size is larger than max then clip the label width
1475- float lHeight = dims.height;
1476- float lWidth;
1477- if (dims.width <= (frame.size.width - 2 * MARGIN)) {
1478- lWidth = dims.width;
1479- }
1480- else {
1481- lWidth = frame.size.width - 4 * MARGIN;
1482- }
1483-
1484- // Set label properties
1485- label.font = self.labelFont;
1486- label.adjustsFontSizeToFitWidth = NO;
1487- label.textAlignment = UITextAlignmentCenter;
1488- label.opaque = NO;
1489- label.backgroundColor = [UIColor clearColor];
1490- label.textColor = [UIColor whiteColor];
1491- label.text = self.labelText;
1492-
1493- // Update HUD size
1494- if (self.width < (lWidth + 2 * MARGIN)) {
1495- self.width = lWidth + 2 * MARGIN;
1496- }
1497- self.height = self.height + lHeight + PADDING;
1498-
1499- // Move indicator to make room for the label
1500- indFrame.origin.y -= (floor(lHeight / 2 + PADDING / 2));
1501- indicator.frame = indFrame;
1502-
1503- // Set the label position and dimensions
1504- CGRect lFrame = CGRectMake(floor((frame.size.width - lWidth) / 2) + xOffset,
1505- floor(indFrame.origin.y + indFrame.size.height + PADDING),
1506- lWidth, lHeight);
1507- label.frame = lFrame;
1508-
1509- [self addSubview:label];
1510-
1511- // Add details label delatils text was set
1512- if (nil != self.detailsLabelText) {
1513- // Get size of label text
1514- dims = [self.detailsLabelText sizeWithFont:self.detailsLabelFont];
1515-
1516- // Compute label dimensions based on font metrics if size is larger than max then clip the label width
1517- lHeight = dims.height;
1518- if (dims.width <= (frame.size.width - 2 * MARGIN)) {
1519- lWidth = dims.width;
1520- }
1521- else {
1522- lWidth = frame.size.width - 4 * MARGIN;
1523- }
1524-
1525- // Set label properties
1526- detailsLabel.font = self.detailsLabelFont;
1527- detailsLabel.adjustsFontSizeToFitWidth = NO;
1528- detailsLabel.textAlignment = UITextAlignmentCenter;
1529- detailsLabel.opaque = NO;
1530- detailsLabel.backgroundColor = [UIColor clearColor];
1531- detailsLabel.textColor = [UIColor whiteColor];
1532- detailsLabel.text = self.detailsLabelText;
1533-
1534- // Update HUD size
1535- if (self.width < lWidth) {
1536- self.width = lWidth + 2 * MARGIN;
1537- }
1538- self.height = self.height + lHeight + PADDING;
1539-
1540- // Move indicator to make room for the new label
1541- indFrame.origin.y -= (floor(lHeight / 2 + PADDING / 2));
1542- indicator.frame = indFrame;
1543-
1544- // Move first label to make room for the new label
1545- lFrame.origin.y -= (floor(lHeight / 2 + PADDING / 2));
1546- label.frame = lFrame;
1547-
1548- // Set label position and dimensions
1549- CGRect lFrameD = CGRectMake(floor((frame.size.width - lWidth) / 2) + xOffset,
1550- lFrame.origin.y + lFrame.size.height + PADDING, lWidth, lHeight);
1551- detailsLabel.frame = lFrameD;
1552-
1553- [self addSubview:detailsLabel];
1554- }
1555- }
1556-}
1557-
1558-#pragma mark -
1559-#pragma mark Showing and execution
1560-
1561-- (void)show:(BOOL)animated {
1562- useAnimation = animated;
1563-
1564- // If the grace time is set postpone the HUD display
1565- if (self.graceTime > 0.0) {
1566- self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime
1567- target:self
1568- selector:@selector(handleGraceTimer:)
1569- userInfo:nil
1570- repeats:NO];
1571- }
1572- // ... otherwise show the HUD imediately
1573- else {
1574- [self setNeedsDisplay];
1575- [self showUsingAnimation:useAnimation];
1576- }
1577-}
1578-
1579-- (void)hide:(BOOL)animated {
1580- useAnimation = animated;
1581-
1582- // If the minShow time is set, calculate how long the hud was shown,
1583- // and pospone the hiding operation if necessary
1584- if (self.minShowTime > 0.0 && showStarted) {
1585- NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
1586- if (interv < self.minShowTime) {
1587- self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv)
1588- target:self
1589- selector:@selector(handleMinShowTimer:)
1590- userInfo:nil
1591- repeats:NO];
1592- return;
1593- }
1594- }
1595-
1596- // ... otherwise hide the HUD immediately
1597- [self hideUsingAnimation:useAnimation];
1598-}
1599-
1600-- (void)handleGraceTimer:(NSTimer *)theTimer {
1601- // Show the HUD only if the task is still running
1602- if (taskInProgress) {
1603- [self setNeedsDisplay];
1604- [self showUsingAnimation:useAnimation];
1605- }
1606-}
1607-
1608-- (void)handleMinShowTimer:(NSTimer *)theTimer {
1609- [self hideUsingAnimation:useAnimation];
1610-}
1611-
1612-- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
1613-
1614- methodForExecution = method;
1615- targetForExecution = [target retain];
1616- objectForExecution = [object retain];
1617-
1618- // Launch execution in new thread
1619- taskInProgress = YES;
1620- [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
1621-
1622- // Show HUD view
1623- [self show:animated];
1624-}
1625-
1626-- (void)launchExecution {
1627- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1628-
1629- // Start executing the requested task
1630- [targetForExecution performSelector:methodForExecution withObject:objectForExecution];
1631-
1632- // Task completed, update view in main thread (note: view operations should
1633- // be done only in the main thread)
1634- [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
1635-
1636- [pool release];
1637-}
1638-
1639-- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
1640- [self done];
1641-}
1642-
1643-- (void)done {
1644- isFinished = YES;
1645-
1646- // If delegate was set make the callback
1647- self.alpha = 0.0;
1648-
1649- if(delegate != nil && [delegate conformsToProtocol:@protocol(MBProgressHUDDelegate)]) {
1650- if([delegate respondsToSelector:@selector(hudWasHidden)]) {
1651- [delegate performSelector:@selector(hudWasHidden)];
1652- }
1653- }
1654-}
1655-
1656-- (void)cleanUp {
1657- taskInProgress = NO;
1658-
1659- self.indicator = nil;
1660-
1661- [targetForExecution release];
1662- [objectForExecution release];
1663-
1664- [self hide:useAnimation];
1665-}
1666-
1667-#pragma mark -
1668-#pragma mark Fade in and Fade out
1669-
1670-- (void)showUsingAnimation:(BOOL)animated {
1671- self.showStarted = [NSDate date];
1672- // Fade in
1673- if (animated) {
1674- [UIView beginAnimations:nil context:NULL];
1675- [UIView setAnimationDuration:0.40];
1676- self.alpha = 1.0;
1677- [UIView commitAnimations];
1678- }
1679- else {
1680- self.alpha = 1.0;
1681- }
1682-}
1683-
1684-- (void)hideUsingAnimation:(BOOL)animated {
1685- // Fade out
1686- if (animated) {
1687- [UIView beginAnimations:nil context:NULL];
1688- [UIView setAnimationDuration:0.40];
1689- [UIView setAnimationDelegate:self];
1690- [UIView setAnimationDidStopSelector:@selector(animationFinished: finished: context:)];
1691- // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
1692- // in the done method
1693- self.alpha = 0.02;
1694- [UIView commitAnimations];
1695- }
1696- else {
1697- self.alpha = 0.0;
1698- [self done];
1699- }
1700-}
1701-
1702-#pragma mark BG Drawing
1703-
1704-- (void)drawRect:(CGRect)rect {
1705- // Center HUD
1706- CGRect allRect = self.bounds;
1707- // Draw rounded HUD bacgroud rect
1708- CGRect boxRect = CGRectMake(((allRect.size.width - self.width) / 2) + self.xOffset,
1709- ((allRect.size.height - self.height) / 2) + self.yOffset, self.width, self.height);
1710- CGContextRef ctxt = UIGraphicsGetCurrentContext();
1711- [self fillRoundedRect:boxRect inContext:ctxt];
1712-}
1713-
1714-- (void)fillRoundedRect:(CGRect)rect inContext:(CGContextRef)context {
1715- float radius = 10.0f;
1716-
1717- CGContextBeginPath(context);
1718- CGContextSetGrayFillColor(context, 0.0, self.opacity);
1719- CGContextMoveToPoint(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect));
1720- CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMinY(rect) + radius, radius, 3 * M_PI / 2, 0, 0);
1721- CGContextAddArc(context, CGRectGetMaxX(rect) - radius, CGRectGetMaxY(rect) - radius, radius, 0, M_PI / 2, 0);
1722- CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMaxY(rect) - radius, radius, M_PI / 2, M_PI, 0);
1723- CGContextAddArc(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect) + radius, radius, M_PI, 3 * M_PI / 2, 0);
1724- CGContextClosePath(context);
1725- CGContextFillPath(context);
1726-}
1727-
1728-@end
1729-
1730-
1731-@implementation MBRoundProgressView
1732-
1733-- (id)initWithDefaultSize {
1734- return [super initWithFrame:CGRectMake(0.0f, 0.0f, 37.0f, 37.0f)];
1735-}
1736-
1737-- (void)drawRect:(CGRect)rect {
1738- CGRect allRect = self.bounds;
1739- CGRect circleRect = CGRectMake(allRect.origin.x + 2, allRect.origin.y + 2, allRect.size.width - 4,
1740- allRect.size.height - 4);
1741-
1742- CGContextRef context = UIGraphicsGetCurrentContext();
1743-
1744- // Draw background
1745- CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); // white
1746- CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.1); // translucent white
1747- CGContextSetLineWidth(context, 2.0);
1748- CGContextFillEllipseInRect(context, circleRect);
1749- CGContextStrokeEllipseInRect(context, circleRect);
1750-
1751- // Draw progress
1752- float x = (allRect.size.width / 2);
1753- float y = (allRect.size.height / 2);
1754- CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); // white
1755- CGContextMoveToPoint(context, x, y);
1756- CGContextAddArc(context, x, y, (allRect.size.width - 4) / 2, -(PI / 2), (self.progress * 2 * PI) - PI / 2, 0);
1757- CGContextClosePath(context);
1758- CGContextFillPath(context);
1759-}
1760-
1761-@end
1762
1763=== added file 'musicstreaming/controls/RIButtonItem.h'
1764--- musicstreaming/controls/RIButtonItem.h 1970-01-01 00:00:00 +0000
1765+++ musicstreaming/controls/RIButtonItem.h 2011-06-13 16:58:33 +0000
1766@@ -0,0 +1,23 @@
1767+//
1768+// RIButtonItem.h
1769+// Shibui
1770+//
1771+// Created by Jiva DeVoe on 1/12/11.
1772+// Copyright 2011 Random Ideas, LLC. All rights reserved.
1773+//
1774+
1775+#import <Foundation/Foundation.h>
1776+
1777+typedef void (^RISimpleAction)();
1778+
1779+@interface RIButtonItem : NSObject
1780+{
1781+ NSString *label;
1782+ RISimpleAction action;
1783+}
1784+@property (retain, nonatomic) NSString *label;
1785+@property (copy, nonatomic) RISimpleAction action;
1786++(id)item;
1787++(id)itemWithLabel:(NSString *)inLabel;
1788+@end
1789+
1790
1791=== added file 'musicstreaming/controls/RIButtonItem.m'
1792--- musicstreaming/controls/RIButtonItem.m 1970-01-01 00:00:00 +0000
1793+++ musicstreaming/controls/RIButtonItem.m 2011-06-13 16:58:33 +0000
1794@@ -0,0 +1,28 @@
1795+//
1796+// RIButtonItem.m
1797+// Shibui
1798+//
1799+// Created by Jiva DeVoe on 1/12/11.
1800+// Copyright 2011 Random Ideas, LLC. All rights reserved.
1801+//
1802+
1803+#import "RIButtonItem.h"
1804+
1805+@implementation RIButtonItem
1806+@synthesize label;
1807+@synthesize action;
1808+
1809++(id)item
1810+{
1811+ return [[self new] autorelease];
1812+}
1813+
1814++(id)itemWithLabel:(NSString *)inLabel
1815+{
1816+ id newItem = [self item];
1817+ [newItem setLabel:inLabel];
1818+ return newItem;
1819+}
1820+
1821+@end
1822+
1823
1824=== modified file 'musicstreaming/controls/SongUITableViewCell.h'
1825--- musicstreaming/controls/SongUITableViewCell.h 2010-09-30 00:08:28 +0000
1826+++ musicstreaming/controls/SongUITableViewCell.h 2011-06-13 16:58:33 +0000
1827@@ -31,6 +31,8 @@
1828 #import <UIKit/UIKit.h>
1829
1830 @interface SongUITableViewCell : UITableViewCell {
1831+ UIButton *cacheButton;
1832+ UIActivityIndicatorView *spinner;
1833 UILabel *songNameLabel;
1834 BOOL evenRow;
1835 UIView *coloredView;
1836@@ -38,6 +40,8 @@
1837 UILabel *durationLabel;
1838 UIImageView *nowPlaying;
1839 }
1840+@property(nonatomic,retain) UIButton *cacheButton;
1841+@property(nonatomic,retain) UIActivityIndicatorView *spinner;
1842 @property(nonatomic,assign) BOOL evenRow;
1843 @property(nonatomic,retain) UIImageView *nowPlaying;
1844 @property(nonatomic,retain) UILabel *songNameLabel;
1845
1846=== modified file 'musicstreaming/controls/SongUITableViewCell.m'
1847--- musicstreaming/controls/SongUITableViewCell.m 2011-04-14 04:24:43 +0000
1848+++ musicstreaming/controls/SongUITableViewCell.m 2011-06-13 16:58:33 +0000
1849@@ -34,7 +34,7 @@
1850
1851 @implementation SongUITableViewCell
1852
1853-@synthesize songNameLabel, positionLabel, durationLabel, nowPlaying, evenRow;
1854+@synthesize cacheButton, spinner, songNameLabel, positionLabel, durationLabel, nowPlaying, evenRow;
1855
1856 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
1857 {
1858@@ -44,10 +44,18 @@
1859
1860 coloredView = [[UIView alloc] initWithFrame:self.frame];
1861 coloredView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
1862-
1863 [self.contentView addSubview:coloredView];
1864+
1865+ // XXX: This is added first so that it sits behind the cacheButton. This isn't optimal. Something better would be to delegate taps on the spinner to the cacheButton, regardless of its front-to-back ordering in the subviews.
1866+ spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] retain];
1867+ spinner.hidesWhenStopped = YES;
1868+ [coloredView addSubview:spinner];
1869
1870- songNameLabel = [[UILabel alloc] init];
1871+ cacheButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
1872+ [cacheButton setImage:[UIImage imageNamed:@"uncached"] forState:UIControlStateNormal];
1873+ [coloredView addSubview:self.cacheButton];
1874+
1875+ songNameLabel = [[UILabel alloc] init];
1876 songNameLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
1877 [coloredView addSubview:self.songNameLabel];
1878
1879@@ -64,7 +72,7 @@
1880 [coloredView addSubview:durationLabel];
1881
1882 nowPlaying = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 13, 12)];
1883- nowPlaying.image = [UIImage imageNamed:@"speaker.png"];
1884+ nowPlaying.image = [UIImage imageNamed:@"speaker"];
1885 nowPlaying.backgroundColor = [UIColor clearColor];
1886 nowPlaying.contentMode = UIViewContentModeCenter;
1887 [coloredView addSubview:nowPlaying];
1888@@ -79,10 +87,12 @@
1889 {
1890 [super layoutSubviews];
1891 self.positionLabel.frame = CGRectMake(0, 0, 35, self.frame.size.height);
1892- self.songNameLabel.frame = CGRectMake(47, 0, 197, self.frame.size.height);
1893- self.nowPlaying.frame = CGRectMake(252, 0, 13, self.frame.size.height);
1894- self.durationLabel.frame = CGRectMake(272, 0, 48, self.frame.size.height);
1895-
1896+ self.songNameLabel.frame = CGRectMake(37, 0, 164, self.frame.size.height);
1897+ self.nowPlaying.frame = CGRectMake(203, 0, 13, self.frame.size.height);
1898+ self.durationLabel.frame = CGRectMake(218, 0, 48, self.frame.size.height);
1899+ self.cacheButton.frame = CGRectMake(268, 0, 44, self.frame.size.height);
1900+ self.spinner.frame = CGRectMake(282, 14, 16, 16);
1901+
1902 if (self.evenRow)
1903 {
1904 coloredView.backgroundColor = [UIColor whiteColor];
1905@@ -103,6 +113,8 @@
1906
1907 - (void)dealloc
1908 {
1909+ [cacheButton release];
1910+ [spinner release];
1911 [durationLabel release];
1912 [songNameLabel release];
1913 [positionLabel release];
1914
1915=== modified file 'musicstreaming/iSub-Info.plist'
1916--- musicstreaming/iSub-Info.plist 2010-10-01 04:00:55 +0000
1917+++ musicstreaming/iSub-Info.plist 2011-06-13 16:58:33 +0000
1918@@ -6,6 +6,8 @@
1919 <string>English</string>
1920 <key>CFBundleDisplayName</key>
1921 <string>${PRODUCT_NAME}</string>
1922+ <key>CFBundleDocumentTypes</key>
1923+ <array/>
1924 <key>CFBundleExecutable</key>
1925 <string>${EXECUTABLE_NAME}</string>
1926 <key>CFBundleIconFile</key>
1927@@ -23,6 +25,8 @@
1928 <string>${PRODUCT_NAME}</string>
1929 <key>CFBundlePackageType</key>
1930 <string>APPL</string>
1931+ <key>CFBundleShortVersionString</key>
1932+ <string>2.0</string>
1933 <key>CFBundleSignature</key>
1934 <string>UOMS</string>
1935 <key>CFBundleURLTypes</key>
1936@@ -37,7 +41,7 @@
1937 </dict>
1938 </array>
1939 <key>CFBundleVersion</key>
1940- <string>1.0</string>
1941+ <string>6</string>
1942 <key>LSRequiresIPhoneOS</key>
1943 <false/>
1944 <key>NSMainNibFile</key>
1945@@ -50,5 +54,9 @@
1946 <false/>
1947 <key>UIStatusBarStyle</key>
1948 <string>UIStatusBarStyleBlackOpaque</string>
1949+ <key>UTExportedTypeDeclarations</key>
1950+ <array/>
1951+ <key>UTImportedTypeDeclarations</key>
1952+ <array/>
1953 </dict>
1954 </plist>
1955
1956=== modified file 'musicstreaming/iSub.xcodeproj/project.pbxproj' (properties changed: +x to -x)
1957--- musicstreaming/iSub.xcodeproj/project.pbxproj 2011-05-05 06:54:40 +0000
1958+++ musicstreaming/iSub.xcodeproj/project.pbxproj 2011-06-13 16:58:33 +0000
1959@@ -31,6 +31,16 @@
1960 9110DC41134E4EEF0046B8E4 /* NSMutableSet+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 9110DC40134E4EEF0046B8E4 /* NSMutableSet+Extras.m */; };
1961 9110DC44134EAC740046B8E4 /* AlbumListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 9110DC43134EAC730046B8E4 /* AlbumListParser.m */; };
1962 9110DC571354A7490046B8E4 /* AlbumArtistUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9110DC561354A7470046B8E4 /* AlbumArtistUITableViewCell.m */; };
1963+ 9112B0241383FF27003C1D93 /* albums.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B01E1383FF27003C1D93 /* albums.png */; };
1964+ 9112B0251383FF27003C1D93 /* artists.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B01F1383FF27003C1D93 /* artists.png */; };
1965+ 9112B0261383FF27003C1D93 /* playlists.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B0201383FF27003C1D93 /* playlists.png */; };
1966+ 9112B0281383FF27003C1D93 /* settings.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B0221383FF27003C1D93 /* settings.png */; };
1967+ 9112B0291383FF27003C1D93 /* songs.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B0231383FF27003C1D93 /* songs.png */; };
1968+ 9112B02F1383FF37003C1D93 /* albums@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B02A1383FF37003C1D93 /* albums@2x.png */; };
1969+ 9112B0301383FF37003C1D93 /* artists@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B02B1383FF37003C1D93 /* artists@2x.png */; };
1970+ 9112B0311383FF37003C1D93 /* playlists@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B02C1383FF37003C1D93 /* playlists@2x.png */; };
1971+ 9112B0321383FF37003C1D93 /* settings@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B02D1383FF37003C1D93 /* settings@2x.png */; };
1972+ 9112B0331383FF37003C1D93 /* songs@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9112B02E1383FF37003C1D93 /* songs@2x.png */; };
1973 9123D1A113257ADF00B40210 /* PlaylistListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9123D1A013257ADF00B40210 /* PlaylistListViewController.m */; };
1974 9123D1BB13280B3400B40210 /* PlaylistViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9123D1BA13280B3400B40210 /* PlaylistViewController.m */; };
1975 9123D1C3132816D200B40210 /* PlaylistListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 9123D1C2132816D200B40210 /* PlaylistListParser.m */; };
1976@@ -39,8 +49,18 @@
1977 912D1267134B609700721EE4 /* PlaylistEditViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 912D1266134B609600721EE4 /* PlaylistEditViewController.m */; };
1978 912D392213311F2700A443B2 /* AlbumListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 912D392113311F2500A443B2 /* AlbumListViewController.m */; };
1979 912D392613311FC600A443B2 /* SongListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 912D392513311FC400A443B2 /* SongListViewController.m */; };
1980+ 91406E9413849F2400A7DA67 /* cached.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E8E13849F2400A7DA67 /* cached.png */; };
1981+ 91406E9513849F2400A7DA67 /* cached@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E8F13849F2400A7DA67 /* cached@2x.png */; };
1982+ 91406E9613849F2400A7DA67 /* partiallycached.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9013849F2400A7DA67 /* partiallycached.png */; };
1983+ 91406E9713849F2400A7DA67 /* partiallycached@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9113849F2400A7DA67 /* partiallycached@2x.png */; };
1984+ 91406E9813849F2400A7DA67 /* uncached.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9213849F2400A7DA67 /* uncached.png */; };
1985+ 91406E9913849F2400A7DA67 /* uncached@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91406E9313849F2400A7DA67 /* uncached@2x.png */; };
1986+ 91406EEA138AEEE000A7DA67 /* NSString+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 91406EE9138AEEE000A7DA67 /* NSString+Extras.m */; };
1987 919376F3135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 919376F2135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m */; };
1988 91B3A4441344CF92006C8193 /* SubsonicIndexedTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 91B3A4431344CF91006C8193 /* SubsonicIndexedTableViewController.m */; };
1989+ 91BE4018138E8D2300D44D68 /* UIActionSheet+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE4015138E8D2300D44D68 /* UIActionSheet+Blocks.m */; };
1990+ 91BE4019138E8D2300D44D68 /* UIAlertView+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE4017138E8D2300D44D68 /* UIAlertView+Blocks.m */; };
1991+ 91BE401C138E8D3A00D44D68 /* RIButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */; };
1992 91E88609132DA82000618994 /* PlaylistParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E88608132DA82000618994 /* PlaylistParser.m */; };
1993 9316628812264A74003B0EB7 /* NSDate+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 9316628712264A74003B0EB7 /* NSDate+Extras.m */; };
1994 932E7A6E1254747E00E7C8FF /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 932E7A6D1254747E00E7C8FF /* Default@2x.png */; };
1995@@ -49,14 +69,18 @@
1996 932E7B0912552CB400E7C8FF /* PullRefreshTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 932E7B0812552CB400E7C8FF /* PullRefreshTableViewController.m */; };
1997 932E7B0B12552CD500E7C8FF /* arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 932E7B0A12552CD500E7C8FF /* arrow.png */; };
1998 9354D0AB1248267B00733067 /* NSDictionary+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = 9354D0AA1248267B00733067 /* NSDictionary+Extras.m */; };
1999- 936F1F3112271B6500070F43 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 936F1F3012271B6500070F43 /* MBProgressHUD.m */; };
2000 936F20681227364200070F43 /* Playlist.m in Sources */ = {isa = PBXBuildFile; fileRef = 936F20671227364200070F43 /* Playlist.m */; };
2001 936F209012273D9000070F43 /* Song.m in Sources */ = {isa = PBXBuildFile; fileRef = 936F208F12273D9000070F43 /* Song.m */; };
2002 936F230A12284D1900070F43 /* NamedTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 936F230912284D1900070F43 /* NamedTextFieldCell.m */; };
2003+ 937FAA11137CFC1B00507E51 /* AlbumArtLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAA0E137CFC1B00507E51 /* AlbumArtLoader.m */; };
2004+ 937FAA12137CFC1B00507E51 /* AlbumArtLoadingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAA10137CFC1B00507E51 /* AlbumArtLoadingOperation.m */; };
2005+ 937FAA15137CFC5000507E51 /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAA14137CFC5000507E51 /* Downloader.m */; };
2006+ 937FAA18137CFC7200507E51 /* DownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAA17137CFC7200507E51 /* DownloadOperation.m */; };
2007+ 937FAA1B137CFCA600507E51 /* AbstractNetworkOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAA1A137CFCA600507E51 /* AbstractNetworkOperation.m */; };
2008+ 937FAAEF137D15E800507E51 /* Globals.m in Sources */ = {isa = PBXBuildFile; fileRef = 937FAAEE137D15E800507E51 /* Globals.m */; };
2009 93A54024123188880048BC3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93A54023123188880048BC3D /* Security.framework */; };
2010 93BC5209124C181600B7587C /* Subsonic.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BC5208124C181600B7587C /* Subsonic.m */; };
2011 93BC52A7124C1E6900B7587C /* StreamingPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BC52A6124C1E6900B7587C /* StreamingPlayer.m */; };
2012- 93BC53FB124C7C8D00B7587C /* DataCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 93BC53FA124C7C8D00B7587C /* DataCache.m */; };
2013 93CCBD4F1254039900AFFC22 /* speaker.png in Resources */ = {isa = PBXBuildFile; fileRef = 93CCBD4D1254039900AFFC22 /* speaker.png */; };
2014 93CCBD501254039900AFFC22 /* speaker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93CCBD4E1254039900AFFC22 /* speaker@2x.png */; };
2015 93CCBD7D12540CB700AFFC22 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CCBD7B12540CB700AFFC22 /* AboutViewController.m */; };
2016@@ -81,16 +105,12 @@
2017 93DFFE45135D71030061F29F /* MOC.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE44135D71030061F29F /* MOC.m */; };
2018 93DFFE4D135D71760061F29F /* music.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE4C135D71760061F29F /* music.xcdatamodeld */; };
2019 93DFFE54135D72420061F29F /* NSManagedObjectContext+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DFFE53135D72420061F29F /* NSManagedObjectContext+Additions.m */; };
2020- 93ED1AD8137276810058FC98 /* ImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED1AD7137276810058FC98 /* ImageLoader.m */; };
2021- 93ED1ADF137279A30058FC98 /* ImageLoadingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 93ED1ADE137279A30058FC98 /* ImageLoadingOperation.m */; };
2022 93EE2AF0124993F100E7E060 /* ArtistListParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 93EE2AEF124993F100E7E060 /* ArtistListParser.m */; };
2023 93EE2BA31249F33D00E7E060 /* ArtistParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 93EE2BA21249F33D00E7E060 /* ArtistParser.m */; };
2024 93F334471247F9DE006C6707 /* SFHFKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F334461247F9DE006C6707 /* SFHFKeychainUtils.m */; };
2025 93F3344D1247FA0B006C6707 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F3344C1247FA0B006C6707 /* Reachability.m */; };
2026 93F334521247FA2C006C6707 /* Album.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F3344F1247FA2C006C6707 /* Album.m */; };
2027 93F334531247FA2C006C6707 /* Artist.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F334511247FA2C006C6707 /* Artist.m */; };
2028- 93F334561247FA52006C6707 /* AsynchronousImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F334551247FA52006C6707 /* AsynchronousImageView.m */; };
2029- 93F334591247FA68006C6707 /* AsynchronousImageViewCached.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F334581247FA68006C6707 /* AsynchronousImageViewCached.m */; };
2030 93F3345C1247FA85006C6707 /* AlbumUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F3345B1247FA85006C6707 /* AlbumUITableViewCell.m */; };
2031 93F3345F1247FA97006C6707 /* SongUITableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F3345E1247FA97006C6707 /* SongUITableViewCell.m */; };
2032 93F334681247FB02006C6707 /* SongViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93F334641247FB02006C6707 /* SongViewController.xib */; };
2033@@ -110,7 +130,6 @@
2034 93FA42B0124DC1350080DF62 /* 05-shuffle.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA42A9124DC1350080DF62 /* 05-shuffle.png */; };
2035 93FA42B1124DC1350080DF62 /* 05-shuffle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA42AA124DC1350080DF62 /* 05-shuffle@2x.png */; };
2036 93FA42B2124DC1350080DF62 /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA42AB124DC1350080DF62 /* background.png */; };
2037- 93FA42B3124DC1350080DF62 /* default-album-art-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA42AC124DC1350080DF62 /* default-album-art-small.png */; };
2038 93FA42B4124DC1350080DF62 /* default-album-art.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA42AD124DC1350080DF62 /* default-album-art.png */; };
2039 93FA4336124DE0D80080DF62 /* player_back.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA4334124DE0D80080DF62 /* player_back.png */; };
2040 93FA4337124DE0D80080DF62 /* player_back@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 93FA4335124DE0D80080DF62 /* player_back@2x.png */; };
2041@@ -160,6 +179,16 @@
2042 9110DC43134EAC730046B8E4 /* AlbumListParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlbumListParser.m; sourceTree = "<group>"; };
2043 9110DC551354A7460046B8E4 /* AlbumArtistUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AlbumArtistUITableViewCell.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
2044 9110DC561354A7470046B8E4 /* AlbumArtistUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AlbumArtistUITableViewCell.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
2045+ 9112B01E1383FF27003C1D93 /* albums.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = albums.png; sourceTree = "<group>"; };
2046+ 9112B01F1383FF27003C1D93 /* artists.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = artists.png; sourceTree = "<group>"; };
2047+ 9112B0201383FF27003C1D93 /* playlists.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = playlists.png; sourceTree = "<group>"; };
2048+ 9112B0221383FF27003C1D93 /* settings.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = settings.png; sourceTree = "<group>"; };
2049+ 9112B0231383FF27003C1D93 /* songs.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = songs.png; sourceTree = "<group>"; };
2050+ 9112B02A1383FF37003C1D93 /* albums@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "albums@2x.png"; sourceTree = "<group>"; };
2051+ 9112B02B1383FF37003C1D93 /* artists@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "artists@2x.png"; sourceTree = "<group>"; };
2052+ 9112B02C1383FF37003C1D93 /* playlists@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playlists@2x.png"; sourceTree = "<group>"; };
2053+ 9112B02D1383FF37003C1D93 /* settings@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "settings@2x.png"; sourceTree = "<group>"; };
2054+ 9112B02E1383FF37003C1D93 /* songs@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "songs@2x.png"; sourceTree = "<group>"; };
2055 9123D19F13257ADF00B40210 /* PlaylistListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistListViewController.h; sourceTree = "<group>"; };
2056 9123D1A013257ADF00B40210 /* PlaylistListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PlaylistListViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
2057 9123D1B913280B3400B40210 /* PlaylistViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistViewController.h; path = view_controllers/PlaylistViewController.h; sourceTree = "<group>"; };
2058@@ -176,10 +205,24 @@
2059 912D392113311F2500A443B2 /* AlbumListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AlbumListViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
2060 912D392413311FC400A443B2 /* SongListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SongListViewController.h; sourceTree = "<group>"; };
2061 912D392513311FC400A443B2 /* SongListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SongListViewController.m; sourceTree = "<group>"; };
2062+ 91406E8E13849F2400A7DA67 /* cached.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cached.png; sourceTree = "<group>"; };
2063+ 91406E8F13849F2400A7DA67 /* cached@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cached@2x.png"; sourceTree = "<group>"; };
2064+ 91406E9013849F2400A7DA67 /* partiallycached.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = partiallycached.png; sourceTree = "<group>"; };
2065+ 91406E9113849F2400A7DA67 /* partiallycached@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "partiallycached@2x.png"; sourceTree = "<group>"; };
2066+ 91406E9213849F2400A7DA67 /* uncached.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = uncached.png; sourceTree = "<group>"; };
2067+ 91406E9313849F2400A7DA67 /* uncached@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "uncached@2x.png"; sourceTree = "<group>"; };
2068+ 91406EE8138AEEE000A7DA67 /* NSString+Extras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Extras.h"; sourceTree = "<group>"; };
2069+ 91406EE9138AEEE000A7DA67 /* NSString+Extras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Extras.m"; sourceTree = "<group>"; };
2070 919376F1135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistEditSongUITableViewCell.h; sourceTree = "<group>"; };
2071 919376F2135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistEditSongUITableViewCell.m; sourceTree = "<group>"; };
2072 91B3A4421344CF90006C8193 /* SubsonicIndexedTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubsonicIndexedTableViewController.h; sourceTree = "<group>"; };
2073 91B3A4431344CF91006C8193 /* SubsonicIndexedTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SubsonicIndexedTableViewController.m; sourceTree = "<group>"; };
2074+ 91BE4014138E8D2300D44D68 /* UIActionSheet+Blocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+Blocks.h"; sourceTree = "<group>"; };
2075+ 91BE4015138E8D2300D44D68 /* UIActionSheet+Blocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+Blocks.m"; sourceTree = "<group>"; };
2076+ 91BE4016138E8D2300D44D68 /* UIAlertView+Blocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+Blocks.h"; sourceTree = "<group>"; };
2077+ 91BE4017138E8D2300D44D68 /* UIAlertView+Blocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+Blocks.m"; sourceTree = "<group>"; };
2078+ 91BE401A138E8D3A00D44D68 /* RIButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIButtonItem.h; sourceTree = "<group>"; };
2079+ 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIButtonItem.m; sourceTree = "<group>"; };
2080 91E88607132DA82000618994 /* PlaylistParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistParser.h; sourceTree = "<group>"; };
2081 91E88608132DA82000618994 /* PlaylistParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistParser.m; sourceTree = "<group>"; };
2082 9316628612264A74003B0EB7 /* NSDate+Extras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+Extras.h"; sourceTree = "<group>"; };
2083@@ -193,22 +236,30 @@
2084 9354D0A91248267B00733067 /* NSDictionary+Extras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Extras.h"; sourceTree = "<group>"; };
2085 9354D0AA1248267B00733067 /* NSDictionary+Extras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Extras.m"; sourceTree = "<group>"; };
2086 935FCC42123766E600B5DF9B /* Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = "<group>"; };
2087- 936F1F2F12271B6500070F43 /* MBProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBProgressHUD.h; sourceTree = "<group>"; };
2088- 936F1F3012271B6500070F43 /* MBProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = "<group>"; };
2089 936F20661227364200070F43 /* Playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Playlist.h; sourceTree = "<group>"; };
2090 936F20671227364200070F43 /* Playlist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Playlist.m; sourceTree = "<group>"; };
2091 936F208E12273D9000070F43 /* Song.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Song.h; sourceTree = "<group>"; };
2092 936F208F12273D9000070F43 /* Song.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Song.m; sourceTree = "<group>"; };
2093 936F230812284D1900070F43 /* NamedTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NamedTextFieldCell.h; sourceTree = "<group>"; };
2094 936F230912284D1900070F43 /* NamedTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NamedTextFieldCell.m; sourceTree = "<group>"; };
2095+ 937FAA0D137CFC1B00507E51 /* AlbumArtLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlbumArtLoader.h; sourceTree = "<group>"; };
2096+ 937FAA0E137CFC1B00507E51 /* AlbumArtLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlbumArtLoader.m; sourceTree = "<group>"; };
2097+ 937FAA0F137CFC1B00507E51 /* AlbumArtLoadingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlbumArtLoadingOperation.h; sourceTree = "<group>"; };
2098+ 937FAA10137CFC1B00507E51 /* AlbumArtLoadingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AlbumArtLoadingOperation.m; sourceTree = "<group>"; };
2099+ 937FAA13137CFC5000507E51 /* Downloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downloader.h; sourceTree = "<group>"; };
2100+ 937FAA14137CFC5000507E51 /* Downloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downloader.m; sourceTree = "<group>"; };
2101+ 937FAA16137CFC7200507E51 /* DownloadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadOperation.h; sourceTree = "<group>"; };
2102+ 937FAA17137CFC7200507E51 /* DownloadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadOperation.m; sourceTree = "<group>"; };
2103+ 937FAA19137CFCA600507E51 /* AbstractNetworkOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractNetworkOperation.h; sourceTree = "<group>"; };
2104+ 937FAA1A137CFCA600507E51 /* AbstractNetworkOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AbstractNetworkOperation.m; sourceTree = "<group>"; };
2105+ 937FAAED137D15E800507E51 /* Globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Globals.h; sourceTree = "<group>"; };
2106+ 937FAAEE137D15E800507E51 /* Globals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Globals.m; sourceTree = "<group>"; };
2107 93A54023123188880048BC3D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
2108 93BC5207124C181600B7587C /* Subsonic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Subsonic.h; sourceTree = "<group>"; };
2109 93BC5208124C181600B7587C /* Subsonic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Subsonic.m; sourceTree = "<group>"; };
2110 93BC520A124C187700B7587C /* SynthesizeSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SynthesizeSingleton.h; sourceTree = SOURCE_ROOT; };
2111 93BC52A5124C1E6900B7587C /* StreamingPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamingPlayer.h; sourceTree = "<group>"; };
2112 93BC52A6124C1E6900B7587C /* StreamingPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingPlayer.m; sourceTree = "<group>"; };
2113- 93BC53F9124C7C8D00B7587C /* DataCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataCache.h; sourceTree = "<group>"; };
2114- 93BC53FA124C7C8D00B7587C /* DataCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataCache.m; sourceTree = "<group>"; };
2115 93CCBD4D1254039900AFFC22 /* speaker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = speaker.png; sourceTree = "<group>"; };
2116 93CCBD4E1254039900AFFC22 /* speaker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "speaker@2x.png"; sourceTree = "<group>"; };
2117 93CCBD7A12540CB700AFFC22 /* AboutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AboutViewController.h; path = view_controllers/AboutViewController.h; sourceTree = "<group>"; };
2118@@ -242,10 +293,6 @@
2119 93DFFE48135D71550061F29F /* music.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = music.xcdatamodel; sourceTree = "<group>"; };
2120 93DFFE52135D72420061F29F /* NSManagedObjectContext+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectContext+Additions.h"; sourceTree = "<group>"; };
2121 93DFFE53135D72420061F29F /* NSManagedObjectContext+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectContext+Additions.m"; sourceTree = "<group>"; };
2122- 93ED1AD6137276810058FC98 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageLoader.h; sourceTree = "<group>"; };
2123- 93ED1AD7137276810058FC98 /* ImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageLoader.m; sourceTree = "<group>"; };
2124- 93ED1ADD137279A30058FC98 /* ImageLoadingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageLoadingOperation.h; sourceTree = "<group>"; };
2125- 93ED1ADE137279A30058FC98 /* ImageLoadingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageLoadingOperation.m; sourceTree = "<group>"; };
2126 93EE2AEE124993F100E7E060 /* ArtistListParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArtistListParser.h; sourceTree = "<group>"; };
2127 93EE2AEF124993F100E7E060 /* ArtistListParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArtistListParser.m; sourceTree = "<group>"; };
2128 93EE2BA11249F33D00E7E060 /* ArtistParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArtistParser.h; sourceTree = "<group>"; };
2129@@ -258,10 +305,6 @@
2130 93F3344F1247FA2C006C6707 /* Album.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Album.m; sourceTree = "<group>"; };
2131 93F334501247FA2C006C6707 /* Artist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Artist.h; sourceTree = "<group>"; };
2132 93F334511247FA2C006C6707 /* Artist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Artist.m; sourceTree = "<group>"; };
2133- 93F334541247FA52006C6707 /* AsynchronousImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsynchronousImageView.h; sourceTree = "<group>"; };
2134- 93F334551247FA52006C6707 /* AsynchronousImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsynchronousImageView.m; sourceTree = "<group>"; };
2135- 93F334571247FA68006C6707 /* AsynchronousImageViewCached.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AsynchronousImageViewCached.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
2136- 93F334581247FA68006C6707 /* AsynchronousImageViewCached.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AsynchronousImageViewCached.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
2137 93F3345A1247FA85006C6707 /* AlbumUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AlbumUITableViewCell.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
2138 93F3345B1247FA85006C6707 /* AlbumUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AlbumUITableViewCell.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
2139 93F3345D1247FA97006C6707 /* SongUITableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SongUITableViewCell.h; sourceTree = "<group>"; };
2140@@ -292,7 +335,6 @@
2141 93FA42A9124DC1350080DF62 /* 05-shuffle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "05-shuffle.png"; sourceTree = "<group>"; };
2142 93FA42AA124DC1350080DF62 /* 05-shuffle@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "05-shuffle@2x.png"; sourceTree = "<group>"; };
2143 93FA42AB124DC1350080DF62 /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = background.png; sourceTree = "<group>"; };
2144- 93FA42AC124DC1350080DF62 /* default-album-art-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default-album-art-small.png"; sourceTree = "<group>"; };
2145 93FA42AD124DC1350080DF62 /* default-album-art.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default-album-art.png"; sourceTree = "<group>"; };
2146 93FA4334124DE0D80080DF62 /* player_back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = player_back.png; sourceTree = "<group>"; };
2147 93FA4335124DE0D80080DF62 /* player_back@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "player_back@2x.png"; sourceTree = "<group>"; };
2148@@ -358,12 +400,28 @@
2149 29B97317FDCFA39411CA2CEA /* Resources */ = {
2150 isa = PBXGroup;
2151 children = (
2152+ 9112B01F1383FF27003C1D93 /* artists.png */,
2153+ 9112B02B1383FF37003C1D93 /* artists@2x.png */,
2154+ 9112B01E1383FF27003C1D93 /* albums.png */,
2155+ 9112B02A1383FF37003C1D93 /* albums@2x.png */,
2156+ 9112B0231383FF27003C1D93 /* songs.png */,
2157+ 9112B02E1383FF37003C1D93 /* songs@2x.png */,
2158+ 9112B0201383FF27003C1D93 /* playlists.png */,
2159+ 9112B02C1383FF37003C1D93 /* playlists@2x.png */,
2160+ 9112B0221383FF27003C1D93 /* settings.png */,
2161+ 9112B02D1383FF37003C1D93 /* settings@2x.png */,
2162+ 91406E8E13849F2400A7DA67 /* cached.png */,
2163+ 91406E8F13849F2400A7DA67 /* cached@2x.png */,
2164+ 91406E9013849F2400A7DA67 /* partiallycached.png */,
2165+ 91406E9113849F2400A7DA67 /* partiallycached@2x.png */,
2166+ 91406E9213849F2400A7DA67 /* uncached.png */,
2167+ 91406E9313849F2400A7DA67 /* uncached@2x.png */,
2168+ 5305C2051157F4F800BC78F0 /* Default.png */,
2169 932E7A6D1254747E00E7C8FF /* Default@2x.png */,
2170+ 93D6B50B1252CA71007880B0 /* music_57.png */,
2171 93D6B5141252CB34007880B0 /* music_57@2x.png */,
2172 93D6B50A1252CA71007880B0 /* music_29.png */,
2173- 93D6B50B1252CA71007880B0 /* music_57.png */,
2174 93D6B50D1252CA71007880B0 /* music_512.png */,
2175- 5305C2051157F4F800BC78F0 /* Default.png */,
2176 53F0CD8111589F0A00A665CD /* progress-label-background.png */,
2177 8D1107310486CEB800E47090 /* iSub-Info.plist */,
2178 935FCC42123766E600B5DF9B /* Entitlements.plist */,
2179@@ -466,6 +524,10 @@
2180 9316628512264A74003B0EB7 /* Categories */ = {
2181 isa = PBXGroup;
2182 children = (
2183+ 91BE4014138E8D2300D44D68 /* UIActionSheet+Blocks.h */,
2184+ 91BE4015138E8D2300D44D68 /* UIActionSheet+Blocks.m */,
2185+ 91BE4016138E8D2300D44D68 /* UIAlertView+Blocks.h */,
2186+ 91BE4017138E8D2300D44D68 /* UIAlertView+Blocks.m */,
2187 93DFFE52135D72420061F29F /* NSManagedObjectContext+Additions.h */,
2188 93DFFE53135D72420061F29F /* NSManagedObjectContext+Additions.m */,
2189 93D6B119124ED3B3007880B0 /* UIDevice+Hardware.h */,
2190@@ -486,6 +548,8 @@
2191 91018B0D13573D150051EFDC /* UIImage+Alpha.m */,
2192 91018B0F13573D350051EFDC /* UIImage+RoundedCorner.h */,
2193 91018B1013573D350051EFDC /* UIImage+RoundedCorner.m */,
2194+ 91406EE8138AEEE000A7DA67 /* NSString+Extras.h */,
2195+ 91406EE9138AEEE000A7DA67 /* NSString+Extras.m */,
2196 );
2197 name = Categories;
2198 path = categories;
2199@@ -504,6 +568,8 @@
2200 936F1F2E12271B6500070F43 /* Controls */ = {
2201 isa = PBXGroup;
2202 children = (
2203+ 91BE401A138E8D3A00D44D68 /* RIButtonItem.h */,
2204+ 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */,
2205 93F3345D1247FA97006C6707 /* SongUITableViewCell.h */,
2206 93F3345E1247FA97006C6707 /* SongUITableViewCell.m */,
2207 919376F1135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.h */,
2208@@ -512,14 +578,8 @@
2209 93F3345B1247FA85006C6707 /* AlbumUITableViewCell.m */,
2210 9110DC551354A7460046B8E4 /* AlbumArtistUITableViewCell.h */,
2211 9110DC561354A7470046B8E4 /* AlbumArtistUITableViewCell.m */,
2212- 93F334571247FA68006C6707 /* AsynchronousImageViewCached.h */,
2213- 93F334581247FA68006C6707 /* AsynchronousImageViewCached.m */,
2214- 93F334541247FA52006C6707 /* AsynchronousImageView.h */,
2215- 93F334551247FA52006C6707 /* AsynchronousImageView.m */,
2216 936F230812284D1900070F43 /* NamedTextFieldCell.h */,
2217 936F230912284D1900070F43 /* NamedTextFieldCell.m */,
2218- 936F1F2F12271B6500070F43 /* MBProgressHUD.h */,
2219- 936F1F3012271B6500070F43 /* MBProgressHUD.m */,
2220 912D1262134A162000721EE4 /* AlertPrompt.h */,
2221 912D1263134A162000721EE4 /* AlertPrompt.m */,
2222 93DF12D7136E968E00D6C085 /* MiniHudView.h */,
2223@@ -547,9 +607,27 @@
2224 path = models;
2225 sourceTree = "<group>";
2226 };
2227+ 937FAA0C137CFC1B00507E51 /* operations */ = {
2228+ isa = PBXGroup;
2229+ children = (
2230+ 937FAA16137CFC7200507E51 /* DownloadOperation.h */,
2231+ 937FAA17137CFC7200507E51 /* DownloadOperation.m */,
2232+ 937FAA13137CFC5000507E51 /* Downloader.h */,
2233+ 937FAA14137CFC5000507E51 /* Downloader.m */,
2234+ 937FAA0D137CFC1B00507E51 /* AlbumArtLoader.h */,
2235+ 937FAA0E137CFC1B00507E51 /* AlbumArtLoader.m */,
2236+ 937FAA0F137CFC1B00507E51 /* AlbumArtLoadingOperation.h */,
2237+ 937FAA10137CFC1B00507E51 /* AlbumArtLoadingOperation.m */,
2238+ 937FAA19137CFCA600507E51 /* AbstractNetworkOperation.h */,
2239+ 937FAA1A137CFCA600507E51 /* AbstractNetworkOperation.m */,
2240+ );
2241+ path = operations;
2242+ sourceTree = "<group>";
2243+ };
2244 93F334441247F9B9006C6707 /* Utilities */ = {
2245 isa = PBXGroup;
2246 children = (
2247+ 937FAA0C137CFC1B00507E51 /* operations */,
2248 93BC5207124C181600B7587C /* Subsonic.h */,
2249 93BC5208124C181600B7587C /* Subsonic.m */,
2250 91018B29135924730051EFDC /* Parsers */,
2251@@ -561,14 +639,10 @@
2252 93F334461247F9DE006C6707 /* SFHFKeychainUtils.m */,
2253 93BC52A5124C1E6900B7587C /* StreamingPlayer.h */,
2254 93BC52A6124C1E6900B7587C /* StreamingPlayer.m */,
2255- 93BC53F9124C7C8D00B7587C /* DataCache.h */,
2256- 93BC53FA124C7C8D00B7587C /* DataCache.m */,
2257 93D6B5491252CE57007880B0 /* URLQueryStringParser.h */,
2258 93D6B54A1252CE57007880B0 /* URLQueryStringParser.m */,
2259- 93ED1AD6137276810058FC98 /* ImageLoader.h */,
2260- 93ED1AD7137276810058FC98 /* ImageLoader.m */,
2261- 93ED1ADD137279A30058FC98 /* ImageLoadingOperation.h */,
2262- 93ED1ADE137279A30058FC98 /* ImageLoadingOperation.m */,
2263+ 937FAAED137D15E800507E51 /* Globals.h */,
2264+ 937FAAEE137D15E800507E51 /* Globals.m */,
2265 );
2266 name = Utilities;
2267 path = utilities;
2268@@ -642,7 +716,6 @@
2269 93FA42A9124DC1350080DF62 /* 05-shuffle.png */,
2270 93FA42AA124DC1350080DF62 /* 05-shuffle@2x.png */,
2271 93FA42AB124DC1350080DF62 /* background.png */,
2272- 93FA42AC124DC1350080DF62 /* default-album-art-small.png */,
2273 93FA42AD124DC1350080DF62 /* default-album-art.png */,
2274 );
2275 name = Images;
2276@@ -712,7 +785,6 @@
2277 93FA42B0124DC1350080DF62 /* 05-shuffle.png in Resources */,
2278 93FA42B1124DC1350080DF62 /* 05-shuffle@2x.png in Resources */,
2279 93FA42B2124DC1350080DF62 /* background.png in Resources */,
2280- 93FA42B3124DC1350080DF62 /* default-album-art-small.png in Resources */,
2281 93FA42B4124DC1350080DF62 /* default-album-art.png in Resources */,
2282 93FA4336124DE0D80080DF62 /* player_back.png in Resources */,
2283 93FA4337124DE0D80080DF62 /* player_back@2x.png in Resources */,
2284@@ -739,6 +811,22 @@
2285 932E7AA11255265A00E7C8FF /* about.html in Resources */,
2286 932E7B0B12552CD500E7C8FF /* arrow.png in Resources */,
2287 91018B25135922BF0051EFDC /* PlaylistEditViewController.xib in Resources */,
2288+ 9112B0241383FF27003C1D93 /* albums.png in Resources */,
2289+ 9112B0251383FF27003C1D93 /* artists.png in Resources */,
2290+ 9112B0261383FF27003C1D93 /* playlists.png in Resources */,
2291+ 9112B0281383FF27003C1D93 /* settings.png in Resources */,
2292+ 9112B0291383FF27003C1D93 /* songs.png in Resources */,
2293+ 9112B02F1383FF37003C1D93 /* albums@2x.png in Resources */,
2294+ 9112B0301383FF37003C1D93 /* artists@2x.png in Resources */,
2295+ 9112B0311383FF37003C1D93 /* playlists@2x.png in Resources */,
2296+ 9112B0321383FF37003C1D93 /* settings@2x.png in Resources */,
2297+ 9112B0331383FF37003C1D93 /* songs@2x.png in Resources */,
2298+ 91406E9413849F2400A7DA67 /* cached.png in Resources */,
2299+ 91406E9513849F2400A7DA67 /* cached@2x.png in Resources */,
2300+ 91406E9613849F2400A7DA67 /* partiallycached.png in Resources */,
2301+ 91406E9713849F2400A7DA67 /* partiallycached@2x.png in Resources */,
2302+ 91406E9813849F2400A7DA67 /* uncached.png in Resources */,
2303+ 91406E9913849F2400A7DA67 /* uncached@2x.png in Resources */,
2304 );
2305 runOnlyForDeploymentPostprocessing = 0;
2306 };
2307@@ -750,7 +838,6 @@
2308 buildActionMask = 2147483647;
2309 files = (
2310 9316628812264A74003B0EB7 /* NSDate+Extras.m in Sources */,
2311- 936F1F3112271B6500070F43 /* MBProgressHUD.m in Sources */,
2312 936F20681227364200070F43 /* Playlist.m in Sources */,
2313 936F209012273D9000070F43 /* Song.m in Sources */,
2314 936F230A12284D1900070F43 /* NamedTextFieldCell.m in Sources */,
2315@@ -758,8 +845,6 @@
2316 93F3344D1247FA0B006C6707 /* Reachability.m in Sources */,
2317 93F334521247FA2C006C6707 /* Album.m in Sources */,
2318 93F334531247FA2C006C6707 /* Artist.m in Sources */,
2319- 93F334561247FA52006C6707 /* AsynchronousImageView.m in Sources */,
2320- 93F334591247FA68006C6707 /* AsynchronousImageViewCached.m in Sources */,
2321 93F3345C1247FA85006C6707 /* AlbumUITableViewCell.m in Sources */,
2322 93F3345F1247FA97006C6707 /* SongUITableViewCell.m in Sources */,
2323 93F334711247FB78006C6707 /* iSubAppDelegate.m in Sources */,
2324@@ -776,7 +861,6 @@
2325 93EE2BA31249F33D00E7E060 /* ArtistParser.m in Sources */,
2326 93BC5209124C181600B7587C /* Subsonic.m in Sources */,
2327 93BC52A7124C1E6900B7587C /* StreamingPlayer.m in Sources */,
2328- 93BC53FB124C7C8D00B7587C /* DataCache.m in Sources */,
2329 93D6B0FF124ED061007880B0 /* SubsonicViewController.m in Sources */,
2330 93D6B11B124ED3B3007880B0 /* UIDevice+Hardware.m in Sources */,
2331 93D6B214124F0F62007880B0 /* NSNumber+Extras.m in Sources */,
2332@@ -811,8 +895,16 @@
2333 93DFFE4D135D71760061F29F /* music.xcdatamodeld in Sources */,
2334 93DFFE54135D72420061F29F /* NSManagedObjectContext+Additions.m in Sources */,
2335 93DF12D9136E968E00D6C085 /* MiniHudView.m in Sources */,
2336- 93ED1AD8137276810058FC98 /* ImageLoader.m in Sources */,
2337- 93ED1ADF137279A30058FC98 /* ImageLoadingOperation.m in Sources */,
2338+ 937FAA11137CFC1B00507E51 /* AlbumArtLoader.m in Sources */,
2339+ 937FAA12137CFC1B00507E51 /* AlbumArtLoadingOperation.m in Sources */,
2340+ 937FAA15137CFC5000507E51 /* Downloader.m in Sources */,
2341+ 937FAA18137CFC7200507E51 /* DownloadOperation.m in Sources */,
2342+ 937FAA1B137CFCA600507E51 /* AbstractNetworkOperation.m in Sources */,
2343+ 937FAAEF137D15E800507E51 /* Globals.m in Sources */,
2344+ 91406EEA138AEEE000A7DA67 /* NSString+Extras.m in Sources */,
2345+ 91BE4018138E8D2300D44D68 /* UIActionSheet+Blocks.m in Sources */,
2346+ 91BE4019138E8D2300D44D68 /* UIAlertView+Blocks.m in Sources */,
2347+ 91BE401C138E8D3A00D44D68 /* RIButtonItem.m in Sources */,
2348 );
2349 runOnlyForDeploymentPostprocessing = 0;
2350 };
2351@@ -823,20 +915,23 @@
2352 isa = XCBuildConfiguration;
2353 buildSettings = {
2354 ALWAYS_SEARCH_USER_PATHS = NO;
2355- CODE_SIGN_ENTITLEMENTS = Entitlements.plist;
2356- CODE_SIGN_IDENTITY = "iPhone Developer: Zachery Bir (P7WDPDEFHU)";
2357- "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer: Zachery Bir (P7WDPDEFHU)";
2358- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Zachery Bir (P7WDPDEFHU)";
2359+ CODE_SIGN_ENTITLEMENTS = "";
2360+ CODE_SIGN_IDENTITY = "iPhone Developer";
2361+ "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer";
2362+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
2363 COPY_PHASE_STRIP = NO;
2364+ CURRENT_PROJECT_VERSION = 6;
2365 GCC_DYNAMIC_NO_PIC = NO;
2366 GCC_OPTIMIZATION_LEVEL = 0;
2367 GCC_PRECOMPILE_PREFIX_HEADER = YES;
2368 GCC_PREFIX_HEADER = iSub_Prefix.pch;
2369+ GCC_VERSION = "";
2370 INFOPLIST_FILE = "iSub-Info.plist";
2371 PRODUCT_NAME = "U1 Music";
2372- PROVISIONING_PROFILE = "6CD9666E-9AEA-40F3-8973-7914D90DB031";
2373- "PROVISIONING_PROFILE[sdk=*]" = "FE49C826-C565-422D-A0D7-2F02D179FC58";
2374- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "FE49C826-C565-422D-A0D7-2F02D179FC58";
2375+ PROVISIONING_PROFILE = "";
2376+ "PROVISIONING_PROFILE[sdk=*]" = "";
2377+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2378+ RUN_CLANG_STATIC_ANALYZER = NO;
2379 SDKROOT = iphoneos;
2380 };
2381 name = Debug;
2382@@ -846,16 +941,19 @@
2383 buildSettings = {
2384 ALWAYS_SEARCH_USER_PATHS = NO;
2385 CODE_SIGN_ENTITLEMENTS = Entitlements.plist;
2386- CODE_SIGN_IDENTITY = "iPhone Developer: Zachery Bir (P7WDPDEFHU)";
2387- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Canonical Group Limited";
2388- COPY_PHASE_STRIP = YES;
2389+ CODE_SIGN_IDENTITY = "iPhone Distribution";
2390+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
2391+ COPY_PHASE_STRIP = NO;
2392+ CURRENT_PROJECT_VERSION = 6;
2393 GCC_PRECOMPILE_PREFIX_HEADER = YES;
2394 GCC_PREFIX_HEADER = iSub_Prefix.pch;
2395+ GCC_VERSION = "";
2396 INFOPLIST_FILE = "iSub-Info.plist";
2397 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
2398 PRODUCT_NAME = "U1 Music";
2399- PROVISIONING_PROFILE = "6CD9666E-9AEA-40F3-8973-7914D90DB031";
2400- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "C363FA5F-63C0-4EEA-8F4A-99821B6B3DDF";
2401+ PROVISIONING_PROFILE = "";
2402+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2403+ RUN_CLANG_STATIC_ANALYZER = NO;
2404 SDKROOT = iphoneos;
2405 };
2406 name = Release;
2407@@ -864,16 +962,19 @@
2408 isa = XCBuildConfiguration;
2409 buildSettings = {
2410 ARCHS = "$(ARCHS_STANDARD_32_BIT)";
2411- CODE_SIGN_ENTITLEMENTS = "";
2412+ CODE_SIGN_ENTITLEMENTS = Entitlements.plist;
2413+ CODE_SIGN_IDENTITY = "iPhone Developer";
2414 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
2415+ COPY_PHASE_STRIP = NO;
2416 GCC_C_LANGUAGE_STANDARD = c99;
2417 GCC_WARN_ABOUT_RETURN_TYPE = YES;
2418 GCC_WARN_UNUSED_VARIABLE = YES;
2419 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
2420 OTHER_CFLAGS = "";
2421 PREBINDING = NO;
2422+ PROVISIONING_PROFILE = "";
2423 "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2424- SDKROOT = iphoneos4.1;
2425+ SDKROOT = iphoneos;
2426 };
2427 name = Debug;
2428 };
2429@@ -881,15 +982,18 @@
2430 isa = XCBuildConfiguration;
2431 buildSettings = {
2432 ARCHS = "$(ARCHS_STANDARD_32_BIT)";
2433- CODE_SIGN_ENTITLEMENTS = "";
2434+ CODE_SIGN_ENTITLEMENTS = Entitlements.plist;
2435+ CODE_SIGN_IDENTITY = "iPhone Distribution";
2436 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
2437+ COPY_PHASE_STRIP = NO;
2438 GCC_C_LANGUAGE_STANDARD = c99;
2439 GCC_WARN_ABOUT_RETURN_TYPE = YES;
2440 GCC_WARN_UNUSED_VARIABLE = YES;
2441 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
2442 PREBINDING = NO;
2443+ PROVISIONING_PROFILE = "";
2444 "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
2445- SDKROOT = iphoneos4.1;
2446+ SDKROOT = iphoneos;
2447 };
2448 name = Release;
2449 };
2450
2451=== modified file 'musicstreaming/iSub_Prefix.pch'
2452--- musicstreaming/iSub_Prefix.pch 2011-04-19 08:04:45 +0000
2453+++ musicstreaming/iSub_Prefix.pch 2011-06-13 16:58:33 +0000
2454@@ -12,12 +12,12 @@
2455 #import <Foundation/Foundation.h>
2456 #import <UIKit/UIKit.h>
2457 #import <CoreData/CoreData.h>
2458+ #import "Globals.h"
2459 #import "MOC.h"
2460 #import "NSManagedObjectContext+Additions.h"
2461 #import "SFHFKeychainUtils.h"
2462 #define RELEASE_SAFELY(__obj) [__obj release], __obj = nil;
2463
2464- //xxx todo - I believe serviceName needs to have the bundle identifier (ABC12345DEF.) prepended to it.
2465 #define UBUNTU_ONE_SERVICE_NAME @"com.ubuntu.one"
2466 #define UBUNTU_ONE_DUMMY_USER_NAME @"_UBUNTU_ONE_USER_NAME"
2467 #endif
2468
2469=== removed file 'musicstreaming/images/default-album-art-small.png'
2470Binary files musicstreaming/images/default-album-art-small.png 2010-09-25 05:48:41 +0000 and musicstreaming/images/default-album-art-small.png 1970-01-01 00:00:00 +0000 differ
2471=== modified file 'musicstreaming/images/default-album-art.png'
2472Binary files musicstreaming/images/default-album-art.png 2010-09-25 05:48:41 +0000 and musicstreaming/images/default-album-art.png 2011-06-13 16:58:33 +0000 differ
2473=== modified file 'musicstreaming/models/Album.h'
2474--- musicstreaming/models/Album.h 2011-05-02 07:15:23 +0000
2475+++ musicstreaming/models/Album.h 2011-06-13 16:58:33 +0000
2476@@ -30,7 +30,7 @@
2477
2478 @class Artist, Song;
2479
2480-@interface Album : NSManagedObject <NSCoding, NSCopying>
2481+@interface Album : NSManagedObject
2482 {
2483 }
2484 @property (nonatomic, retain) NSString *title;
2485@@ -43,6 +43,8 @@
2486 + (BOOL)albumWithIdExists:(NSString*)anAlbumId;
2487 + (Album*)albumWithId:(NSString*)anAlbumId;
2488 - (BOOL)matchesSearchQuery:(NSString *)query;
2489+- (NSError*)loadSongs;
2490+- (NSString*)dearticlizedTitle;
2491 @end
2492
2493 // coalesce these into one @interface Album (CoreDataGeneratedAccessors) section
2494
2495=== modified file 'musicstreaming/models/Album.m'
2496--- musicstreaming/models/Album.m 2011-05-02 07:15:23 +0000
2497+++ musicstreaming/models/Album.m 2011-06-13 16:58:33 +0000
2498@@ -30,6 +30,10 @@
2499
2500 #import "Album.h"
2501 #import "Artist.h"
2502+#import "NSMutableSet+Extras.h"
2503+#import "NSString+Extras.h"
2504+#import "AlbumParser.h"
2505+#import "Subsonic.h"
2506
2507 @implementation Album
2508 @dynamic title, artist, albumId, coverArtId;
2509@@ -63,46 +67,36 @@
2510 }
2511 }
2512
2513-- (id)copyWithZone:(NSZone *)zone
2514-{
2515- Album *copy = [[Album allocWithZone:zone] init];
2516-
2517- NSString *t = [self.title copyWithZone:zone];
2518- copy.title = t;
2519-
2520- NSString *a = [self.artist copyWithZone:zone];
2521- copy.artist = a;
2522-
2523- NSString *i = [self.albumId copyWithZone:zone];
2524- copy.albumId = i;
2525-
2526- NSString *c = [self.coverArtId copyWithZone:zone];
2527- copy.coverArtId = c;
2528-
2529- [t release];
2530- [a release];
2531- [i release];
2532- [c release];
2533-
2534- return copy;
2535-}
2536-
2537-- (void)encodeWithCoder:(NSCoder*)encoder
2538-{
2539- [encoder encodeObject:self.title forKey:@"title"];
2540- [encoder encodeObject:self.artist forKey:@"artist"];
2541- [encoder encodeObject:self.albumId forKey:@"albumId"];
2542- [encoder encodeObject:self.coverArtId forKey:@"coverArtId"];
2543-}
2544-
2545-- (id)initWithCoder:(NSCoder*)decoder
2546-{
2547- self.title = [[decoder decodeObjectForKey:@"title"] retain];
2548- self.artist = [[decoder decodeObjectForKey:@"artist"] retain];
2549- self.albumId = [[decoder decodeObjectForKey:@"albumId"] retain];
2550- self.coverArtId = [[decoder decodeObjectForKey:@"coverArtId"] retain];
2551-
2552- return self;
2553+- (NSString *)dearticlizedTitle
2554+{
2555+ return [self.title dearticlizedString];
2556+}
2557+
2558+- (NSError*)loadSongs
2559+{
2560+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2561+ NSError *error = nil;
2562+
2563+ NSMutableSet *parameters = [NSMutableSet set];
2564+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.albumId, nil]];
2565+ NSURL *url = [[Subsonic sharedSubsonic] getBaseURL:@"getAlbum.view" parameters:parameters];
2566+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
2567+
2568+ AlbumParser *parser = [[AlbumParser alloc] init];
2569+ parser.albumObjectId = [self objectID];
2570+
2571+ [xmlParser setDelegate:parser];
2572+
2573+ if (![xmlParser parse])
2574+ {
2575+ error = [[xmlParser parserError] copy];
2576+ }
2577+
2578+ [xmlParser release];
2579+ [parser release];
2580+ [pool release];
2581+
2582+ return [error autorelease];
2583 }
2584
2585 - (NSString*)description
2586
2587=== modified file 'musicstreaming/models/Artist.h'
2588--- musicstreaming/models/Artist.h 2011-05-02 03:10:39 +0000
2589+++ musicstreaming/models/Artist.h 2011-06-13 16:58:33 +0000
2590@@ -30,7 +30,7 @@
2591
2592 @class Album, Song;
2593
2594-@interface Artist : NSManagedObject <NSCoding>
2595+@interface Artist : NSManagedObject
2596 {
2597 }
2598 @property (nonatomic, retain) NSString *name;
2599@@ -39,6 +39,8 @@
2600 @property (nonatomic, retain) NSSet* songs;
2601 + (BOOL)artistWithIdExists:(NSString*)anArtistId;
2602 - (BOOL)matchesSearchQuery:(NSString *)query;
2603+- (NSString *)dearticlizedName;
2604+- (NSError*)loadAlbums;
2605 @end
2606
2607 @interface Artist (CoreDataGeneratedAccessors)
2608
2609=== modified file 'musicstreaming/models/Artist.m'
2610--- musicstreaming/models/Artist.m 2011-05-02 03:10:39 +0000
2611+++ musicstreaming/models/Artist.m 2011-06-13 16:58:33 +0000
2612@@ -30,6 +30,10 @@
2613
2614 #import "Artist.h"
2615 #import "Album.h"
2616+#import "Subsonic.h"
2617+#import "NSMutableSet+Extras.h"
2618+#import "NSString+Extras.h"
2619+#import "ArtistParser.h"
2620
2621 @implementation Artist
2622
2623@@ -54,23 +58,6 @@
2624 return (0 != [matches count]);
2625 }
2626
2627-- (id)initWithCoder:(NSCoder*)decoder
2628-{
2629- if ((self = [super init]))
2630- {
2631- self.name = [decoder decodeObjectForKey:@"name"];
2632- self.artistId = [decoder decodeObjectForKey:@"artistId"];
2633- }
2634-
2635- return self;
2636-}
2637-
2638-- (void)encodeWithCoder:(NSCoder*)encoder
2639-{
2640- [encoder encodeObject:self.name forKey:@"name"];
2641- [encoder encodeObject:self.artistId forKey:@"artistId"];
2642-}
2643-
2644 - (NSString*)description
2645 {
2646 return [NSString stringWithFormat:@"%@ (%@) MOC %@",self.name, self.artistId, [self managedObjectContext]];
2647@@ -82,4 +69,35 @@
2648 return titleResultsRange.length > 0;
2649 }
2650
2651+- (NSString *)dearticlizedName
2652+{
2653+ return [self.name dearticlizedString];
2654+}
2655+
2656+// Note: executes synchronously!
2657+- (NSError*)loadAlbums
2658+{
2659+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2660+ NSError *error = nil;
2661+
2662+ NSMutableSet *parameters = [NSMutableSet set];
2663+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.artistId, nil]];
2664+ NSURL *url = [[Subsonic sharedSubsonic] getBaseURL:@"getMusicDirectory.view" parameters:parameters];
2665+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
2666+ ArtistParser *parser = [[ArtistParser alloc] init];
2667+ parser.artistManagedObjectID = [self objectID];
2668+ [xmlParser setDelegate:parser];
2669+
2670+ if (![xmlParser parse])
2671+ {
2672+ error = [[xmlParser parserError] copy];
2673+ }
2674+
2675+ [xmlParser release];
2676+ [parser release];
2677+ [pool release];
2678+
2679+ return [error autorelease];
2680+}
2681+
2682 @end
2683
2684=== modified file 'musicstreaming/models/MOC.m'
2685--- musicstreaming/models/MOC.m 2011-04-19 08:05:04 +0000
2686+++ musicstreaming/models/MOC.m 2011-06-13 16:58:33 +0000
2687@@ -52,11 +52,6 @@
2688 }
2689 }
2690
2691-NSURL* ApplicationDocumentsDirectory( void )
2692-{
2693- return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
2694-}
2695-
2696 NSPersistentStoreCoordinator * GetPersistentStoreCoordinator( void )
2697 {
2698 if (persistentStoreCoordinator_ != nil)
2699@@ -64,7 +59,7 @@
2700 return persistentStoreCoordinator_;
2701 }
2702
2703- NSURL *storeURL = [ApplicationDocumentsDirectory() URLByAppendingPathComponent:@"music.sqlite"];
2704+ NSURL *storeURL = DatabaseFile();
2705
2706 NSError *error = nil;
2707 persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:GetManagedObjectModel()];
2708
2709=== modified file 'musicstreaming/models/Playlist.h'
2710--- musicstreaming/models/Playlist.h 2011-04-07 20:50:30 +0000
2711+++ musicstreaming/models/Playlist.h 2011-06-13 16:58:33 +0000
2712@@ -19,25 +19,25 @@
2713
2714 #import <Foundation/Foundation.h>
2715
2716+@class Song;
2717
2718-@interface Playlist : NSObject <NSCoding>
2719+@interface Playlist : NSManagedObject
2720 {
2721- NSString *playlistId;
2722- NSString *name;
2723- NSMutableArray *contents;
2724 }
2725 @property(nonatomic,retain) NSString *playlistId;
2726 @property(nonatomic,retain) NSString *name;
2727-@property(nonatomic,retain) NSMutableArray *contents;
2728-- (id)initWithCoder:(NSCoder *)aDecoder;
2729-- (BOOL)matchesSearchQuery:(NSString*)query;
2730-@end
2731-
2732-//Subordinate Data:
2733-//<?xml version="1.0" encoding="UTF-8"?>
2734-//<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.2.0">
2735-// <playlist>
2736-// <entry id="2f55736572732f6161726f6e2f4d757369632f6954756e65732f6954756e6573204d757369632f4d757369632f41726361646520466972652f54686520537562757262732f30312054686520537562757262732e6d7033" title="The Suburbs" isDir="false" album="The Suburbs" artist="Arcade Fire" duration="315" bitRate="320" track="1" year="2010" genre="Rock" size="12797916" suffix="mp3" contentType="audio/mpeg" coverArt="2f55736572732f6161726f6e2f4d757369632f6954756e65732f6954756e6573204d757369632f4d757369632f41726361646520466972652f54686520537562757262732f30312054686520537562757262732e6d7033" path="Arcade Fire/The Suburbs/01 The Suburbs.mp3"/>
2737-// <entry id="2f55736572732f6161726f6e2f4d757369632f6954756e65732f6954756e6573204d757369632f4d757369632f41726361646520466972652f54686520537562757262732f303220526561647920546f2053746172742e6d7033" title="Ready To Start" isDir="false" album="The Suburbs" artist="Arcade Fire" duration="255" bitRate="320" track="2" year="2010" genre="Rock" size="10413098" suffix="mp3" contentType="audio/mpeg" coverArt="2f55736572732f6161726f6e2f4d757369632f6954756e65732f6954756e6573204d757369632f4d757369632f41726361646520466972652f54686520537562757262732f30312054686520537562757262732e6d7033" path="Arcade Fire/The Suburbs/02 Ready To Start.mp3"/>
2738-// </playlist>
2739-//</subsonic-response>
2740\ No newline at end of file
2741+@property(nonatomic,retain) NSSet *songs;
2742+
2743++ (BOOL)playlistWithIdExists:(NSString*)aPlaylistId;
2744++ (Playlist*)playlistWithId:(NSString*)aPlaylistId;
2745+- (BOOL)matchesSearchQuery:(NSString *)query;
2746+- (NSError*)loadSongs;
2747+@end
2748+
2749+// coalesce these into one @interface Album (CoreDataGeneratedAccessors) section
2750+@interface Playlist (CoreDataGeneratedAccessors)
2751+- (void)addSongsObject:(Song *)value;
2752+- (void)removeSongsObject:(Song *)value;
2753+- (void)addSongs:(NSSet *)value;
2754+- (void)removeSongs:(NSSet *)value;
2755+@end
2756
2757=== modified file 'musicstreaming/models/Playlist.m'
2758--- musicstreaming/models/Playlist.m 2011-04-07 23:59:24 +0000
2759+++ musicstreaming/models/Playlist.m 2011-06-13 16:58:33 +0000
2760@@ -18,51 +18,77 @@
2761 // along with this program. If not, see <http://www.gnu.org/licenses/>.
2762
2763 #import "Playlist.h"
2764+#import "Subsonic.h"
2765+#import "NSMutableSet+Extras.h"
2766+#import "PlaylistParser.h"
2767
2768 @implementation Playlist
2769
2770-@synthesize playlistId, name, contents;
2771-
2772-- (id)init
2773-{
2774- if ((self = [super init]))
2775- {
2776- self.playlistId = nil;
2777- self.name = nil;
2778- self.contents = [NSMutableArray array];
2779- }
2780- return self;
2781-}
2782-
2783-- (void)encodeWithCoder:(NSCoder*)encoder
2784-{
2785- [encoder encodeObject:playlistId forKey:@"playlistId"];
2786- [encoder encodeObject:name forKey:@"name"];
2787- [encoder encodeObject:contents forKey:@"contents"];
2788-}
2789-
2790-
2791-- (id)initWithCoder:(NSCoder*)decoder
2792-{
2793- playlistId = [[decoder decodeObjectForKey:@"playlistId"] retain];
2794- name = [[decoder decodeObjectForKey:@"name"] retain];
2795- contents = [[decoder decodeObjectForKey:@"contents"] retain];
2796-
2797- return self;
2798-}
2799-
2800-- (void)dealloc
2801-{
2802- self.playlistId = NULL;
2803- self.name = NULL;
2804- self.contents = NULL;
2805-
2806- [super dealloc];
2807+@dynamic playlistId, name, songs;
2808+
2809++ (BOOL)playlistWithIdExists:(NSString *)aPlaylistId
2810+{
2811+ return (nil != [Playlist playlistWithId:aPlaylistId]);
2812+}
2813+
2814++ (Playlist *)playlistWithId:(NSString *)aPlaylistId
2815+{
2816+ NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
2817+ NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Playlist" inManagedObjectContext:PerThreadManagedObjectContext()];
2818+ [fetch setEntity:entityDescription];
2819+
2820+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"playlistId == %@", aPlaylistId];
2821+ [fetch setPredicate:predicate];
2822+
2823+ NSError * error = nil;
2824+ NSArray * matches = [PerThreadManagedObjectContext() executeFetchRequest:fetch error:&error];
2825+ [fetch release];
2826+
2827+ if ([matches count] > 0)
2828+ {
2829+ return [matches objectAtIndex:0];
2830+ }
2831+ else
2832+ {
2833+ return nil;
2834+ }
2835+}
2836+
2837+- (NSError*)loadSongs
2838+{
2839+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2840+ NSError *error = nil;
2841+
2842+ NSMutableSet *parameters = [NSMutableSet set];
2843+ [parameters addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"id", self.playlistId, nil]];
2844+ NSURL *url = [[Subsonic sharedSubsonic] getBaseURL:@"getPlaylist.view" parameters:parameters];
2845+ NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
2846+
2847+ PlaylistParser *parser = [[PlaylistParser alloc] init];
2848+ parser.playlistObjectId = [self objectID];
2849+
2850+ [xmlParser setDelegate:parser];
2851+
2852+ if (![xmlParser parse])
2853+ {
2854+ error = [[xmlParser parserError] copy];
2855+ }
2856+
2857+ [xmlParser release];
2858+ [parser release];
2859+ [pool release];
2860+
2861+ return [error autorelease];
2862+}
2863+
2864+- (NSString*)description
2865+{
2866+ return [NSString stringWithFormat:@"%@ (%@) MOC %@", self.name, self.playlistId, [self managedObjectContext]];
2867 }
2868
2869 - (BOOL)matchesSearchQuery:(NSString *)query
2870 {
2871- NSRange titleResultsRange = [name rangeOfString:query options:NSCaseInsensitiveSearch];
2872+ NSRange titleResultsRange = [self.name rangeOfString:query options:NSCaseInsensitiveSearch];
2873 return titleResultsRange.length > 0;
2874 }
2875
2876
2877=== modified file 'musicstreaming/models/Song.h'
2878--- musicstreaming/models/Song.h 2011-05-05 18:12:15 +0000
2879+++ musicstreaming/models/Song.h 2011-06-13 16:58:33 +0000
2880@@ -28,9 +28,9 @@
2881 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2882 // DAMAGE.
2883
2884-@class Artist, Album;
2885+@class Artist, Album, Playlist;
2886
2887-@interface Song : NSManagedObject <NSCoding>
2888+@interface Song : NSManagedObject
2889 {
2890 }
2891 @property (nonatomic, retain) NSString *title;
2892@@ -47,8 +47,19 @@
2893 @property (nonatomic, retain) NSNumber *size;
2894 @property (nonatomic, retain) Artist *artistEntity;
2895 @property (nonatomic, retain) Album *albumEntity;
2896+@property (nonatomic, retain) NSSet *playlistEntities;
2897
2898 + (BOOL)songWithIdExists:(NSString*)aSongId;
2899 + (Song*)songWithId:(NSString*)aSongId;
2900 - (BOOL)matchesSearchQuery:(NSString *)query;
2901-@end
2902\ No newline at end of file
2903+- (NSString *)dearticlizedTitle;
2904+- (BOOL)cachedSongExists;
2905+- (NSURL*)cachedSongURL;
2906+@end
2907+
2908+@interface Song (CoreDataGeneratedAccessors)
2909+- (void)addPlaylistEntitiesObject:(Playlist *)value;
2910+- (void)removePlaylistEntitiesObject:(Playlist *)value;
2911+- (void)addPlaylistEntities:(NSSet *)value;
2912+- (void)removePlaylistEntities:(NSSet *)value;
2913+@end
2914
2915=== modified file 'musicstreaming/models/Song.m'
2916--- musicstreaming/models/Song.m 2011-05-05 18:12:15 +0000
2917+++ musicstreaming/models/Song.m 2011-06-13 16:58:33 +0000
2918@@ -31,10 +31,11 @@
2919 #import "Song.h"
2920 #import "Artist.h"
2921 #import "Album.h"
2922+#import "NSString+Extras.h"
2923
2924 @implementation Song
2925 @dynamic title, songId, artist, album, genre, coverArtId, path, duration, bitRate, track, year, size;
2926-@dynamic artistEntity, albumEntity;
2927+@dynamic artistEntity, albumEntity, playlistEntities;
2928
2929 + (BOOL)songWithIdExists:(NSString*)aSongId
2930 {
2931@@ -64,42 +65,14 @@
2932 }
2933 }
2934
2935-
2936-- (void)encodeWithCoder:(NSCoder*)encoder
2937+- (NSURL*)cachedSongURL
2938 {
2939- [encoder encodeObject:self.title forKey:@"title"];
2940- [encoder encodeObject:self.songId forKey:@"songId"];
2941- [encoder encodeObject:self.artist forKey:@"artist"];
2942- [encoder encodeObject:self.album forKey:@"album"];
2943- [encoder encodeObject:self.genre forKey:@"genre"];
2944- [encoder encodeObject:self.coverArtId forKey:@"coverArtId"];
2945- [encoder encodeObject:self.path forKey:@"path"];
2946- [encoder encodeObject:self.duration forKey:@"duration"];
2947- [encoder encodeObject:self.bitRate forKey:@"bitRate"];
2948- [encoder encodeObject:self.track forKey:@"track"];
2949- [encoder encodeObject:self.year forKey:@"year"];
2950- [encoder encodeObject:self.size forKey:@"size"];
2951+ return [CachedMusicDirectory() URLByAppendingPathComponent:self.songId];
2952 }
2953
2954-
2955-- (id)initWithCoder:(NSCoder*)decoder
2956+- (BOOL)cachedSongExists
2957 {
2958- if ((self = [super init]))
2959- {
2960- self.title = [[decoder decodeObjectForKey:@"title"] retain];
2961- self.songId = [[decoder decodeObjectForKey:@"songId"] retain];
2962- self.artist = [[decoder decodeObjectForKey:@"artist"] retain];
2963- self.album = [[decoder decodeObjectForKey:@"album"] retain];
2964- self.genre = [[decoder decodeObjectForKey:@"genre"] retain];
2965- self.coverArtId = [[decoder decodeObjectForKey:@"coverArtId"] retain];
2966- self.path = [[decoder decodeObjectForKey:@"path"] retain];
2967- self.duration = [[decoder decodeObjectForKey:@"duration"] retain];
2968- self.bitRate = [[decoder decodeObjectForKey:@"bitRate"] retain];
2969- self.track = [[decoder decodeObjectForKey:@"track"] retain];
2970- self.year = [[decoder decodeObjectForKey:@"year"] retain];
2971- self.size = [[decoder decodeObjectForKey:@"size"] retain];
2972- }
2973- return self;
2974+ return [[NSFileManager defaultManager] fileExistsAtPath:[[self cachedSongURL] path]];
2975 }
2976
2977 - (NSComparisonResult)compare:(Song*)s
2978@@ -126,4 +99,9 @@
2979 return titleResultsRange.length > 0;
2980 }
2981
2982+- (NSString *)dearticlizedTitle
2983+{
2984+ return [self.title dearticlizedString];
2985+}
2986+
2987 @end
2988
2989=== modified file 'musicstreaming/music.xcdatamodeld/music.xcdatamodel/elements'
2990Binary files musicstreaming/music.xcdatamodeld/music.xcdatamodel/elements 2011-05-02 04:54:02 +0000 and musicstreaming/music.xcdatamodeld/music.xcdatamodel/elements 2011-06-13 16:58:33 +0000 differ
2991=== modified file 'musicstreaming/music.xcdatamodeld/music.xcdatamodel/layout'
2992Binary files musicstreaming/music.xcdatamodeld/music.xcdatamodel/layout 2011-05-02 04:54:02 +0000 and musicstreaming/music.xcdatamodeld/music.xcdatamodel/layout 2011-06-13 16:58:33 +0000 differ
2993=== added file 'musicstreaming/partiallycached.png'
2994Binary files musicstreaming/partiallycached.png 1970-01-01 00:00:00 +0000 and musicstreaming/partiallycached.png 2011-06-13 16:58:33 +0000 differ
2995=== added file 'musicstreaming/partiallycached@2x.png'
2996Binary files musicstreaming/partiallycached@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/partiallycached@2x.png 2011-06-13 16:58:33 +0000 differ
2997=== added file 'musicstreaming/playlists.png'
2998Binary files musicstreaming/playlists.png 1970-01-01 00:00:00 +0000 and musicstreaming/playlists.png 2011-06-13 16:58:33 +0000 differ
2999=== added file 'musicstreaming/playlists@2x.png'
3000Binary files musicstreaming/playlists@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/playlists@2x.png 2011-06-13 16:58:33 +0000 differ
3001=== added file 'musicstreaming/settings.png'
3002Binary files musicstreaming/settings.png 1970-01-01 00:00:00 +0000 and musicstreaming/settings.png 2011-06-13 16:58:33 +0000 differ
3003=== added file 'musicstreaming/settings@2x.png'
3004Binary files musicstreaming/settings@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/settings@2x.png 2011-06-13 16:58:33 +0000 differ
3005=== added file 'musicstreaming/songs.png'
3006Binary files musicstreaming/songs.png 1970-01-01 00:00:00 +0000 and musicstreaming/songs.png 2011-06-13 16:58:33 +0000 differ
3007=== added file 'musicstreaming/songs@2x.png'
3008Binary files musicstreaming/songs@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/songs@2x.png 2011-06-13 16:58:33 +0000 differ
3009=== added file 'musicstreaming/uncached.png'
3010Binary files musicstreaming/uncached.png 1970-01-01 00:00:00 +0000 and musicstreaming/uncached.png 2011-06-13 16:58:33 +0000 differ
3011=== added file 'musicstreaming/uncached@2x.png'
3012Binary files musicstreaming/uncached@2x.png 1970-01-01 00:00:00 +0000 and musicstreaming/uncached@2x.png 2011-06-13 16:58:33 +0000 differ
3013=== modified file 'musicstreaming/utilities/AlbumListParser.m'
3014--- musicstreaming/utilities/AlbumListParser.m 2011-05-02 07:15:23 +0000
3015+++ musicstreaming/utilities/AlbumListParser.m 2011-06-13 16:58:33 +0000
3016@@ -22,16 +22,6 @@
3017
3018 @implementation AlbumListParser
3019
3020-- (id)init
3021-{
3022- if ((self = [super init]))
3023- {
3024- //
3025- }
3026- return self;
3027-}
3028-
3029-
3030 - (void) subsonicErrorCode:(NSString *)errorCode message:(NSString *)message
3031 {
3032 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", @"") otherButtonTitles:nil];
3033@@ -55,27 +45,11 @@
3034 album.title = [attributeDict objectForKey:@"title"];
3035 album.albumId = [attributeDict objectForKey:@"id"];
3036 album.artist = [attributeDict objectForKey:@"artist"];
3037-
3038- if ([attributeDict objectForKey:@"coverArt"])
3039- {
3040- album.coverArtId = [attributeDict objectForKey:@"coverArt"];
3041- }
3042+ album.coverArtId = [attributeDict objectForKey:@"coverArt"];
3043
3044 SaveContext();
3045 }
3046 }
3047 }
3048
3049-
3050-- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
3051-{
3052- //
3053-}
3054-
3055-
3056-- (void) dealloc
3057-{
3058- [super dealloc];
3059-}
3060-
3061 @end
3062\ No newline at end of file
3063
3064=== modified file 'musicstreaming/utilities/AlbumParser.m'
3065--- musicstreaming/utilities/AlbumParser.m 2011-05-05 18:12:15 +0000
3066+++ musicstreaming/utilities/AlbumParser.m 2011-06-13 16:58:33 +0000
3067@@ -51,11 +51,11 @@
3068 }
3069 else if ([elementName isEqualToString:@"song"])
3070 {
3071- Song *existing = [Song songWithId:[attributeDict objectForKey:@"id"]];
3072+ Song *song = [Song songWithId:[attributeDict objectForKey:@"id"]];
3073
3074- if (nil == existing)
3075+ if (nil == song)
3076 {
3077- Song *song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:PerThreadManagedObjectContext()];
3078+ song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:PerThreadManagedObjectContext()];
3079
3080 song.title = [attributeDict nilifiedValueForKey:@"title"];
3081 song.songId = [attributeDict nilifiedValueForKey:@"id"];
3082@@ -74,14 +74,10 @@
3083 song.year = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"year"]];
3084 song.size = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"size"]];
3085
3086- [album addSongsObject:song];
3087- SaveContext();
3088- }
3089- else if (nil == existing.albumEntity)
3090- {
3091- [album addSongsObject:existing];
3092- SaveContext();
3093- }
3094+ }
3095+
3096+ [album addSongsObject:song];
3097+ SaveContext();
3098 }
3099 }
3100
3101
3102=== modified file 'musicstreaming/utilities/ArtistListParser.h'
3103--- musicstreaming/utilities/ArtistListParser.h 2011-04-07 16:11:14 +0000
3104+++ musicstreaming/utilities/ArtistListParser.h 2011-06-13 16:58:33 +0000
3105@@ -23,15 +23,5 @@
3106
3107 @interface ArtistListParser : NSObject <NSXMLParserDelegate>
3108 {
3109- NSMutableArray *artists;
3110- NSMutableArray *indexes;
3111- Artist *artist;
3112- NSString *index;
3113- NSMutableDictionary *groupedArtists;
3114 }
3115-@property(nonatomic,retain) NSMutableDictionary *groupedArtists;
3116-@property(nonatomic,retain) NSMutableArray *artists;
3117-@property(nonatomic,retain) NSMutableArray *indexes;
3118-@property(nonatomic,retain) Artist *artist;
3119-@property(nonatomic,retain) NSString *index;
3120 @end
3121
3122=== modified file 'musicstreaming/utilities/ArtistListParser.m'
3123--- musicstreaming/utilities/ArtistListParser.m 2011-04-19 08:38:37 +0000
3124+++ musicstreaming/utilities/ArtistListParser.m 2011-06-13 16:58:33 +0000
3125@@ -22,23 +22,6 @@
3126
3127 @implementation ArtistListParser
3128
3129-@synthesize artists, indexes, groupedArtists;
3130-@synthesize artist, index;
3131-
3132-- (id)init
3133-{
3134- if ((self = [super init]))
3135- {
3136- artists = [[NSMutableArray alloc] init];
3137- indexes = [[NSMutableArray alloc] init];
3138- groupedArtists = [[NSMutableDictionary alloc] init];
3139- self.artist = NULL;
3140- self.index = NULL;
3141- }
3142- return self;
3143-}
3144-
3145-
3146 - (void)subsonicErrorCode:(NSString *)errorCode message:(NSString *)message
3147 {
3148 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", @"") otherButtonTitles:nil];
3149@@ -46,57 +29,22 @@
3150 [alert release];
3151 }
3152
3153-
3154 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
3155 {
3156 if ([elementName isEqualToString:@"error"])
3157 {
3158 [self subsonicErrorCode:[attributeDict objectForKey:@"code"] message:[attributeDict objectForKey:@"message"]];
3159 }
3160- else if ([elementName isEqualToString:@"index"]) //letters that appear on the right side of the table.
3161- {
3162- self.index = [attributeDict objectForKey:@"name"];
3163- [indexes addObject:self.index];
3164- [groupedArtists setObject:[NSMutableArray array] forKey:self.index];
3165- }
3166 else if ([elementName isEqualToString:@"artist"])
3167 {
3168 if (![Artist artistWithIdExists:[attributeDict objectForKey:@"id"]] && ![[attributeDict objectForKey:@"name"] isEqual:@".AppleDouble"])
3169 {
3170- self.artist = [NSEntityDescription insertNewObjectForEntityForName:@"Artist" inManagedObjectContext:PerThreadManagedObjectContext()];
3171- self.artist.name = [attributeDict objectForKey:@"name"];
3172- self.artist.artistId = [attributeDict objectForKey:@"id"];
3173-
3174- [artists addObject:self.artist];
3175- NSMutableArray *group = [groupedArtists objectForKey:self.index];
3176- [group addObject:self.artist];
3177+ Artist *artist = [NSEntityDescription insertNewObjectForEntityForName:@"Artist" inManagedObjectContext:PerThreadManagedObjectContext()];
3178+ artist.name = [attributeDict objectForKey:@"name"];
3179+ artist.artistId = [attributeDict objectForKey:@"id"];
3180+ SaveContext();
3181 }
3182 }
3183 }
3184
3185-
3186-- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
3187-{
3188- if ([elementName isEqualToString:@"artist"])
3189- {
3190- SaveContext();
3191- self.artist = NULL;
3192- }
3193- else if ([elementName isEqualToString:@"index"])
3194- {
3195- self.index = NULL;
3196- }
3197-}
3198-
3199-
3200-- (void) dealloc
3201-{
3202- self.artist = NULL;
3203- self.index = NULL;
3204- RELEASE_SAFELY(groupedArtists);
3205- RELEASE_SAFELY(artists);
3206- RELEASE_SAFELY(indexes);
3207- [super dealloc];
3208-}
3209-
3210 @end
3211\ No newline at end of file
3212
3213=== modified file 'musicstreaming/utilities/ArtistParser.m'
3214--- musicstreaming/utilities/ArtistParser.m 2011-05-02 07:19:18 +0000
3215+++ musicstreaming/utilities/ArtistParser.m 2011-06-13 16:58:33 +0000
3216@@ -68,20 +68,17 @@
3217
3218 if (nil == album)
3219 {
3220- Album *album = [NSEntityDescription insertNewObjectForEntityForName:@"Album" inManagedObjectContext:PerThreadManagedObjectContext()];
3221+ album = [NSEntityDescription insertNewObjectForEntityForName:@"Album" inManagedObjectContext:PerThreadManagedObjectContext()];
3222
3223 album.title = [attributeDict objectForKey:@"title"];
3224 album.albumId = [attributeDict objectForKey:@"id"];
3225-
3226- if ([attributeDict objectForKey:@"coverArt"])
3227- {
3228- album.coverArtId = [attributeDict objectForKey:@"coverArt"];
3229- }
3230+ album.coverArtId = [attributeDict objectForKey:@"coverArt"];
3231
3232 [artist addAlbumsObject:album];
3233 SaveContext();
3234 }
3235- else if (nil == album.artistEntity)
3236+
3237+ if (nil == album.artistEntity)
3238 {
3239 album.artistEntity = artist;
3240 SaveContext();
3241
3242=== modified file 'musicstreaming/utilities/AudioStreamer.h'
3243--- musicstreaming/utilities/AudioStreamer.h 2010-10-01 22:26:06 +0000
3244+++ musicstreaming/utilities/AudioStreamer.h 2011-06-13 16:58:33 +0000
3245@@ -23,7 +23,7 @@
3246
3247 #define LOG_QUEUED_BUFFERS 0
3248
3249-#define kNumAQBufs 32 // Number of audio queue buffers we allocate.
3250+#define kNumAQBufs 64 // Number of audio queue buffers we allocate.
3251 // Needs to be big enough to keep audio pipeline
3252 // busy (non-zero number of queued buffers) but
3253 // not so big that audio takes too long to begin
3254@@ -100,6 +100,7 @@
3255
3256 @interface AudioStreamer : NSObject
3257 {
3258+ UIBackgroundTaskIdentifier bgTaskId;
3259 NSURL *url;
3260
3261 //
3262@@ -155,6 +156,9 @@
3263 // time)
3264 double packetDuration; // sample rate times frames per packet
3265 double lastProgress; // last calculated progress point
3266+
3267+ // Flag to indicate if we are playing fixed length files.
3268+ BOOL fixedLength;
3269 }
3270
3271 @property AudioStreamerErrorCode errorCode;
3272@@ -165,6 +169,7 @@
3273 @property (readonly) NSDictionary *httpHeaders;
3274
3275 - (id)initWithURL:(NSURL *)aURL;
3276+- (id)initWithFileURL:(NSURL*)aURL;
3277 - (void)start;
3278 - (void)stop;
3279 - (void)stop:(BOOL)force;
3280
3281=== modified file 'musicstreaming/utilities/AudioStreamer.m'
3282--- musicstreaming/utilities/AudioStreamer.m 2010-10-01 22:26:06 +0000
3283+++ musicstreaming/utilities/AudioStreamer.m 2011-06-13 16:58:33 +0000
3284@@ -225,12 +225,25 @@
3285 self = [super init];
3286 if (self != nil)
3287 {
3288+ fixedLength = NO;
3289 url = [aURL retain];
3290 }
3291 return self;
3292 }
3293
3294 //
3295+// initWithFileURL
3296+//
3297+// Initialize with a local MP3
3298+//
3299+- (id)initWithFileURL:(NSURL*)aURL
3300+{
3301+ [self initWithURL:aURL];
3302+ fixedLength = YES;
3303+ return self;
3304+}
3305+
3306+//
3307 // dealloc
3308 //
3309 // Releases instance memory.
3310@@ -620,57 +633,64 @@
3311 @"File stream download must be started on the internalThread");
3312 NSAssert(stream == nil, @"Download stream already initialized");
3313
3314- //
3315- // Create the HTTP GET request
3316- //
3317- CFHTTPMessageRef message= CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);
3318-
3319- //
3320- // If we are creating this request to seek to a location, set the
3321- // requested byte range in the headers.
3322- //
3323- if (fileLength > 0 && seekByteOffset > 0)
3324- {
3325- CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"),
3326- (CFStringRef)[NSString stringWithFormat:@"bytes=%ld-%ld", seekByteOffset, fileLength]);
3327- discontinuous = YES;
3328- }
3329-
3330- //
3331- // Create the read stream that will receive data from the HTTP request
3332- //
3333- stream = CFReadStreamCreateForHTTPRequest(NULL, message);
3334- CFRelease(message);
3335-
3336- //
3337- // Enable stream redirection
3338- //
3339- if (CFReadStreamSetProperty(
3340- stream,
3341- kCFStreamPropertyHTTPShouldAutoredirect,
3342- kCFBooleanTrue) == false)
3343- {
3344- [self presentAlertWithTitle:NSLocalizedStringFromTable(@"File Error", @"Errors", nil)
3345- message:NSLocalizedStringFromTable(@"Unable to configure network read stream.", @"Errors", nil)];
3346- return NO;
3347- }
3348-
3349- //
3350- // Handle SSL connections
3351- //
3352- if( [[url absoluteString] rangeOfString:@"https"].location != NSNotFound )
3353- {
3354- NSDictionary *sslSettings =
3355- [NSDictionary dictionaryWithObjectsAndKeys:
3356- (NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
3357- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
3358- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
3359- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
3360- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
3361- [NSNull null], kCFStreamSSLPeerName,
3362- nil];
3363-
3364- CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, sslSettings);
3365+ if (fixedLength)
3366+ {
3367+ stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, (CFURLRef)url);
3368+ }
3369+ else
3370+ {
3371+ //
3372+ // Create the HTTP GET request
3373+ //
3374+ CFHTTPMessageRef message= CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);
3375+
3376+ //
3377+ // If we are creating this request to seek to a location, set the
3378+ // requested byte range in the headers.
3379+ //
3380+ if (fileLength > 0 && seekByteOffset > 0)
3381+ {
3382+ CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"),
3383+ (CFStringRef)[NSString stringWithFormat:@"bytes=%ld-%ld", seekByteOffset, fileLength]);
3384+ discontinuous = YES;
3385+ }
3386+
3387+ //
3388+ // Create the read stream that will receive data from the HTTP request
3389+ //
3390+ stream = CFReadStreamCreateForHTTPRequest(NULL, message);
3391+ CFRelease(message);
3392+
3393+ //
3394+ // Enable stream redirection
3395+ //
3396+ if (CFReadStreamSetProperty(
3397+ stream,
3398+ kCFStreamPropertyHTTPShouldAutoredirect,
3399+ kCFBooleanTrue) == false)
3400+ {
3401+ [self presentAlertWithTitle:NSLocalizedStringFromTable(@"File Error", @"Errors", nil)
3402+ message:NSLocalizedStringFromTable(@"Unable to configure network read stream.", @"Errors", nil)];
3403+ return NO;
3404+ }
3405+
3406+ //
3407+ // Handle SSL connections
3408+ //
3409+ if( [[url absoluteString] rangeOfString:@"https"].location != NSNotFound )
3410+ {
3411+ NSDictionary *sslSettings =
3412+ [NSDictionary dictionaryWithObjectsAndKeys:
3413+ (NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
3414+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
3415+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
3416+ [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
3417+ [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
3418+ [NSNull null], kCFStreamSSLPeerName,
3419+ nil];
3420+
3421+ CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, sslSettings);
3422+ }
3423 }
3424
3425 //
3426@@ -1111,6 +1131,7 @@
3427 else if (state == AS_PAUSED)
3428 {
3429 err = AudioQueueStart(audioQueue, NULL);
3430+ bgTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
3431 if (err)
3432 {
3433 [self failWithErrorCode:AS_AUDIO_QUEUE_START_FAILED];
3434@@ -1272,19 +1293,21 @@
3435 {
3436 if (!httpHeaders)
3437 {
3438- CFTypeRef message =
3439- CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
3440- httpHeaders =
3441- (NSDictionary *)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)message);
3442- CFRelease(message);
3443+ CFTypeRef message = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
3444
3445- //
3446- // Only read the content length if we seeked to time zero, otherwise
3447- // we only have a subset of the total bytes.
3448- //
3449- if (seekByteOffset == 0)
3450+ if (message)
3451 {
3452- fileLength = [[httpHeaders objectForKey:@"Content-Length"] integerValue];
3453+ httpHeaders = (NSDictionary *)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)message);
3454+ CFRelease(message);
3455+
3456+ //
3457+ // Only read the content length if we seeked to time zero, otherwise
3458+ // we only have a subset of the total bytes.
3459+ //
3460+ if (seekByteOffset == 0)
3461+ {
3462+ fileLength = [[httpHeaders objectForKey:@"Content-Length"] integerValue];
3463+ }
3464 }
3465 }
3466
3467@@ -1415,6 +1438,7 @@
3468 if (self.state == AS_BUFFERING)
3469 {
3470 err = AudioQueueStart(audioQueue, NULL);
3471+ bgTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
3472 if (err)
3473 {
3474 [self failWithErrorCode:AS_AUDIO_QUEUE_START_FAILED];
3475@@ -1427,6 +1451,7 @@
3476 self.state = AS_WAITING_FOR_QUEUE_TO_START;
3477
3478 err = AudioQueueStart(audioQueue, NULL);
3479+ bgTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
3480 if (err)
3481 {
3482 [self failWithErrorCode:AS_AUDIO_QUEUE_START_FAILED];
3483@@ -1888,6 +1913,8 @@
3484 propertyID:(AudioQueuePropertyID)inID
3485 {
3486 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3487+ UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
3488+
3489
3490 @synchronized(self)
3491 {
3492@@ -1914,9 +1941,17 @@
3493 // thread destruction order and seems to avoid this crash bug -- or
3494 // at least I haven't had it since (nasty hard to reproduce error!)
3495 //
3496+
3497+ newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
3498 [NSRunLoop currentRunLoop];
3499
3500 self.state = AS_PLAYING;
3501+
3502+ if (bgTaskId != UIBackgroundTaskInvalid) {
3503+ [[UIApplication sharedApplication] endBackgroundTask: bgTaskId];
3504+ }
3505+
3506+ bgTaskId = newTaskId;
3507 }
3508 else
3509 {
3510
3511=== removed file 'musicstreaming/utilities/DataCache.h'
3512--- musicstreaming/utilities/DataCache.h 2011-03-29 14:22:42 +0000
3513+++ musicstreaming/utilities/DataCache.h 1970-01-01 00:00:00 +0000
3514@@ -1,29 +0,0 @@
3515-//
3516-// DataCache.h
3517-// iSub
3518-//
3519-// Created by Aaron Brethorst on 9/23/10.
3520-// Copyright 2010 Canonical Ltd.
3521-//
3522-// This program is free software: you can redistribute it and/or modify it
3523-// under the terms of the GNU Affero General Public License version 3,
3524-// as published by the Free Software Foundation.
3525-//
3526-// This program is distributed in the hope that it will be useful, but
3527-// WITHOUT ANY WARRANTY; without even the implied warranties of
3528-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3529-// PURPOSE. See the GNU Affero General Public License for more details.
3530-//
3531-// You should have received a copy of the GNU Affero General Public License
3532-// along with this program. If not, see <http://www.gnu.org/licenses/>.
3533-
3534-#import <Foundation/Foundation.h>
3535-
3536-@interface DataCache : NSObject
3537-{
3538- NSMutableDictionary *cacheDictionary;
3539-}
3540-+ (DataCache*)sharedDataCache;
3541-- (void)setObject:(id<NSCoding>)object forKey:(NSString*)key;
3542-- (id)objectForKey:(NSString*)key;
3543-@end
3544
3545=== removed file 'musicstreaming/utilities/DataCache.m'
3546--- musicstreaming/utilities/DataCache.m 2011-03-29 14:22:42 +0000
3547+++ musicstreaming/utilities/DataCache.m 1970-01-01 00:00:00 +0000
3548@@ -1,56 +0,0 @@
3549-//
3550-// DataCache.m
3551-// iSub
3552-//
3553-// Created by Aaron Brethorst on 9/23/10.
3554-// Copyright 2010 Canonical Ltd.
3555-//
3556-// This program is free software: you can redistribute it and/or modify it
3557-// under the terms of the GNU Affero General Public License version 3,
3558-// as published by the Free Software Foundation.
3559-//
3560-// This program is distributed in the hope that it will be useful, but
3561-// WITHOUT ANY WARRANTY; without even the implied warranties of
3562-// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3563-// PURPOSE. See the GNU Affero General Public License for more details.
3564-//
3565-// You should have received a copy of the GNU Affero General Public License
3566-// along with this program. If not, see <http://www.gnu.org/licenses/>.
3567-
3568-#import "DataCache.h"
3569-#import "SynthesizeSingleton.h"
3570-
3571-@implementation DataCache
3572-
3573-SYNTHESIZE_SINGLETON_FOR_CLASS(DataCache)
3574-
3575-- (id)init
3576-{
3577- if ((self = [super init]))
3578- {
3579- cacheDictionary = [[NSMutableDictionary alloc] init];
3580- }
3581- return self;
3582-}
3583-
3584-- (void)setObject:(id<NSCoding>)object forKey:(NSString*)key
3585-{
3586- NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
3587- [cacheDictionary setObject:data forKey:key];
3588-}
3589-
3590-- (id)objectForKey:(NSString*)key
3591-{
3592- NSData *data = [cacheDictionary objectForKey:key];
3593-
3594- if (data)
3595- {
3596- return [NSKeyedUnarchiver unarchiveObjectWithData:data];
3597- }
3598- else
3599- {
3600- return nil;
3601- }
3602-}
3603-
3604-@end
3605
3606=== added file 'musicstreaming/utilities/Globals.h'
3607--- musicstreaming/utilities/Globals.h 1970-01-01 00:00:00 +0000
3608+++ musicstreaming/utilities/Globals.h 2011-06-13 16:58:33 +0000
3609@@ -0,0 +1,21 @@
3610+//
3611+// Globals.h
3612+// iSub
3613+//
3614+// Created by Aaron Brethorst on 5/13/11.
3615+// Copyright 2011 Canonical. All rights reserved.
3616+//
3617+
3618+#import <Foundation/Foundation.h>
3619+
3620+NSURL* ApplicationDocumentsDirectory( void );
3621+NSURL* DatabaseFile( void );
3622+BOOL CacheContainsFile(NSString *aFilePath);
3623+NSURL* CachedMusicDirectory( void );
3624+NSString* CachedMusicPathForFilename(NSString *aFileName);
3625+NSURL* CachedAlbumArtDirectory( void );
3626+NSString* CachedAlbumArtPathForArtId(NSString *anArtId);
3627+
3628+extern NSString * const NOTIF_downloadComplete;
3629+extern NSString * const NOTIF_removeCachedContent;
3630+extern NSString * const NOTIF_reloadAccountCredentials;
3631
3632=== added file 'musicstreaming/utilities/Globals.m'
3633--- musicstreaming/utilities/Globals.m 1970-01-01 00:00:00 +0000
3634+++ musicstreaming/utilities/Globals.m 2011-06-13 16:58:33 +0000
3635@@ -0,0 +1,51 @@
3636+//
3637+// Globals.m
3638+// iSub
3639+//
3640+// Created by Aaron Brethorst on 5/13/11.
3641+// Copyright 2011 Canonical. All rights reserved.
3642+//
3643+
3644+#import "Globals.h"
3645+
3646+NSURL* ApplicationDocumentsDirectory( void )
3647+{
3648+ return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
3649+}
3650+
3651+NSURL* DatabaseFile( void )
3652+{
3653+ NSURL* docs = ApplicationDocumentsDirectory();
3654+ return [docs URLByAppendingPathComponent:@"music.sqlite"];
3655+}
3656+
3657+BOOL CacheContainsFile(NSString *aFilePath)
3658+{
3659+ return [[NSFileManager defaultManager] fileExistsAtPath:aFilePath];
3660+}
3661+
3662+NSURL* CachedMusicDirectory( void )
3663+{
3664+ NSURL* docs = ApplicationDocumentsDirectory();
3665+ return [docs URLByAppendingPathComponent:@"cached_music"];
3666+}
3667+
3668+NSString* CachedMusicPathForFilename(NSString *aFileName)
3669+{
3670+ return [[CachedMusicDirectory() path] stringByAppendingPathComponent:aFileName];
3671+}
3672+
3673+NSURL* CachedAlbumArtDirectory( void )
3674+{
3675+ NSURL* docs = ApplicationDocumentsDirectory();
3676+ return [docs URLByAppendingPathComponent:@"cached_album_art"];
3677+}
3678+
3679+NSString* CachedAlbumArtPathForArtId(NSString *anArtId)
3680+{
3681+ return [[CachedAlbumArtDirectory() path] stringByAppendingPathComponent:anArtId];
3682+}
3683+
3684+NSString * const NOTIF_downloadComplete = @"DownloadComplete";
3685+NSString * const NOTIF_removeCachedContent = @"RemoveCachedContent";
3686+NSString * const NOTIF_reloadAccountCredentials = @"ReloadAccountCredentials";
3687
3688=== modified file 'musicstreaming/utilities/PlaylistListParser.h'
3689--- musicstreaming/utilities/PlaylistListParser.h 2011-04-07 16:11:14 +0000
3690+++ musicstreaming/utilities/PlaylistListParser.h 2011-06-13 16:58:33 +0000
3691@@ -21,7 +21,5 @@
3692
3693 @interface PlaylistListParser : NSObject <NSXMLParserDelegate>
3694 {
3695- NSMutableArray *names;
3696 }
3697-@property(nonatomic,retain) NSMutableArray *names;
3698 @end
3699
3700=== modified file 'musicstreaming/utilities/PlaylistListParser.m'
3701--- musicstreaming/utilities/PlaylistListParser.m 2011-04-07 20:51:01 +0000
3702+++ musicstreaming/utilities/PlaylistListParser.m 2011-06-13 16:58:33 +0000
3703@@ -22,18 +22,6 @@
3704
3705 @implementation PlaylistListParser
3706
3707-@synthesize names;
3708-
3709-- (id)init
3710-{
3711- if ((self = [super init]))
3712- {
3713- names = [[NSMutableArray alloc] init];
3714- }
3715- return self;
3716-}
3717-
3718-
3719 - (void) subsonicErrorCode:(NSString *)errorCode message:(NSString *)message
3720 {
3721 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", @"") otherButtonTitles:nil];
3722@@ -49,22 +37,16 @@
3723 }
3724 else if ([elementName isEqualToString:@"playlist"])
3725 {
3726- Playlist *playlist = [[[Playlist alloc] init] autorelease];
3727- playlist.name = [attributeDict objectForKey:@"name"];
3728- playlist.playlistId = [attributeDict objectForKey:@"id"];
3729-
3730- if (![playlist.name isEqual:@".AppleDouble"])
3731+ if (![Playlist playlistWithIdExists:[attributeDict objectForKey:@"id"]] && ![[attributeDict objectForKey:@"title"] isEqual:@".AppleDouble"])
3732 {
3733- [names addObject:playlist];
3734- }
3735- }
3736-}
3737-
3738-
3739-- (void) dealloc
3740-{
3741- RELEASE_SAFELY(names);
3742- [super dealloc];
3743+ Playlist *playlist = [NSEntityDescription insertNewObjectForEntityForName:@"Playlist" inManagedObjectContext:PerThreadManagedObjectContext()];
3744+
3745+ playlist.name = [attributeDict objectForKey:@"name"];
3746+ playlist.playlistId = [attributeDict objectForKey:@"id"];
3747+
3748+ SaveContext();
3749+ }
3750+ }
3751 }
3752
3753 @end
3754
3755=== modified file 'musicstreaming/utilities/PlaylistParser.h'
3756--- musicstreaming/utilities/PlaylistParser.h 2011-04-13 15:27:34 +0000
3757+++ musicstreaming/utilities/PlaylistParser.h 2011-06-13 16:58:33 +0000
3758@@ -19,15 +19,14 @@
3759
3760 #import <Foundation/Foundation.h>
3761
3762+@class Playlist;
3763+
3764 @interface PlaylistParser : NSObject <NSXMLParserDelegate>
3765 {
3766- NSString *playlistId;
3767- NSString *name;
3768- NSMutableArray *contents;
3769- NSMutableArray *songIds;
3770+ Playlist *playlist;
3771+ NSMutableArray *songs;
3772+ NSNumberFormatter *numberFormatter;
3773 }
3774-@property(nonatomic,retain) NSString *playlistId;
3775-@property(nonatomic,retain) NSString *name;
3776-@property(nonatomic,retain) NSMutableArray *contents;
3777-@property(nonatomic,retain) NSMutableArray *songIds;
3778+@property(nonatomic,retain) NSManagedObjectID *playlistObjectId;
3779+@property(nonatomic,retain) NSMutableArray *songs;
3780 @end
3781
3782=== modified file 'musicstreaming/utilities/PlaylistParser.m'
3783--- musicstreaming/utilities/PlaylistParser.m 2011-04-13 15:27:34 +0000
3784+++ musicstreaming/utilities/PlaylistParser.m 2011-06-13 16:58:33 +0000
3785@@ -18,25 +18,24 @@
3786 // along with this program. If not, see <http://www.gnu.org/licenses/>.
3787
3788 #import "PlaylistParser.h"
3789+#import "Playlist.h"
3790 #import "Song.h"
3791-
3792+#import "NSDictionary+Extras.h"
3793
3794 @implementation PlaylistParser
3795-@synthesize playlistId, name, contents, songIds;
3796+@synthesize songs;
3797+@dynamic playlistObjectId;
3798
3799 - (id)init
3800 {
3801 if ((self = [super init]))
3802 {
3803- playlistId = nil;
3804- name = nil;
3805- contents = [[NSMutableArray alloc] init];
3806- songIds = [[NSMutableArray alloc] init];
3807+ songs = [[NSMutableArray alloc] init];
3808+ numberFormatter = [[NSNumberFormatter alloc] init];
3809 }
3810 return self;
3811 }
3812
3813-
3814 - (void) subsonicErrorCode:(NSString *)errorCode message:(NSString *)message
3815 {
3816 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", @"") otherButtonTitles:nil];
3817@@ -50,32 +49,61 @@
3818 {
3819 [self subsonicErrorCode:[attributeDict objectForKey:@"code"] message:[attributeDict objectForKey:@"message"]];
3820 }
3821- else if ([elementName isEqualToString:@"playlist"])
3822- {
3823- if (![[attributeDict objectForKey:@"name"] isEqual:@".AppleDouble"])
3824+ else if ([elementName isEqualToString:@"entry"])
3825+ {
3826+ Song *song = [Song songWithId:[attributeDict objectForKey:@"id"]];
3827+
3828+ if (nil == song)
3829 {
3830- self.playlistId = [attributeDict objectForKey:@"id"];
3831- self.name = [attributeDict objectForKey:@"name"];
3832+ song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:PerThreadManagedObjectContext()];
3833+
3834+ song.title = [attributeDict nilifiedValueForKey:@"title"];
3835+ song.songId = [attributeDict nilifiedValueForKey:@"id"];
3836+ song.artist = [attributeDict nilifiedValueForKey:@"artist"];
3837+
3838+ song.album = [attributeDict nilifiedValueForKey:@"album"];
3839+ song.genre = [attributeDict nilifiedValueForKey:@"genre"];
3840+
3841+ song.coverArtId = [attributeDict nilifiedValueForKey:@"coverArt"];
3842+
3843+ song.path = [attributeDict nilifiedValueForKey:@"path"];
3844+
3845+ song.duration = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"duration"]];
3846+ song.bitRate = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"bitRate"]];
3847+ song.track = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"track"]];
3848+ song.year = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"year"]];
3849+ song.size = [numberFormatter numberFromString:[attributeDict nilifiedValueForKey:@"size"]];
3850+
3851 }
3852- }
3853- else if ([elementName isEqualToString:@"entry"])
3854- {
3855- Song *song = [[Song alloc] initWithDictionary:attributeDict];
3856- [contents addObject:song];
3857- [songIds addObject:song.songId];
3858- [song release];
3859+
3860+ // do something with the order of songs.
3861+ [song addPlaylistEntitiesObject:playlist];
3862+ [playlist addSongsObject:song];
3863+ SaveContext();
3864 }
3865 }
3866
3867-
3868 - (void) dealloc
3869 {
3870- playlistId = NULL;
3871- name = NULL;
3872- RELEASE_SAFELY(contents);
3873- RELEASE_SAFELY(songIds);
3874+ [numberFormatter release];
3875+ [playlist release];
3876+ [songs release];
3877 [super dealloc];
3878 }
3879
3880+#pragma mark -
3881+#pragma mark Accessors
3882+
3883+- (void)setPlaylistObjectId:(NSManagedObjectID *)oid
3884+{
3885+ Playlist *aPlaylist = [[PerThreadManagedObjectContext() objectWithID:oid] retain];
3886+ [playlist release];
3887+ playlist = aPlaylist;
3888+}
3889+
3890+- (NSManagedObjectID*)playlistObjectID
3891+{
3892+ return [playlist objectID];
3893+}
3894
3895 @end
3896
3897=== modified file 'musicstreaming/utilities/StreamingPlayer.m'
3898--- musicstreaming/utilities/StreamingPlayer.m 2011-04-08 19:47:46 +0000
3899+++ musicstreaming/utilities/StreamingPlayer.m 2011-06-13 16:58:33 +0000
3900@@ -154,7 +154,17 @@
3901 {
3902 [self destroyStreamer];
3903
3904- AudioStreamer *as = [[AudioStreamer alloc] initWithURL:songUrl];
3905+ AudioStreamer *as = nil;
3906+
3907+ if ([self.currentSong cachedSongExists])
3908+ {
3909+ as = [[AudioStreamer alloc] initWithFileURL:[self.currentSong cachedSongURL]];
3910+ }
3911+ else
3912+ {
3913+ as = [[AudioStreamer alloc] initWithURL:songUrl];
3914+ }
3915+
3916 self.streamer = as;
3917 [as release];
3918 #if DEBUG_STREAMER_RETAIN
3919
3920=== modified file 'musicstreaming/utilities/Subsonic.m'
3921--- musicstreaming/utilities/Subsonic.m 2011-04-19 01:50:55 +0000
3922+++ musicstreaming/utilities/Subsonic.m 2011-06-13 16:58:33 +0000
3923@@ -36,7 +36,9 @@
3924 if ((self = [super init]))
3925 {
3926 self.defaultURL = @"https://streaming.one.ubuntu.com/rest";
3927+// self.defaultURL = @"http://10.59.1.19:35204/musicstreaming/rest";
3928 self.credsURL = @"https://one.ubuntu.com/phones/creds/ios?scheme=x-ubuntuone-music";
3929+// self.credsURL = @"http://10.59.1.19:41858/phones/creds/ios?scheme=x-ubuntuone-music";
3930 }
3931 return self;
3932 }
3933@@ -105,7 +107,9 @@
3934 [params addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"v", @"1.1.0", nil]];
3935 [params addKeyValueObjectFromArray:[NSArray arrayWithObjects:@"c", @"iSub", nil]];
3936
3937- return [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@?%@", self.defaultURL, action, [params urlEncodedString]]];
3938+ NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@?%@", self.defaultURL, action, [params urlEncodedString]]];
3939+ NSLog(@"URL generated: %@", url);
3940+ return url;
3941 }
3942 else
3943 {
3944
3945=== added directory 'musicstreaming/utilities/operations'
3946=== added file 'musicstreaming/utilities/operations/AbstractNetworkOperation.h'
3947--- musicstreaming/utilities/operations/AbstractNetworkOperation.h 1970-01-01 00:00:00 +0000
3948+++ musicstreaming/utilities/operations/AbstractNetworkOperation.h 2011-06-13 16:58:33 +0000
3949@@ -0,0 +1,35 @@
3950+//
3951+// AbstractNetworkOperation.h
3952+// iSub
3953+//
3954+// Created by Aaron Brethorst on 5/12/11.
3955+// Copyright 2011 Canonical. All rights reserved.
3956+//
3957+
3958+#import <Foundation/Foundation.h>
3959+
3960+@interface AbstractNetworkOperation : NSOperation
3961+{
3962+ NSURL * _url;
3963+ NSURLConnection * _connection;
3964+ NSInteger _statusCode;
3965+ NSError * _error;
3966+
3967+ BOOL _isExecuting;
3968+ BOOL _isFinished;
3969+}
3970+@property (readonly, copy) NSURL * url;
3971+@property (readonly) NSInteger statusCode;
3972+@property (readonly, retain) NSError * error;
3973+
3974+@property (readonly) BOOL isExecuting;
3975+@property (readonly) BOOL isFinished;
3976+
3977+- (id)initWithUrl:(NSURL *)url;
3978+
3979+//NSURLConnection delegate methods (for subclasses to implement)
3980+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
3981+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
3982+- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
3983+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
3984+@end
3985
3986=== added file 'musicstreaming/utilities/operations/AbstractNetworkOperation.m'
3987--- musicstreaming/utilities/operations/AbstractNetworkOperation.m 1970-01-01 00:00:00 +0000
3988+++ musicstreaming/utilities/operations/AbstractNetworkOperation.m 2011-06-13 16:58:33 +0000
3989@@ -0,0 +1,116 @@
3990+//
3991+// AbstractNetworkOperation.m
3992+// iSub
3993+//
3994+// Created by Aaron Brethorst on 5/12/11.
3995+// Copyright 2011 Canonical. All rights reserved.
3996+//
3997+
3998+#import "AbstractNetworkOperation.h"
3999+
4000+@interface AbstractNetworkOperation ()
4001+- (void)finish;
4002+@end
4003+
4004+@implementation AbstractNetworkOperation
4005+@synthesize url = _url;
4006+@synthesize statusCode = _statusCode;
4007+@synthesize error = _error;
4008+@synthesize isExecuting = _isExecuting;
4009+@synthesize isFinished = _isFinished;
4010+
4011+- (id)initWithUrl:(NSURL *)url
4012+{
4013+ self = [super init];
4014+ if (self == nil)
4015+ return nil;
4016+
4017+ _url = [url copy];
4018+ _isExecuting = NO;
4019+ _isFinished = NO;
4020+
4021+ return self;
4022+}
4023+
4024+- (void)dealloc
4025+{
4026+ [_url release];
4027+ [_connection release];
4028+ [_error release];
4029+ [super dealloc];
4030+}
4031+
4032+- (BOOL)isConcurrent
4033+{
4034+ return YES;
4035+}
4036+
4037+- (void)start
4038+{
4039+ if (![NSThread isMainThread])
4040+ {
4041+ [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
4042+ return;
4043+ }
4044+
4045+ NSLog(@"operation for <%@> started.", _url);
4046+
4047+ [self willChangeValueForKey:@"isExecuting"];
4048+ _isExecuting = YES;
4049+ [self didChangeValueForKey:@"isExecuting"];
4050+
4051+ NSURLRequest * request = [NSURLRequest requestWithURL:_url];
4052+ _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
4053+ if (nil == _connection)
4054+ {
4055+ [self finish];
4056+ }
4057+}
4058+
4059+- (void)finish
4060+{
4061+ NSLog(@"operation for <%@> finished. "
4062+ @"status code: %d, error: %@",
4063+ _url, _statusCode, _error);
4064+
4065+ [_connection release];
4066+ _connection = nil;
4067+
4068+ [self willChangeValueForKey:@"isExecuting"];
4069+ [self willChangeValueForKey:@"isFinished"];
4070+
4071+ _isExecuting = NO;
4072+ _isFinished = YES;
4073+
4074+ [self didChangeValueForKey:@"isExecuting"];
4075+ [self didChangeValueForKey:@"isFinished"];
4076+}
4077+
4078+#pragma mark -
4079+#pragma mark NSURLConnection delegate
4080+
4081+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
4082+{
4083+ NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
4084+ _statusCode = [httpResponse statusCode];
4085+}
4086+
4087+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
4088+{
4089+ //no-op, subclasses must implement this.
4090+}
4091+
4092+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
4093+{
4094+ [self finish];
4095+}
4096+
4097+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
4098+{
4099+ _error = [error copy];
4100+ [self finish];
4101+}
4102+
4103+
4104+
4105+@end
4106
4107=== renamed file 'musicstreaming/utilities/ImageLoader.h' => 'musicstreaming/utilities/operations/AlbumArtLoader.h'
4108--- musicstreaming/utilities/ImageLoader.h 2011-05-05 16:52:14 +0000
4109+++ musicstreaming/utilities/operations/AlbumArtLoader.h 2011-06-13 16:58:33 +0000
4110@@ -1,5 +1,5 @@
4111 //
4112-// ImageLoader.h
4113+// AlbumArtLoader.h
4114 // iSub
4115 //
4116 // Created by Aaron Brethorst on 5/4/11.
4117@@ -7,25 +7,28 @@
4118 //
4119
4120 #import <UIKit/UIKit.h>
4121-#import "ImageLoadingOperation.h"
4122+#import "AlbumArtLoadingOperation.h"
4123
4124-@protocol ImageLoaderDelegate<NSObject>
4125+@protocol AlbumArtLoaderDelegate<NSObject>
4126 - (void)reloadImages;
4127 @optional
4128 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
4129 @end
4130
4131
4132-@interface ImageLoader : NSObject <ImageLoadingOperationDelegate>
4133+@interface AlbumArtLoader : NSObject <AlbumArtLoadingOperationDelegate>
4134 {
4135 NSMutableSet *downloadedURLs;
4136 NSMutableDictionary *downloadCache;
4137 NSOperationQueue *queue;
4138- id<ImageLoaderDelegate> delegate;
4139+ id<AlbumArtLoaderDelegate> delegate;
4140 int imageSize;
4141+ UIImage *defaultAlbumArt;
4142 }
4143 @property(nonatomic) int imageSize;
4144-@property(nonatomic,assign) id<ImageLoaderDelegate> delegate;
4145-- (UIImage*)imageForArtID:(NSString*)artID;
4146+@property(nonatomic,assign) id<AlbumArtLoaderDelegate> delegate;
4147+@property(nonatomic,retain) UIImage *defaultAlbumArt;
4148+- (UIImage*)imageForArtID:(NSString*)artID error:(NSError**)error;
4149 - (void)download:(NSString*)artID;
4150++ (NSString*)cacheSize;
4151 @end
4152
4153=== renamed file 'musicstreaming/utilities/ImageLoader.m' => 'musicstreaming/utilities/operations/AlbumArtLoader.m'
4154--- musicstreaming/utilities/ImageLoader.m 2011-05-05 16:52:14 +0000
4155+++ musicstreaming/utilities/operations/AlbumArtLoader.m 2011-06-13 16:58:33 +0000
4156@@ -1,30 +1,35 @@
4157 //
4158-// ImageLoader.m
4159+// AlbumArtLoader.m
4160 // iSub
4161 //
4162 // Created by Aaron Brethorst on 5/4/11.
4163 // Copyright 2011 Canonical. All rights reserved.
4164 //
4165
4166-#import "ImageLoader.h"
4167+#import "AlbumArtLoader.h"
4168 #import "Subsonic.h"
4169 #import "NSMutableSet+Extras.h"
4170 #import "UIImage+Resize.h"
4171
4172-@interface ImageLoader ()
4173+@interface AlbumArtLoader ()
4174+- (void)createAlbumArtDirectory;
4175 - (NSString*)URLFromCoverArtID:(NSString*)artID;
4176 @end
4177
4178
4179-@implementation ImageLoader
4180+@implementation AlbumArtLoader
4181 @synthesize delegate;
4182 @synthesize imageSize;
4183+@synthesize defaultAlbumArt;
4184
4185 - (id)init
4186 {
4187 if ((self = [super init]))
4188 {
4189- self.imageSize = 120;
4190+ [self createAlbumArtDirectory];
4191+
4192+ self.imageSize = 640; // Default to maximum size we need
4193+ self.defaultAlbumArt = [UIImage imageNamed:@"default-album-art"];
4194
4195 downloadCache = [[NSMutableDictionary alloc] init];
4196 downloadedURLs = [[NSMutableSet alloc] init];
4197@@ -39,6 +44,7 @@
4198 [downloadCache release];
4199 [downloadedURLs release];
4200 [queue release];
4201+ [defaultAlbumArt release];
4202
4203 [super dealloc];
4204 }
4205@@ -46,9 +52,16 @@
4206 #pragma mark -
4207 #pragma mark Public Methods
4208
4209-- (UIImage*)imageForArtID:(NSString*)artID
4210+- (UIImage*)imageForArtID:(NSString*)artID error:(NSError**)error
4211 {
4212- UIImage *image = [downloadCache objectForKey:[self URLFromCoverArtID:artID]];
4213+ NSString *path = CachedAlbumArtPathForArtId(artID);
4214+ UIImage *image = [UIImage imageWithContentsOfFile:path];
4215+
4216+ if (nil == image) {
4217+ // Cache fault: return the default image, and set the error so the caller can dispatch for an attempt at download
4218+ image = self.defaultAlbumArt;
4219+ *error = [NSError errorWithDomain:@"U1Music" code:1 userInfo:nil];
4220+ }
4221 return [image resizedImage:CGSizeMake(self.imageSize, self.imageSize) interpolationQuality:kCGInterpolationHigh];
4222 }
4223
4224@@ -67,7 +80,7 @@
4225 {
4226 [downloadedURLs addObject:url];
4227
4228- ImageLoadingOperation *operation = [[ImageLoadingOperation alloc] initWithUrl:url];
4229+ AlbumArtLoadingOperation *operation = [[AlbumArtLoadingOperation alloc] initWithArtId: artID URL:url];
4230 operation.delegate = self;
4231 [queue addOperation:operation];
4232 [operation release];
4233@@ -75,6 +88,30 @@
4234 }
4235 }
4236
4237++ (NSString*)cacheSize
4238+{
4239+ static int minBlock = 4096;
4240+
4241+ NSString *file = nil;
4242+ NSDictionary *fileAttrs = nil;
4243+
4244+ double totalSize = 0;
4245+
4246+ NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:[CachedAlbumArtDirectory() path]];
4247+
4248+ while (file = [enumerator nextObject])
4249+ {
4250+ fileAttrs = [enumerator fileAttributes];
4251+
4252+ if (![[fileAttrs valueForKey:NSFileType] isEqual:NSFileTypeDirectory])
4253+ {
4254+ totalSize += ((([[fileAttrs valueForKey:NSFileSize] unsignedIntValue] + minBlock - 1) / minBlock) * minBlock);
4255+ }
4256+ }
4257+
4258+ return [NSString stringWithFormat:@"%.2f MB",((totalSize / 1024.0f) / 1024.0f)];
4259+}
4260+
4261 #pragma mark -
4262 #pragma mark Private Methods
4263
4264@@ -90,14 +127,26 @@
4265 return [url absoluteString];
4266 }
4267
4268+- (void)createAlbumArtDirectory
4269+{
4270+ NSError *error = nil;
4271+ BOOL yn = [[NSFileManager defaultManager] createDirectoryAtPath:[CachedAlbumArtDirectory() path] withIntermediateDirectories:YES attributes:nil error:&error];
4272+
4273+ if (!yn || error)
4274+ {
4275+ NSLog(@"%@", error);
4276+ }
4277+}
4278+
4279 #pragma mark -
4280-#pragma mark ImageLoadingOperationDelegate
4281+#pragma mark AlbumArtLoadingOperationDelegate
4282
4283-- (void)imageLoaded:(UIImage*)image forURL:(NSURL*)URL
4284+- (void)imageLoaded:(UIImage*)image forArtId:(NSString *)artId
4285 {
4286- [downloadCache setObject:image forKey:[URL absoluteString]];
4287+ NSData *imageData = UIImagePNGRepresentation(image);
4288+ [imageData writeToFile:CachedAlbumArtPathForArtId(artId) atomically:YES];
4289
4290- if (self.delegate && [self.delegate respondsToSelector:@selector(reloadImages)])
4291+ if (self.delegate)
4292 {
4293 [self.delegate performSelectorOnMainThread:@selector(reloadImages) withObject:nil waitUntilDone:NO];
4294 }
4295
4296=== renamed file 'musicstreaming/utilities/ImageLoadingOperation.h' => 'musicstreaming/utilities/operations/AlbumArtLoadingOperation.h'
4297--- musicstreaming/utilities/ImageLoadingOperation.h 2011-05-05 07:27:41 +0000
4298+++ musicstreaming/utilities/operations/AlbumArtLoadingOperation.h 2011-06-13 16:58:33 +0000
4299@@ -1,5 +1,5 @@
4300 //
4301-// ImageLoadingOperation.h
4302+// AlbumArtLoadingOperation.h
4303 // iSub
4304 //
4305 // Created by Aaron Brethorst on 5/4/11.
4306@@ -7,36 +7,22 @@
4307 //
4308
4309 #import <UIKit/UIKit.h>
4310+#import "AbstractNetworkOperation.h"
4311
4312-@protocol ImageLoadingOperationDelegate<NSObject>
4313-- (void)imageLoaded:(UIImage*)image forURL:(NSURL*)URL;
4314+@protocol AlbumArtLoadingOperationDelegate<NSObject>
4315+- (void)imageLoaded:(UIImage*)image forArtId:(NSString *)artId;
4316 @end
4317
4318-
4319-@interface ImageLoadingOperation : NSOperation
4320-{
4321- NSURL * _url;
4322- NSURLConnection * _connection;
4323- NSInteger _statusCode;
4324- NSMutableData * _data;
4325- NSError * _error;
4326-
4327- BOOL _isExecuting;
4328- BOOL _isFinished;
4329-
4330- id<ImageLoadingOperationDelegate> delegate;
4331+@interface AlbumArtLoadingOperation : AbstractNetworkOperation
4332+{
4333+ NSMutableData * _data;
4334+ NSString *_artId;
4335+ id<AlbumArtLoadingOperationDelegate> delegate;
4336 }
4337-@property (nonatomic, assign) id<ImageLoadingOperationDelegate> delegate;
4338-@property (readonly, copy) NSURL * url;
4339-@property (readonly) NSInteger statusCode;
4340+@property (nonatomic, assign) id<AlbumArtLoadingOperationDelegate> delegate;
4341 @property (readonly, retain) NSData * data;
4342-@property (readonly, retain) NSError * error;
4343-
4344-@property (readonly) BOOL isExecuting;
4345-@property (readonly) BOOL isFinished;
4346-
4347-+ (id)imageLoaderWithUrlString:(NSString *)urlString;
4348-
4349-- (id)initWithUrl:(NSURL *)url;
4350+- (id)initWithArtId:(NSString *)artId URL:(NSURL *)url;
4351+
4352++ (id)AlbumArtLoaderWithUrlString:(NSString *)urlString;
4353
4354 @end
4355\ No newline at end of file
4356
4357=== renamed file 'musicstreaming/utilities/ImageLoadingOperation.m' => 'musicstreaming/utilities/operations/AlbumArtLoadingOperation.m'
4358--- musicstreaming/utilities/ImageLoadingOperation.m 2011-05-05 07:27:41 +0000
4359+++ musicstreaming/utilities/operations/AlbumArtLoadingOperation.m 2011-06-13 16:58:33 +0000
4360@@ -1,5 +1,5 @@
4361 //
4362-// ImageLoadingOperation.m
4363+// AlbumArtLoadingOperation.m
4364 // iSub
4365 //
4366 // Created by Aaron Brethorst on 5/4/11.
4367@@ -8,131 +8,68 @@
4368
4369 // adapted from http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/
4370
4371-#import "ImageLoadingOperation.h"
4372-
4373-@interface ImageLoadingOperation ()
4374-- (void)finish;
4375-@end
4376-
4377-@implementation ImageLoadingOperation
4378+#import "AlbumArtLoadingOperation.h"
4379+
4380+@implementation AlbumArtLoadingOperation
4381 @synthesize delegate;
4382-@synthesize url = _url;
4383-@synthesize statusCode = _statusCode;
4384 @synthesize data = _data;
4385-@synthesize error = _error;
4386-@synthesize isExecuting = _isExecuting;
4387-@synthesize isFinished = _isFinished;
4388
4389-+ (id)imageLoaderWithUrlString:(NSString *)urlString
4390++ (id)AlbumArtLoaderWithUrlString:(NSString *)urlString
4391 {
4392 NSURL * url = [NSURL URLWithString:urlString];
4393- ImageLoadingOperation * operation = [[self alloc] initWithUrl:url];
4394+ AlbumArtLoadingOperation * operation = [[self alloc] initWithUrl:url];
4395 return [operation autorelease];
4396 }
4397
4398-- (id)initWithUrl:(NSURL *)url
4399+- (id)initWithArtId:(NSString *)artId URL:(NSURL *)url
4400 {
4401- self = [super init];
4402+ self = [super initWithUrl:url];
4403 if (self == nil)
4404 return nil;
4405
4406- _url = [url copy];
4407- _isExecuting = NO;
4408- _isFinished = NO;
4409-
4410+ _artId = [artId retain];
4411 return self;
4412+
4413 }
4414
4415 - (void)dealloc
4416 {
4417- [_url release];
4418- [_connection release];
4419- [_data release];
4420- [_error release];
4421- [super dealloc];
4422-}
4423-
4424-- (BOOL)isConcurrent
4425-{
4426- return YES;
4427-}
4428-
4429-- (void)start
4430-{
4431- if (![NSThread isMainThread])
4432- {
4433- [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
4434- return;
4435- }
4436-
4437- NSLog(@"opeartion for <%@> started.", _url);
4438-
4439- [self willChangeValueForKey:@"isExecuting"];
4440- _isExecuting = YES;
4441- [self didChangeValueForKey:@"isExecuting"];
4442-
4443- NSURLRequest * request = [NSURLRequest requestWithURL:_url];
4444- _connection = [[NSURLConnection alloc] initWithRequest:request
4445- delegate:self];
4446- if (_connection == nil)
4447- [self finish];
4448-}
4449-
4450-- (void)finish
4451-{
4452- NSLog(@"operation for <%@> finished. "
4453- @"status code: %d, error: %@, data size: %u",
4454- _url, _statusCode, _error, [_data length]);
4455-
4456- [_connection release];
4457- _connection = nil;
4458-
4459- [self willChangeValueForKey:@"isExecuting"];
4460- [self willChangeValueForKey:@"isFinished"];
4461-
4462- _isExecuting = NO;
4463- _isFinished = YES;
4464-
4465- [self didChangeValueForKey:@"isExecuting"];
4466- [self didChangeValueForKey:@"isFinished"];
4467+ [_data release];
4468+ [_artId release];
4469+ [super dealloc];
4470 }
4471
4472 #pragma mark -
4473 #pragma mark NSURLConnection delegate
4474
4475-- (void)connection:(NSURLConnection *)connection
4476-didReceiveResponse:(NSURLResponse *)response
4477+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
4478 {
4479+ [super connection:connection didReceiveResponse:response];
4480+
4481 [_data release];
4482 _data = [[NSMutableData alloc] init];
4483-
4484- NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
4485- _statusCode = [httpResponse statusCode];
4486 }
4487
4488-- (void)connection:(NSURLConnection *)connection
4489- didReceiveData:(NSData *)data
4490+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
4491 {
4492+ [super connection:connection didReceiveData:data];
4493+
4494 [_data appendData:data];
4495 }
4496
4497 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
4498 {
4499- UIImage *image = [[UIImage alloc] initWithData:_data];
4500+ UIImage *image = [[[UIImage alloc] initWithData:_data] autorelease];
4501 if (image)
4502 {
4503- [self.delegate imageLoaded:image forURL:_url];
4504+ [self.delegate imageLoaded:image forArtId:_artId];
4505 }
4506- [self finish];
4507+ [super connectionDidFinishLoading:connection];
4508 }
4509
4510-- (void)connection:(NSURLConnection *)connection
4511- didFailWithError:(NSError *)error
4512+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
4513 {
4514- _error = [error copy];
4515- [self finish];
4516+ [super connection:connection didFailWithError:error];
4517 }
4518
4519-
4520-
4521 @end
4522
4523=== added file 'musicstreaming/utilities/operations/DownloadOperation.h'
4524--- musicstreaming/utilities/operations/DownloadOperation.h 1970-01-01 00:00:00 +0000
4525+++ musicstreaming/utilities/operations/DownloadOperation.h 2011-06-13 16:58:33 +0000
4526@@ -0,0 +1,32 @@
4527+//
4528+// DownloadOperation.h
4529+// iSub
4530+//
4531+// Created by Aaron Brethorst on 5/12/11.
4532+// Copyright 2011 Canonical. All rights reserved.
4533+//
4534+
4535+#import <UIKit/UIKit.h>
4536+#import "AbstractNetworkOperation.h"
4537+
4538+@protocol DownloadOperationDelegate
4539+- (void)songDownloadedToPath:(NSString*)path;
4540+@optional
4541+- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
4542+@end
4543+
4544+
4545+@interface DownloadOperation : AbstractNetworkOperation
4546+{
4547+ NSString *_filePath;
4548+ NSFileHandle *_fileHandle;
4549+
4550+ id<DownloadOperationDelegate> _delegate;
4551+}
4552+@property(nonatomic,retain) NSString *filePath;
4553+@property(nonatomic,assign) id<DownloadOperationDelegate> delegate;
4554+
4555++ (id)downloadOperationWithURL:(NSURL*)anURL filePath:(NSString*)aFilePath;
4556+- (id)initWithUrl:(NSURL*)anUrl filePath:(NSString*)aFilePath;
4557+
4558+@end
4559\ No newline at end of file
4560
4561=== added file 'musicstreaming/utilities/operations/DownloadOperation.m'
4562--- musicstreaming/utilities/operations/DownloadOperation.m 1970-01-01 00:00:00 +0000
4563+++ musicstreaming/utilities/operations/DownloadOperation.m 2011-06-13 16:58:33 +0000
4564@@ -0,0 +1,79 @@
4565+//
4566+// DownloadOperation.m
4567+// iSub
4568+//
4569+// Created by Aaron Brethorst on 5/12/11.
4570+// Copyright 2011 Canonical. All rights reserved.
4571+//
4572+
4573+#import "DownloadOperation.h"
4574+
4575+@implementation DownloadOperation
4576+@synthesize filePath = _filePath;
4577+@synthesize delegate = _delegate;
4578+
4579++ (id)downloadOperationWithURL:(NSURL*)anURL filePath:(NSString*)aFilePath
4580+{
4581+ DownloadOperation *operation = [[[DownloadOperation alloc] initWithUrl:anURL filePath:aFilePath] autorelease];
4582+ return operation;
4583+}
4584+
4585+- (id)initWithUrl:(NSURL*)anUrl filePath:(NSString*)aFilePath
4586+{
4587+ if ((self = [super initWithUrl:anUrl]))
4588+ {
4589+ self.delegate = nil;
4590+
4591+ self.filePath = aFilePath;
4592+
4593+ [[NSFileManager defaultManager] createFileAtPath:self.filePath contents:[NSData data] attributes:nil];
4594+ _fileHandle = [[NSFileHandle fileHandleForWritingAtPath:self.filePath] retain];
4595+ }
4596+ return self;
4597+}
4598+
4599+- (void)dealloc
4600+{
4601+ self.delegate = nil;
4602+ self.filePath = nil;
4603+ [_fileHandle release];
4604+ [super dealloc];
4605+}
4606+
4607+#pragma mark -
4608+#pragma mark NSURLConnection delegate
4609+
4610+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
4611+{
4612+ [super connection:connection didReceiveResponse:response];
4613+}
4614+
4615+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
4616+{
4617+ [super connection:connection didReceiveData:data];
4618+
4619+ [_fileHandle writeData:data];
4620+}
4621+
4622+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
4623+{
4624+ [_fileHandle closeFile];
4625+
4626+ if (nil != self.delegate)
4627+ {
4628+ [self.delegate performSelectorOnMainThread:@selector(songDownloadedToPath:) withObject:self.filePath waitUntilDone:NO];
4629+ }
4630+
4631+ [super connectionDidFinishLoading:connection];
4632+}
4633+
4634+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
4635+{
4636+ [_fileHandle closeFile];
4637+ [[NSFileManager defaultManager] removeItemAtPath:self.filePath error:nil];
4638+
4639+ [super connection:connection didFailWithError:error];
4640+}
4641+
4642+@end
4643+
4644
4645=== added file 'musicstreaming/utilities/operations/Downloader.h'
4646--- musicstreaming/utilities/operations/Downloader.h 1970-01-01 00:00:00 +0000
4647+++ musicstreaming/utilities/operations/Downloader.h 2011-06-13 16:58:33 +0000
4648@@ -0,0 +1,29 @@
4649+//
4650+// Downloader.h
4651+// iSub
4652+//
4653+// Created by Aaron Brethorst on 5/12/11.
4654+// Copyright 2011 Canonical. All rights reserved.
4655+//
4656+
4657+#import <UIKit/UIKit.h>
4658+#import "DownloadOperation.h"
4659+
4660+@class Artist, Album;
4661+
4662+@interface Downloader : NSObject <DownloadOperationDelegate>
4663+{
4664+ NSOperationQueue *contentEnumerationQueue;
4665+ NSOperationQueue *queue;
4666+ NSMutableSet *downloads;
4667+}
4668++ (Downloader*)sharedDownloader;
4669++ (NSString*)cacheSize;
4670+
4671+- (void)enqueueArtist:(Artist*)anArtist;
4672+- (void)enqueueAlbum:(Album*)anAlbum;
4673+- (void)downloadFile:(NSURL*)url withName:(NSString*)fileName;
4674+- (BOOL)isDownloading:(NSString *)fileName;
4675+
4676+extern NSString * const NOTIF_downloadComplete;
4677+@end
4678
4679=== added file 'musicstreaming/utilities/operations/Downloader.m'
4680--- musicstreaming/utilities/operations/Downloader.m 1970-01-01 00:00:00 +0000
4681+++ musicstreaming/utilities/operations/Downloader.m 2011-06-13 16:58:33 +0000
4682@@ -0,0 +1,188 @@
4683+//
4684+// Downloader.m
4685+// iSub
4686+//
4687+// Created by Aaron Brethorst on 5/12/11.
4688+// Copyright 2011 Canonical. All rights reserved.
4689+//
4690+
4691+#import "Downloader.h"
4692+#import "DownloadOperation.h"
4693+#import "SynthesizeSingleton.h"
4694+#import "Artist.h"
4695+#import "Album.h"
4696+#import "Song.h"
4697+#import "Subsonic.h"
4698+
4699+@interface Downloader ()
4700+- (void)createMusicDirectory;
4701+
4702+- (void)startArtistEnqueueing:(Artist*)anArtist;
4703+- (void)completeArtistEnqueueing:(Artist*)anArtist;
4704+
4705+- (void)startAlbumEnqueueing:(Album*)anAlbum;
4706+- (void)completeAlbumEnqueueing:(Album*)anAlbum;
4707+@end
4708+
4709+@implementation Downloader
4710+
4711+SYNTHESIZE_SINGLETON_FOR_CLASS(Downloader);
4712+
4713+- (id)init
4714+{
4715+ if ((self = [super init]))
4716+ {
4717+ [self createMusicDirectory];
4718+
4719+ downloads = [[NSMutableSet alloc] init];
4720+
4721+ queue = [[NSOperationQueue alloc] init];
4722+ [queue setSuspended:NO];
4723+
4724+ contentEnumerationQueue = [[NSOperationQueue alloc] init];
4725+ [contentEnumerationQueue setSuspended:NO];
4726+ }
4727+ return self;
4728+}
4729+
4730+- (void)dealloc
4731+{
4732+ [contentEnumerationQueue release];
4733+ [downloads release];
4734+ [queue release];
4735+
4736+ [super dealloc];
4737+}
4738+
4739+#pragma mark -
4740+#pragma mark Public Methods
4741+
4742+- (void)enqueueArtist:(Artist*)anArtist
4743+{
4744+ NSInvocationOperation *invOp = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startArtistEnqueueing:) object:anArtist] autorelease];
4745+ [contentEnumerationQueue addOperation:invOp];
4746+}
4747+
4748+- (void)enqueueAlbum:(Album*)anAlbum
4749+{
4750+ NSInvocationOperation *invOp = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startAlbumEnqueueing:) object:anAlbum] autorelease];
4751+ [contentEnumerationQueue addOperation:invOp];
4752+}
4753+
4754+- (void)downloadFile:(NSURL*)url withName:(NSString*)fileName
4755+{
4756+ @synchronized(self)
4757+ {
4758+ if ([downloads containsObject:fileName])
4759+ {
4760+ return;
4761+ }
4762+ else if (CacheContainsFile(CachedMusicPathForFilename(fileName)))
4763+ {
4764+ return;
4765+ }
4766+ else
4767+ {
4768+ [downloads addObject:fileName];
4769+ // Disable idle timer sleep while downloading
4770+ [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
4771+
4772+ DownloadOperation *download = [DownloadOperation downloadOperationWithURL:url
4773+ filePath:CachedMusicPathForFilename(fileName)];
4774+ download.delegate = self;
4775+ [queue addOperation:download];
4776+ }
4777+ }
4778+}
4779+
4780+- (BOOL)isDownloading:(NSString *)fileName
4781+{
4782+ return [downloads containsObject:fileName];
4783+}
4784+
4785++ (NSString*)cacheSize
4786+{
4787+ static int minBlock = 4096;
4788+
4789+ NSString *file = nil;
4790+ NSDictionary *fileAttrs = nil;
4791+
4792+ double totalSize = 0;
4793+
4794+ NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:[CachedMusicDirectory() path]];
4795+
4796+ while (file = [enumerator nextObject])
4797+ {
4798+ fileAttrs = [enumerator fileAttributes];
4799+
4800+ if (![[fileAttrs valueForKey:NSFileType] isEqual:NSFileTypeDirectory])
4801+ {
4802+ totalSize += ((([[fileAttrs valueForKey:NSFileSize] unsignedIntValue] + minBlock - 1) / minBlock) * minBlock);
4803+ }
4804+ }
4805+
4806+ return [NSString stringWithFormat:@"%.2f MB",((totalSize / 1024.0f) / 1024.0f)];
4807+}
4808+
4809+#pragma mark -
4810+#pragma mark DownloadOperationDelegate
4811+
4812+- (void)songDownloadedToPath:(NSString*)path
4813+{
4814+ [downloads removeObject:[path lastPathComponent]];
4815+ if ([downloads count] == 0)
4816+ {
4817+ // If downloads is empty, go back to idle timing for sleep
4818+ [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
4819+ }
4820+ // Coarse-grained, blanket notification that any download has completed.
4821+ [[NSNotificationCenter defaultCenter] postNotificationName:NOTIF_downloadComplete object:nil];
4822+}
4823+
4824+#pragma mark -
4825+#pragma mark Private Methods
4826+
4827+- (void)startArtistEnqueueing:(Artist*)anArtist
4828+{
4829+ [anArtist loadAlbums];
4830+ [self performSelectorOnMainThread:@selector(completeArtistEnqueueing:) withObject:anArtist waitUntilDone:NO];
4831+}
4832+
4833+- (void)completeArtistEnqueueing:(Artist*)anArtist
4834+{
4835+ [PerThreadManagedObjectContext() refreshObject:anArtist mergeChanges:YES]; //force the artist to reload its set of albums from the persistent store.
4836+
4837+ for (Album *a in anArtist.albums)
4838+ {
4839+ [self enqueueAlbum:a];
4840+ }
4841+}
4842+
4843+- (void)startAlbumEnqueueing:(Album*)anAlbum
4844+{
4845+ [anAlbum loadSongs];
4846+ [self performSelectorOnMainThread:@selector(completeAlbumEnqueueing:) withObject:anAlbum waitUntilDone:NO];
4847+}
4848+
4849+- (void)completeAlbumEnqueueing:(Album*)anAlbum
4850+{
4851+ [PerThreadManagedObjectContext() refreshObject:anAlbum mergeChanges:YES]; //force the album to reload its set of songs from the persistent store.
4852+
4853+ for (Song *s in anAlbum.songs)
4854+ {
4855+ [self downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:s.songId] withName:s.songId];
4856+ }
4857+}
4858+
4859+- (void)createMusicDirectory
4860+{
4861+ NSError *error = nil;
4862+ BOOL yn = [[NSFileManager defaultManager] createDirectoryAtPath:[CachedMusicDirectory() path] withIntermediateDirectories:YES attributes:nil error:&error];
4863+
4864+ if (!yn || error)
4865+ {
4866+ NSLog(@"%@", error);
4867+ }
4868+}
4869+
4870+@end
4871
4872=== modified file 'musicstreaming/view_controllers/AlbumListViewController.h'
4873--- musicstreaming/view_controllers/AlbumListViewController.h 2011-05-05 07:27:41 +0000
4874+++ musicstreaming/view_controllers/AlbumListViewController.h 2011-06-13 16:58:33 +0000
4875@@ -18,11 +18,11 @@
4876 // along with this program. If not, see <http://www.gnu.org/licenses/>.
4877
4878 #import "SubsonicIndexedTableViewController.h"
4879-#import "ImageLoader.h"
4880+#import "AlbumArtLoader.h"
4881
4882-@interface AlbumListViewController : SubsonicIndexedTableViewController <ImageLoaderDelegate>
4883+@interface AlbumListViewController : SubsonicIndexedTableViewController <AlbumArtLoaderDelegate>
4884 {
4885- ImageLoader *loader;
4886+ AlbumArtLoader *loader;
4887 }
4888 + (UINavigationController *)navigableViewController;
4889 @end
4890
4891=== modified file 'musicstreaming/view_controllers/AlbumListViewController.m'
4892--- musicstreaming/view_controllers/AlbumListViewController.m 2011-05-06 00:41:57 +0000
4893+++ musicstreaming/view_controllers/AlbumListViewController.m 2011-06-13 16:58:33 +0000
4894@@ -18,12 +18,11 @@
4895 // along with this program. If not, see <http://www.gnu.org/licenses/>.
4896
4897 #import "AlbumListViewController.h"
4898-#import "DataCache.h"
4899 #import "AlbumListParser.h"
4900 #import "AlbumArtistUITableViewCell.h"
4901 #import "Album.h"
4902 #import "AlbumViewController.h"
4903-#import "AsynchronousImageViewCached.h"
4904+#import "NSString+Extras.h"
4905
4906 @implementation AlbumListViewController
4907
4908@@ -32,6 +31,7 @@
4909 AlbumListViewController *albumListViewController = [[[AlbumListViewController alloc] initWithTitle:NSLocalizedString(@"Albums", @"")] autorelease];
4910 UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:albumListViewController] autorelease];
4911 nav.navigationBar.barStyle = UIBarStyleBlack;
4912+ nav.tabBarItem.image = [UIImage imageNamed:@"albums"];
4913 return nav;
4914 }
4915
4916@@ -42,7 +42,7 @@
4917 self.tableView.rowHeight = 60.0f;
4918 [super viewDidLoad];
4919
4920- loader = [[ImageLoader alloc] init];
4921+ loader = [[AlbumArtLoader alloc] init];
4922 loader.delegate = self;
4923 loader.imageSize = 120;
4924 }
4925@@ -63,7 +63,7 @@
4926
4927 for (Album *a in self.tableData)
4928 {
4929- NSString *index = [[a.title substringToIndex:1] uppercaseString];
4930+ NSString *index = [[a.title dearticlizedIndex] uppercaseString];
4931 NSMutableArray *group = [self.groupedTableData objectForKey:index];
4932
4933 if (nil == group)
4934@@ -149,29 +149,17 @@
4935 cell.albumNameLabel.text = a.title;
4936 cell.artistNameLabel.text = a.artist;
4937
4938- if (a.coverArtId)
4939- {
4940- UIImage *img = [loader imageForArtID:a.coverArtId];
4941-
4942- if (img)
4943- {
4944- cell.coverArtView.image = img;
4945- }
4946- else
4947- {
4948- cell.coverArtView.image = [UIImage imageNamed:@"default-album-art.png"];
4949-
4950- if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
4951- {
4952- [loader download:a.coverArtId];
4953- }
4954- }
4955- }
4956- else
4957- {
4958- cell.coverArtView.image = [UIImage imageNamed:@"default-album-art.png"];
4959- }
4960-
4961+ NSError *error = nil;
4962+ cell.coverArtView.image = [loader imageForArtID:a.coverArtId error:&error];
4963+
4964+ if ([error code]) {
4965+ // Let's see if we need to wait for non-dragging/decelerating
4966+// if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
4967+// {
4968+ [loader download:a.coverArtId];
4969+// }
4970+ }
4971+
4972 return cell;
4973 }
4974
4975@@ -214,11 +202,11 @@
4976 for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows])
4977 {
4978 Album *album = [self.tableData objectAtIndex:indexPath.row];
4979+ // This will prompt the album art to download, but doesn't try to load the image when it's done?
4980 [loader download:album.coverArtId];
4981 }
4982 }
4983
4984-
4985 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
4986 {
4987 [super scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
4988
4989=== modified file 'musicstreaming/view_controllers/AlbumViewController.h'
4990--- musicstreaming/view_controllers/AlbumViewController.h 2011-05-02 04:54:02 +0000
4991+++ musicstreaming/view_controllers/AlbumViewController.h 2011-06-13 16:58:33 +0000
4992@@ -19,15 +19,18 @@
4993
4994 #import <UIKit/UIKit.h>
4995 #import "SubsonicTableViewController.h"
4996-
4997-@class Artist, Album, AsynchronousImageViewCached;
4998-
4999-@interface AlbumViewController : SubsonicTableViewController
5000+#import "AlbumArtLoader.h"
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: