Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 39fa4ddcf550e66713531d11324e2d0feac779d8 0 parents
Simon Whitaker authored
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/
Please sign in to comment.
Something went wrong with that request. Please try again.