Merge lp:~rockstar/ubuntuone-ios-music/swipe-download into lp:ubuntuone-ios-music

Proposed by Paul Hummer
Status: Merged
Approved by: Zachery Bir
Approved revision: 241
Merged at revision: 225
Proposed branch: lp:~rockstar/ubuntuone-ios-music/swipe-download
Merge into: lp:ubuntuone-ios-music
Diff against target: 908 lines (+422/-196)
12 files modified
U1Music.xcodeproj/project.pbxproj (+36/-0)
categories/UIImage+RoundedCorner.m (+3/-0)
controls/PlaylistEditSongUITableViewCell.m (+0/-2)
controls/SongUITableView.h (+35/-0)
controls/SongUITableView.m (+178/-0)
controls/SongUITableViewCell.h (+4/-15)
controls/SongUITableViewCell.m (+5/-68)
controls/UOPullGestureRecognizer.h (+20/-0)
controls/UOPullGestureRecognizer.m (+77/-0)
view_controllers/AlbumViewController.h (+2/-2)
view_controllers/AlbumViewController.m (+60/-94)
view_controllers/PlaylistEditAlbumViewController.m (+2/-15)
To merge this branch: bzr merge lp:~rockstar/ubuntuone-ios-music/swipe-download
Reviewer Review Type Date Requested Status
Zachery Bir Approve
Review via email: mp+116403@code.launchpad.net

Commit message

Add swipe gesture to the SongUITableViewCell

Description of the change

This branch adds the ability to swipe to the right to download or clear the cache of a song. It moves all the logic from SongUIViewCell to a SongUIView.

The one thing from the design that I did not add is the removal of the three dots as handles. I disagree with design, and would like to continue that discussion before I completed remove them.

