Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit 39fa4ddcf550e66713531d11324e2d0feac779d8 Simon Whitaker committed Nov 24, 2012
22 .gitignore
@@ -0,0 +1,22 @@
+### /Users/simon/.gitignore-boilerplates/Objective-C.gitignore
+
+# Xcode
+.DS_Store
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+*.xcworkspace
+!default.xcworkspace
+xcuserdata
+profile
+*.moved-aside
+DerivedData
+.idea/
+
+
15 GSDropboxActivity.h
@@ -0,0 +1,15 @@
+//
+// GSDropboxActivity.h
+// Zippity
+//
+// Created by Simon Whitaker on 19/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface GSDropboxActivity : UIActivity
+
++ (NSString*)activityTypeString;
+
+@end
81 GSDropboxActivity.m
@@ -0,0 +1,81 @@
+//
+// GSDropboxActivity.m
+// Zippity
+//
+// Created by Simon Whitaker on 19/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import "GSDropboxActivity.h"
+#import "GSDropboxDestinationSelectionViewController.h"
+#import "GSDropboxUploader.h"
+#import <DropboxSDK/DropboxSDK.h>
+
+#import "ZPAboutViewController.h"
+
+@interface GSDropboxActivity() <GSDropboxDestinationSelectionViewControllerDelegate>
+
+@property (nonatomic, copy) NSArray *activityItems;
+@property (nonatomic, retain) GSDropboxDestinationSelectionViewController *dropboxDestinationViewController;
+@end
+
+@implementation GSDropboxActivity
+
++ (NSString *)activityTypeString
+{
+ return @"uk.co.goosoftware.DropboxActivity";
+}
+
+- (NSString *)activityType {
+ return [GSDropboxActivity activityTypeString];
+}
+
+- (NSString *)activityTitle {
+ return NSLocalizedString(@"Dropbox", @"Name of the service at www.dropbox.com");
+}
+- (UIImage *)activityImage {
+ return [UIImage imageNamed:@"GSDropboxActivityIcon"];
+}
+
+- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
+ for (id obj in activityItems) {
+ if ([obj isKindOfClass:[NSURL class]] || [obj isKindOfClass:[UIImage class]]) {
+ return YES;
+ }
+ }
+ return NO;
+};
+
+- (void)prepareWithActivityItems:(NSArray *)activityItems {
+ self.activityItems = activityItems;
+}
+
+- (UIViewController *)activityViewController {
+ GSDropboxDestinationSelectionViewController *vc = [[GSDropboxDestinationSelectionViewController alloc] initWithStyle:UITableViewStylePlain];
+ vc.delegate = self;
+
+ UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
+ nc.modalPresentationStyle = UIModalPresentationFormSheet;
+
+ return nc;
+}
+
+#pragma mark - GSDropboxDestinationSelectionViewController delegate methods
+
+- (void)dropboxDestinationSelectionViewController:(GSDropboxDestinationSelectionViewController *)viewController
+ didSelectDestinationPath:(NSString *)destinationPath
+{
+ for (NSURL *fileURL in self.activityItems) {
+ [[GSDropboxUploader sharedUploader] uploadFileWithURL:fileURL toPath:destinationPath];
+ }
+ self.activityItems = nil;
+ [self activityDidFinish:YES];
+}
+
+- (void)dropboxDestinationSelectionViewControllerDidCancel:(GSDropboxDestinationSelectionViewController *)viewController
+{
+ self.activityItems = nil;
+ [self activityDidFinish:NO];
+}
+
+@end
BIN GSDropboxActivityIcon.opacity
Binary file not shown.
BIN GSDropboxActivityIcon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN GSDropboxActivityIcon@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN GSDropboxActivityIcon@2x~ipad.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN GSDropboxActivityIcon~ipad.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 GSDropboxDestinationSelectionViewController.h
@@ -0,0 +1,26 @@
+//
+// GSDropboxDestinationSelectionViewController.h
+// Zippity
+//
+// Created by Simon Whitaker on 06/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@protocol GSDropboxDestinationSelectionViewControllerDelegate;
+
+@interface GSDropboxDestinationSelectionViewController : UITableViewController
+
+@property (nonatomic, strong) NSString *rootPath;
+@property (nonatomic, weak) id<GSDropboxDestinationSelectionViewControllerDelegate> delegate;
+
+@end
+
+@protocol GSDropboxDestinationSelectionViewControllerDelegate <NSObject>
+
+- (void)dropboxDestinationSelectionViewController:(GSDropboxDestinationSelectionViewController*)viewController
+ didSelectDestinationPath:(NSString *)destinationPath;
+- (void)dropboxDestinationSelectionViewControllerDidCancel:(GSDropboxDestinationSelectionViewController*)viewController;
+
+@end
198 GSDropboxDestinationSelectionViewController.m
@@ -0,0 +1,198 @@
+//
+// GSDropboxDestinationSelectionViewController.m
+// Zippity
+//
+// Created by Simon Whitaker on 06/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import "GSDropboxDestinationSelectionViewController.h"
+#import <DropboxSDK/DropboxSDK.h>
+
+@interface GSDropboxDestinationSelectionViewController () <DBRestClientDelegate>
+@property (nonatomic) BOOL isLoading;
+@property (nonatomic, strong) NSArray *subdirectories;
+@property (nonatomic, strong) DBRestClient *dropboxClient;
+
+- (void)handleCancel;
+- (void)handleSelectDestination;
+
+@end
+
+@implementation GSDropboxDestinationSelectionViewController
+
+- (id)initWithStyle:(UITableViewStyle)style
+{
+ self = [super initWithStyle:style];
+ if (self) {
+ _isLoading = YES;
+ }
+ return self;
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
+ target:self
+ action:@selector(handleCancel)];
+
+ self.toolbarItems = @[
+ [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
+ [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Choose", @"Title for button that user taps to specify the current folder as the storage location for uploads.")
+ style:UIBarButtonItemStyleDone
+ target:self
+ action:@selector(handleSelectDestination)]
+ ];
+
+ [self.navigationController setToolbarHidden:NO];
+ self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor];
+ self.navigationController.toolbar.tintColor = [UIColor darkGrayColor];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ if (self.rootPath == nil)
+ self.rootPath = @"/";
+
+ if ([self.rootPath isEqualToString:@"/"]) {
+ self.title = NSLocalizedString(@"Dropbox", @"The name of the service at www.dropbox.com");
+ } else {
+ self.title = [self.rootPath lastPathComponent];
+ }
+ self.navigationItem.prompt = NSLocalizedString(@"Choose a destination for uploads.", @"Prompt asking user to select a destination folder on Dropbox to which uploads will be saved.") ;
+ self.isLoading = YES;
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ if (![[DBSession sharedSession] isLinked]) {
+ [[DBSession sharedSession] linkFromController:self];
+ } else {
+ [self.dropboxClient loadMetadata:self.rootPath];
+ }
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
+{
+ if (isIpad)
+ return YES;
+
+ return toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
+}
+
+- (DBRestClient *)dropboxClient
+{
+ if (_dropboxClient == nil && [DBSession sharedSession] != nil) {
+ _dropboxClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
+ _dropboxClient.delegate = self;
+ }
+ return _dropboxClient;
+}
+
+#pragma mark - Table view data source
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ if (self.isLoading || self.subdirectories == nil || [self.subdirectories count] == 0) {
+ return 1;
+ }
+ return [self.subdirectories count];
+
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ static NSString *CellIdentifier = @"Cell";
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
+ }
+
+ // Configure the cell...
+ if (self.isLoading) {
+ cell.textLabel.text = NSLocalizedString(@"Loading...", @"Progress message while app is loading a list of folders from Dropbox");
+ } else if (self.subdirectories == nil) {
+ cell.textLabel.text = NSLocalizedString(@"Error loading folder contents", @"Error message if the app couldn't load a list of a folder's contents from Dropbox");
+ } else if ([self.subdirectories count] == 0) {
+ cell.textLabel.text = NSLocalizedString(@"Contains no folders", @"Status message when the current folder contains no sub-folders");
+ } else {
+ cell.textLabel.text = [[self.subdirectories objectAtIndex:indexPath.row] lastPathComponent];
+ cell.imageView.image = [UIImage imageNamed:@"folder-icon.png"];
+ }
+
+ return cell;
+}
+
+#pragma mark - Table view delegate
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return 56.0;
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ if ([self.subdirectories count] > indexPath.row) {
+ GSDropboxDestinationSelectionViewController *vc = [[GSDropboxDestinationSelectionViewController alloc] init];
+ vc.delegate = self.delegate;
+ vc.rootPath = [self.rootPath stringByAppendingPathComponent:[self.subdirectories objectAtIndex:indexPath.row]];
+ [self.navigationController pushViewController:vc animated:YES];
+ } else {
+ [tableView deselectRowAtIndexPath:indexPath animated:YES];
+ }
+}
+
+#pragma mark - Dropbox client delegate methods
+
+- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata
+{
+ NSMutableArray *array = [NSMutableArray array];
+ for (DBMetadata *file in metadata.contents) {
+ if (file.isDirectory && [file.filename length] > 0 && [file.filename characterAtIndex:0] != '.') {
+ [array addObject:file.filename];
+ }
+ }
+ self.subdirectories = [array sortedArrayUsingSelector:@selector(compare:)];
+ self.isLoading = NO;
+}
+
+- (void)restClient:(DBRestClient *)client loadMetadataFailedWithError:(NSError *)error
+{
+ self.isLoading = NO;
+}
+
+- (void)setIsLoading:(BOOL)isLoading
+{
+ if (_isLoading != isLoading) {
+ _isLoading = isLoading;
+ [self.tableView reloadData];
+ }
+}
+
+- (void)handleCancel
+{
+ if ([self.delegate respondsToSelector:@selector(dropboxDestinationSelectionViewControllerDidCancel:)]) {
+ [self.delegate dropboxDestinationSelectionViewControllerDidCancel:self];
+ }
+}
+
+- (void)handleSelectDestination
+{
+ if ([self.delegate respondsToSelector:@selector(dropboxDestinationSelectionViewController:didSelectDestinationPath:)]) {
+ [self.delegate dropboxDestinationSelectionViewController:self
+ didSelectDestinationPath:self.rootPath];
+ }
+}
+
+@end
18 GSDropboxUploadJob.h
@@ -0,0 +1,18 @@
+//
+// GSDropboxUploadJob.h
+// Zippity
+//
+// Created by Simon Whitaker on 24/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface GSDropboxUploadJob : NSObject
+
+@property (nonatomic, strong) NSURL *fileURL;
+@property (nonatomic, strong) NSString *destinationPath;
+
++ (GSDropboxUploadJob *)uploadJobWithFileURL:(NSURL *)fileURL andDestinationPath:(NSString *)destinationPath;
+
+@end
21 GSDropboxUploadJob.m
@@ -0,0 +1,21 @@
+//
+// GSDropboxUploadJob.m
+// Zippity
+//
+// Created by Simon Whitaker on 24/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import "GSDropboxUploadJob.h"
+
+@implementation GSDropboxUploadJob
+
++ (GSDropboxUploadJob *)uploadJobWithFileURL:(NSURL *)fileURL andDestinationPath:(NSString *)destinationPath
+{
+ GSDropboxUploadJob *job = [[GSDropboxUploadJob alloc] init];
+ job.fileURL = fileURL;
+ job.destinationPath = destinationPath;
+ return job;
+}
+
+@end
31 GSDropboxUploader.h
@@ -0,0 +1,31 @@
+//
+// GSDropboxUploader.h
+// Zippity
+//
+// Created by Simon Whitaker on 06/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface GSDropboxUploader : NSObject
+
+/* NSNotification names */
+extern NSString *const GSDropboxUploaderDidStartUploadingFileNotification;
+extern NSString *const GSDropboxUploaderDidFinishUploadingFileNotification;
+extern NSString *const GSDropboxUploaderDidGetProgressUpdateNotification;
+extern NSString *const GSDropboxUploaderDidFailNotification;
+
+/* UserInfo dictionary keys */
+extern NSString *const GSDropboxUploaderFileURLKey;
+extern NSString *const GSDropboxUploaderProgressKey;
+
+/* The singleton Dropbox uploader - use this for all your Dropbox uploads */
++ (GSDropboxUploader*)sharedUploader;
+
+- (void)uploadFileWithURL:(NSURL *)fileURL toPath:(NSString*)destinationPath;
+
+/* Uploads are processed one at a time. If you call uploadFileWithURL:toPath: while an upload's already in progress the new upload will be queued. pendingUploadCount returns the number of uploads currently in the queue pending processing. */
+- (NSUInteger)pendingUploadCount;
+
+@end
125 GSDropboxUploader.m
@@ -0,0 +1,125 @@
+//
+// GSDropboxUploader.m
+// Zippity
+//
+// Created by Simon Whitaker on 06/11/2012.
+// Copyright (c) 2012 Goo Software Ltd. All rights reserved.
+//
+
+#import "GSDropboxUploader.h"
+#import "GSDropboxUploadJob.h"
+#import <DropboxSDK/DropboxSDK.h>
+
+NSString *const GSDropboxUploaderDidStartUploadingFileNotification = @"GSDropboxUploaderDidStartUploadingFileNotification";
+NSString *const GSDropboxUploaderDidFinishUploadingFileNotification = @"GSDropboxUploaderDidFinishUploadingFileNotification";
+NSString *const GSDropboxUploaderDidGetProgressUpdateNotification = @"GSDropboxUploaderDidGetProgressUpdateNotification";
+NSString *const GSDropboxUploaderDidFailNotification = @"GSDropboxUploaderDidFailNotification";
+
+NSString *const GSDropboxUploaderFileURLKey = @"GSDropboxUploaderFileURLKey";
+NSString *const GSDropboxUploaderProgressKey = @"GSDropboxUploaderProgressKey";
+
+@interface GSDropboxUploader() <DBRestClientDelegate>
+
+// inFlightUploadJob: the file wrapper currently being uploaded
+@property (nonatomic, strong) GSDropboxUploadJob *_inFlightUploadJob;
+@property (nonatomic, strong) NSMutableArray *_uploadQueue;
+@property (nonatomic, strong) DBRestClient *_dropboxClient;
+
+- (void)_serviceQueue;
+
+@end
+
+@implementation GSDropboxUploader
+
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ self._uploadQueue = [NSMutableArray array];
+ }
+ return self;
+}
+
++ (GSDropboxUploader *)sharedUploader
+{
+ static dispatch_once_t once;
+ static GSDropboxUploader *singleton;
+ dispatch_once(&once, ^ { singleton = [[GSDropboxUploader alloc] init]; });
+ return singleton;
+}
+
+- (void)uploadFileWithURL:(NSURL *)fileURL toPath:(NSString *)destinationPath
+{
+ [self._uploadQueue addObject:[GSDropboxUploadJob uploadJobWithFileURL:fileURL
+ andDestinationPath:destinationPath]];
+ [self _serviceQueue];
+}
+
+- (void)start
+{
+ [self _serviceQueue];
+}
+
+- (void)_serviceQueue
+{
+ if ([self._uploadQueue count] > 0 && self._inFlightUploadJob == nil) {
+ @synchronized(self) {
+ self._inFlightUploadJob = [self._uploadQueue objectAtIndex:0];
+ [self._uploadQueue removeObjectAtIndex:0];
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:GSDropboxUploaderDidStartUploadingFileNotification
+ object:self
+ userInfo:@{GSDropboxUploaderFileURLKey: self._inFlightUploadJob.fileURL}];
+ [self._dropboxClient uploadFile:self._inFlightUploadJob.fileURL.lastPathComponent
+ toPath:self._inFlightUploadJob.destinationPath
+ withParentRev:nil
+ fromPath:self._inFlightUploadJob.fileURL.path];
+ }
+}
+
+- (NSUInteger)pendingUploadCount
+{
+ return [self._uploadQueue count];
+}
+
+- (DBRestClient *)_dropboxClient
+{
+ if (__dropboxClient == nil && [DBSession sharedSession] != nil) {
+ __dropboxClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
+ __dropboxClient.delegate = self;
+ }
+ return __dropboxClient;
+}
+
+#pragma mark - Dropbox client delegate methods
+
+- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath from:(NSString*)srcPath metadata:(DBMetadata*)metadata {
+ [[NSNotificationCenter defaultCenter] postNotificationName:GSDropboxUploaderDidFinishUploadingFileNotification
+ object:self
+ userInfo:@{GSDropboxUploaderFileURLKey: self._inFlightUploadJob.fileURL}];
+ self._inFlightUploadJob = nil;
+ [self _serviceQueue];
+}
+
+- (void)restClient:(DBRestClient*)client uploadFileFailedWithError:(NSError*)error {
+ [[NSNotificationCenter defaultCenter] postNotificationName:GSDropboxUploaderDidFailNotification
+ object:self
+ userInfo:@{GSDropboxUploaderFileURLKey: self._inFlightUploadJob.fileURL}];
+ self._inFlightUploadJob = nil;
+ [self _serviceQueue];
+}
+
+- (void)restClient:(DBRestClient*)client uploadProgress:(CGFloat)progress
+ forFile:(NSString*)destPath from:(NSString*)srcPath
+{
+ NSDictionary *userInfo = @{
+ GSDropboxUploaderFileURLKey: self._inFlightUploadJob.fileURL,
+ GSDropboxUploaderProgressKey: @(progress),
+ };
+ [[NSNotificationCenter defaultCenter] postNotificationName:GSDropboxUploaderDidGetProgressUpdateNotification
+ object:self
+ userInfo:userInfo];
+}
+
+@end
1 LICENSE
@@ -0,0 +1 @@
+This work is licensed under the Creative Commons Attribution 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
18 README.md
@@ -0,0 +1,18 @@
+# GSDropboxActivity
+
+An iOS 6 UIActivity subclass for uploading to Dropbox.
+
+This is currently work in progress. All suggestions gratefully received!
+
+# License
+
+[![Creative Commons License][cc-by-30-icon]][cc-by-30]
+
+This work is licensed under a [Creative Commons Attribution 3.0 Unported License][cc-by-30].
+
+You're free to use this code in any project, including commercial. Please include the following text somewhere suitable, e.g. your app's About screen:
+
+**Uses GSDropboxArchive by Simon Whitaker**
+
+[cc-by-30-icon]: http://i.creativecommons.org/l/by/3.0/88x31.png
+[cc-by-30]: http://creativecommons.org/licenses/by/3.0/

0 comments on commit 39fa4dd

Please sign in to comment.
Something went wrong with that request. Please try again.