Merge lp:~urbanape/ubuntuone-ios-files/auto-upload-pics into lp:ubuntuone-ios-files

Proposed by Zachery Bir
Status: Merged
Merge reported by: Zachery Bir
Merged at revision: not available
Proposed branch: lp:~urbanape/ubuntuone-ios-files/auto-upload-pics
Merge into: lp:ubuntuone-ios-files
Diff against target: 1698 lines (+1018/-63)
26 files modified
Files.xcodeproj/project.pbxproj (+33/-3)
Files/Files-Info.plist (+9/-1)
Files/FilesAppDelegate.m (+89/-29)
Files/Generated/_U1Asset.h (+76/-0)
Files/Generated/_U1Asset.m (+64/-0)
Files/Generated/_U1Volume.h (+16/-1)
Files/Generated/_U1Volume.m (+7/-0)
Files/U1Asset.h (+5/-0)
Files/U1Asset.m (+7/-0)
Files/U1AssetUploadOperation.h (+25/-0)
Files/U1AssetUploadOperation.m (+74/-0)
Files/U1DataRepository.h (+5/-1)
Files/U1DataRepository.m (+53/-0)
Files/U1FilePreviewViewController.m (+1/-0)
Files/U1Files.xcdatamodeld/.xccurrentversion (+1/-1)
Files/U1FilesClient.h (+6/-1)
Files/U1FilesClient.m (+124/-8)
Files/U1FilesService.h (+5/-3)
Files/U1FilesService.m (+77/-1)
Files/U1FolderViewController.m (+1/-1)
Files/U1LocalAssetsManager.h (+29/-0)
Files/U1LocalAssetsManager.m (+209/-0)
Files/U1Volume.h (+18/-1)
Files/U1Volume.m (+53/-1)
Files/U1VolumesViewController.h (+4/-3)
Files/U1VolumesViewController.m (+27/-8)
To merge this branch: bzr merge lp:~urbanape/ubuntuone-ios-files/auto-upload-pics
Reviewer Review Type Date Requested Status
Jason Foreman (community) Approve
Review via email: mp+77757@code.launchpad.net

Description of the change

This branch adds rough auto-upload of the user's photo library (only photos, no video).

It provides no feedback or controls to cancel the operation yet, and is very, very chatty.

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

Also, this branch Singletonizes a bunch of the classes we use.

36. By Zachery Bir

Renamed to match the new bundle version

37. By Zachery Bir

Renamed data model

38. By Zachery Bir

Updated bundle version

39. By Zachery Bir

No longer a private property

40. By Zachery Bir

Using a FilesClient, rather than the bare FilesService

41. By Zachery Bir

Move the static variable outside the implementation, and close an errant code block

42. By Zachery Bir

Uncomment the icon block

43. By Zachery Bir

Updates to make build more better good.

Revision history for this message
Jason Foreman (threeve) wrote :