To post a comment you must log in.
Revision history for this message
Zachery Bir (urbanape) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'U1Music.xcodeproj/project.pbxproj'
2--- U1Music.xcodeproj/project.pbxproj 2012-06-28 02:52:49 +0000
3+++ U1Music.xcodeproj/project.pbxproj 2012-07-24 05:24:19 +0000
4@@ -10,6 +10,14 @@
5 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
6 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
7 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; };
8+ 523B3CDE15B4C42F004394F4 /* SongUITableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 523B3CDD15B4C42F004394F4 /* SongUITableView.m */; };
9+ 523B3CE215B5D64F004394F4 /* grabber.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CE015B5D64F004394F4 /* grabber.png */; };
10+ 523B3CE315B5D64F004394F4 /* grabber@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CE115B5D64F004394F4 /* grabber@2x.png */; };
11+ 523B3CE615B5D814004394F4 /* UOPullGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 523B3CE515B5D814004394F4 /* UOPullGestureRecognizer.m */; };
12+ 523B3CF915B73BA0004394F4 /* download-grey.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF515B73BA0004394F4 /* download-grey.png */; };
13+ 523B3CFA15B73BA0004394F4 /* download-grey@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF615B73BA0004394F4 /* download-grey@2x.png */; };
14+ 523B3CFB15B73BA0004394F4 /* download.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF715B73BA0004394F4 /* download.png */; };
15+ 523B3CFC15B73BA0004394F4 /* download@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 523B3CF815B73BA0004394F4 /* download@2x.png */; };
16 5305C2061157F4F800BC78F0 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 5305C2051157F4F800BC78F0 /* Default.png */; };
17 536D620B1144495400DFCE56 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 536D620A1144495400DFCE56 /* SystemConfiguration.framework */; };
18 537DE2D9113F008C00875852 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 537DE2D8113F008C00875852 /* CoreFoundation.framework */; };
19@@ -211,6 +219,16 @@
20 1D6058910D05DD3D006BFB54 /* U1 Music.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "U1 Music.app"; sourceTree = BUILT_PRODUCTS_DIR; };
21 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
22 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
23+ 523B3CDC15B4C42F004394F4 /* SongUITableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SongUITableView.h; sourceTree = "<group>"; };
24+ 523B3CDD15B4C42F004394F4 /* SongUITableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SongUITableView.m; sourceTree = "<group>"; };
25+ 523B3CE015B5D64F004394F4 /* grabber.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = grabber.png; sourceTree = "<group>"; };
26+ 523B3CE115B5D64F004394F4 /* grabber@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "grabber@2x.png"; sourceTree = "<group>"; };
27+ 523B3CE415B5D814004394F4 /* UOPullGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UOPullGestureRecognizer.h; sourceTree = "<group>"; };
28+ 523B3CE515B5D814004394F4 /* UOPullGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UOPullGestureRecognizer.m; sourceTree = "<group>"; };
29+ 523B3CF515B73BA0004394F4 /* download-grey.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download-grey.png"; sourceTree = "<group>"; };
30+ 523B3CF615B73BA0004394F4 /* download-grey@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download-grey@2x.png"; sourceTree = "<group>"; };
31+ 523B3CF715B73BA0004394F4 /* download.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = download.png; sourceTree = "<group>"; };
32+ 523B3CF815B73BA0004394F4 /* download@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "download@2x.png"; sourceTree = "<group>"; };
33 5305C2051157F4F800BC78F0 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
34 536D620A1144495400DFCE56 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
35 537DE2D8113F008C00875852 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
36@@ -798,6 +816,8 @@
37 9674C2C413A7DC01004509E4 /* UORadialProgressControl.m */,
38 91BE401A138E8D3A00D44D68 /* RIButtonItem.h */,
39 91BE401B138E8D3A00D44D68 /* RIButtonItem.m */,
40+ 523B3CDC15B4C42F004394F4 /* SongUITableView.h */,
41+ 523B3CDD15B4C42F004394F4 /* SongUITableView.m */,
42 93F3345D1247FA97006C6707 /* SongUITableViewCell.h */,
43 93F3345E1247FA97006C6707 /* SongUITableViewCell.m */,
44 919376F1135CC1CF0030B7BD /* PlaylistEditSongUITableViewCell.h */,
45@@ -814,6 +834,8 @@
46 91C8CCAE14B7883600A0E311 /* ArtistUITableViewCell.m */,
47 91D3A17014BE12FE003D4FC0 /* PlaylistUITableViewCell.h */,
48 91D3A17114BE12FE003D4FC0 /* PlaylistUITableViewCell.m */,
49+ 523B3CE415B5D814004394F4 /* UOPullGestureRecognizer.h */,
50+ 523B3CE515B5D814004394F4 /* UOPullGestureRecognizer.m */,
51 );
52 name = Controls;
53 path = controls;
54@@ -951,6 +973,12 @@
55 93FA42A6124DC1350080DF62 /* Images */ = {
56 isa = PBXGroup;
57 children = (
58+ 523B3CF515B73BA0004394F4 /* download-grey.png */,
59+ 523B3CF615B73BA0004394F4 /* download-grey@2x.png */,
60+ 523B3CF715B73BA0004394F4 /* download.png */,
61+ 523B3CF815B73BA0004394F4 /* download@2x.png */,
62+ 523B3CE015B5D64F004394F4 /* grabber.png */,
63+ 523B3CE115B5D64F004394F4 /* grabber@2x.png */,
64 9149D9C9159BEC3D009AE771 /* default-album-art-120.png */,
65 9149D9CB159BEC3D009AE771 /* default-album-art-120@2x.png */,
66 9149D9CA159BEC3D009AE771 /* default-album-art-200.png */,
67@@ -1257,6 +1285,12 @@
68 9149D9D2159BEC3D009AE771 /* default-album-art-200@2x.png in Resources */,
69 9149D9D3159BEC3D009AE771 /* default-album-art-640.png in Resources */,
70 9149D9D4159BEC3D009AE771 /* default-album-art-640@2x.png in Resources */,
71+ 523B3CE215B5D64F004394F4 /* grabber.png in Resources */,
72+ 523B3CE315B5D64F004394F4 /* grabber@2x.png in Resources */,
73+ 523B3CF915B73BA0004394F4 /* download-grey.png in Resources */,
74+ 523B3CFA15B73BA0004394F4 /* download-grey@2x.png in Resources */,
75+ 523B3CFB15B73BA0004394F4 /* download.png in Resources */,
76+ 523B3CFC15B73BA0004394F4 /* download@2x.png in Resources */,
77 );
78 runOnlyForDeploymentPostprocessing = 0;
79 };
80@@ -1405,6 +1439,8 @@
81 960E4B3514E58844002AAB79 /* U1CachedFile.m in Sources */,
82 960E4B3814E5884B002AAB79 /* _U1CachedFile.m in Sources */,
83 91F2653014EACFCC0027232B /* U1MigrationViewController.m in Sources */,
84+ 523B3CDE15B4C42F004394F4 /* SongUITableView.m in Sources */,
85+ 523B3CE615B5D814004394F4 /* UOPullGestureRecognizer.m in Sources */,
86 );
87 runOnlyForDeploymentPostprocessing = 0;
88 };
89
90=== modified file 'categories/UIImage+RoundedCorner.m'
91--- categories/UIImage+RoundedCorner.m 2011-04-14 18:53:36 +0000
92+++ categories/UIImage+RoundedCorner.m 2012-07-24 05:24:19 +0000
93@@ -55,6 +55,8 @@
94 #pragma mark -
95 #pragma mark Private helper methods
96
97+#pragma clang diagnostic push
98+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
99 // Adds a rectangular path to the given context and rounds its corners by the given extents
100 // Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/
101 - (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight {
102@@ -75,5 +77,6 @@
103 CGContextClosePath(context);
104 CGContextRestoreGState(context);
105 }
106+#pragma clang diagnostic pop
107
108 @end
109\ No newline at end of file
110
111=== modified file 'controls/PlaylistEditSongUITableViewCell.m'
112--- controls/PlaylistEditSongUITableViewCell.m 2011-04-19 01:48:10 +0000
113+++ controls/PlaylistEditSongUITableViewCell.m 2012-07-24 05:24:19 +0000
114@@ -25,7 +25,5 @@
115 - (void)layoutSubviews
116 {
117 [super layoutSubviews];
118- self.durationLabel.frame = CGRectMake(252, 0, 68, self.frame.size.height);
119- self.durationLabel.textAlignment = UITextAlignmentLeft;
120 }
121 @end
122
123=== added file 'controls/SongUITableView.h'
124--- controls/SongUITableView.h 1970-01-01 00:00:00 +0000
125+++ controls/SongUITableView.h 2012-07-24 05:24:19 +0000
126@@ -0,0 +1,35 @@
127+//
128+// SongUITableView.h
129+// U1Music
130+//
131+// Created by Paul Hummer on 7/16/12.
132+// Copyright (c) 2012 Canonical. All rights reserved.
133+//
134+
135+#import <UIKit/UIKit.h>
136+
137+@class SongUITableView;
138+
139+@protocol SongUITableViewDelegate <NSObject>
140+- (void)viewDidBeginTouchGesture:(SongUITableView*)view;
141+- (void)viewDidEndPullRightGesture:(SongUITableView *)view;
142+@end
143+
144+@interface SongUITableView : UIView {
145+ UIImageView *actionIconView;
146+ UIView *bottomView;
147+ UIView *progressView;
148+ UIView *topView;
149+}
150+@property (nonatomic) BOOL cancelled;
151+@property (nonatomic) BOOL downloading;
152+@property (nonatomic) BOOL downloaded;
153+@property (assign) IBOutlet id <SongUITableViewDelegate> delegate;
154+
155+@property (nonatomic,retain) UILabel *songNameLabel;
156+@property(nonatomic,retain) UIImageView *nowPlaying;
157+
158+-(void)setCached:(BOOL)cached;
159+-(void)closeBottomView;
160+
161+@end
162
163=== added file 'controls/SongUITableView.m'
164--- controls/SongUITableView.m 1970-01-01 00:00:00 +0000
165+++ controls/SongUITableView.m 2012-07-24 05:24:19 +0000
166@@ -0,0 +1,178 @@
167+//
168+// SongUITableView.m
169+// U1Music
170+//
171+// Created by Paul Hummer on 7/16/12.
172+// Copyright (c) 2012 Canonical. All rights reserved.
173+//
174+
175+#import "SongUITableView.h"
176+#import "UOPullGestureRecognizer.h"
177+
178+@implementation SongUITableView
179+
180+@synthesize cancelled;
181+@synthesize delegate;
182+@synthesize downloaded;
183+@synthesize downloading;
184+
185+@synthesize songNameLabel;
186+@synthesize nowPlaying;
187+
188+- (id)initWithFrame:(CGRect)frame
189+{
190+ self = [super initWithFrame:frame];
191+ if (self) {
192+ /* XXX: rockstar - This is a hack, as it seems that the subviews aren't ready by the time configureCell hits them. I think some restructuring might help here, but until then, this works.
193+ */
194+ [self layoutSubviews];
195+ }
196+ return self;
197+}
198+
199+- (void)closeBottomView {
200+ [UIView animateWithDuration:0.333 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
201+ CGRect frame = UIEdgeInsetsInsetRect(bottomView.bounds, UIEdgeInsetsMake(0, 12, 0, 0));
202+ topView.frame = frame;
203+ } completion:^(BOOL finished) {
204+ }];
205+}
206+
207+- (void)layoutSubviews {
208+ [super layoutSubviews];
209+
210+ if (bottomView == nil) {
211+ bottomView = [[UIView alloc] initWithFrame:self.bounds];
212+ bottomView.backgroundColor = [UIColor orangeColor];
213+ [self addSubview:bottomView];
214+
215+ //Currently this isn't really a progress view as much as it is a cache indicator.
216+ progressView = [[UIView alloc] init];
217+ progressView.backgroundColor = [UIColor whiteColor];
218+ [bottomView addSubview:progressView];
219+
220+ actionIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"download-grey"]];
221+ actionIconView.highlightedImage = [UIImage imageNamed:@"download"];
222+ actionIconView.frame = CGRectMake(24, (CGRectGetHeight(self.bounds) - 28.f) / 2.f, 28, 28);
223+ [bottomView addSubview:actionIconView];
224+ }
225+
226+ if (topView == nil) {
227+ CGRect frame = UIEdgeInsetsInsetRect(bottomView.bounds, UIEdgeInsetsMake(0, 12, 0, 0));
228+ topView = [[UIView alloc] initWithFrame:frame];
229+ topView.backgroundColor = [UIColor whiteColor];
230+ [bottomView addSubview:topView];
231+
232+
233+ UIImageView *handleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"grabber"]];
234+ handleView.frame = CGRectMake(0, (CGRectGetHeight(self.bounds) - 52.f) / 2.f, 22, 52);
235+ [topView addSubview:handleView];
236+
237+ songNameLabel = [[UILabel alloc] initWithFrame:UIEdgeInsetsInsetRect(topView.bounds, UIEdgeInsetsMake(0, 27, 0, 0))];
238+ [topView addSubview:songNameLabel];
239+
240+ nowPlaying = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.bounds) - 52.f, (CGRectGetHeight(self.bounds) - 48.f) / 2.f, 22, 52)];
241+ nowPlaying.image = [UIImage imageNamed:@"speaker"];
242+ nowPlaying.backgroundColor = [UIColor clearColor];
243+ nowPlaying.contentMode = UIViewContentModeCenter;
244+ nowPlaying.hidden = YES;
245+ [topView addSubview:nowPlaying];
246+
247+ UOPullGestureRecognizer *pullRight = [[UOPullGestureRecognizer alloc] initWithTarget:self action:@selector(pullRight:)];
248+ [topView addGestureRecognizer:pullRight];
249+ [pullRight setDirection:UISwipeGestureRecognizerDirectionRight];
250+ [pullRight setActionThreshold:52.f];
251+ [pullRight setActivationRegion:CGRectMake(0, (CGRectGetHeight(self.bounds) - 52.f) / 2.f, 64, 52)];
252+ [pullRight setFiresActionOnThresholdReached:NO];
253+ }
254+}
255+
256+- (void)pullRight:(UOPullGestureRecognizer*)pan {
257+ CGPoint offset = [pan translationInView:pan.view];
258+
259+ switch (pan.state) {
260+ case UIGestureRecognizerStateBegan:
261+ [self.delegate viewDidBeginTouchGesture:self];
262+ break;
263+ case UIGestureRecognizerStateChanged:
264+ [UIView beginAnimations:nil context:NULL];
265+ [UIView setAnimationBeginsFromCurrentState:YES];
266+ [UIView setAnimationsEnabled:NO];
267+
268+ CGRect frameRect = topView.frame;
269+ CGFloat myOffset = MAX(0, MIN(offset.x, pan.actionThreshold));
270+ frameRect.origin.x = myOffset + 12.f;
271+
272+ if (myOffset >= pan.actionThreshold) {
273+ [UIView animateWithDuration:0.333 animations:^{
274+ actionIconView.highlighted = YES;
275+ }];
276+ } else {
277+ [UIView animateWithDuration:0.333 animations:^{
278+ actionIconView.highlighted = NO;
279+ }];
280+ }
281+
282+ topView.frame = frameRect;
283+ [UIView commitAnimations];
284+ [UIView setAnimationsEnabled:YES];
285+ break;
286+ case UIGestureRecognizerStateEnded:
287+ cancelled = downloading;
288+ downloading = !downloading;
289+ [self.delegate viewDidEndPullRightGesture:self];
290+ [self closeBottomView];
291+ break;
292+ case UIGestureRecognizerStateCancelled:
293+ [UIView animateWithDuration:0.333 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
294+ CGRect frame = UIEdgeInsetsInsetRect(bottomView.bounds, UIEdgeInsetsMake(0, 12, 0, 0));
295+ topView.frame = frame;
296+ } completion:^(BOOL finished) {
297+ // change the icon to whatever its new state should be
298+ if (downloading)
299+ {
300+ actionIconView.image = [UIImage imageNamed:@"cancel-grey"];
301+ actionIconView.highlightedImage = [UIImage imageNamed:@"cancel"];
302+ }
303+ else if (downloaded)
304+ {
305+ actionIconView.image = [UIImage imageNamed:@"trash-grey"];
306+ actionIconView.highlightedImage = [UIImage imageNamed:@"trash"];
307+ }
308+ else
309+ {
310+ actionIconView.image = [UIImage imageNamed:@"download-grey"];
311+ actionIconView.highlightedImage = [UIImage imageNamed:@"download"];
312+ }
313+ actionIconView.highlighted = NO;
314+ }];
315+
316+ [UIView animateWithDuration:0.333 animations:^{
317+ bottomView.backgroundColor = [UIColor orangeColor];
318+ }];
319+ break;
320+ default:
321+ break;
322+ }
323+}
324+
325+-(void)setCached:(BOOL)cached {
326+ CGRect frame;
327+ if (cached) {
328+ frame = CGRectMake(0, CGRectGetMaxY(self.bounds), 12, CGRectGetHeight(self.bounds));
329+ } else {
330+ frame = CGRectMake(0, 0, 12, CGRectGetHeight(self.bounds));
331+ }
332+ progressView.frame = frame;
333+}
334+
335+/*
336+// Only override drawRect: if you perform custom drawing.
337+// An empty implementation adversely affects performance during animation.
338+- (void)drawRect:(CGRect)rect
339+{
340+ // Drawing code
341+}
342+*/
343+
344+@end
345
346=== modified file 'controls/SongUITableViewCell.h'
347--- controls/SongUITableViewCell.h 2011-05-27 18:22:51 +0000
348+++ controls/SongUITableViewCell.h 2012-07-24 05:24:19 +0000
349@@ -29,22 +29,11 @@
350 // DAMAGE.
351
352 #import <UIKit/UIKit.h>
353+#import "SongUITableView.h"
354
355 @interface SongUITableViewCell : UITableViewCell {
356- UIButton *cacheButton;
357- UIActivityIndicatorView *spinner;
358- UILabel *songNameLabel;
359- BOOL evenRow;
360- UIView *coloredView;
361- UILabel *positionLabel;
362- UILabel *durationLabel;
363- UIImageView *nowPlaying;
364+ SongUITableView *songUITableView;
365 }
366-@property(nonatomic,retain) UIButton *cacheButton;
367-@property(nonatomic,retain) UIActivityIndicatorView *spinner;
368-@property(nonatomic,assign) BOOL evenRow;
369-@property(nonatomic,retain) UIImageView *nowPlaying;
370-@property(nonatomic,retain) UILabel *songNameLabel;
371-@property(nonatomic,retain) UILabel *positionLabel;
372-@property(nonatomic,retain) UILabel *durationLabel;
373+@property (nonatomic, retain) SongUITableView *songUITableView;
374+
375 @end
376
377=== modified file 'controls/SongUITableViewCell.m'
378--- controls/SongUITableViewCell.m 2011-06-07 09:20:03 +0000
379+++ controls/SongUITableViewCell.m 2012-07-24 05:24:19 +0000
380@@ -34,50 +34,14 @@
381
382 @implementation SongUITableViewCell
383
384-@synthesize cacheButton, spinner, songNameLabel, positionLabel, durationLabel, nowPlaying, evenRow;
385+//@synthesize spinner, songNameLabel, positionLabel, durationLabel, nowPlaying, evenRow;
386+@synthesize songUITableView;
387
388 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
389 {
390- if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]))
391- {
392- self.evenRow = YES;
393-
394- coloredView = [[UIView alloc] initWithFrame:self.frame];
395- coloredView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
396- [self.contentView addSubview:coloredView];
397-
398- // 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.
399- spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] retain];
400- spinner.hidesWhenStopped = YES;
401- [coloredView addSubview:spinner];
402-
403- cacheButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
404- [cacheButton setImage:[UIImage imageNamed:@"uncached"] forState:UIControlStateNormal];
405- [coloredView addSubview:self.cacheButton];
406+ if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
407
408- songNameLabel = [[UILabel alloc] init];
409- songNameLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
410- [coloredView addSubview:self.songNameLabel];
411-
412- positionLabel = [[UILabel alloc] init];
413- positionLabel.text = @"1";
414- positionLabel.textAlignment = UITextAlignmentCenter;
415- positionLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
416- [coloredView addSubview:positionLabel];
417-
418- durationLabel = [[UILabel alloc] init];
419- durationLabel.text = @"0:00";
420- durationLabel.textAlignment = UITextAlignmentCenter;
421- durationLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
422- [coloredView addSubview:durationLabel];
423-
424- nowPlaying = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 13, 12)];
425- nowPlaying.image = [UIImage imageNamed:@"speaker"];
426- nowPlaying.backgroundColor = [UIColor clearColor];
427- nowPlaying.contentMode = UIViewContentModeCenter;
428- [coloredView addSubview:nowPlaying];
429-
430- self.textLabel.hidden = YES;
431+ songUITableView = [[SongUITableView alloc] initWithFrame:self.frame];
432 }
433
434 return self;
435@@ -86,38 +50,11 @@
436 - (void)layoutSubviews
437 {
438 [super layoutSubviews];
439- self.positionLabel.frame = CGRectMake(0, 0, 35, self.frame.size.height);
440- self.songNameLabel.frame = CGRectMake(37, 0, 164, self.frame.size.height);
441- self.nowPlaying.frame = CGRectMake(203, 0, 13, self.frame.size.height);
442- self.durationLabel.frame = CGRectMake(218, 0, 48, self.frame.size.height);
443- self.cacheButton.frame = CGRectMake(268, 0, 44, self.frame.size.height);
444- self.spinner.frame = CGRectMake(282, 14, 16, 16);
445-
446- if (self.evenRow)
447- {
448- coloredView.backgroundColor = [UIColor whiteColor];
449- self.backgroundColor = [UIColor whiteColor];
450- self.songNameLabel.backgroundColor = [UIColor whiteColor];
451- self.positionLabel.backgroundColor = [UIColor whiteColor];
452- self.durationLabel.backgroundColor = [UIColor whiteColor];
453- }
454- else
455- {
456- coloredView.backgroundColor = [UIColor colorWithRed:0.9529f green:0.9529f blue:0.9529f alpha:1.0f];
457- self.backgroundColor = [UIColor colorWithRed:0.9529f green:0.9529f blue:0.9529f alpha:1.0f];
458- self.songNameLabel.backgroundColor = [UIColor colorWithRed:0.9529f green:0.9529f blue:0.9529f alpha:1.0f];
459- self.positionLabel.backgroundColor = [UIColor colorWithRed:0.9529f green:0.9529f blue:0.9529f alpha:1.0f];
460- self.durationLabel.backgroundColor = [UIColor colorWithRed:0.9529f green:0.9529f blue:0.9529f alpha:1.0f];
461- }
462+ [self.contentView addSubview:songUITableView];
463 }
464
465 - (void)dealloc
466 {
467- [cacheButton release];
468- [spinner release];
469- [durationLabel release];
470- [songNameLabel release];
471- [positionLabel release];
472 [super dealloc];
473 }
474
475
476=== added file 'controls/UOPullGestureRecognizer.h'
477--- controls/UOPullGestureRecognizer.h 1970-01-01 00:00:00 +0000
478+++ controls/UOPullGestureRecognizer.h 2012-07-24 05:24:19 +0000
479@@ -0,0 +1,20 @@
480+//
481+// UOPullGestureRecognizer.h
482+// U1Music
483+//
484+// Created by Paul Hummer on 7/17/12.
485+// Copyright (c) 2012 Canonical. All rights reserved.
486+//
487+
488+#import <UIKit/UIKit.h>
489+
490+@interface UOPullGestureRecognizer : UIPanGestureRecognizer {
491+ CGPoint inceptionPoint;
492+}
493+@property UISwipeGestureRecognizerDirection direction;
494+@property BOOL firesActionOnThresholdReached;
495+@property CGFloat actionThreshold;
496+@property CGRect activationRegion;
497+
498+//@property(nonatomic,readwrite) UIGestureRecognizerState state;
499+@end
500
501=== added file 'controls/UOPullGestureRecognizer.m'
502--- controls/UOPullGestureRecognizer.m 1970-01-01 00:00:00 +0000
503+++ controls/UOPullGestureRecognizer.m 2012-07-24 05:24:19 +0000
504@@ -0,0 +1,77 @@
505+//
506+// UOPullGestureRecognizer.m
507+// U1Music
508+//
509+// Created by Paul Hummer on 7/17/12.
510+// Copyright (c) 2012 Canonical. All rights reserved.
511+//
512+
513+#import <UIKit/UIGestureRecognizerSubclass.h>
514+#import "UOPullGestureRecognizer.h"
515+
516+@implementation UOPullGestureRecognizer
517+@synthesize direction;
518+@synthesize firesActionOnThresholdReached;
519+@synthesize actionThreshold;
520+@synthesize activationRegion;
521+
522+- (id)initWithTarget:(id)target action:(SEL)action {
523+ self = [super initWithTarget:target action:action];
524+ if (self != nil) {
525+ actionThreshold = 64.f;
526+ }
527+ return self;
528+}
529+
530+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
531+ NSParameterAssert([touches count] == 1);
532+
533+ CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
534+ if (!CGRectEqualToRect(activationRegion, CGRectZero)) {
535+ if (!CGRectContainsPoint(activationRegion, touchPoint)) {
536+ self.state = UIGestureRecognizerStateCancelled;
537+ return;
538+ }
539+ }
540+
541+ [super touchesBegan:touches withEvent:event];
542+ inceptionPoint = [[touches anyObject] locationInView:nil];
543+}
544+
545+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
546+ for (UITouch *touch in touches) {
547+ CGPoint currentPoint = [touch locationInView:nil];
548+ CGFloat offset = currentPoint.x - inceptionPoint.x;
549+ if ((offset < 0 && direction == UISwipeGestureRecognizerDirectionRight) ||
550+ (offset > 0 && direction == UISwipeGestureRecognizerDirectionLeft)) {
551+ /* Pull cancelled */
552+ self.state = UIGestureRecognizerStateCancelled;
553+ return;
554+ }
555+
556+ if (fabs(offset) > actionThreshold && self.firesActionOnThresholdReached) {
557+ /* Pull completed */
558+ self.state = UIGestureRecognizerStateEnded;
559+ }
560+ }
561+ [super touchesMoved:touches withEvent:event];
562+}
563+
564+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
565+ // still passed the activation threshold?
566+
567+ CGPoint currentPoint = [[touches anyObject] locationInView:nil];
568+ CGFloat offset = currentPoint.x - inceptionPoint.x;
569+
570+ if (fabs(offset) > actionThreshold && !self.firesActionOnThresholdReached) {
571+ NSLog(@"DO THE THING!");
572+ [super touchesEnded:touches withEvent:event];
573+ }
574+ else
575+ {
576+ self.state = UIGestureRecognizerStateCancelled;
577+ }
578+}
579+
580+
581+@end
582
583=== added file 'images/download-grey.png'
584Binary files images/download-grey.png 1970-01-01 00:00:00 +0000 and images/download-grey.png 2012-07-24 05:24:19 +0000 differ
585=== added file 'images/download-grey@2x.png'
586Binary files images/download-grey@2x.png 1970-01-01 00:00:00 +0000 and images/download-grey@2x.png 2012-07-24 05:24:19 +0000 differ
587=== added file 'images/download.png'
588Binary files images/download.png 1970-01-01 00:00:00 +0000 and images/download.png 2012-07-24 05:24:19 +0000 differ
589=== added file 'images/download@2x.png'
590Binary files images/download@2x.png 1970-01-01 00:00:00 +0000 and images/download@2x.png 2012-07-24 05:24:19 +0000 differ
591=== added file 'images/grabber.png'
592Binary files images/grabber.png 1970-01-01 00:00:00 +0000 and images/grabber.png 2012-07-24 05:24:19 +0000 differ
593=== added file 'images/grabber@2x.png'
594Binary files images/grabber@2x.png 1970-01-01 00:00:00 +0000 and images/grabber@2x.png 2012-07-24 05:24:19 +0000 differ
595=== modified file 'view_controllers/AlbumViewController.h'
596--- view_controllers/AlbumViewController.h 2011-06-07 09:20:03 +0000
597+++ view_controllers/AlbumViewController.h 2012-07-24 05:24:19 +0000
598@@ -18,12 +18,13 @@
599 // along with this program. If not, see <http://www.gnu.org/licenses/>.
600
601 #import <UIKit/UIKit.h>
602+#import "SongUITableView.h"
603 #import "SubsonicTableViewController.h"
604 #import "AlbumArtLoader.h"
605
606 @class Artist, Album;
607
608-@interface AlbumViewController : SubsonicTableViewController <AlbumArtLoaderDelegate>
609+@interface AlbumViewController : SubsonicTableViewController <AlbumArtLoaderDelegate,SongUITableViewDelegate>
610 {
611 AlbumArtLoader *loader;
612 UIView *headerView;
613@@ -38,7 +39,6 @@
614 @property(nonatomic,retain) NSMutableArray *songs;
615 @property(nonatomic,retain) NSString *artist;
616 @property(nonatomic,retain) Album *album;
617-@property(nonatomic,retain) UIButton *cacheButton;
618
619 extern NSString * const NOTIF_downloadComplete;
620 extern NSString * const NOTIF_removeCachedContent;
621
622=== modified file 'view_controllers/AlbumViewController.m'
623--- view_controllers/AlbumViewController.m 2012-06-22 17:56:56 +0000
624+++ view_controllers/AlbumViewController.m 2012-07-24 05:24:19 +0000
625@@ -23,6 +23,7 @@
626 #import "AlbumParser.h"
627 #import "Album.h"
628 #import "Song.h"
629+#import "SongUITableView.h"
630 #import "SongUITableViewCell.h"
631 #import "NSDate+Extras.h"
632 #import "StreamingPlayer.h"
633@@ -41,7 +42,7 @@
634 @end
635
636 @implementation AlbumViewController
637-@synthesize artist, album, songs, cacheButton;
638+@synthesize artist, album, songs;
639
640 - (id)initWithStyle:(UITableViewStyle)aStyle
641 {
642@@ -83,7 +84,8 @@
643 @synchronized(self.songs)
644 {
645 [self.songs removeAllObjects];
646- [PerThreadManagedObjectContext() refreshObject:self.album mergeChanges:YES]; //force the album to reload its set of songs from the persistent store.
647+ //force the album to reload its set of songs from the persistent store.
648+ [PerThreadManagedObjectContext() refreshObject:self.album mergeChanges:YES];
649 [self.songs addObjectsFromArray:[self.album.songs allObjects]];
650 [self.songs sortUsingSelector:@selector(compare:)];
651 }
652@@ -135,11 +137,15 @@
653 if (nil == cell)
654 {
655 cell = [[[SongUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SongCellIdentifier] autorelease];
656+ cell.songUITableView.delegate = self;
657 }
658 [self configureCell:cell forSongAtIndexPath:indexPath];
659 return cell;
660 }
661
662+- (void)viewDidBeginTouchGesture:(SongUITableView*)view {
663+ // Stub for when we have a middle view.
664+}
665
666 #pragma mark -
667 #pragma mark Table view delegate
668@@ -209,82 +215,14 @@
669 }
670 }
671
672-@end
673-
674-@implementation AlbumViewController (Private)
675-
676-- (void)configureCell:(SongUITableViewCell*)cell forSongAtIndexPath:(NSIndexPath*)indexPath
677-{
678- Song *song = [self.songs objectAtIndex:indexPath.row];
679-
680- // explicitly re-set the default state of the button/spinner
681- [cell.cacheButton setImage:(canCache ? [UIImage imageNamed:@"uncached"] : [UIImage imageNamed:@"uncached-disabled"]) forState:UIControlStateNormal];
682- [cell.spinner stopAnimating];
683-
684- cell.cacheButton.tag = indexPath.row;
685- [cell.cacheButton addTarget:self action:@selector(toggleCacheForSong:) forControlEvents:UIControlEventTouchUpInside];
686-
687- U1CacheFileManager *cacheFileManager = [U1CacheFileManager sharedCacheFileManager];
688- BOOL offline = NO;
689- NSString *cachedPath = [cacheFileManager cachedPathForFileId:song.idPath offline:&offline];
690-
691- if ([[Downloader sharedDownloader] isDownloading:song.idPath] && offline)
692- {
693- [cell.spinner startAnimating];
694- }
695- else if (offline)
696- {
697- [cell.cacheButton setImage:[UIImage imageNamed:@"cached"] forState:UIControlStateNormal];
698- }
699-
700- cell.songNameLabel.text = song.title;
701-
702- cell.positionLabel.text = [NSString stringWithFormat:@"%@", [song.track intValue] > 0 ? [song.track stringValue] : @""];
703-
704- if (song.duration && [song.duration intValue] > 0)
705- {
706- cell.durationLabel.text = [NSDate stringFromDuration:song.duration];
707- }
708- else
709- {
710- cell.durationLabel.text = @"";
711- }
712-
713- if ([StreamingPlayer sharedStreamingPlayer].currentSong && [song.songId isEqual:[StreamingPlayer sharedStreamingPlayer].currentSong.songId])
714- {
715- cell.nowPlaying.hidden = NO;
716- }
717- else
718- {
719- cell.nowPlaying.hidden = YES;
720- }
721-
722- [cell.cacheButton setHidden:!canStream];
723-
724- cell.evenRow = (0 == indexPath.row % 2);
725- if (cachedPath == nil && !canStream)
726- {
727- cell.songNameLabel.textColor = [UIColor lightGrayColor];
728- cell.positionLabel.textColor = [UIColor lightGrayColor];
729- cell.durationLabel.textColor = [UIColor lightGrayColor];
730- }
731- else
732- {
733- cell.songNameLabel.textColor = [UIColor blackColor];
734- cell.positionLabel.textColor = [UIColor blackColor];
735- cell.durationLabel.textColor = [UIColor blackColor];
736- }
737-}
738-
739-- (void)toggleCacheForSong:(id)sender
740-{
741+-(void)viewDidEndPullRightGesture:(SongUITableView *)view {
742 // This is a binary toggle, and right now, we don't bother trying to cancel a download in progress.
743- int row = [(UIButton *)sender tag];
744-
745+ int row = [(UIButton *)view tag];
746+
747 NSIndexPath *songPath = [NSIndexPath indexPathForRow:row inSection:0];
748 SongUITableViewCell *cell = (SongUITableViewCell *)[self.tableView cellForRowAtIndexPath:songPath];
749 Song *song = [self.songs objectAtIndex:row];
750-
751+
752 if (![[Downloader sharedDownloader] isDownloading:song.idPath])
753 // If we're downloading at all, don't do anything.
754 {
755@@ -295,26 +233,22 @@
756 {
757 [cacheFileManager toggleOfflineStatusForFileId:song.idPath];
758
759- [cell.spinner stopAnimating];
760 [[NSNotificationCenter defaultCenter] postNotificationName:NOTIF_removeCachedContent object:nil];
761- [cell.cacheButton setImage:[UIImage imageNamed:@"uncached"] forState:UIControlStateNormal];
762+ [cell.songUITableView setCached:NO];
763 }
764 else if (cachedPath && !offline)
765 {
766 [cacheFileManager toggleOfflineStatusForFileId:song.idPath];
767- [cell.spinner stopAnimating];
768- [cell.cacheButton setImage:[UIImage imageNamed:@"cached"] forState:UIControlStateNormal];
769+ [cell.songUITableView setCached:YES];
770 }
771 else
772 {
773 if (canCache)
774 {
775- [cell.spinner startAnimating];
776 [[Downloader sharedDownloader] downloadFile:[[Subsonic sharedSubsonic] getStreamingURLForSongId:song.songId] withName:song.idPath forOffline:YES completionBlock:^(NSString *path) {
777 dispatch_async(dispatch_get_main_queue(), ^(void) {
778 SongUITableViewCell *cell = (SongUITableViewCell *)[self.tableView cellForRowAtIndexPath:songPath];
779- [cell.spinner stopAnimating];
780- [cell.cacheButton setImage:[UIImage imageNamed:@"cached"] forState:UIControlStateNormal];
781+ [cell.songUITableView setCached:YES];
782 });
783 }];
784 }
785@@ -325,14 +259,14 @@
786 {
787 RIButtonItem *cancelButton = [RIButtonItem item];
788 cancelButton.label = NSLocalizedString(@"Remind Me", @"");
789-
790+
791 RIButtonItem *dismissButton = [RIButtonItem item];
792 dismissButton.label = NSLocalizedString(@"Dismiss", @"");
793 dismissButton.action = ^
794 {
795 [defaults setBool:YES forKey:@"acknowledged-no-cache-on-3g"];
796 };
797-
798+
799 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Downloading Disabled", @"")
800 message:NSLocalizedString(@"Downloading songs for offline use can only be done on WiFi networks.", @"")
801 cancelButtonItem:cancelButton otherButtonItems:dismissButton, nil];
802@@ -344,6 +278,49 @@
803 }
804 }
805
806+@end
807+
808+@implementation AlbumViewController (Private)
809+
810+-(void)toggleCacheForSong:(id)sender {
811+
812+}
813+
814+- (void)configureCell:(SongUITableViewCell*)cell forSongAtIndexPath:(NSIndexPath*)indexPath
815+{
816+ Song *song = [self.songs objectAtIndex:indexPath.row];
817+
818+ [cell.songUITableView setCached:NO];
819+ cell.songUITableView.nowPlaying.hidden = YES;
820+ cell.songUITableView.songNameLabel.text = @"";
821+
822+ cell.songUITableView.tag = indexPath.row;
823+
824+ U1CacheFileManager *cacheFileManager = [U1CacheFileManager sharedCacheFileManager];
825+ BOOL offline = NO;
826+ NSString *cachedPath = [cacheFileManager cachedPathForFileId:song.idPath offline:&offline];
827+
828+ if ([[Downloader sharedDownloader] isDownloading:song.idPath] && offline) {
829+ /* TODO: We need to implement the progress bar */
830+ } else if (offline) {
831+ [cell.songUITableView setCached:YES];
832+ }
833+
834+ cell.songUITableView.songNameLabel.text = song.title;
835+
836+ if ([StreamingPlayer sharedStreamingPlayer].currentSong && [song.songId isEqual:[StreamingPlayer sharedStreamingPlayer].currentSong.songId]) {
837+ cell.songUITableView.nowPlaying.hidden = NO;
838+ } else {
839+ cell.songUITableView.nowPlaying.hidden = YES;
840+ }
841+
842+ if (cachedPath == nil && !canStream) {
843+ cell.songUITableView.songNameLabel.textColor = [UIColor lightGrayColor];
844+ } else {
845+ cell.songUITableView.songNameLabel.textColor = [UIColor blackColor];
846+ }
847+}
848+
849 - (void)toggleCacheForAlbum:(id)sender
850 {
851
852@@ -393,16 +370,6 @@
853 [headerView addSubview:albumLabel];
854
855 albumLabel.text = self.album.title;
856-
857- /* Not yet
858- cacheButton = [UIButton buttonWithType:UIButtonTypeCustom];
859- cacheButton.frame = CGRectMake(268, 76, 44, 44);
860- // If none of the songs are cached, it should use uncached.png
861- // If some of the songs are cached, it should use partiallycached.png
862- // If all of the songs are cachced, it should use cached.png
863- [cacheButton setImage:[UIImage imageNamed:@"uncached"] forState:UIControlStateNormal];
864- [headerView addSubview:cacheButton];
865- */
866
867 self.tableView.tableHeaderView = headerView;
868
869@@ -415,5 +382,4 @@
870 [self.tableView.tableFooterView addSubview:bigFooterView];
871 [bigFooterView release];
872 }
873-
874 @end
875
876=== modified file 'view_controllers/PlaylistEditAlbumViewController.m'
877--- view_controllers/PlaylistEditAlbumViewController.m 2011-06-23 13:54:49 +0000
878+++ view_controllers/PlaylistEditAlbumViewController.m 2012-07-24 05:24:19 +0000
879@@ -67,28 +67,15 @@
880
881 Song *song = [self.songs objectAtIndex:indexPath.row];
882
883- cell.songNameLabel.text = song.title;
884+ cell.songUITableView.songNameLabel.text = song.title;
885
886- cell.positionLabel.text = [NSString stringWithFormat:@"%@", [song.track intValue] > 0 ? [song.track stringValue] : @""];
887-
888- if (song.duration && [song.duration intValue] > 0)
889- {
890- cell.durationLabel.text = [NSDate stringFromDuration:song.duration];
891- }
892- else
893- {
894- cell.durationLabel.text = @"";
895- }
896-
897- cell.nowPlaying.hidden = YES;
898+ cell.songUITableView.nowPlaying.hidden = YES;
899
900 if ([self.originator.playlist.songs containsObject:song])
901 {
902 cell.accessoryType = UITableViewCellAccessoryCheckmark;
903 }
904
905- cell.evenRow = (0 == indexPath.row % 2);
906-
907 return cell;
908 }
909

Subscribers

People subscribed via source and target branches