I've ended up un-singletonizing (just the bits about overriding retain/release) in a later changeset, but otherwise looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Files.xcodeproj/project.pbxproj'
2--- Files.xcodeproj/project.pbxproj 2011-09-27 14:36:00 +0000
3+++ Files.xcodeproj/project.pbxproj 2011-10-04 14:38:28 +0000
4@@ -7,6 +7,11 @@
5 objects = {
6
7 /* Begin PBXBuildFile section */
8+ 91A5E2DC142A70DF00EAAC2B /* U1LocalAssetsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 91A5E2DB142A70DF00EAAC2B /* U1LocalAssetsManager.m */; };
9+ 91A5E2DF142B727600EAAC2B /* U1AssetUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 91A5E2DE142B727500EAAC2B /* U1AssetUploadOperation.m */; };
10+ 91B3F2D5141E87F900939B3C /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91B3F2D4141E87F900939B3C /* AssetsLibrary.framework */; };
11+ 91B3F2DC141FDFAB00939B3C /* _U1Asset.m in Sources */ = {isa = PBXBuildFile; fileRef = 91B3F2DB141FDFAB00939B3C /* _U1Asset.m */; };
12+ 91B3F2DF141FDFBE00939B3C /* U1Asset.m in Sources */ = {isa = PBXBuildFile; fileRef = 91B3F2DE141FDFBE00939B3C /* U1Asset.m */; };
13 960D46131409E0E100B73177 /* PullRefreshTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 960D46001409E09F00B73177 /* PullRefreshTableViewController.m */; };
14 960D46141409E0F000B73177 /* arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 960D45FB1409E09F00B73177 /* arrow.png */; };
15 960D46161409E10200B73177 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 960D46151409E10200B73177 /* QuartzCore.framework */; };
16@@ -116,6 +121,15 @@
17 /* End PBXContainerItemProxy section */
18
19 /* Begin PBXFileReference section */
20+ 91A5E2DB142A70DF00EAAC2B /* U1LocalAssetsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1LocalAssetsManager.m; sourceTree = "<group>"; };
21+ 91A5E2DD142B727500EAAC2B /* U1AssetUploadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1AssetUploadOperation.h; sourceTree = "<group>"; };
22+ 91A5E2DE142B727500EAAC2B /* U1AssetUploadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1AssetUploadOperation.m; sourceTree = "<group>"; };
23+ 91B3F2D4141E87F900939B3C /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
24+ 91B3F2D7141FC9BE00939B3C /* U1LocalAssetsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1LocalAssetsManager.h; sourceTree = "<group>"; };
25+ 91B3F2DA141FDFAB00939B3C /* _U1Asset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _U1Asset.h; sourceTree = "<group>"; };
26+ 91B3F2DB141FDFAB00939B3C /* _U1Asset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _U1Asset.m; sourceTree = "<group>"; };
27+ 91B3F2DD141FDFBE00939B3C /* U1Asset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1Asset.h; sourceTree = "<group>"; };
28+ 91B3F2DE141FDFBE00939B3C /* U1Asset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1Asset.m; sourceTree = "<group>"; };
29 960D45FA1409E09F00B73177 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
30 960D45FB1409E09F00B73177 /* arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = arrow.png; sourceTree = "<group>"; };
31 960D45FD1409E09F00B73177 /* DemoTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoTableViewController.h; sourceTree = "<group>"; };
32@@ -194,6 +208,7 @@
33 96A1699F1430C9C900E4C990 /* 265-download@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "265-download@2x.png"; path = "Assets/265-download@2x.png"; sourceTree = "<group>"; };
34 96A169A21430D53600E4C990 /* U1LocalFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = U1LocalFileInfo.h; sourceTree = "<group>"; };
35 96A169A31430D53600E4C990 /* U1LocalFileInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = U1LocalFileInfo.m; sourceTree = "<group>"; };
36+ 96A56430143265B4007A1A93 /* U1Files 1.0-3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "U1Files 1.0-3.xcdatamodel"; sourceTree = "<group>"; };
37 96CC17C71417E5E400EFC1BA /* U1Files.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = U1Files.xcdatamodel; sourceTree = "<group>"; };
38 96CC17CB1417EA0700EFC1BA /* _U1FileNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _U1FileNode.h; sourceTree = "<group>"; };
39 96CC17CC1417EA0700EFC1BA /* _U1FileNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _U1FileNode.m; sourceTree = "<group>"; };
40@@ -292,6 +307,7 @@
41 isa = PBXFrameworksBuildPhase;
42 buildActionMask = 2147483647;
43 files = (
44+ 91B3F2D5141E87F900939B3C /* AssetsLibrary.framework in Frameworks */,
45 96CC17D81417EA2B00EFC1BA /* CoreData.framework in Frameworks */,
46 96733B12140DFD6D0074D545 /* Security.framework in Frameworks */,
47 960D46161409E10200B73177 /* QuartzCore.framework in Frameworks */,
48@@ -350,6 +366,8 @@
49 96CC17CA1417E9E500EFC1BA /* Generated */,
50 96CC17DF1418091100EFC1BA /* Parsers */,
51 96CC17C61417E5E400EFC1BA /* U1Files.xcdatamodeld */,
52+ 91B3F2DD141FDFBE00939B3C /* U1Asset.h */,
53+ 91B3F2DE141FDFBE00939B3C /* U1Asset.m */,
54 96CC17D91417EBF500EFC1BA /* U1Volume.h */,
55 96CC17DA1417EBF500EFC1BA /* U1Volume.m */,
56 960D4629140D99DB00B73177 /* U1Node.h */,
57@@ -407,6 +425,8 @@
58 96CC17CA1417E9E500EFC1BA /* Generated */ = {
59 isa = PBXGroup;
60 children = (
61+ 91B3F2DA141FDFAB00939B3C /* _U1Asset.h */,
62+ 91B3F2DB141FDFAB00939B3C /* _U1Asset.m */,
63 96CC17CB1417EA0700EFC1BA /* _U1FileNode.h */,
64 96CC17CC1417EA0700EFC1BA /* _U1FileNode.m */,
65 96CC17CD1417EA0700EFC1BA /* _U1FolderNode.h */,
66@@ -451,6 +471,7 @@
67 96E860B413F7251D0026783D /* Frameworks */ = {
68 isa = PBXGroup;
69 children = (
70+ 91B3F2D4141E87F900939B3C /* AssetsLibrary.framework */,
71 96CC17D71417EA2B00EFC1BA /* CoreData.framework */,
72 96733B11140DFD6D0074D545 /* Security.framework */,
73 960D46151409E10200B73177 /* QuartzCore.framework */,
74@@ -486,6 +507,8 @@
75 96E8614213F728380026783D /* U1FilesService.m */,
76 96CC17E314180C5F00EFC1BA /* U1FilesClient.h */,
77 96CC17E414180C5F00EFC1BA /* U1FilesClient.m */,
78+ 91B3F2D7141FC9BE00939B3C /* U1LocalAssetsManager.h */,
79+ 91A5E2DB142A70DF00EAAC2B /* U1LocalAssetsManager.m */,
80 969EF22613F8C10C00CEF6CB /* U1VolumesViewController.h */,
81 969EF22713F8C10C00CEF6CB /* U1VolumesViewController.m */,
82 969EF22813F8C10C00CEF6CB /* U1VolumesViewController.xib */,
83@@ -498,6 +521,8 @@
84 960D4624140D8AF200B73177 /* U1ByteSizeValueTransformer.m */,
85 960D4626140D8B4400B73177 /* U1FriendlyDateValueTransformer.h */,
86 960D4627140D8B4400B73177 /* U1FriendlyDateValueTransformer.m */,
87+ 91A5E2DD142B727500EAAC2B /* U1AssetUploadOperation.h */,
88+ 91A5E2DE142B727500EAAC2B /* U1AssetUploadOperation.m */,
89 965D7EB71429690C00E4754F /* U1NavigationBar.h */,
90 965D7EB81429690C00E4754F /* U1NavigationBar.m */,
91 96A169A21430D53600E4C990 /* U1LocalFileInfo.h */,
92@@ -818,6 +843,10 @@
93 96CC17DE1417EF7200EFC1BA /* U1DataRepository.m in Sources */,
94 96CC17E21418094800EFC1BA /* U1NodeJSONParser.m in Sources */,
95 96CC17E514180C6000EFC1BA /* U1FilesClient.m in Sources */,
96+ 91B3F2DC141FDFAB00939B3C /* _U1Asset.m in Sources */,
97+ 91B3F2DF141FDFBE00939B3C /* U1Asset.m in Sources */,
98+ 91A5E2DC142A70DF00EAAC2B /* U1LocalAssetsManager.m in Sources */,
99+ 91A5E2DF142B727600EAAC2B /* U1AssetUploadOperation.m in Sources */,
100 965D7EB91429690C00E4754F /* U1NavigationBar.m in Sources */,
101 96A169A41430D53700E4C990 /* U1LocalFileInfo.m in Sources */,
102 );
103@@ -939,14 +968,14 @@
104 96E860F013F7251D0026783D /* Release */ = {
105 isa = XCBuildConfiguration;
106 buildSettings = {
107- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
108+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Zachery Bir (P7WDPDEFHU)";
109 GCC_PRECOMPILE_PREFIX_HEADER = YES;
110 GCC_PREFIX_HEADER = "Files/Files-Prefix.pch";
111 GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
112 INFOPLIST_FILE = "Files/Files-Info.plist";
113 IPHONEOS_DEPLOYMENT_TARGET = 4.0;
114 PRODUCT_NAME = "$(TARGET_NAME)";
115- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
116+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "84E0B7E6-02A2-46EF-9C76-AA80C48130A7";
117 TARGETED_DEVICE_FAMILY = 1;
118 WRAPPER_EXTENSION = app;
119 };
120@@ -1022,9 +1051,10 @@
121 96CC17C61417E5E400EFC1BA /* U1Files.xcdatamodeld */ = {
122 isa = XCVersionGroup;
123 children = (
124+ 96A56430143265B4007A1A93 /* U1Files 1.0-3.xcdatamodel */,
125 96CC17C71417E5E400EFC1BA /* U1Files.xcdatamodel */,
126 );
127- currentVersion = 96CC17C71417E5E400EFC1BA /* U1Files.xcdatamodel */;
128+ currentVersion = 96A56430143265B4007A1A93 /* U1Files 1.0-3.xcdatamodel */;
129 path = U1Files.xcdatamodeld;
130 sourceTree = "<group>";
131 versionGroupType = wrapper.xcdatamodel;
132
133=== modified file 'Files/Files-Info.plist'
134--- Files/Files-Info.plist 2011-09-27 14:42:09 +0000
135+++ Files/Files-Info.plist 2011-10-04 14:38:28 +0000
136@@ -6,6 +6,8 @@
137 <string>en</string>
138 <key>CFBundleDisplayName</key>
139 <string>${PRODUCT_NAME}</string>
140+ <key>CFBundleDocumentTypes</key>
141+ <array/>
142 <key>CFBundleExecutable</key>
143 <string>${EXECUTABLE_NAME}</string>
144 <key>CFBundleIconFile</key>
145@@ -22,8 +24,10 @@
146 <string>1.0</string>
147 <key>CFBundleSignature</key>
148 <string>????</string>
149+ <key>CFBundleURLTypes</key>
150+ <array/>
151 <key>CFBundleVersion</key>
152- <string>2</string>
153+ <string>3</string>
154 <key>LSRequiresIPhoneOS</key>
155 <true/>
156 <key>NSMainNibFile</key>
157@@ -43,5 +47,9 @@
158 <string>UIInterfaceOrientationLandscapeLeft</string>
159 <string>UIInterfaceOrientationLandscapeRight</string>
160 </array>
161+ <key>UTExportedTypeDeclarations</key>
162+ <array/>
163+ <key>UTImportedTypeDeclarations</key>
164+ <array/>
165 </dict>
166 </plist>
167
168=== modified file 'Files/FilesAppDelegate.m'
169--- Files/FilesAppDelegate.m 2011-09-27 14:36:00 +0000
170+++ Files/FilesAppDelegate.m 2011-10-04 14:38:28 +0000
171@@ -13,6 +13,10 @@
172 // You should have received a copy of the GNU Affero General Public License
173 // along with this program. If not, see <http://www.gnu.org/licenses/>.
174
175+#import <AssetsLibrary/ALAssetsLibrary.h>
176+#import <AssetsLibrary/ALAssetsGroup.h>
177+#import <AssetsLibrary/ALAsset.h>
178+
179 #import "FilesAppDelegate.h"
180
181 #import "U1AccountManager.h"
182@@ -20,35 +24,41 @@
183 #import "U1FileNode.h"
184 #import "U1FilePreviewViewController.h"
185 #import "U1FilesClient.h"
186+#import "U1LocalAssetsManager.h"
187 #import "U1FilesService.h"
188 #import "U1FolderNode.h"
189 #import "U1FolderViewController.h"
190 #import "U1LoginController.h"
191 #import "U1Node.h"
192 #import "U1VolumesViewController.h"
193+#import "U1Volume.h"
194
195
196 @interface FilesAppDelegate () <U1FolderViewControllerDelegate, U1VolumesViewControllerDelegate, U1LoginControllerDelegate>
197 @property (retain) U1FilesService *filesService;
198 @property (retain) U1FilesClient *filesClient;
199+@property (retain) U1LocalAssetsManager *localAssetsManager;
200 @property (retain) U1LoginController *loginController;
201 @property (retain) U1DataRepository *dataRepository;
202-- (NSURL*)dataStoreURL;
203 - (void)showVolumes;
204 - (void)showLoginController;
205+- (void)ensureUploadFolder;
206+- (NSString*)remoteUploadFolderPath;
207+- (NSString*)localUploadQueuePath;
208 @end
209
210
211 @implementation FilesAppDelegate
212
213 @synthesize window;
214-@synthesize filesService, filesClient, navController, loginController, dataRepository;
215+@synthesize filesService, filesClient, localAssetsManager, navController, loginController, dataRepository;
216
217 - (void)dealloc
218 {
219 [window release];
220 [filesService release];
221 [filesClient release];
222+ [localAssetsManager release];
223 [navController release];
224 [loginController release];
225 [dataRepository release];
226@@ -59,51 +69,104 @@
227 {
228 [self.window makeKeyAndVisible];
229
230+ return YES;
231+}
232+
233+- (void)applicationDidBecomeActive:(UIApplication *)application;
234+{
235 if (![[U1AccountManager sharedAccountManager] hasCredentials])
236 {
237 [self showLoginController];
238 }
239 else
240 {
241- [self showVolumes];
242+ self.filesService = [U1FilesService sharedFilesService];
243+ self.filesClient = [U1FilesClient sharedFilesClient];
244+
245+ self.localAssetsManager = [U1LocalAssetsManager sharedFilesManager];
246+ NSLog(@"Showing Volumes: Reading localAssetsToUpload from filesystem.");
247+ NSMutableArray *uploadQueue = [NSMutableArray arrayWithContentsOfFile:[self localUploadQueuePath]];
248+ if (uploadQueue != nil) {
249+ NSLog(@"Retrieved the upload queue from the filesystem.");
250+ NSLog(@"Length of the array on filesystem: %d", [uploadQueue count]);
251+ self.localAssetsManager.localAssetsToUpload = uploadQueue;
252+ NSLog(@"Read in the file, now we delete it.");
253+ NSError *error = nil;
254+ [[NSFileManager defaultManager] removeItemAtPath:[self localUploadQueuePath] error:&error];
255+ }
256+
257+ [self ensureUploadFolder];
258 }
259-
260- return YES;
261+}
262+
263+- (void)ensureUploadFolder;
264+{
265+ [self.filesClient volumesWithCompletionBlock:^(NSArray *volumeInfos, NSError *error) {
266+ dispatch_async(dispatch_get_main_queue(), ^(void) {
267+ // iterate over volumeInfos. If there isn't one there called "Pictures - <device name>", create it.
268+ BOOL found = NO;
269+ NSLog(@"Walking the volumes, to see if it has been created already.");
270+ for (U1Volume *volume in volumeInfos) {
271+ if ([volume.path isEqualToString:[self remoteUploadFolderPath]])
272+ {
273+ NSLog(@"Found the volume. No need to try to create it.");
274+ found = YES;
275+ self.localAssetsManager.remoteUploadVolume = volume;
276+ break;
277+ }
278+ }
279+ if (!found)
280+ {
281+ NSLog(@"Didn't find the volume. Creating it.");
282+ [self.filesClient createVolumeAtPath:[self remoteUploadFolderPath]
283+ completionBlock:^(U1Volume *volume, NSError *error) {
284+ self.localAssetsManager.remoteUploadVolume = volume;
285+ [self.localAssetsManager uploadPendingAssets];
286+ [self.localAssetsManager checkForNewAssets];
287+ NSLog(@"Error: %@", error);
288+ }];
289+ } else
290+ {
291+ [self.localAssetsManager uploadPendingAssets];
292+ [self.localAssetsManager checkForNewAssets];
293+ }
294+ [self showVolumes];
295+ });
296+ }];
297+}
298+
299+- (NSString *)remoteUploadFolderPath;
300+{
301+ return [NSString stringWithFormat:@"/~/Pictures - %@", [[UIDevice currentDevice] name]];
302+}
303+
304+- (NSString*)localUploadQueuePath;
305+{
306+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
307+ return [[paths objectAtIndex:0] stringByAppendingPathComponent:@"localAssetsToUpload.array"];
308 }
309
310 - (void)applicationWillResignActive:(UIApplication *)application;
311 {
312 [self.dataRepository save:NULL];
313+ NSLog(@"Application Resigning: Writing localAssetsToUpload (%d items) to the filesystem.", [self.localAssetsManager.localAssetsToUpload count]);
314+ [self.localAssetsManager.localAssetsToUpload writeToFile:[self localUploadQueuePath] atomically:YES];
315+ // serialize self.filesManager's localAssetsToUpload to the filesystem
316 }
317
318 - (void)applicationWillTerminate:(UIApplication *)application;
319 {
320 // TODO: check error
321 [self.dataRepository save:NULL];
322-}
323-
324-- (NSURL*)dataStoreURL;
325-{
326- NSURL *cachesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
327- NSURL *storeURL = [NSURL URLWithString:@"U1Files.sqlite" relativeToURL:cachesDirectory];
328- return storeURL;
329+ NSLog(@"Application Terminating: Writing localAssetsToUpload (%d items) to the filesystem.", [self.localAssetsManager.localAssetsToUpload count]);
330+ [self.localAssetsManager.localAssetsToUpload writeToFile:[self localUploadQueuePath] atomically:YES];
331+ // serialize self.filesManager's localAssetsToUpload to the filesystem
332 }
333
334 - (void)showVolumes;
335 {
336- filesService = [U1FilesService new];
337-
338- NSURL *storeURL = [self dataStoreURL];
339- self.dataRepository = [[U1DataRepository alloc] initWithStoreURL:storeURL];
340- [dataRepository release];
341-
342- filesClient = [[U1FilesClient alloc] init];
343- [self.filesClient setFilesService:filesService];
344- [self.filesClient setDataRepository:self.dataRepository];
345-
346 U1VolumesViewController *rootController = [U1VolumesViewController new];
347 rootController.delegate = self;
348- rootController.filesService = self.filesService;
349
350 [self.navController setViewControllers:[NSArray arrayWithObject:rootController] animated:NO];
351 }
352@@ -119,7 +182,7 @@
353 {
354 [self.navController dismissModalViewControllerAnimated:YES];
355 self.loginController = nil;
356- [self showVolumes];
357+ [self applicationDidBecomeActive:nil];
358 }
359
360
361@@ -130,14 +193,12 @@
362 if ([node isFolder])
363 {
364 U1FolderViewController *nextFolderController = [[U1FolderViewController alloc] initWithNodeResourcePath:node.resourcePath];
365- nextFolderController.filesClient = self.filesClient;
366 nextFolderController.delegate = self;
367 [self.navController pushViewController:nextFolderController animated:YES];
368 }
369 else if ([node isFile])
370 {
371 U1FilePreviewViewController *fileController = [[U1FilePreviewViewController alloc] initWithNode:(id)node];
372- fileController.filesClient = self.filesClient;
373 [self.navController pushViewController:fileController animated:YES];
374 }
375 }
376@@ -145,11 +206,10 @@
377
378 #pragma mark U1VolumesControllerDelegate Methods
379
380-- (void)volumesController:(U1VolumesViewController *)volumesController didSelectVolume:(NSDictionary *)volume;
381+- (void)volumesController:(U1VolumesViewController *)volumesController didSelectVolume:(U1Volume *)volume;
382 {
383- NSString *nodePath = [volume objectForKey:@"node_path"];
384+ NSString *nodePath = volume.nodePath;
385 U1FolderViewController *folderController = [[U1FolderViewController alloc] initWithNodeResourcePath:nodePath];
386- folderController.filesClient = self.filesClient;
387 folderController.delegate = self;
388 [self.navController pushViewController:folderController animated:YES];
389 }
390
391=== added file 'Files/Generated/_U1Asset.h'
392--- Files/Generated/_U1Asset.h 1970-01-01 00:00:00 +0000
393+++ Files/Generated/_U1Asset.h 2011-10-04 14:38:28 +0000
394@@ -0,0 +1,76 @@
395+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
396+// Make changes to U1Asset.h instead.
397+
398+#import <CoreData/CoreData.h>
399+
400+
401+
402+
403+
404+
405+
406+@interface U1AssetID : NSManagedObjectID {}
407+@end
408+
409+@interface _U1Asset : NSManagedObject {}
410++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
411++ (NSString*)entityName;
412++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
413+- (U1AssetID*)objectID;
414+
415+
416+
417+
418+@property (nonatomic, retain) NSString *filename;
419+
420+
421+//- (BOOL)validateFilename:(id*)value_ error:(NSError**)error_;
422+
423+
424+
425+
426+@property (nonatomic, retain) NSString *groupId;
427+
428+
429+//- (BOOL)validateGroupId:(id*)value_ error:(NSError**)error_;
430+
431+
432+
433+
434+@property (nonatomic, retain) NSString *url;
435+
436+
437+//- (BOOL)validateUrl:(id*)value_ error:(NSError**)error_;
438+
439+
440+
441+
442+
443+@end
444+
445+@interface _U1Asset (CoreDataGeneratedAccessors)
446+
447+@end
448+
449+@interface _U1Asset (CoreDataGeneratedPrimitiveAccessors)
450+
451+
452+- (NSString*)primitiveFilename;
453+- (void)setPrimitiveFilename:(NSString*)value;
454+
455+
456+
457+
458+- (NSString*)primitiveGroupId;
459+- (void)setPrimitiveGroupId:(NSString*)value;
460+
461+
462+
463+
464+- (NSString*)primitiveUrl;
465+- (void)setPrimitiveUrl:(NSString*)value;
466+
467+
468+
469+
470+@end
471
472=== added file 'Files/Generated/_U1Asset.m'
473--- Files/Generated/_U1Asset.m 1970-01-01 00:00:00 +0000
474+++ Files/Generated/_U1Asset.m 2011-10-04 14:38:28 +0000
475@@ -0,0 +1,64 @@
476+// DO NOT EDIT. This file is machine-generated and constantly overwritten.
477+// Make changes to U1Asset.m instead.
478+
479+#import "_U1Asset.h"
480+
481+@implementation U1AssetID
482+@end
483+
484+@implementation _U1Asset
485+
486++ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
487+ NSParameterAssert(moc_);
488+ return [NSEntityDescription insertNewObjectForEntityForName:@"Asset" inManagedObjectContext:moc_];
489+}
490+
491++ (NSString*)entityName {
492+ return @"Asset";
493+}
494+
495++ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ {
496+ NSParameterAssert(moc_);
497+ return [NSEntityDescription entityForName:@"Asset" inManagedObjectContext:moc_];
498+}
499+
500+- (U1AssetID*)objectID {
501+ return (U1AssetID*)[super objectID];
502+}
503+
504++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
505+ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
506+
507+
508+ return keyPaths;
509+}
510+
511+
512+
513+
514+@dynamic filename;
515+
516+
517+
518+
519+
520+
521+@dynamic groupId;
522+
523+
524+
525+
526+
527+
528+@dynamic url;
529+
530+
531+
532+
533+
534+
535+
536+
537+
538+
539+@end
540
541=== modified file 'Files/Generated/_U1Volume.h'
542--- Files/Generated/_U1Volume.h 2011-09-08 16:59:34 +0000
543+++ Files/Generated/_U1Volume.h 2011-10-04 14:38:28 +0000
544@@ -2,6 +2,7 @@
545 // Make changes to U1Volume.h instead.
546
547 #import <CoreData/CoreData.h>
548+#import "U1Node.h"
549
550
551
552@@ -9,7 +10,7 @@
553 @interface U1VolumeID : NSManagedObjectID {}
554 @end
555
556-@interface _U1Volume : NSManagedObject {}
557+@interface _U1Volume : U1Node {}
558 + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;
559 + (NSString*)entityName;
560 + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_;
561@@ -18,6 +19,14 @@
562
563
564
565+@property (nonatomic, retain) NSString *nodePath;
566+
567+
568+//- (BOOL)validateNodePath:(id*)value_ error:(NSError**)error_;
569+
570+
571+
572+
573
574 @end
575
576@@ -28,4 +37,10 @@
577 @interface _U1Volume (CoreDataGeneratedPrimitiveAccessors)
578
579
580+- (NSString*)primitiveNodePath;
581+- (void)setPrimitiveNodePath:(NSString*)value;
582+
583+
584+
585+
586 @end
587
588=== modified file 'Files/Generated/_U1Volume.m'
589--- Files/Generated/_U1Volume.m 2011-09-08 16:59:34 +0000
590+++ Files/Generated/_U1Volume.m 2011-10-04 14:38:28 +0000
591@@ -36,6 +36,13 @@
592
593
594
595+@dynamic nodePath;
596+
597+
598+
599+
600+
601+
602
603
604
605
606=== added file 'Files/U1Asset.h'
607--- Files/U1Asset.h 1970-01-01 00:00:00 +0000
608+++ Files/U1Asset.h 2011-10-04 14:38:28 +0000
609@@ -0,0 +1,5 @@
610+#import "_U1Asset.h"
611+
612+@interface U1Asset : _U1Asset {}
613+// Custom logic goes here.
614+@end
615
616=== added file 'Files/U1Asset.m'
617--- Files/U1Asset.m 1970-01-01 00:00:00 +0000
618+++ Files/U1Asset.m 2011-10-04 14:38:28 +0000
619@@ -0,0 +1,7 @@
620+#import "U1Asset.h"
621+
622+@implementation U1Asset
623+
624+// Custom logic goes here.
625+
626+@end
627
628=== added file 'Files/U1AssetUploadOperation.h'
629--- Files/U1AssetUploadOperation.h 1970-01-01 00:00:00 +0000
630+++ Files/U1AssetUploadOperation.h 2011-10-04 14:38:28 +0000
631@@ -0,0 +1,25 @@
632+//
633+// U1AssetUploadOperation.h
634+// Files
635+//
636+// Created by Zachery Bir on 9/22/11.
637+// Copyright 2011 __MyCompanyName__. All rights reserved.
638+//
639+
640+#import <Foundation/Foundation.h>
641+#import <AssetsLibrary/AssetsLibrary.h>
642+
643+@class U1Asset;
644+@class U1FolderNode;
645+
646+@interface U1AssetUploadOperation : NSOperation {
647+@private
648+
649+}
650+@property (retain) ALAssetRepresentation *representation;
651+@property (retain) NSData *assetData;
652+@property (retain) U1Asset *asset;
653+@property (retain) U1FolderNode *folder;
654+@property (retain) NSString *filename;
655+@property (retain) NSError *error;
656+@end
657
658=== added file 'Files/U1AssetUploadOperation.m'
659--- Files/U1AssetUploadOperation.m 1970-01-01 00:00:00 +0000
660+++ Files/U1AssetUploadOperation.m 2011-10-04 14:38:28 +0000
661@@ -0,0 +1,74 @@
662+//
663+// U1AssetUploadOperation.m
664+// Files
665+//
666+// Created by Zachery Bir on 9/22/11.
667+// Copyright 2011 __MyCompanyName__. All rights reserved.
668+//
669+
670+#import "U1AssetUploadOperation.h"
671+#import "U1Asset.h"
672+#import "U1FilesClient.h"
673+
674+@interface U1AssetUploadOperation ()
675+@property (getter=isExecuting) BOOL executing;
676+@property (getter=isFinished) BOOL finished;
677+- (void)finishExecuting;
678+@end
679+
680+@implementation U1AssetUploadOperation
681+
682+@synthesize executing, finished, asset, assetData, representation, folder, filename, error;
683+
684+- (id)init
685+{
686+ self = [super init];
687+ if (self) {
688+ // Initialization code here.
689+ }
690+
691+ return self;
692+}
693+
694+- (BOOL)isConcurrent;
695+{
696+ return YES;
697+}
698+
699+- (void)finishExecuting;
700+{
701+ [self willChangeValueForKey:@"isExecuting"];
702+ [self willChangeValueForKey:@"isFinished"];
703+ self.finished = YES;
704+ self.executing = NO;
705+ [self didChangeValueForKey:@"isFinished"];
706+ [self didChangeValueForKey:@"isExecuting"];
707+// [UONetworkStatusCoordinator removeNetworkActivity];
708+}
709+
710+- (void)start;
711+{
712+ // Trigger the filesClient to upload our asset, setting the completion block to marking ourself completed.
713+ [self willChangeValueForKey:@"isExecuting"];
714+ self.executing = YES;
715+ [self didChangeValueForKey:@"isExecuting"];
716+ dispatch_async(dispatch_get_main_queue(), ^(void) {
717+ U1FilesClient *filesClient = [U1FilesClient sharedFilesClient];
718+ [filesClient uploadContentData:self.assetData
719+ toFolder:self.folder withResourceName:self.filename progressBlock:^(long long bytesUploaded, long long totalBytes) {
720+ dispatch_async(dispatch_get_main_queue(), ^(void){
721+ NSLog(@"Uploaded %lld of %lld for %@", bytesUploaded, totalBytes, self.filename);
722+ });
723+ } completionBlock:^(U1FileNode *updatedNode, NSError *uploadError) {
724+ self.error = uploadError;
725+ [self finishExecuting];
726+ }];
727+ });
728+}
729+
730+- (void)dealloc
731+{
732+ [super dealloc];
733+}
734+
735+@end
736
737=== modified file 'Files/U1DataRepository.h'
738--- Files/U1DataRepository.h 2011-09-08 16:59:34 +0000
739+++ Files/U1DataRepository.h 2011-10-04 14:38:28 +0000
740@@ -20,12 +20,16 @@
741
742
743 @interface U1DataRepository : NSObject
744-
745++ (U1DataRepository*)sharedDataRepository;
746 - (id)initWithStoreURL:(NSURL*)storeURL;
747 - (BOOL)save:(NSError**)error;
748
749 - (U1Node*)nodeWithResourcePath:(NSString*)resourcePath;
750
751+//- (void)fetchResultsForEntityClass:(Class)entityClass matchingPredicate:(NSPredicate*)predicate withSortDescriptors:(NSArray*)sortDescriptors completionBlock:(void(^)(NSArray *results, NSError *error))completionBlock;
752+
753+- (NSArray*)resultsForEntityClass:(Class)entityClass matchingPredicate:(NSPredicate*)predicate withSortDescriptors:(NSArray*)sortDescriptors error:(NSError**)error;
754+
755 - (void)dispatchBlockWithManagedObjectContext:(void(^)(NSManagedObjectContext *context))block;
756
757 - (void)dispatchAsyncBlockWithManagedObjectContext:(void(^)(NSManagedObjectContext *context))block;
758
759=== modified file 'Files/U1DataRepository.m'
760--- Files/U1DataRepository.m 2011-09-08 16:59:34 +0000
761+++ Files/U1DataRepository.m 2011-10-04 14:38:28 +0000
762@@ -19,6 +19,7 @@
763
764 #import "U1Node.h"
765
766+static U1DataRepository *sharedDataRepository = nil;
767
768 @interface U1DataRepository ()
769
770@@ -35,6 +36,48 @@
771
772 @synthesize model, mainContext, storeCoordinator;
773
774++ (U1DataRepository*)sharedDataRepository;
775+{
776+ if (sharedDataRepository == nil)
777+ {
778+ NSURL *cachesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory
779+ inDomains:NSUserDomainMask] lastObject];
780+ NSURL *storeURL = [NSURL URLWithString:@"U1Files.sqlite" relativeToURL:cachesDirectory];
781+ sharedDataRepository = [[super allocWithZone:NULL] initWithStoreURL:storeURL];
782+ }
783+ return sharedDataRepository;
784+}
785+
786++ (id)allocWithZone:(NSZone *)zone;
787+{
788+ return [[self sharedDataRepository] retain];
789+}
790+
791+- (id)copyWithZone:(NSZone *)zone;
792+{
793+ return self;
794+}
795+
796+- (id)retain;
797+{
798+ return self;
799+}
800+
801+- (NSUInteger)retainCount;
802+{
803+ return NSUIntegerMax;
804+}
805+
806+- (void)release;
807+{
808+
809+}
810+
811+- (id)autorelease;
812+{
813+ return self;
814+}
815+
816 - (id)initWithStoreURL:(NSURL*)storeURL;
817 {
818 if (!(self = [super init]))
819@@ -93,6 +136,16 @@
820 return node;
821 }
822
823+//- (void)fetchResultsForEntityClass:(Class)entityClass matchingPredicate:(NSPredicate*)predicate withSortDescriptors:(NSArray*)sortDescriptors completionBlock:(void(^)(NSArray *results, NSError *error))completionBlock;
824+//{
825+//
826+//}
827+
828+- (NSArray*)resultsForEntityClass:(Class)entityClass matchingPredicate:(NSPredicate*)predicate withSortDescriptors:(NSArray*)sortDescriptors error:(NSError**)error;
829+{
830+ return [self fetchEntitiesForClass:entityClass inContext:self.mainContext withPredicate:predicate sortDescriptors:sortDescriptors error:error];
831+}
832+
833 - (NSArray*)fetchEntitiesForClass:(Class)class inContext:(NSManagedObjectContext*)context withPredicate:(NSPredicate*)predicate sortDescriptors:(NSArray*)sortDescriptors error:(NSError**)error;
834 {
835 NSParameterAssert(class != nil);
836
837=== modified file 'Files/U1FilePreviewViewController.m'
838--- Files/U1FilePreviewViewController.m 2011-09-27 14:36:00 +0000
839+++ Files/U1FilePreviewViewController.m 2011-10-04 14:38:28 +0000
840@@ -78,6 +78,7 @@
841 self = [super initWithNibName:@"U1FilePreviewViewController" bundle:nil];
842 if (self == nil)
843 return nil;
844+ filesClient = [U1FilesClient sharedFilesClient];
845 self.node = theNode;
846 self.title = [node.path lastPathComponent];
847
848
849=== modified file 'Files/U1Files.xcdatamodeld/.xccurrentversion'
850--- Files/U1Files.xcdatamodeld/.xccurrentversion 2011-09-08 16:59:34 +0000
851+++ Files/U1Files.xcdatamodeld/.xccurrentversion 2011-10-04 14:38:28 +0000
852@@ -3,6 +3,6 @@
853 <plist version="1.0">
854 <dict>
855 <key>_XCCurrentVersionName</key>
856- <string>U1Files.xcdatamodel</string>
857+ <string>U1Files 1.0-3.xcdatamodel</string>
858 </dict>
859 </plist>
860
861=== added directory 'Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel'
862=== added file 'Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/elements'
863Binary files Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/elements 1970-01-01 00:00:00 +0000 and Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/elements 2011-10-04 14:38:28 +0000 differ
864=== added file 'Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/layout'
865Binary files Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/layout 1970-01-01 00:00:00 +0000 and Files/U1Files.xcdatamodeld/U1Files 1.0-3.xcdatamodel/layout 2011-10-04 14:38:28 +0000 differ
866=== modified file 'Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/elements'
867Binary files Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/elements 2011-09-08 16:59:34 +0000 and Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/elements 2011-10-04 14:38:28 +0000 differ
868=== modified file 'Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/layout'
869Binary files Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/layout 2011-09-08 16:59:34 +0000 and Files/U1Files.xcdatamodeld/U1Files.xcdatamodel/layout 2011-10-04 14:38:28 +0000 differ
870=== modified file 'Files/U1FilesClient.h'
871--- Files/U1FilesClient.h 2011-09-27 14:36:00 +0000
872+++ Files/U1FilesClient.h 2011-10-04 14:38:28 +0000
873@@ -16,7 +16,7 @@
874 #import <Foundation/Foundation.h>
875
876 @class NSFetchedResultsController;
877-@class U1Node, U1FileNode, U1FolderNode, U1NodeChildrenResultSet, U1LocalFileInfo;
878+@class U1Node, U1FileNode, U1FolderNode, U1NodeChildrenResultSet, U1Volume, U1LocalFileInfo;
879 @class U1DataRepository, U1FilesService;
880
881
882@@ -25,11 +25,16 @@
883 @property (retain) U1DataRepository *dataRepository;
884 @property (retain) U1FilesService *filesService;
885
886++ (U1FilesClient *)sharedFilesClient;
887+
888+- (id)volumesWithCompletionBlock:(void(^)(NSArray *volumes, NSError *error))completionBlock;
889 - (id)fetchNodeWithResourcePath:(NSString*)nodePath completionBlock:(void(^)(U1Node *node, NSFetchedResultsController *childrenResultsController, NSError *error))completionBlock;
890 - (id)deleteNode:(U1Node*)node completionBlock:(void(^)(NSError *error))completionBlock;
891 - (id)uploadContentData:(NSData*)contentData toFolder:(U1FolderNode*)folderNode withResourceName:(NSString*)resourceName progressBlock:(void(^)(long long bytesUploaded, long long totalBytes))progressBlock completionBlock:(void(^)(U1FileNode *updatedNode, NSError *error))completionBlock;
892 - (id)publishNode:(U1FileNode*)node completionBlock:(void(^)(U1FileNode *node, NSError *error))completionBlock;
893 - (id)unpublishNode:(U1FileNode*)node completionBlock:(void(^)(U1FileNode *node, NSError *error))completionBlock;
894+- (id)createFolderNamed:(NSString*)name inFolder:(U1FolderNode*)folderNode completionBlock:(void(^)(U1FolderNode *newFolderNode, NSError *error))completionBlock;
895+- (id)createVolumeAtPath:(NSString*)folderPath completionBlock:(void(^)(U1Volume *volume, NSError *error))completionBlock;
896 - (BOOL)isNodeSavedLocally:(U1FileNode*)node;
897 - (U1LocalFileInfo*)localInfoForNode:(U1FileNode*)node;
898
899
900=== modified file 'Files/U1FilesClient.m'
901--- Files/U1FilesClient.m 2011-09-27 14:36:00 +0000
902+++ Files/U1FilesClient.m 2011-10-04 14:38:28 +0000
903@@ -23,22 +23,93 @@
904 #import "U1FolderNode.h"
905 #import "U1LocalFileInfo.h"
906 #import "U1Node.h"
907+#import "U1Volume.h"
908
909
910 @interface U1FilesClient ()
911 - (NSURL*)fileURLForNode:(U1FileNode*)node;
912 @end
913
914+static U1FilesClient *sharedFilesClient = nil;
915
916 @implementation U1FilesClient
917
918 @synthesize dataRepository, filesService;
919
920-- (void)dealloc;
921-{
922- [dataRepository release];
923- [filesService release];
924- [super dealloc];
925++ (U1FilesClient*)sharedFilesClient;
926+{
927+ if (sharedFilesClient == nil)
928+ {
929+ sharedFilesClient = [[super allocWithZone:NULL] init];
930+ }
931+ return sharedFilesClient;
932+}
933+
934++ (id)allocWithZone:(NSZone *)zone;
935+{
936+ return [[self sharedFilesClient] retain];
937+}
938+
939+- (id)copyWithZone:(NSZone *)zone;
940+{
941+ return self;
942+}
943+
944+- (id)retain;
945+{
946+ return self;
947+}
948+
949+- (NSUInteger)retainCount;
950+{
951+ return NSUIntegerMax;
952+}
953+
954+- (void)release;
955+{
956+
957+}
958+
959+- (id)autorelease;
960+{
961+ return self;
962+}
963+
964+- (id)init;
965+{
966+ self = [super init];
967+ if (self == nil)
968+ return nil;
969+ dataRepository = [U1DataRepository sharedDataRepository];
970+ filesService = [U1FilesService sharedFilesService];
971+ return self;
972+}
973+
974+- (id)volumesWithCompletionBlock:(void(^)(NSArray *volumes, NSError *error))completionBlock;
975+{
976+ NSParameterAssert(completionBlock != NULL);
977+
978+ [self.filesService volumeInfoWithCompletionBlock:^(NSArray *volumeInfos, NSError *error) {
979+
980+ NSMutableArray *volumes = [NSMutableArray array];
981+ [self.dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
982+ for (NSDictionary *volumeInfo in volumeInfos)
983+ {
984+ NSString *path = [volumeInfo objectForKey:@"path"];
985+ NSPredicate *p = [NSPredicate predicateWithFormat:@"path = %@", path];
986+ U1Volume *volume = [[self.dataRepository resultsForEntityClass:[U1Volume class] matchingPredicate:p withSortDescriptors:nil error:NULL] lastObject];
987+ if (!volume)
988+ {
989+ volume = [U1Volume insertInManagedObjectContext:context];
990+ }
991+ [volume updatePropertiesFromJSONDictionary:volumeInfo];
992+ [volumes addObject:volume];
993+ }
994+ [context save:NULL];
995+ }];
996+ completionBlock(volumes, error);
997+ }];
998+ return nil;
999 }
1000
1001 - (id)fetchNodeWithResourcePath:(NSString*)nodePath completionBlock:(void(^)(U1Node *node, NSFetchedResultsController *childrenResultsController, NSError *error))completionBlock;
1002@@ -144,9 +215,18 @@
1003 }
1004
1005 return [self.filesService uploadContentData:contentData forNode:node progressBlock:progressBlock completionBlock:^(NSDictionary *updatedNodeInfo, NSError *error) {
1006-
1007- [node updatePropertiesFromJSONDictionary:updatedNodeInfo];
1008- completionBlock(node, error);
1009+ if (error != nil)
1010+ {
1011+ [self.dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
1012+ [context deleteObject:node];
1013+ }];
1014+ completionBlock(nil, error);
1015+ }
1016+ else
1017+ {
1018+ [node updatePropertiesFromJSONDictionary:updatedNodeInfo];
1019+ completionBlock(node, error);
1020+ }
1021 }];
1022 }
1023
1024@@ -160,6 +240,42 @@
1025 return [self.filesService unpublishNode:node completionBlock:completionBlock];
1026 }
1027
1028+- (id)createFolderNamed:(NSString*)name inFolder:(U1FolderNode*)folderNode completionBlock:(void(^)(U1FolderNode *newFolderNode, NSError *error))completionBlock;
1029+{
1030+ NSString *resourcePath = folderNode.resourcePath;
1031+ resourcePath = [resourcePath stringByAppendingPathComponent:name];
1032+
1033+ U1FolderNode *node = (id)[self.dataRepository nodeWithResourcePath:resourcePath];
1034+ if (!node)
1035+ {
1036+ [self.dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
1037+ U1FolderNode *newNode = [U1FolderNode insertInManagedObjectContext:context];
1038+ newNode.resourcePath = resourcePath;
1039+ newNode.kind = @"directory";
1040+ newNode.contentPath = [folderNode.contentPath stringByAppendingPathComponent:name];
1041+ newNode.parent = folderNode;
1042+ [context save:NULL];
1043+ }];
1044+ node = (id)[self.dataRepository nodeWithResourcePath:resourcePath];
1045+ }
1046+
1047+ return [self.filesService createFolderAtPath:resourcePath completionBlock:^(NSDictionary *nodeInfo, NSError *error) {
1048+ [node updatePropertiesFromJSONDictionary:nodeInfo];
1049+ completionBlock(node, error);
1050+ }];
1051+}
1052+
1053+- (id)createVolumeAtPath:(NSString*)folderPath completionBlock:(void(^)(U1Volume *volume, NSError *error))completionBlock;
1054+{
1055+ return [self.filesService createVolumeAtPath:folderPath completionBlock:^(NSDictionary *volumeInfo, NSError *error) {
1056+ [self.dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
1057+ U1Volume *volume = [U1Volume insertInManagedObjectContext:context];
1058+ [volume updatePropertiesFromJSONDictionary:volumeInfo];
1059+ completionBlock(volume, error);
1060+ }];
1061+ }];
1062+}
1063+
1064 - (BOOL)isNodeSavedLocally:(U1FileNode*)node;
1065 {
1066 NSString *contentPath = node.contentPath;
1067
1068=== modified file 'Files/U1FilesService.h'
1069--- Files/U1FilesService.h 2011-09-08 16:59:34 +0000
1070+++ Files/U1FilesService.h 2011-10-04 14:38:28 +0000
1071@@ -15,12 +15,12 @@
1072
1073 #import <Foundation/Foundation.h>
1074
1075-@class U1Node, U1FileNode;
1076+@class U1Node, U1FileNode, U1FolderNode;
1077
1078
1079 @interface U1FilesService : NSObject
1080-
1081-- (id)volumeInfoWithCompletionBlock:(void(^)(NSArray *volumeInfo, NSError *error))completionBlock;
1082++ (U1FilesService *)sharedFilesService;
1083+- (id)volumeInfoWithCompletionBlock:(void(^)(NSArray *volumeInfos, NSError *error))completionBlock;
1084
1085 - (id)infoForNodeAtResourcePath:(NSString*)resourcePath includeChildren:(BOOL)includeChildren completionBlock:(void(^)(NSDictionary *node, NSError *error))completionBlock;
1086 - (id)infoForNode:(U1Node*)node includeChildren:(BOOL)includeChildren completionBlock:(void(^)(NSDictionary *node, NSError *error))completionBlock;
1087@@ -30,5 +30,7 @@
1088 - (id)deleteNodeAtResourcePath:(NSString*)resourcePath completionBlock:(void(^)(NSError *error))completionBlock;
1089 - (id)publishNode:(U1FileNode*)node completionBlock:(void(^)(U1FileNode *node, NSError *error))completionBlock;
1090 - (id)unpublishNode:(U1FileNode*)node completionBlock:(void(^)(U1FileNode *node, NSError *error))completionBlock;
1091+- (id)createFolderAtPath:(NSString*)folderPath completionBlock:(void(^)(NSDictionary *nodeInfo, NSError *error))completionBlock;
1092+- (id)createVolumeAtPath:(NSString*)folderPath completionBlock:(void(^)(NSDictionary *volumeInfo, NSError *error))completionBlock;
1093
1094 @end
1095
1096=== modified file 'Files/U1FilesService.m'
1097--- Files/U1FilesService.m 2011-09-27 14:36:00 +0000
1098+++ Files/U1FilesService.m 2011-10-04 14:38:28 +0000
1099@@ -23,11 +23,13 @@
1100 #import "U1Node.h"
1101 #import "U1FileNode.h"
1102
1103+static U1FilesService *sharedFilesService = nil;
1104
1105 @interface U1FilesService ()
1106
1107 @property (retain) NSOperationQueue *networkQueue;
1108
1109+- (NSURL*)URLForVolumeWithPath:(NSString*)volumePath;
1110 - (NSURL*)URLForNode:(NSString*)nodePath includeChildren:(BOOL)includeChildren;
1111 - (id)execute:(NSString*)method toURL:(NSURL*)url withParameters:(NSDictionary*)parameters requestBody:(NSString*)body parseResponseBody:(BOOL)parseResponseBody completionBlock:(void(^)(id results, NSError *error))completionBlock;
1112 - (id)execute:(NSString*)method toURL:(NSURL*)url withParameters:(NSDictionary*)parameters requestData:(NSData*)bodyData contentType:(NSString*)contentType parseResponseBody:(BOOL)parseResponseBody completionBlock:(void(^)(id results, NSError *error))completionBlock;
1113@@ -44,6 +46,45 @@
1114
1115 @synthesize networkQueue;
1116
1117++ (U1FilesService*)sharedFilesService;
1118+{
1119+ if (sharedFilesService == nil)
1120+ {
1121+ sharedFilesService = [[super allocWithZone:NULL] init];
1122+ }
1123+ return sharedFilesService;
1124+}
1125+
1126++ (id)allocWithZone:(NSZone *)zone;
1127+{
1128+ return [[self sharedFilesService] retain];
1129+}
1130+
1131+- (id)copyWithZone:(NSZone *)zone;
1132+{
1133+ return self;
1134+}
1135+
1136+- (id)retain;
1137+{
1138+ return self;
1139+}
1140+
1141+- (NSUInteger)retainCount;
1142+{
1143+ return NSUIntegerMax;
1144+}
1145+
1146+- (void)release;
1147+{
1148+
1149+}
1150+
1151+- (id)autorelease;
1152+{
1153+ return self;
1154+}
1155+
1156 - (id)init;
1157 {
1158 self = [super init];
1159@@ -59,7 +100,7 @@
1160 [super dealloc];
1161 }
1162
1163-- (id)volumeInfoWithCompletionBlock:(void(^)(NSArray *volumeInfo, NSError *error))completionBlock;
1164+- (id)volumeInfoWithCompletionBlock:(void(^)(NSArray *volumeInfos, NSError *error))completionBlock;
1165 {
1166 NSURL *volumesURL = [self URLForNode:@"/volumes" includeChildren:NO];
1167 return [self execute:@"GET" toURL:volumesURL withParameters:nil requestBody:nil parseResponseBody:YES completionBlock:^(id results, NSError *error) {
1168@@ -205,9 +246,44 @@
1169 }];
1170 }
1171
1172+- (id)createFolderAtPath:(NSString*)folderPath completionBlock:(void(^)(NSDictionary *nodeInfo, NSError *error))completionBlock;
1173+{
1174+ NSParameterAssert(folderPath != nil);
1175+ NSParameterAssert(completionBlock != NULL);
1176+
1177+ NSURL *nodeURL = [self URLForNode:folderPath includeChildren:NO];
1178+ return [self execute:@"PUT" toURL:nodeURL withParameters:nil requestBody:@"{\"kind\": \"directory\"}" parseResponseBody:YES completionBlock:^(id results, NSError *error) {
1179+ completionBlock(results, error);
1180+ }];
1181+}
1182+
1183+- (id)createVolumeAtPath:(NSString*)volumePath completionBlock:(void(^)(NSDictionary *volumeInfo, NSError *error))completionBlock;
1184+{
1185+ NSParameterAssert(volumePath != nil);
1186+ NSParameterAssert(completionBlock != NULL);
1187+
1188+ NSURL *volumeURL = [self URLForVolumeWithPath:volumePath];
1189+ return [self execute:@"PUT" toURL:volumeURL withParameters:nil requestBody:@"" parseResponseBody:YES completionBlock:^(id results, NSError *error) {
1190+ completionBlock(results, error);
1191+ }];
1192+}
1193+
1194
1195 #pragma mark Private Methods
1196
1197+- (NSURL*)URLForVolumeWithPath:(NSString*)volumePath;
1198+{
1199+ NSParameterAssert(volumePath != nil);
1200+ NSParameterAssert([volumePath hasPrefix:@"/"]);
1201+
1202+ NSMutableString *urlString = [NSMutableString stringWithString:U1FilesServiceAPIBase];
1203+ [urlString appendString:U1FilesServiceAPIPath];
1204+ [urlString appendString:U1FilesServiceAPIVersion1];
1205+ [urlString appendString:@"/volumes"];
1206+ [urlString appendString:volumePath];
1207+ return [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1208+}
1209+
1210 - (NSURL*)URLForNode:(NSString*)nodePath includeChildren:(BOOL)includeChildren;
1211 {
1212 NSMutableString *urlString = [NSMutableString stringWithString:U1FilesServiceAPIBase];
1213
1214=== modified file 'Files/U1FolderViewController.m'
1215--- Files/U1FolderViewController.m 2011-09-27 14:36:00 +0000
1216+++ Files/U1FolderViewController.m 2011-10-04 14:38:28 +0000
1217@@ -50,6 +50,7 @@
1218 self.title = [theResourcePath lastPathComponent];
1219 self.resourcePath = [theResourcePath copy];
1220 self.byteSizeTransformer = [[U1ByteSizeValueTransformer new] autorelease];
1221+ filesClient = [U1FilesClient sharedFilesClient];
1222
1223 UIBarButtonItem *uploadItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(uploadImage:)];
1224 [self.navigationItem setRightBarButtonItem:uploadItem];
1225@@ -74,7 +75,6 @@
1226 - (void)dealloc;
1227 {
1228 [loadingCell release];
1229- [filesClient release];
1230 [resourcePath release];
1231 [node release];
1232 [byteSizeTransformer release];
1233
1234=== added file 'Files/U1LocalAssetsManager.h'
1235--- Files/U1LocalAssetsManager.h 1970-01-01 00:00:00 +0000
1236+++ Files/U1LocalAssetsManager.h 2011-10-04 14:38:28 +0000
1237@@ -0,0 +1,29 @@
1238+//
1239+// Copyright 2011 Canonical Ltd.
1240+//
1241+// This program is free software: you can redistribute it and/or modify it
1242+// under the terms of the GNU Affero General Public License version 3,
1243+// as published by the Free Software Foundation.
1244+//
1245+// This program is distributed in the hope that it will be useful, but
1246+// WITHOUT ANY WARRANTY; without even the implied warranties of
1247+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1248+// PURPOSE. See the GNU Affero General Public License for more details.
1249+//
1250+// You should have received a copy of the GNU Affero General Public License
1251+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1252+
1253+#import <Foundation/Foundation.h>
1254+
1255+@class U1DataRepository;
1256+@class U1Volume;
1257+
1258+
1259+@interface U1LocalAssetsManager : NSObject
1260+@property (retain) U1DataRepository *dataRepository;
1261+@property (retain) NSMutableArray *localAssetsToUpload;
1262+@property (retain) U1Volume *remoteUploadVolume;
1263++ (U1LocalAssetsManager *)sharedFilesManager;
1264+- (void)checkForNewAssets;
1265+- (void)uploadPendingAssets;
1266+@end
1267
1268=== added file 'Files/U1LocalAssetsManager.m'
1269--- Files/U1LocalAssetsManager.m 1970-01-01 00:00:00 +0000
1270+++ Files/U1LocalAssetsManager.m 2011-10-04 14:38:28 +0000
1271@@ -0,0 +1,209 @@
1272+//
1273+// Copyright 2011 Canonical Ltd.
1274+//
1275+// This program is free software: you can redistribute it and/or modify it
1276+// under the terms of the GNU Affero General Public License version 3,
1277+// as published by the Free Software Foundation.
1278+//
1279+// This program is distributed in the hope that it will be useful, but
1280+// WITHOUT ANY WARRANTY; without even the implied warranties of
1281+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1282+// PURPOSE. See the GNU Affero General Public License for more details.
1283+//
1284+// You should have received a copy of the GNU Affero General Public License
1285+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1286+
1287+#import <AssetsLibrary/AssetsLibrary.h>
1288+
1289+#import "U1Asset.h"
1290+#import "U1DataRepository.h"
1291+#import "U1LocalAssetsManager.h"
1292+#import "U1AssetUploadOperation.h"
1293+#import "U1FilesClient.h"
1294+#import "U1Volume.h"
1295+
1296+@interface U1LocalAssetsManager ()
1297+@property (retain) ALAssetsLibrary *assetsLibrary;
1298+@property (retain) NSOperationQueue *uploadQueue;
1299+- (void)walkAssetsInLibrary;
1300+- (void)uploadRepresentation:(ALAssetRepresentation*)rep forAsset:(U1Asset*)asset;
1301+- (NSString *)generateFilenameForAsset:(ALAsset *)asset;
1302+@end
1303+
1304+static U1LocalAssetsManager *sharedFilesManager = nil;
1305+
1306+@implementation U1LocalAssetsManager
1307+
1308+@synthesize dataRepository, uploadQueue, localAssetsToUpload, remoteUploadVolume;
1309+@synthesize assetsLibrary;
1310+
1311++ (U1LocalAssetsManager *)sharedFilesManager;
1312+{
1313+ if (sharedFilesManager == nil)
1314+ {
1315+ sharedFilesManager = [[super allocWithZone:NULL] init];
1316+ }
1317+ return sharedFilesManager;
1318+}
1319+
1320++ (id)allocWithZone:(NSZone *)zone;
1321+{
1322+ return [[self sharedFilesManager] retain];
1323+}
1324+
1325+- (id)copyWithZone:(NSZone *)zone;
1326+{
1327+ return self;
1328+}
1329+
1330+- (id)retain;
1331+{
1332+ return self;
1333+}
1334+
1335+- (NSUInteger)retainCount;
1336+{
1337+ return NSUIntegerMax;
1338+}
1339+
1340+- (void)release;
1341+{
1342+
1343+}
1344+
1345+- (id)autorelease;
1346+{
1347+ return self;
1348+}
1349+
1350+- (id)init
1351+{
1352+ if (!(self = [super init]))
1353+ return nil;
1354+
1355+ assetsLibrary = [[ALAssetsLibrary alloc] init];
1356+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(libraryChanged:) name:ALAssetsLibraryChangedNotification object:assetsLibrary];
1357+ dataRepository = [U1DataRepository sharedDataRepository];
1358+ uploadQueue = [[NSOperationQueue alloc] init];
1359+ self.localAssetsToUpload = [NSMutableArray array];
1360+ return self;
1361+}
1362+
1363+- (void)dealloc
1364+{
1365+ [[NSNotificationCenter defaultCenter] removeObserver:self];
1366+ [assetsLibrary release];
1367+ [super dealloc];
1368+}
1369+
1370+- (void)checkForNewAssets;
1371+{
1372+ NSLog(@"Checking for new assets.");
1373+ [self walkAssetsInLibrary];
1374+}
1375+
1376+- (void)libraryChanged:(NSNotification*)notification;
1377+{
1378+ NSLog(@"The library changed, so we'll walk the assets again.");
1379+ [self walkAssetsInLibrary];
1380+}
1381+
1382+- (void)walkAssetsInLibrary;
1383+{
1384+ [self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
1385+ usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
1386+ [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop) {
1387+
1388+ ALAsset *someAsset = asset;
1389+ if (someAsset == nil)
1390+ return;
1391+ dispatch_async(dispatch_get_main_queue(), ^(void) {
1392+ ALAssetRepresentation *defaultRep = [someAsset defaultRepresentation];
1393+ NSString *assetURL = [[defaultRep url] absoluteString];
1394+ NSString *groupId = [group valueForProperty:ALAssetsGroupPropertyPersistentID];
1395+
1396+ NSPredicate *p = [NSPredicate predicateWithFormat:@"groupId = %@ and url = %@",
1397+ groupId, assetURL];
1398+ NSError *error = nil;
1399+ NSArray *results = [self.dataRepository resultsForEntityClass:[U1Asset class] matchingPredicate:p withSortDescriptors:nil error:&error];
1400+ U1Asset *trackedAsset = [results lastObject];
1401+ NSLog(@"Did we find an asset? (%@)", [trackedAsset description]);
1402+ if (trackedAsset == nil)
1403+ {
1404+ if ([[someAsset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) {
1405+ [self.dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
1406+
1407+ U1Asset *newAsset = [U1Asset insertInManagedObjectContext:context];
1408+ newAsset.groupId = groupId;
1409+ newAsset.url = assetURL;
1410+ newAsset.filename = [self generateFilenameForAsset:someAsset];
1411+ NSLog(@"Adding and uploading an asset for %@ (filename: %@)", newAsset.url, newAsset.filename);
1412+ [self.localAssetsToUpload addObject:newAsset];
1413+ [self uploadRepresentation:defaultRep forAsset:newAsset];
1414+ [self.dataRepository save:NULL];
1415+ }];
1416+ }
1417+ }
1418+ });
1419+ }];
1420+ }
1421+ failureBlock:^(NSError *error) {
1422+ NSLog(@"Some error happened.");
1423+ }];
1424+}
1425+
1426+- (NSString *)generateFilenameForAsset:(ALAsset *)asset;
1427+{
1428+ // get the type of the asset
1429+ NSString *ext = @"JPG";
1430+
1431+ if ([asset valueForProperty:ALAssetPropertyType] == ALAssetTypeVideo) {
1432+ ext = @"M4V";
1433+ }
1434+
1435+ NSError *error = nil;
1436+ int idx = [[self.dataRepository resultsForEntityClass:[U1Asset class]
1437+ matchingPredicate:nil
1438+ withSortDescriptors:nil
1439+ error:&error] count];
1440+
1441+ return [NSString stringWithFormat:@"IMG_%d.%@", idx, ext];
1442+}
1443+
1444+- (void)uploadPendingAssets;
1445+{
1446+ NSLog(@"Uploading %d assets", [self.localAssetsToUpload count]);
1447+ [self.localAssetsToUpload enumerateObjectsUsingBlock:^(U1Asset *obj, NSUInteger idx, BOOL *stop) {
1448+ [self.assetsLibrary assetForURL:[NSURL URLWithString:obj.url]
1449+ resultBlock:^(ALAsset *asset) {
1450+ [self uploadRepresentation:[asset defaultRepresentation] forAsset:obj];
1451+ } failureBlock:^(NSError *error) {
1452+ NSLog(@"Error: %@", error);
1453+ }];
1454+ }];
1455+}
1456+
1457+- (void)uploadRepresentation:(ALAssetRepresentation *)rep forAsset:(U1Asset *)asset
1458+{
1459+ // iterate self.localAssetsToUpload, creating NSOperations for each, giving each a completionBlock to remove the asset from localAssetsToUpload if it completed properly
1460+ Byte *buffer = (Byte*)malloc(rep.size);
1461+ NSUInteger length = [rep getBytes:buffer fromOffset:0 length:rep.size error:nil];
1462+ NSData *assetData = [NSData dataWithBytesNoCopy:buffer length:length freeWhenDone:YES];
1463+
1464+ U1AssetUploadOperation *operation = [[U1AssetUploadOperation alloc] init];
1465+ operation.asset = asset;
1466+ operation.folder = [self.remoteUploadVolume rootFolder];
1467+ operation.filename = asset.filename;
1468+ operation.assetData = assetData;
1469+ [operation setCompletionBlock:^{
1470+ dispatch_async(dispatch_get_main_queue(), ^(void) {
1471+ if (operation.error == nil) // or the error is somehow unrecoverable (e.g., over quota)
1472+ {
1473+ [self.localAssetsToUpload removeObject:asset];
1474+ }
1475+ });
1476+ }];
1477+ [self.uploadQueue addOperation:operation];
1478+}
1479+
1480+@end
1481
1482=== modified file 'Files/U1Volume.h'
1483--- Files/U1Volume.h 2011-09-08 16:59:34 +0000
1484+++ Files/U1Volume.h 2011-10-04 14:38:28 +0000
1485@@ -1,5 +1,22 @@
1486+//
1487+// Copyright 2011 Canonical Ltd.
1488+//
1489+// This program is free software: you can redistribute it and/or modify it
1490+// under the terms of the GNU Affero General Public License version 3,
1491+// as published by the Free Software Foundation.
1492+//
1493+// This program is distributed in the hope that it will be useful, but
1494+// WITHOUT ANY WARRANTY; without even the implied warranties of
1495+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1496+// PURPOSE. See the GNU Affero General Public License for more details.
1497+//
1498+// You should have received a copy of the GNU Affero General Public License
1499+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1500+
1501 #import "_U1Volume.h"
1502+#import "U1FolderNode.h"
1503+
1504
1505 @interface U1Volume : _U1Volume {}
1506-// Custom logic goes here.
1507+- (U1FolderNode *)rootFolder;
1508 @end
1509
1510=== modified file 'Files/U1Volume.m'
1511--- Files/U1Volume.m 2011-09-08 16:59:34 +0000
1512+++ Files/U1Volume.m 2011-10-04 14:38:28 +0000
1513@@ -1,7 +1,59 @@
1514+//
1515+// Copyright 2011 Canonical Ltd.
1516+//
1517+// This program is free software: you can redistribute it and/or modify it
1518+// under the terms of the GNU Affero General Public License version 3,
1519+// as published by the Free Software Foundation.
1520+//
1521+// This program is distributed in the hope that it will be useful, but
1522+// WITHOUT ANY WARRANTY; without even the implied warranties of
1523+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1524+// PURPOSE. See the GNU Affero General Public License for more details.
1525+//
1526+// You should have received a copy of the GNU Affero General Public License
1527+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1528+
1529 #import "U1Volume.h"
1530+#import "U1FolderNode.h"
1531+#import "U1DataRepository.h"
1532+
1533
1534 @implementation U1Volume
1535
1536-// Custom logic goes here.
1537+- (void)updatePropertiesFromJSONDictionary:(NSDictionary *)jsonDictionary;
1538+{
1539+ [super updatePropertiesFromJSONDictionary:jsonDictionary];
1540+ self.nodePath = [jsonDictionary objectForKey:@"node_path"];
1541+}
1542+
1543+- (U1FolderNode *)rootFolder;
1544+{
1545+ // Look up a U1FolderNode with us as the volume path and some part of us as its name?
1546+ // If it doesn't exist, create it, and return it.
1547+ U1DataRepository *dataRepository = [U1DataRepository sharedDataRepository];
1548+ NSPredicate *p = [NSPredicate predicateWithFormat:@"resourcePath = %@", self.nodePath];
1549+ NSError *error = nil;
1550+ NSArray *results = [dataRepository resultsForEntityClass:[U1FolderNode class] matchingPredicate:p withSortDescriptors:nil error:&error];
1551+ U1FolderNode *rootFolder = [results lastObject];
1552+ NSLog(@"Did we find a root folder? (%@)", [rootFolder description]);
1553+
1554+ if (rootFolder == nil)
1555+ {
1556+ // Upload
1557+ [dataRepository dispatchBlockWithManagedObjectContext:^(NSManagedObjectContext *context) {
1558+
1559+ U1FolderNode *rootFolder = [U1FolderNode insertInManagedObjectContext:context];
1560+ rootFolder.resourcePath = self.nodePath;
1561+ rootFolder.contentPath = self.contentPath;
1562+ NSLog(@"Adding a rootFolder node for %@", self.nodePath);
1563+ [dataRepository save:NULL];
1564+ }];
1565+
1566+ results = [dataRepository resultsForEntityClass:[U1FolderNode class] matchingPredicate:p withSortDescriptors:nil error:&error];
1567+ rootFolder = [results lastObject];
1568+ }
1569+
1570+ return rootFolder;
1571+}
1572
1573 @end
1574
1575=== modified file 'Files/U1VolumesViewController.h'
1576--- Files/U1VolumesViewController.h 2011-08-31 14:56:30 +0000
1577+++ Files/U1VolumesViewController.h 2011-10-04 14:38:28 +0000
1578@@ -17,7 +17,8 @@
1579
1580 #import "PullRefreshTableViewController.h"
1581
1582-@class U1FilesService;
1583+@class U1FilesClient;
1584+@class U1Volume;
1585
1586 @protocol U1VolumesViewControllerDelegate;
1587
1588@@ -27,12 +28,12 @@
1589 @property (nonatomic, retain, readonly) IBOutlet UITableViewCell *loadingCell;
1590
1591 @property (assign) id<U1VolumesViewControllerDelegate> delegate;
1592-@property (nonatomic, retain) U1FilesService *filesService;
1593+@property (retain) U1FilesClient *filesClient;
1594
1595 @end
1596
1597
1598 @protocol U1VolumesViewControllerDelegate <NSObject>
1599 @required
1600-- (void)volumesController:(U1VolumesViewController*)volumesController didSelectVolume:(NSDictionary*)volume;
1601+- (void)volumesController:(U1VolumesViewController*)volumesController didSelectVolume:(U1Volume*)volume;
1602 @end
1603
1604=== modified file 'Files/U1VolumesViewController.m'
1605--- Files/U1VolumesViewController.m 2011-09-27 14:36:00 +0000
1606+++ Files/U1VolumesViewController.m 2011-10-04 14:38:28 +0000
1607@@ -15,18 +15,20 @@
1608
1609 #import "U1VolumesViewController.h"
1610
1611-#import "U1FilesService.h"
1612+#import "U1FilesClient.h"
1613+#import "U1Volume.h"
1614
1615
1616 @interface U1VolumesViewController ()
1617 @property (retain) NSArray *volumes;
1618+- (void)loadVolumes;
1619 @end
1620
1621
1622 @implementation U1VolumesViewController
1623
1624 @synthesize loadingCell;
1625-@synthesize delegate, filesService;
1626+@synthesize delegate, filesClient;
1627 @synthesize volumes;
1628
1629 - (id)init;
1630@@ -35,6 +37,7 @@
1631 if (!self)
1632 return nil;
1633 self.title = NSLocalizedString(@"Home", @"");
1634+ filesClient = [U1FilesClient sharedFilesClient];
1635 UIImage *logoImage = [UIImage imageNamed:@"logo_4_white"];
1636 UIImageView *logoView = [[UIImageView alloc] initWithImage:logoImage];
1637 [self.navigationItem setTitleView:logoView];
1638@@ -45,7 +48,6 @@
1639 - (void)dealloc;
1640 {
1641 [loadingCell release];
1642- [filesService release];
1643 [volumes release];
1644 [super dealloc];
1645 }
1646@@ -53,12 +55,18 @@
1647 - (void)viewDidLoad;
1648 {
1649 [super viewDidLoad];
1650- [self.filesService volumeInfoWithCompletionBlock:^(NSArray *volumeInfo, NSError *error) {
1651+ [self.filesClient volumesWithCompletionBlock:^(NSArray *volumeInfos, NSError *error) {
1652 dispatch_async(dispatch_get_main_queue(), ^(void) {
1653- self.volumes = volumeInfo;
1654+ self.volumes = volumeInfos;
1655 [self.tableView reloadData];
1656 });
1657 }];
1658+ [self loadVolumes];
1659+}
1660+
1661+- (void)refresh;
1662+{
1663+ [self loadVolumes];
1664 }
1665
1666 - (void)viewDidUnload;
1667@@ -93,8 +101,8 @@
1668 [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
1669 [cell.imageView setImage:[UIImage imageNamed:@"ic_folder"]];
1670 }
1671- NSDictionary *volume = [self.volumes objectAtIndex:indexPath.row];
1672- NSString *volumePath = [volume objectForKey:@"path"];
1673+ U1Volume *volume = [self.volumes objectAtIndex:indexPath.row];
1674+ NSString *volumePath = volume.path;
1675 if ([@"~/.ubuntuone/Purchased from Ubuntu One" isEqualToString:volumePath])
1676 volumePath = @"Purchased Music";
1677 if ([volumePath hasPrefix:@"~/"])
1678@@ -106,8 +114,19 @@
1679
1680 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
1681 {
1682- NSDictionary *volume = [self.volumes objectAtIndex:indexPath.row];
1683+ U1Volume *volume = [self.volumes objectAtIndex:indexPath.row];
1684 [self.delegate volumesController:self didSelectVolume:volume];
1685 }
1686
1687+- (void)loadVolumes;
1688+{
1689+ [self.filesClient volumesWithCompletionBlock:^(NSArray *volumeInfos, NSError *error) {
1690+ dispatch_async(dispatch_get_main_queue(), ^(void) {
1691+ self.volumes = volumeInfos;
1692+ [self stopLoading];
1693+ [self.tableView reloadData];
1694+ });
1695+ }];
1696+}
1697+
1698 @end

Subscribers

People subscribed via source and target branches