Adding Pinboard and Chrome.
|
@ -154,7 +154,7 @@
|
|||
}
|
||||
|
||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||
NSLog(@"appDelegate.recentlyReadStoryLocations: %d - %@", self.isOffline, appDelegate.recentlyReadStoryLocations);
|
||||
// NSLog(@"appDelegate.recentlyReadStoryLocations: %d - %@", self.isOffline, appDelegate.recentlyReadStoryLocations);
|
||||
for (id i in appDelegate.recentlyReadStoryLocations) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[i intValue]
|
||||
inSection:0];
|
||||
|
@ -264,7 +264,7 @@
|
|||
self.feedPage = 1;
|
||||
appDelegate.activeStory = nil;
|
||||
[appDelegate.storyPageControl resetPages];
|
||||
appDelegate.recentlyReadStories = [NSMutableArray array];
|
||||
appDelegate.recentlyReadStories = [NSMutableDictionary dictionary];
|
||||
appDelegate.recentlyReadStoryLocations = [NSMutableArray array];
|
||||
[self.notifier hideIn:0];
|
||||
[self cancelRequests];
|
||||
|
@ -896,11 +896,11 @@
|
|||
|
||||
if (!appDelegate.hasLoadedFeedDetail) {
|
||||
cell.isRead = ![[self.unreadStoryHashes objectForKey:[story objectForKey:@"story_hash"]] boolValue] ||
|
||||
[appDelegate.recentlyReadStories containsObject:[story objectForKey:@"story_hash"]];
|
||||
[[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue];
|
||||
// NSLog(@"Offline: %d - %@ - %@", cell.isRead, [story objectForKey:@"story_title"], [story objectForKey:@"story_hash"]);
|
||||
} else {
|
||||
cell.isRead = [[story objectForKey:@"read_status"] intValue] == 1 ||
|
||||
[appDelegate.recentlyReadStories containsObject:[story objectForKey:@"story_hash"]];
|
||||
[[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue];
|
||||
// NSLog(@"Online: %d (%d/%d) - %@ - %@", cell.isRead, [[story objectForKey:@"read_status"] intValue] == 1, [appDelegate.recentlyReadStories containsObject:[story objectForKey:@"story_hash"]], [story objectForKey:@"story_title"], [story objectForKey:@"story_hash"]);
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
int latestCachedImageDate;
|
||||
int totalUncachedImagesCount;
|
||||
int remainingUncachedImagesCount;
|
||||
NSMutableArray * recentlyReadStories;
|
||||
NSMutableDictionary * recentlyReadStories;
|
||||
NSMutableArray * recentlyReadStoryLocations;
|
||||
NSMutableSet * recentlyReadFeeds;
|
||||
NSMutableArray * readStories;
|
||||
|
@ -221,7 +221,7 @@
|
|||
@property (readwrite) int latestFetchedStoryDate;
|
||||
@property (readwrite) int latestCachedImageDate;
|
||||
@property (readwrite) NSInteger selectedIntelligence;
|
||||
@property (readwrite) NSMutableArray * recentlyReadStories;
|
||||
@property (readwrite) NSMutableDictionary * recentlyReadStories;
|
||||
@property (readwrite) NSMutableArray * recentlyReadStoryLocations;
|
||||
@property (readwrite) NSMutableSet * recentlyReadFeeds;
|
||||
@property (readwrite) NSMutableArray * readStories;
|
||||
|
|
|
@ -1397,7 +1397,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
[self.recentlyReadStories addObject:[self.activeStory objectForKey:@"story_hash"]];
|
||||
[self.recentlyReadStories setObject:[NSNumber numberWithBool:YES]
|
||||
forKey:[self.activeStory objectForKey:@"story_hash"]];
|
||||
[self.recentlyReadStoryLocations addObject:[NSNumber numberWithInt:activeLocation]];
|
||||
[self markStoryRead:story feed:feed];
|
||||
self.activeStory = [self.activeFeedStories objectAtIndex:activeIndex];
|
||||
|
@ -1472,7 +1473,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
[self.recentlyReadStories removeObject:[self.activeStory objectForKey:@"story_hash"]];
|
||||
[self.recentlyReadStories removeObjectForKey:[self.activeStory objectForKey:@"story_hash"]];
|
||||
[self.recentlyReadStoryLocations removeObject:[NSNumber numberWithInt:activeLocation]];
|
||||
[self markStoryUnread:story feed:feed];
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#import "RWInstapaperActivity.h"
|
||||
#import "ReadabilityActivity.h"
|
||||
#import "PocketAPIActivity.h"
|
||||
#import "VUPinboardActivity.h"
|
||||
#import "ARChromeActivity.h"
|
||||
|
||||
@implementation StoryPageControl
|
||||
|
||||
|
@ -813,6 +815,12 @@
|
|||
TUSafariActivity *openInSafari = [[TUSafariActivity alloc] init];
|
||||
[appActivities addObject:openInSafari];
|
||||
|
||||
if ([[UIApplication sharedApplication]
|
||||
canOpenURL:[NSURL URLWithString:@"googlechrome://"]]) {
|
||||
ARChromeActivity *chromeActivity = [[ARChromeActivity alloc]
|
||||
initWithCallbackURL:[NSURL URLWithString:@"newsblur://"]];
|
||||
[appActivities addObject:chromeActivity];
|
||||
}
|
||||
if ([[preferences objectForKey:@"enable_instapaper"] boolValue]) {
|
||||
RWInstapaperActivity *instapaper = [[RWInstapaperActivity alloc] init];
|
||||
instapaper.username = [preferences objectForKey:@"instapaper_username"];
|
||||
|
@ -824,8 +832,14 @@
|
|||
ReadabilityActivity *readabilityActivity = [[ReadabilityActivity alloc] init];
|
||||
[appActivities addObject:readabilityActivity];
|
||||
}
|
||||
PocketAPIActivity *pocket = [[PocketAPIActivity alloc] init];
|
||||
[appActivities addObject:pocket];
|
||||
if ([[preferences objectForKey:@"enable_pocket"] boolValue]) {
|
||||
PocketAPIActivity *pocket = [[PocketAPIActivity alloc] init];
|
||||
[appActivities addObject:pocket];
|
||||
}
|
||||
if ([[preferences objectForKey:@"enable_pinboard"] boolValue]) {
|
||||
VUPinboardActivity *pinboard = [[VUPinboardActivity alloc] init];
|
||||
[appActivities addObject:pinboard];
|
||||
}
|
||||
|
||||
UIActivityViewController *shareSheet = [[UIActivityViewController alloc]
|
||||
initWithActivityItems:@[title, url]
|
||||
|
@ -855,6 +869,8 @@
|
|||
_completedString = @"Saved";
|
||||
} else if ([activityType isEqualToString:@"Pocket"]) {
|
||||
_completedString = @"Saved";
|
||||
} else if ([activityType isEqualToString:@"pinboard"]) {
|
||||
_completedString = @"Saved";
|
||||
}
|
||||
} else {
|
||||
_completedString = nil;
|
||||
|
|
|
@ -54,6 +54,14 @@
|
|||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.newsblur.NewsBlur</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>newsblur</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
|
|
|
@ -248,6 +248,22 @@
|
|||
FF4EE9A4175EC9CF005891B5 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FF4EE9A3175EC9CF005891B5 /* libsqlite3.dylib */; };
|
||||
FF546DF71602930100948020 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF546DF61602930100948020 /* Default-568h@2x.png */; };
|
||||
FF546DF9160298E500948020 /* fleuron@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF546DF8160298E500948020 /* fleuron@2x.png */; };
|
||||
FF5D3FFE1799F53C00349659 /* VUPinboardAccess.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FEB1799F53C00349659 /* VUPinboardAccess.m */; };
|
||||
FF5D3FFF1799F53C00349659 /* VUPinboardActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FED1799F53C00349659 /* VUPinboardActivity.m */; };
|
||||
FF5D40001799F53C00349659 /* VUPinboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FEF1799F53C00349659 /* VUPinboardViewController.m */; };
|
||||
FF5D40011799F53C00349659 /* BBNetworkRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FF21799F53C00349659 /* BBNetworkRequest.m */; };
|
||||
FF5D40021799F53C00349659 /* SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FF41799F53C00349659 /* SSKeychain.m */; };
|
||||
FF5D40031799F53C00349659 /* VUDialogViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FF61799F53C00349659 /* VUDialogViewController.m */; };
|
||||
FF5D40041799F53C00349659 /* VUGradientButton.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FF81799F53C00349659 /* VUGradientButton.m */; };
|
||||
FF5D40051799F53C00349659 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = FF5D3FF91799F53C00349659 /* LICENSE */; };
|
||||
FF5D40061799F53C00349659 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = FF5D3FFA1799F53C00349659 /* README.md */; };
|
||||
FF5D40071799F53C00349659 /* PinboardActivityImage.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D3FFC1799F53C00349659 /* PinboardActivityImage.png */; };
|
||||
FF5D40081799F53C00349659 /* PinboardActivityImage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D3FFD1799F53C00349659 /* PinboardActivityImage@2x.png */; };
|
||||
FF5D40101799F70200349659 /* ARChromeActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D400B1799F70200349659 /* ARChromeActivity.m */; };
|
||||
FF5D40111799F70200349659 /* ARChromeActivity.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D400C1799F70200349659 /* ARChromeActivity.png */; };
|
||||
FF5D40121799F70200349659 /* ARChromeActivity@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D400D1799F70200349659 /* ARChromeActivity@2x.png */; };
|
||||
FF5D40131799F70200349659 /* ARChromeActivity@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D400E1799F70200349659 /* ARChromeActivity@2x~ipad.png */; };
|
||||
FF5D40141799F70200349659 /* ARChromeActivity~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D400F1799F70200349659 /* ARChromeActivity~ipad.png */; };
|
||||
FF5EA47F143B691000B7563D /* AddSiteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5EA47D143B691000B7563D /* AddSiteViewController.m */; };
|
||||
FF5F3A82162B82B5008DBE3E /* rainbow.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5F3A81162B82B5008DBE3E /* rainbow.png */; };
|
||||
FF5F3A85162B831A008DBE3E /* user_orange.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5F3A84162B831A008DBE3E /* user_orange.png */; };
|
||||
|
@ -763,6 +779,30 @@
|
|||
FF4EE9A3175EC9CF005891B5 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
|
||||
FF546DF61602930100948020 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
|
||||
FF546DF8160298E500948020 /* fleuron@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fleuron@2x.png"; sourceTree = "<group>"; };
|
||||
FF5D3FEA1799F53C00349659 /* VUPinboardAccess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUPinboardAccess.h; sourceTree = "<group>"; };
|
||||
FF5D3FEB1799F53C00349659 /* VUPinboardAccess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUPinboardAccess.m; sourceTree = "<group>"; };
|
||||
FF5D3FEC1799F53C00349659 /* VUPinboardActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUPinboardActivity.h; sourceTree = "<group>"; };
|
||||
FF5D3FED1799F53C00349659 /* VUPinboardActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUPinboardActivity.m; sourceTree = "<group>"; };
|
||||
FF5D3FEE1799F53C00349659 /* VUPinboardViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUPinboardViewController.h; sourceTree = "<group>"; };
|
||||
FF5D3FEF1799F53C00349659 /* VUPinboardViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUPinboardViewController.m; sourceTree = "<group>"; };
|
||||
FF5D3FF11799F53C00349659 /* BBNetworkRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BBNetworkRequest.h; sourceTree = "<group>"; };
|
||||
FF5D3FF21799F53C00349659 /* BBNetworkRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BBNetworkRequest.m; sourceTree = "<group>"; };
|
||||
FF5D3FF31799F53C00349659 /* SSKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychain.h; sourceTree = "<group>"; };
|
||||
FF5D3FF41799F53C00349659 /* SSKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychain.m; sourceTree = "<group>"; };
|
||||
FF5D3FF51799F53C00349659 /* VUDialogViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUDialogViewController.h; sourceTree = "<group>"; };
|
||||
FF5D3FF61799F53C00349659 /* VUDialogViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUDialogViewController.m; sourceTree = "<group>"; };
|
||||
FF5D3FF71799F53C00349659 /* VUGradientButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUGradientButton.h; sourceTree = "<group>"; };
|
||||
FF5D3FF81799F53C00349659 /* VUGradientButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUGradientButton.m; sourceTree = "<group>"; };
|
||||
FF5D3FF91799F53C00349659 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||
FF5D3FFA1799F53C00349659 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
|
||||
FF5D3FFC1799F53C00349659 /* PinboardActivityImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = PinboardActivityImage.png; sourceTree = "<group>"; };
|
||||
FF5D3FFD1799F53C00349659 /* PinboardActivityImage@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PinboardActivityImage@2x.png"; sourceTree = "<group>"; };
|
||||
FF5D400A1799F70200349659 /* ARChromeActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARChromeActivity.h; sourceTree = "<group>"; };
|
||||
FF5D400B1799F70200349659 /* ARChromeActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARChromeActivity.m; sourceTree = "<group>"; };
|
||||
FF5D400C1799F70200349659 /* ARChromeActivity.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ARChromeActivity.png; sourceTree = "<group>"; };
|
||||
FF5D400D1799F70200349659 /* ARChromeActivity@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ARChromeActivity@2x.png"; sourceTree = "<group>"; };
|
||||
FF5D400E1799F70200349659 /* ARChromeActivity@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ARChromeActivity@2x~ipad.png"; sourceTree = "<group>"; };
|
||||
FF5D400F1799F70200349659 /* ARChromeActivity~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ARChromeActivity~ipad.png"; sourceTree = "<group>"; };
|
||||
FF5EA47C143B691000B7563D /* AddSiteViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddSiteViewController.h; sourceTree = "<group>"; };
|
||||
FF5EA47D143B691000B7563D /* AddSiteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddSiteViewController.m; sourceTree = "<group>"; };
|
||||
FF5F3A81162B82B5008DBE3E /* rainbow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rainbow.png; sourceTree = "<group>"; };
|
||||
|
@ -1092,10 +1132,12 @@
|
|||
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D40091799F70200349659 /* ARChromeActivity */,
|
||||
FFAF53951799E69D00C7FCCB /* PocketSDK */,
|
||||
FFAF53A61799E69D00C7FCCB /* ReadabilityActivity */,
|
||||
FFAF53741799D1C800C7FCCB /* TUSafariActivity */,
|
||||
FFAF53411799D18B00C7FCCB /* InstapaperActivity */,
|
||||
FF5D3FE71799F53C00349659 /* VUPinboardActivity */,
|
||||
FFF1E498177504CA00BF59D3 /* InAppSettingsKit */,
|
||||
FF753CC2175858FC00344EC9 /* fmdb */,
|
||||
FF22FE5316E53ADC0046165A /* Underscore */,
|
||||
|
@ -1750,6 +1792,78 @@
|
|||
path = Reachability;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D3FE71799F53C00349659 /* VUPinboardActivity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D3FE81799F53C00349659 /* Classes */,
|
||||
FF5D3FF91799F53C00349659 /* LICENSE */,
|
||||
FF5D3FFA1799F53C00349659 /* README.md */,
|
||||
FF5D3FFB1799F53C00349659 /* Resources */,
|
||||
);
|
||||
name = VUPinboardActivity;
|
||||
path = "Other Sources/VUPinboardActivity";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D3FE81799F53C00349659 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D3FE91799F53C00349659 /* Activity */,
|
||||
FF5D3FF01799F53C00349659 /* Common */,
|
||||
);
|
||||
path = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D3FE91799F53C00349659 /* Activity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D3FEA1799F53C00349659 /* VUPinboardAccess.h */,
|
||||
FF5D3FEB1799F53C00349659 /* VUPinboardAccess.m */,
|
||||
FF5D3FEC1799F53C00349659 /* VUPinboardActivity.h */,
|
||||
FF5D3FED1799F53C00349659 /* VUPinboardActivity.m */,
|
||||
FF5D3FEE1799F53C00349659 /* VUPinboardViewController.h */,
|
||||
FF5D3FEF1799F53C00349659 /* VUPinboardViewController.m */,
|
||||
);
|
||||
path = Activity;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D3FF01799F53C00349659 /* Common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D3FF11799F53C00349659 /* BBNetworkRequest.h */,
|
||||
FF5D3FF21799F53C00349659 /* BBNetworkRequest.m */,
|
||||
FF5D3FF31799F53C00349659 /* SSKeychain.h */,
|
||||
FF5D3FF41799F53C00349659 /* SSKeychain.m */,
|
||||
FF5D3FF51799F53C00349659 /* VUDialogViewController.h */,
|
||||
FF5D3FF61799F53C00349659 /* VUDialogViewController.m */,
|
||||
FF5D3FF71799F53C00349659 /* VUGradientButton.h */,
|
||||
FF5D3FF81799F53C00349659 /* VUGradientButton.m */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D3FFB1799F53C00349659 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D3FFC1799F53C00349659 /* PinboardActivityImage.png */,
|
||||
FF5D3FFD1799F53C00349659 /* PinboardActivityImage@2x.png */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF5D40091799F70200349659 /* ARChromeActivity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FF5D400A1799F70200349659 /* ARChromeActivity.h */,
|
||||
FF5D400B1799F70200349659 /* ARChromeActivity.m */,
|
||||
FF5D400C1799F70200349659 /* ARChromeActivity.png */,
|
||||
FF5D400D1799F70200349659 /* ARChromeActivity@2x.png */,
|
||||
FF5D400E1799F70200349659 /* ARChromeActivity@2x~ipad.png */,
|
||||
FF5D400F1799F70200349659 /* ARChromeActivity~ipad.png */,
|
||||
);
|
||||
name = ARChromeActivity;
|
||||
path = "Other Sources/ARChromeActivity";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF753CC2175858FC00344EC9 /* fmdb */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2317,6 +2431,14 @@
|
|||
FFAF53B71799E69D00C7FCCB /* Readability-activity-iPad@2x.png in Resources */,
|
||||
FFAF53B81799E69D00C7FCCB /* Readability-activity-iPhone.png in Resources */,
|
||||
FFAF53B91799E69D00C7FCCB /* Readability-activity-iPhone@2x.png in Resources */,
|
||||
FF5D40051799F53C00349659 /* LICENSE in Resources */,
|
||||
FF5D40061799F53C00349659 /* README.md in Resources */,
|
||||
FF5D40071799F53C00349659 /* PinboardActivityImage.png in Resources */,
|
||||
FF5D40081799F53C00349659 /* PinboardActivityImage@2x.png in Resources */,
|
||||
FF5D40111799F70200349659 /* ARChromeActivity.png in Resources */,
|
||||
FF5D40121799F70200349659 /* ARChromeActivity@2x.png in Resources */,
|
||||
FF5D40131799F70200349659 /* ARChromeActivity@2x~ipad.png in Resources */,
|
||||
FF5D40141799F70200349659 /* ARChromeActivity~ipad.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -2458,6 +2580,14 @@
|
|||
FFAF53B41799E69D00C7FCCB /* PocketAPIOperation.m in Sources */,
|
||||
FFAF53B51799E69D00C7FCCB /* SFHFKeychainUtils.m in Sources */,
|
||||
FFAF53BA1799E69D00C7FCCB /* ReadabilityActivity.m in Sources */,
|
||||
FF5D3FFE1799F53C00349659 /* VUPinboardAccess.m in Sources */,
|
||||
FF5D3FFF1799F53C00349659 /* VUPinboardActivity.m in Sources */,
|
||||
FF5D40001799F53C00349659 /* VUPinboardViewController.m in Sources */,
|
||||
FF5D40011799F53C00349659 /* BBNetworkRequest.m in Sources */,
|
||||
FF5D40021799F53C00349659 /* SSKeychain.m in Sources */,
|
||||
FF5D40031799F53C00349659 /* VUDialogViewController.m in Sources */,
|
||||
FF5D40041799F53C00349659 /* VUGradientButton.m in Sources */,
|
||||
FF5D40101799F70200349659 /* ARChromeActivity.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import "Underscore.h"
|
||||
|
||||
#define DEBUG 1
|
||||
//#define DEBUG 1
|
||||
|
||||
#ifdef DEBUG
|
||||
#define NEWSBLUR_URL [NSString stringWithFormat:@"http://nb.local.com"]
|
||||
|
|
29
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity.h
Executable file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
ARChromeActivity.h
|
||||
|
||||
Copyright (c) 2012 Alex Robinson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface ARChromeActivity : UIActivity
|
||||
|
||||
// Empty by default. Either set this in the initializer, or set this property.
|
||||
@property (strong, nonatomic) NSURL *callbackURL;
|
||||
|
||||
// Uses the "CFBundleName" from your Info.plist by default.
|
||||
@property (strong, nonatomic) NSString *callbackSource;
|
||||
|
||||
// The text beneath the icon. Defaults to "Chrome".
|
||||
@property (strong, nonatomic) NSString *activityTitle;
|
||||
|
||||
- (id)initWithCallbackURL:(NSURL *)callbackURL;
|
||||
|
||||
@end
|
79
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity.m
Executable file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
ARChromeActivity.m
|
||||
|
||||
Copyright (c) 2012 Alex Robinson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#import "ARChromeActivity.h"
|
||||
|
||||
@implementation ARChromeActivity {
|
||||
NSURL *_activityURL;
|
||||
}
|
||||
|
||||
@synthesize callbackURL = _callbackURL;
|
||||
@synthesize callbackSource = _callbackSource;
|
||||
@synthesize activityTitle = _activityTitle;
|
||||
|
||||
- (void)commonInit {
|
||||
_callbackSource = [[NSBundle mainBundle]objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
_activityTitle = @"Chrome";
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCallbackURL:(NSURL *)callbackURL {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self commonInit];
|
||||
_callbackURL = callbackURL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIImage *)activityImage {
|
||||
return [UIImage imageNamed:@"ARChromeActivity"];
|
||||
}
|
||||
|
||||
- (NSString *)activityType {
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
|
||||
return [[activityItems lastObject] isKindOfClass:[NSURL class]] && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"googlechrome-x-callback://"]];
|
||||
}
|
||||
|
||||
- (void)prepareWithActivityItems:(NSArray *)activityItems {
|
||||
_activityURL = [activityItems lastObject];
|
||||
}
|
||||
|
||||
- (void)performActivity {
|
||||
|
||||
NSString *openingURL = [_activityURL.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *callbackURL = [self.callbackURL.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *sourceName = [self.callbackSource stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSURL *activityURL = [NSURL URLWithString:
|
||||
[NSString stringWithFormat:@"googlechrome-x-callback://x-callback-url/open/?url=%@&x-success=%@&x-source=%@",
|
||||
openingURL,
|
||||
callbackURL,
|
||||
sourceName]];
|
||||
|
||||
[[UIApplication sharedApplication] openURL:activityURL];
|
||||
|
||||
[self activityDidFinish:YES];
|
||||
}
|
||||
|
||||
@end
|
BIN
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity.png
Executable file
After Width: | Height: | Size: 2.3 KiB |
BIN
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity@2x.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity@2x~ipad.png
Executable file
After Width: | Height: | Size: 4.7 KiB |
BIN
clients/ios/Other Sources/ARChromeActivity/ARChromeActivity~ipad.png
Executable file
After Width: | Height: | Size: 2.7 KiB |
|
@ -142,7 +142,7 @@
|
|||
DLog();
|
||||
//TODO: This should really be a UIView...
|
||||
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"")
|
||||
message:NSLocalizedString(@"Incorrect password. Please correct in Settings.app", @"")
|
||||
message:NSLocalizedString(@"Incorrect password. Please fix in Preferences.", @"")
|
||||
delegate:nil
|
||||
cancelButtonTitle:NSLocalizedString(@"OK", @"")
|
||||
otherButtonTitles:nil];
|
||||
|
|
BIN
clients/ios/Other Sources/ReadabilityActivity/Readability-activity-iPad.png
Executable file → Normal file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 937 B |
BIN
clients/ios/Other Sources/ReadabilityActivity/Readability-activity-iPad@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
clients/ios/Other Sources/ReadabilityActivity/Readability-activity-iPhone.png
Executable file → Normal file
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 793 B |
BIN
clients/ios/Other Sources/ReadabilityActivity/Readability-activity-iPhone@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -32,9 +32,9 @@ static NSString * const ReadabilityActivityAdd = @"add";
|
|||
|
||||
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems
|
||||
{
|
||||
// if (![ReadabilityActivity canPerformActivity]) {
|
||||
// return NO;
|
||||
// }
|
||||
if (![ReadabilityActivity canPerformActivity]) {
|
||||
return NO;
|
||||
}
|
||||
for (NSObject *item in activityItems) {
|
||||
if (![item isKindOfClass:[NSURL class]] && ![item isKindOfClass:[NSString class]]) {
|
||||
return NO;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// VUPinboardAccess.h
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 30.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void(^VUPinboardCompletionHandler)(BOOL success, NSError* error);
|
||||
|
||||
@interface VUPinboardAccess : NSObject
|
||||
|
||||
-(id)initWithAccessToken:(NSString*)accessToken;
|
||||
|
||||
-(void)addURL:(NSURL*)url description:(NSString*)description tags:(NSString*)tags shared:(BOOL)shared toread:(BOOL)toread
|
||||
withCompletionHandler:(VUPinboardCompletionHandler)completionHandler;
|
||||
|
||||
@end
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// VUPinboardAccess.m
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 30.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BBNetworkRequest.h"
|
||||
#import "VUPinboardAccess.h"
|
||||
|
||||
static NSString* const kBaseURL = @"https://api.pinboard.in/v1/";
|
||||
|
||||
@interface VUPinboardAccess ()
|
||||
|
||||
@property (nonatomic, strong) NSString* accessToken;
|
||||
|
||||
@end
|
||||
|
||||
@implementation VUPinboardAccess
|
||||
|
||||
-(id)initWithAccessToken:(NSString*)accessToken {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.accessToken = accessToken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)addURL:(NSURL*)url description:(NSString*)description tags:(NSString*)tags shared:(BOOL)shared toread:(BOOL)toread
|
||||
withCompletionHandler:(VUPinboardCompletionHandler)completionHandler {
|
||||
if (!description || description.length == 0) {
|
||||
description = @"none";
|
||||
}
|
||||
|
||||
NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{
|
||||
@"auth_token": self.accessToken,
|
||||
@"description": description,
|
||||
@"url" : [url absoluteString],
|
||||
@"shared": shared ? @"yes" : @"no",
|
||||
@"toread": toread ? @"yes" : @"no"
|
||||
}];
|
||||
|
||||
if (tags) {
|
||||
[parameters setValue:tags forKey:@"tags"];
|
||||
}
|
||||
|
||||
BBNetworkRequest* request = [BBNetworkRequest requestWithURLString:[kBaseURL stringByAppendingString:@"posts/add"] parameters:parameters];
|
||||
[request sendAsynchronousRequestWithCompletionHandler:^(NSHTTPURLResponse* response, NSData* data) {
|
||||
if (completionHandler) {
|
||||
completionHandler(YES, NULL);
|
||||
}
|
||||
} errorHandler:^(NSHTTPURLResponse* response, NSData* data, NSError* error) {
|
||||
if (completionHandler) {
|
||||
completionHandler(NO, error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// VUPinboardActivity.h
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 29.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface VUPinboardActivity : UIActivity
|
||||
|
||||
-(void)presentError:(NSError*)error;
|
||||
|
||||
@end
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// VUPinboardActivity.m
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 29.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VUPinboardActivity.h"
|
||||
#import "VUPinboardViewController.h"
|
||||
|
||||
@interface VUPinboardActivity ()
|
||||
|
||||
@property (nonatomic, strong) VUPinboardViewController* viewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation VUPinboardActivity
|
||||
|
||||
#pragma mark - Activity information
|
||||
|
||||
-(UIImage*)activityImage {
|
||||
return [UIImage imageNamed:@"PinboardActivityImage"];
|
||||
}
|
||||
|
||||
-(NSString*)activityTitle {
|
||||
return @"Pinboard";
|
||||
}
|
||||
|
||||
-(NSString*)activityType {
|
||||
return @"pinboard";
|
||||
}
|
||||
|
||||
-(UIViewController*)activityViewController {
|
||||
return self.viewController;
|
||||
}
|
||||
|
||||
#pragma mark - Perform the activity
|
||||
|
||||
-(BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
|
||||
return activityItems.count >= 1 && [[activityItems lastObject] isKindOfClass:[NSURL class]];
|
||||
}
|
||||
|
||||
-(void)performActivity {
|
||||
[self activityDidFinish:NO];
|
||||
}
|
||||
|
||||
-(void)prepareWithActivityItems:(NSArray *)activityItems {
|
||||
self.viewController = [[VUPinboardViewController alloc] initWithURL:[activityItems lastObject] activity:self];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
-(void)presentError:(NSError*)error {
|
||||
NSString* msg = [NSString stringWithFormat:@"%@", error.localizedDescription];
|
||||
UIAlertView* errorView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"") message:msg delegate:nil
|
||||
cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil];
|
||||
[errorView show];
|
||||
|
||||
[self activityDidFinish:NO];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// VUPinboardViewController.h
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 29.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "VUDialogViewController.h"
|
||||
|
||||
@class VUPinboardActivity;
|
||||
|
||||
@interface VUPinboardViewController : VUDialogViewController
|
||||
|
||||
-(id)initWithURL:(NSURL*)url activity:(VUPinboardActivity*)activity;
|
||||
|
||||
@end
|
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// VUPinboardViewController.m
|
||||
// UIActivityDemo
|
||||
//
|
||||
// Created by Boris Buegling on 29.09.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SSKeychain.h"
|
||||
#import "VUPinboardAccess.h"
|
||||
#import "VUPinboardActivity.h"
|
||||
#import "VUPinboardViewController.h"
|
||||
|
||||
static NSString* const kAccount = @"vu.org.Pinboard";
|
||||
static NSString* const kServiceName = @"pinboard.in";
|
||||
|
||||
@interface VUPinboardViewController () <VUDialogDelegate>
|
||||
|
||||
@property (nonatomic, strong) VUPinboardAccess* access;
|
||||
@property (nonatomic, weak) VUPinboardActivity* activity;
|
||||
@property (nonatomic, assign) NSString* pinboardAccessToken;
|
||||
@property (nonatomic, strong) NSURL* url;
|
||||
|
||||
@property (nonatomic, strong) UITextField* accessTokenView;
|
||||
@property (nonatomic, strong) UITextView* descriptionView;
|
||||
@property (nonatomic, strong) UISwitch* sharedView;
|
||||
@property (nonatomic, strong) UITextField* tagsView;
|
||||
@property (nonatomic, strong) UISwitch* toreadView;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation VUPinboardViewController
|
||||
|
||||
@dynamic pinboardAccessToken;
|
||||
|
||||
#pragma mark -
|
||||
|
||||
-(id)initWithURL:(NSURL*)url activity:(VUPinboardActivity*)activity {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.activity = activity;
|
||||
self.delegate = self;
|
||||
self.url = url;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)viewDidLoad {
|
||||
[self headlineWithImageResource:@"PinboardActivityImage" ofType:@"png" text:@"pinboard"];
|
||||
|
||||
if (!self.pinboardAccessToken) {
|
||||
self.accessTokenView = [self textFieldWithLabel:@"Access token"];
|
||||
|
||||
}
|
||||
|
||||
UILabel* urlLabel = [self labelWithText:[NSString stringWithFormat:@"URL: %@", self.url]];
|
||||
urlLabel.font = [UIFont boldSystemFontOfSize:urlLabel.font.pointSize];
|
||||
|
||||
// TODO: Prefill the page title into the description
|
||||
self.descriptionView = [self textViewWithLabel:@"Description"];
|
||||
self.tagsView = [self textFieldWithLabel:@"Tags"];
|
||||
|
||||
self.sharedView = [self switchWithLabel:@"Public"];
|
||||
// TODO: Check actual user default of this property
|
||||
self.sharedView.on = YES;
|
||||
|
||||
self.toreadView = [self switchWithLabel:@"Read later"];
|
||||
self.toreadView.on = NO;
|
||||
|
||||
[self defaultDialogButtonsWithSubmitLabel:@"Submit" cancelLabel:@"Cancel"];
|
||||
}
|
||||
|
||||
#pragma mark - Handle access token
|
||||
|
||||
-(NSString*)pinboardAccessToken {
|
||||
return [SSKeychain passwordForService:kServiceName account:kAccount];
|
||||
}
|
||||
|
||||
-(void)setPinboardAccessToken:(NSString *)pinboardAccessToken {
|
||||
[SSKeychain setPassword:pinboardAccessToken forService:kServiceName account:kAccount];
|
||||
}
|
||||
|
||||
#pragma mark - VUDialog delegate methods
|
||||
|
||||
-(void)cancelWithDialogViewController:(VUDialogViewController *)dialogViewController {
|
||||
[self.activity activityDidFinish:NO];
|
||||
}
|
||||
|
||||
-(void)submitWithDialogViewController:(VUDialogViewController *)dialogViewController {
|
||||
if (self.accessTokenView) {
|
||||
[self setPinboardAccessToken:self.accessTokenView.text];
|
||||
}
|
||||
|
||||
self.access = [[VUPinboardAccess alloc] initWithAccessToken:self.pinboardAccessToken];
|
||||
|
||||
__weak VUPinboardViewController* sself = self;
|
||||
[self.access addURL:self.url
|
||||
description:self.descriptionView.text
|
||||
tags:self.tagsView.text
|
||||
shared:self.sharedView.on
|
||||
toread:self.toreadView.on withCompletionHandler:^(BOOL success, NSError *error) {
|
||||
[sself.activity activityDidFinish:success];
|
||||
|
||||
if (!success) {
|
||||
[self.activity presentError:error];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// BBNetworkRequest.h
|
||||
//
|
||||
// Created by Boris Buegling on 05.08.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface BBNetworkRequest : NSMutableURLRequest
|
||||
|
||||
+(id)requestWithURLString:(NSString*)urlString;
|
||||
+(id)requestWithURLString:(NSString *)urlString parameters:(NSDictionary*)parameters;
|
||||
|
||||
-(void)sendAsynchronousRequestWithCompletionHandler:(void (^)(NSHTTPURLResponse*, NSData*))completionHandler
|
||||
errorHandler:(void (^)(NSHTTPURLResponse*, NSData*, NSError*))errorHandler;
|
||||
|
||||
@end
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// BBNetworkRequest.m
|
||||
//
|
||||
// Created by Boris Buegling on 05.08.12.
|
||||
// Copyright (c) 2012 Boris Buegling. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BBNetworkRequest.h"
|
||||
|
||||
static NSString* const kAuthHeaderField = @"Authorization";
|
||||
|
||||
@implementation BBNetworkRequest
|
||||
|
||||
+(NSString*)escapeStringAsParameter:(NSString*)string {
|
||||
// Encode all the reserved characters, per RFC 3986 (<http://www.ietf.org/rfc/rfc3986.txt>)
|
||||
CFStringRef escaped = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
|
||||
(CFStringRef)string,
|
||||
NULL,
|
||||
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
|
||||
kCFStringEncodingUTF8);
|
||||
return CFBridgingRelease(escaped);
|
||||
}
|
||||
|
||||
+(id)requestWithURLString:(NSString*)urlString {
|
||||
return [self requestWithURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
+(id)requestWithURLString:(NSString *)urlString parameters:(NSDictionary*)parameters {
|
||||
NSMutableArray* parameterArray = [NSMutableArray arrayWithCapacity:parameters.count];
|
||||
|
||||
[parameters enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
|
||||
[parameterArray addObject:[NSString stringWithFormat:@"%@=%@",
|
||||
[self escapeStringAsParameter:key],
|
||||
[self escapeStringAsParameter:value]]];
|
||||
}];
|
||||
|
||||
if (parameterArray.count > 0) {
|
||||
urlString = [urlString stringByAppendingFormat:@"?%@", [parameterArray componentsJoinedByString:@"&"]];
|
||||
}
|
||||
|
||||
return [self requestWithURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
-(void)sendAsynchronousRequestWithCompletionHandler:(void (^)(NSHTTPURLResponse*, NSData*))completionHandler
|
||||
errorHandler:(void (^)(NSHTTPURLResponse*, NSData*, NSError*))errorHandler {
|
||||
[NSURLConnection sendAsynchronousRequest:self queue:[NSOperationQueue mainQueue]
|
||||
completionHandler:^(NSURLResponse* response, NSData* data, NSError* error) {
|
||||
NSHTTPURLResponse* httpResponse = nil;
|
||||
NSUInteger statusCode = 200;
|
||||
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
httpResponse = (NSHTTPURLResponse*)response;
|
||||
statusCode = httpResponse.statusCode;
|
||||
}
|
||||
|
||||
if (data && data.length > 0 && statusCode >= 200 && statusCode < 400) {
|
||||
if (completionHandler) {
|
||||
completionHandler(httpResponse, data);
|
||||
}
|
||||
} else {
|
||||
if (errorHandler) {
|
||||
errorHandler(httpResponse, data, error);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
357
clients/ios/Other Sources/VUPinboardActivity/Classes/Common/SSKeychain.h
Executable file
|
@ -0,0 +1,357 @@
|
|||
//
|
||||
// SSKeychain.h
|
||||
// SSToolkit
|
||||
//
|
||||
// Created by Sam Soffes on 5/19/10.
|
||||
// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
/** Error codes that can be returned in NSError objects. */
|
||||
typedef enum {
|
||||
/** No error. */
|
||||
SSKeychainErrorNone = noErr,
|
||||
|
||||
/** Some of the arguments were invalid. */
|
||||
SSKeychainErrorBadArguments = -1001,
|
||||
|
||||
/** There was no password. */
|
||||
SSKeychainErrorNoPassword = -1002,
|
||||
|
||||
/** One or more parameters passed internally were not valid. */
|
||||
SSKeychainErrorInvalidParameter = errSecParam,
|
||||
|
||||
/** Failed to allocate memory. */
|
||||
SSKeychainErrorFailedToAllocated = errSecAllocate,
|
||||
|
||||
/** No trust results are available. */
|
||||
SSKeychainErrorNotAvailable = errSecNotAvailable,
|
||||
|
||||
/** Authorization/Authentication failed. */
|
||||
SSKeychainErrorAuthorizationFailed = errSecAuthFailed,
|
||||
|
||||
/** The item already exists. */
|
||||
SSKeychainErrorDuplicatedItem = errSecDuplicateItem,
|
||||
|
||||
/** The item cannot be found.*/
|
||||
SSKeychainErrorNotFound = errSecItemNotFound,
|
||||
|
||||
/** Interaction with the Security Server is not allowed. */
|
||||
SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed,
|
||||
|
||||
/** Unable to decode the provided data. */
|
||||
SSKeychainErrorFailedToDecode = errSecDecode
|
||||
} SSKeychainErrorCode;
|
||||
|
||||
extern NSString *const kSSKeychainErrorDomain;
|
||||
|
||||
/** Account name. */
|
||||
extern NSString *const kSSKeychainAccountKey;
|
||||
|
||||
/**
|
||||
Time the item was created.
|
||||
|
||||
The value will be a string.
|
||||
*/
|
||||
extern NSString *const kSSKeychainCreatedAtKey;
|
||||
|
||||
/** Item class. */
|
||||
extern NSString *const kSSKeychainClassKey;
|
||||
|
||||
/** Item description. */
|
||||
extern NSString *const kSSKeychainDescriptionKey;
|
||||
|
||||
/** Item label. */
|
||||
extern NSString *const kSSKeychainLabelKey;
|
||||
|
||||
/** Time the item was last modified.
|
||||
|
||||
The value will be a string.
|
||||
*/
|
||||
extern NSString *const kSSKeychainLastModifiedKey;
|
||||
|
||||
/** Where the item was created. */
|
||||
extern NSString *const kSSKeychainWhereKey;
|
||||
|
||||
/**
|
||||
Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system
|
||||
Keychain on Mac OS X and iOS.
|
||||
|
||||
This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors.
|
||||
SSKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it).
|
||||
*/
|
||||
@interface SSKeychain : NSObject
|
||||
|
||||
///-----------------------
|
||||
/// @name Getting Accounts
|
||||
///-----------------------
|
||||
|
||||
/**
|
||||
Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts.
|
||||
|
||||
See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
|
||||
dictionaries returned by this method.
|
||||
|
||||
@return An array of dictionarires containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
|
||||
accounts. The order of the objects in the array isn't defined.
|
||||
|
||||
@see allAccounts:
|
||||
*/
|
||||
+ (NSArray *)allAccounts;
|
||||
|
||||
/**
|
||||
Returns an array containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
|
||||
accounts.
|
||||
|
||||
See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
|
||||
dictionaries returned by this method.
|
||||
|
||||
@param error If accessing the accounts fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return An array of dictionarires containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
|
||||
accounts. The order of the objects in the array isn't defined.
|
||||
|
||||
@see allAccounts
|
||||
*/
|
||||
+ (NSArray *)allAccounts:(NSError **)error;
|
||||
|
||||
/**
|
||||
Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
|
||||
accounts for the given service.
|
||||
|
||||
See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
|
||||
dictionaries returned by this method.
|
||||
|
||||
@param serviceName The service for which to return the corresponding accounts.
|
||||
|
||||
@return An array of dictionarires containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain
|
||||
doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
|
||||
|
||||
@see accountsForService:error:
|
||||
*/
|
||||
+ (NSArray *)accountsForService:(NSString *)serviceName;
|
||||
|
||||
/**
|
||||
Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
|
||||
accounts for the given service.
|
||||
|
||||
@param serviceName The service for which to return the corresponding accounts.
|
||||
|
||||
@param error If accessing the accounts fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return An array of dictionarires containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain
|
||||
doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
|
||||
|
||||
@see accountsForService:
|
||||
*/
|
||||
+ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error;
|
||||
|
||||
|
||||
///------------------------
|
||||
/// @name Getting Passwords
|
||||
///------------------------
|
||||
|
||||
/**
|
||||
Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't have a
|
||||
password for the given parameters.
|
||||
|
||||
@param serviceName The service for which to return the corresponding password.
|
||||
|
||||
@param account The account for which to return the corresponding password.
|
||||
|
||||
@return Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't
|
||||
have a password for the given parameters.
|
||||
|
||||
@see passwordForService:account:error:
|
||||
*/
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
|
||||
|
||||
/**
|
||||
Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't have a
|
||||
password for the given parameters.
|
||||
|
||||
@param serviceName The service for which to return the corresponding password.
|
||||
|
||||
@param account The account for which to return the corresponding password.
|
||||
|
||||
@param error If accessing the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't
|
||||
have a password for the given parameters.
|
||||
|
||||
@see passwordForService:account:
|
||||
*/
|
||||
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
/**
|
||||
Returns the password data for a given accounts and service, or `nil` if the Keychain doesn't have data
|
||||
for the given parameters.
|
||||
|
||||
@param serviceName The service for which to return the corresponding password.
|
||||
|
||||
@param account The account for which to return the corresponding password.
|
||||
|
||||
@param error If accessing the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't
|
||||
have a password for the given parameters.
|
||||
|
||||
@see passwordDataForService:account:error:
|
||||
*/
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
|
||||
|
||||
/**
|
||||
Returns the password data for a given accounts and service, or `nil` if the Keychain doesn't have data
|
||||
for the given parameters.
|
||||
|
||||
@param serviceName The service for which to return the corresponding password.
|
||||
|
||||
@param account The account for which to return the corresponding password.
|
||||
|
||||
@param error If accessing the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns a string containing the password for a given accounts and service, or `nil` if the Keychain doesn't
|
||||
have a password for the given parameters.
|
||||
|
||||
@see passwordDataForService:account:
|
||||
*/
|
||||
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
|
||||
///-------------------------
|
||||
/// @name Deleting Passwords
|
||||
///-------------------------
|
||||
|
||||
/**
|
||||
Deletes a password from the Keychain.
|
||||
|
||||
@param serviceName The service for which to delete the corresponding password.
|
||||
|
||||
@param account The account for which to delete the corresponding password.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see deletePasswordForService:account:error:
|
||||
*/
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
|
||||
|
||||
/**
|
||||
Deletes a password from the Keychain.
|
||||
|
||||
@param serviceName The service for which to delete the corresponding password.
|
||||
|
||||
@param account The account for which to delete the corresponding password.
|
||||
|
||||
@param error If deleting the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see deletePasswordForService:account:
|
||||
*/
|
||||
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
|
||||
///------------------------
|
||||
/// @name Setting Passwords
|
||||
///------------------------
|
||||
|
||||
/**
|
||||
Sets a password in the Keychain.
|
||||
|
||||
@param password The password to store in the Keychain.
|
||||
|
||||
@param serviceName The service for which to set the corresponding password.
|
||||
|
||||
@param account The account for which to set the corresponding password.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see setPassword:forService:account:error:
|
||||
*/
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
|
||||
|
||||
/**
|
||||
Sets a password in the Keychain.
|
||||
|
||||
@param password The password to store in the Keychain.
|
||||
|
||||
@param serviceName The service for which to set the corresponding password.
|
||||
|
||||
@param account The account for which to set the corresponding password.
|
||||
|
||||
@param error If setting the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see setPassword:forService:account:
|
||||
*/
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
/**
|
||||
Sets arbirary data in the Keychain.
|
||||
|
||||
@param password The data to store in the Keychain.
|
||||
|
||||
@param serviceName The service for which to set the corresponding password.
|
||||
|
||||
@param account The account for which to set the corresponding password.
|
||||
|
||||
@param error If setting the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see setPasswordData:forService:account:error:
|
||||
*/
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
|
||||
|
||||
/**
|
||||
Sets arbirary data in the Keychain.
|
||||
|
||||
@param password The data to store in the Keychain.
|
||||
|
||||
@param serviceName The service for which to set the corresponding password.
|
||||
|
||||
@param account The account for which to set the corresponding password.
|
||||
|
||||
@param error If setting the password fails, upon return contains an error that describes the problem.
|
||||
|
||||
@return Returns `YES` on success, or `NO` on failure.
|
||||
|
||||
@see setPasswordData:forService:account:
|
||||
*/
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
|
||||
|
||||
|
||||
///--------------------
|
||||
/// @name Configuration
|
||||
///--------------------
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
/**
|
||||
Returns the accessibility type for all future passwords saved to the Keychain.
|
||||
|
||||
@return Returns the accessibility type.
|
||||
|
||||
The return value will be `NULL` or one of the "Keychain Item Accessibility Constants" used for determining when a
|
||||
keychain item should be readable.
|
||||
|
||||
@see accessibilityType
|
||||
*/
|
||||
+ (CFTypeRef)accessibilityType;
|
||||
|
||||
/**
|
||||
Sets the accessibility type for all future passwords saved to the Keychain.
|
||||
|
||||
@param accessibilityType One of the "Keychain Item Accessibility Constants" used for determining when a keychain item
|
||||
should be readable.
|
||||
|
||||
If the value is `NULL` (the default), the Keychain default will be used.
|
||||
|
||||
@see accessibilityType
|
||||
*/
|
||||
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
|
||||
#endif
|
||||
|
||||
@end
|
262
clients/ios/Other Sources/VUPinboardActivity/Classes/Common/SSKeychain.m
Executable file
|
@ -0,0 +1,262 @@
|
|||
//
|
||||
// SSKeychain.m
|
||||
// SSToolkit
|
||||
//
|
||||
// Created by Sam Soffes on 5/19/10.
|
||||
// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SSKeychain.h"
|
||||
|
||||
NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain";
|
||||
|
||||
NSString *const kSSKeychainAccountKey = @"acct";
|
||||
NSString *const kSSKeychainCreatedAtKey = @"cdat";
|
||||
NSString *const kSSKeychainClassKey = @"labl";
|
||||
NSString *const kSSKeychainDescriptionKey = @"desc";
|
||||
NSString *const kSSKeychainLabelKey = @"labl";
|
||||
NSString *const kSSKeychainLastModifiedKey = @"mdat";
|
||||
NSString *const kSSKeychainWhereKey = @"svce";
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
CFTypeRef SSKeychainAccessibilityType = NULL;
|
||||
#endif
|
||||
|
||||
@interface SSKeychain ()
|
||||
+ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account;
|
||||
@end
|
||||
|
||||
@implementation SSKeychain
|
||||
|
||||
#pragma mark - Getting Accounts
|
||||
|
||||
+ (NSArray *)allAccounts {
|
||||
return [self accountsForService:nil error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (NSArray *)allAccounts:(NSError **)error {
|
||||
return [self accountsForService:nil error:error];
|
||||
}
|
||||
|
||||
|
||||
+ (NSArray *)accountsForService:(NSString *)service {
|
||||
return [self accountsForService:service error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (NSArray *)accountsForService:(NSString *)service error:(NSError **)error {
|
||||
OSStatus status = SSKeychainErrorBadArguments;
|
||||
NSMutableDictionary *query = [self _queryForService:service account:nil];
|
||||
#if __has_feature(objc_arc)
|
||||
[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
|
||||
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
|
||||
#else
|
||||
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
|
||||
[query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];
|
||||
#endif
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
#if __has_feature(objc_arc)
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
#else
|
||||
status = SecItemCopyMatching((CFDictionaryRef)query, &result);
|
||||
#endif
|
||||
if (status != noErr && error != NULL) {
|
||||
*error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
return (__bridge_transfer NSArray *)result;
|
||||
#else
|
||||
return [(NSArray *)result autorelease];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Getting Passwords
|
||||
|
||||
+ (NSString *)passwordForService:(NSString *)service account:(NSString *)account {
|
||||
return [self passwordForService:service account:account error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
|
||||
NSData *data = [self passwordDataForService:service account:account error:error];
|
||||
if (data.length > 0) {
|
||||
NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding];
|
||||
#if !__has_feature(objc_arc)
|
||||
[string autorelease];
|
||||
#endif
|
||||
return string;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
+ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account {
|
||||
return [self passwordDataForService:service account:account error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
|
||||
OSStatus status = SSKeychainErrorBadArguments;
|
||||
if (!service || !account) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
CFTypeRef result = NULL;
|
||||
NSMutableDictionary *query = [self _queryForService:service account:account];
|
||||
#if __has_feature(objc_arc)
|
||||
[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
|
||||
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
|
||||
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
||||
#else
|
||||
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
|
||||
[query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
|
||||
status = SecItemCopyMatching((CFDictionaryRef)query, &result);
|
||||
#endif
|
||||
|
||||
if (status != noErr && error != NULL) {
|
||||
*error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
return (__bridge_transfer NSData *)result;
|
||||
#else
|
||||
return [(NSData *)result autorelease];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Deleting Passwords
|
||||
|
||||
+ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account {
|
||||
return [self deletePasswordForService:service account:account error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
|
||||
OSStatus status = SSKeychainErrorBadArguments;
|
||||
if (service && account) {
|
||||
NSMutableDictionary *query = [self _queryForService:service account:account];
|
||||
#if __has_feature(objc_arc)
|
||||
status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||
#else
|
||||
status = SecItemDelete((CFDictionaryRef)query);
|
||||
#endif
|
||||
}
|
||||
if (status != noErr && error != NULL) {
|
||||
*error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
|
||||
}
|
||||
return (status == noErr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Setting Passwords
|
||||
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account {
|
||||
return [self setPassword:password forService:service account:account error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
|
||||
NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self setPasswordData:data forService:service account:account error:error];
|
||||
}
|
||||
|
||||
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account {
|
||||
return [self setPasswordData:password forService:service account:account error:nil];
|
||||
}
|
||||
|
||||
|
||||
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
|
||||
OSStatus status = SSKeychainErrorBadArguments;
|
||||
if (password && service && account) {
|
||||
[self deletePasswordForService:service account:account];
|
||||
NSMutableDictionary *query = [self _queryForService:service account:account];
|
||||
#if __has_feature(objc_arc)
|
||||
[query setObject:password forKey:(__bridge id)kSecValueData];
|
||||
#else
|
||||
[query setObject:password forKey:(id)kSecValueData];
|
||||
#endif
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
if (SSKeychainAccessibilityType) {
|
||||
#if __has_feature(objc_arc)
|
||||
[query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible];
|
||||
#else
|
||||
[query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible];
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
||||
#else
|
||||
status = SecItemAdd((CFDictionaryRef)query, NULL);
|
||||
#endif
|
||||
}
|
||||
if (status != noErr && error != NULL) {
|
||||
*error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
|
||||
}
|
||||
return (status == noErr);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Configuration
|
||||
|
||||
#if __IPHONE_4_0 && TARGET_OS_IPHONE
|
||||
+ (CFTypeRef)accessibilityType {
|
||||
return SSKeychainAccessibilityType;
|
||||
}
|
||||
|
||||
|
||||
+ (void)setAccessibilityType:(CFTypeRef)accessibilityType {
|
||||
CFRetain(accessibilityType);
|
||||
if (SSKeychainAccessibilityType) {
|
||||
CFRelease(SSKeychainAccessibilityType);
|
||||
}
|
||||
SSKeychainAccessibilityType = accessibilityType;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||
#if __has_feature(objc_arc)
|
||||
[dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
|
||||
#else
|
||||
[dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
|
||||
#endif
|
||||
|
||||
if (service) {
|
||||
#if __has_feature(objc_arc)
|
||||
[dictionary setObject:service forKey:(__bridge id)kSecAttrService];
|
||||
#else
|
||||
[dictionary setObject:service forKey:(id)kSecAttrService];
|
||||
#endif
|
||||
}
|
||||
|
||||
if (account) {
|
||||
#if __has_feature(objc_arc)
|
||||
[dictionary setObject:account forKey:(__bridge id)kSecAttrAccount];
|
||||
#else
|
||||
[dictionary setObject:account forKey:(id)kSecAttrAccount];
|
||||
#endif
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// VUDialogViewController.h
|
||||
//
|
||||
// Created by Boris Buegling.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class VUDialogViewController;
|
||||
|
||||
@protocol VUDialogDelegate <NSObject>
|
||||
|
||||
-(void)cancelWithDialogViewController:(VUDialogViewController*)dialogViewController;
|
||||
-(void)submitWithDialogViewController:(VUDialogViewController*)dialogViewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface VUDialogViewController : UIViewController
|
||||
|
||||
@property (nonatomic, weak) id<VUDialogDelegate> delegate;
|
||||
|
||||
-(BOOL)defaultDialogButtonsWithSubmitLabel:(NSString*)submitLabel cancelLabel:(NSString*)cancelLabel;
|
||||
-(UILabel*)headlineWithImageResource:(NSString*)resourceName ofType:(NSString*)resourceType text:(NSString*)text;
|
||||
-(UILabel*)labelWithText:(NSString*)text;
|
||||
-(UISwitch*)switchWithLabel:(NSString*)labelText;
|
||||
-(UITextField*)textFieldWithLabel:(NSString*)labelText;
|
||||
-(UITextView*)textViewWithLabel:(NSString*)labelText;
|
||||
|
||||
@end
|
|
@ -0,0 +1,234 @@
|
|||
//
|
||||
// VUDialogViewController.m
|
||||
//
|
||||
// Created by Boris Buegling.
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import "VUDialogViewController.h"
|
||||
#import "VUGradientButton.h"
|
||||
|
||||
static const CGFloat kMargin = 20.0;
|
||||
|
||||
static const CGFloat kButtonWidth = 80.0;
|
||||
static const CGFloat kButtonHeight = 30.0;
|
||||
static const CGFloat kLabelHeight = 20.0;
|
||||
static const CGFloat kSwitchWidth = 60.0;
|
||||
static const CGFloat kSwitchHeight = 20.0;
|
||||
static const CGFloat kTextFieldHeight = 30.0;
|
||||
static const CGFloat kTextViewHeight = 60.0;
|
||||
|
||||
@interface VUDialogViewController () <UITextFieldDelegate, UITextViewDelegate>
|
||||
|
||||
@property (nonatomic, assign) CGPoint point;
|
||||
@property (nonatomic, assign) CGFloat pointX;
|
||||
@property (nonatomic, assign) CGFloat pointY;
|
||||
@property (nonatomic, readonly) CGFloat width;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation VUDialogViewController
|
||||
|
||||
@dynamic pointX;
|
||||
@dynamic pointY;
|
||||
@dynamic width;
|
||||
|
||||
#pragma mark -
|
||||
|
||||
-(id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.point = CGPointMake(kMargin, kMargin);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)loadView {
|
||||
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self.view.backgroundColor = [UIColor underPageBackgroundColor];
|
||||
}
|
||||
|
||||
#pragma mark - Layout values
|
||||
|
||||
-(CGFloat)pointX {
|
||||
return self.point.x;
|
||||
}
|
||||
|
||||
-(CGFloat)pointY {
|
||||
return self.point.y;
|
||||
}
|
||||
|
||||
-(void)setPointX:(CGFloat)pointX {
|
||||
self.point = CGPointMake(pointX, self.point.y);
|
||||
}
|
||||
|
||||
-(void)setPointY:(CGFloat)pointY {
|
||||
self.point = CGPointMake(self.point.x, pointY);
|
||||
}
|
||||
|
||||
-(CGFloat)width {
|
||||
return self.view.frame.size.width - 2.0 * kMargin;
|
||||
}
|
||||
|
||||
#pragma mark - UI Elements
|
||||
|
||||
-(void)adjustLabelWidth:(UILabel*)label {
|
||||
CGFloat labelWidth = [label.text sizeWithFont:label.font].width;
|
||||
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, labelWidth, label.frame.size.height);
|
||||
}
|
||||
|
||||
-(UIButton*)buttonAtPoint:(CGPoint)point title:(NSString*)titleText action:(SEL)action {
|
||||
VUGradientButton* button = [VUGradientButton buttonWithType:UIButtonTypeCustom];
|
||||
button.frame = CGRectMake(point.x, point.y, kButtonWidth, kButtonHeight);
|
||||
button.highColor = [UIColor lightGrayColor];
|
||||
button.lowColor = [UIColor whiteColor];
|
||||
|
||||
[button setTitle:titleText forState:UIControlStateNormal];
|
||||
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
||||
|
||||
[button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
[self.view addSubview:button];
|
||||
return button;
|
||||
}
|
||||
|
||||
-(BOOL)defaultDialogButtonsWithSubmitLabel:(NSString*)submitLabel cancelLabel:(NSString*)cancelLabel {
|
||||
[self buttonAtPoint:CGPointMake(self.width + kMargin - 2.0 * kButtonWidth - kMargin, self.point.y)
|
||||
title:cancelLabel
|
||||
action:@selector(cancelTapped)];
|
||||
|
||||
[self buttonAtPoint:CGPointMake(self.width + kMargin - kButtonWidth, self.point.y)
|
||||
title:submitLabel
|
||||
action:@selector(submitTapped)];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(UILabel*)headlineWithImageResource:(NSString*)resourceName ofType:(NSString*)resourceType text:(NSString*)text {
|
||||
UIImage* logo = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:resourceName ofType:resourceType]];
|
||||
UIImageView* logoView = [[UIImageView alloc] initWithImage:logo];
|
||||
logoView.frame = CGRectMake(self.point.x, self.point.y, logo.size.width, logo.size.height);
|
||||
[self.view addSubview:logoView];
|
||||
|
||||
CGFloat oldPointY = self.pointY;
|
||||
UILabel* headlineLabel = [self labelAtPoint:self.point width:self.width text:text];
|
||||
self.pointY = oldPointY;
|
||||
|
||||
headlineLabel.frame = CGRectMake(self.pointX + logo.size.width + kMargin, self.pointY, self.width, 30.0);
|
||||
headlineLabel.font = [UIFont boldSystemFontOfSize:25.0];
|
||||
headlineLabel.textColor = [UIColor whiteColor];
|
||||
|
||||
self.pointY = self.pointY + logoView.frame.size.height + kMargin / 2.0;
|
||||
|
||||
return headlineLabel;
|
||||
}
|
||||
|
||||
-(UILabel*)labelAtPoint:(CGPoint)point width:(CGFloat)width text:(NSString*)text {
|
||||
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(point.x, point.y, width, kLabelHeight)];
|
||||
label.backgroundColor = [UIColor clearColor];
|
||||
label.text = text;
|
||||
|
||||
[self.view addSubview:label];
|
||||
self.pointY = self.pointY + label.frame.size.height + kMargin;
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
-(UILabel*)labelAtPoint:(CGPoint)point text:(NSString*)text {
|
||||
CGFloat labelWidth = [text sizeWithFont:[UIFont systemFontOfSize:18.0]].width;
|
||||
return [self labelAtPoint:point width:labelWidth text:text];
|
||||
}
|
||||
|
||||
-(UILabel*)labelWithText:(NSString*)text {
|
||||
return [self labelAtPoint:self.point width:self.width text:text];
|
||||
}
|
||||
|
||||
-(UISwitch*)switchAtPoint:(CGPoint)point width:(CGFloat)width withLabel:(NSString*)labelText {
|
||||
CGFloat oldPointY = self.pointY;
|
||||
[self labelAtPoint:CGPointMake(point.x, point.y + 5.0) text:labelText];
|
||||
self.pointY = oldPointY;
|
||||
|
||||
UISwitch* switchObject = [[UISwitch alloc] initWithFrame:CGRectMake(width - kSwitchWidth, point.y, kSwitchWidth, kSwitchHeight)];
|
||||
|
||||
[self.view addSubview:switchObject];
|
||||
self.pointY += switchObject.frame.size.height + kMargin;
|
||||
|
||||
return switchObject;
|
||||
}
|
||||
|
||||
-(UISwitch*)switchWithLabel:(NSString*)labelText {
|
||||
return [self switchAtPoint:self.point width:self.width withLabel:labelText];
|
||||
}
|
||||
|
||||
-(UITextField*)textFieldAtPoint:(CGPoint)point width:(CGFloat)width {
|
||||
UITextField* textField = [[UITextField alloc] initWithFrame:CGRectMake(point.x, point.y, width, kTextFieldHeight)];
|
||||
textField.borderStyle = UITextBorderStyleRoundedRect;
|
||||
textField.delegate = self;
|
||||
|
||||
[self.view addSubview:textField];
|
||||
self.pointY = self.pointY + textField.frame.size.height + kMargin;
|
||||
|
||||
return textField;
|
||||
}
|
||||
|
||||
-(UITextField*)textFieldAtPoint:(CGPoint)point width:(CGFloat)width withLabel:(NSString*)labelText {
|
||||
CGFloat oldPointY = self.pointY;
|
||||
UILabel* label = [self labelAtPoint:CGPointMake(point.x, point.y + 5.0) text:labelText];
|
||||
self.pointY = oldPointY;
|
||||
CGFloat margin = label.frame.size.width + 10.0;
|
||||
return [self textFieldAtPoint:CGPointMake(point.x + margin, point.y) width:width - margin];
|
||||
}
|
||||
|
||||
-(UITextField*)textFieldWithLabel:(NSString*)labelText {
|
||||
return [self textFieldAtPoint:self.point width:self.width withLabel:labelText];
|
||||
}
|
||||
|
||||
-(UITextView*)textViewAtPoint:(CGPoint)point width:(CGFloat)width {
|
||||
UITextView* textView = [[UITextView alloc] initWithFrame:CGRectMake(point.x, point.y, width, kTextViewHeight)];
|
||||
textView.layer.borderWidth = 1.0;
|
||||
textView.layer.cornerRadius = 5.0;
|
||||
textView.clipsToBounds = YES;
|
||||
|
||||
[self.view addSubview:textView];
|
||||
self.pointY = self.pointY + textView.frame.size.height + kMargin;
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
-(UITextView*)textViewAtPoint:(CGPoint)point width:(CGFloat)width withLabel:(NSString*)labelText {
|
||||
[self labelAtPoint:point width:width text:labelText];
|
||||
self.pointY = self.pointY - (kMargin / 2.0);
|
||||
return [self textViewAtPoint:self.point width:width];
|
||||
}
|
||||
|
||||
-(UITextView*)textViewWithLabel:(NSString*)labelText {
|
||||
return [self textViewAtPoint:self.point width:self.width withLabel:labelText];
|
||||
}
|
||||
|
||||
#pragma mark - Handle actions
|
||||
|
||||
-(void)cancelTapped {
|
||||
if ([self.delegate respondsToSelector:@selector(cancelWithDialogViewController:)]) {
|
||||
[self.delegate cancelWithDialogViewController:self];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)submitTapped {
|
||||
if ([self.delegate respondsToSelector:@selector(submitWithDialogViewController:)]) {
|
||||
[self.delegate submitWithDialogViewController:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate methods
|
||||
|
||||
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
[textField resignFirstResponder];
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - UITextView delegate methods
|
||||
|
||||
@end
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// VUGradientButton.h
|
||||
//
|
||||
// Created by Boris Buegling.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface VUGradientButton : UIButton
|
||||
|
||||
@property (nonatomic, strong) UIColor* highColor;
|
||||
@property (nonatomic, strong) UIColor* lowColor;
|
||||
|
||||
@end
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// VUGradientButton.m
|
||||
//
|
||||
// Created by Boris Buegling.
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import "VUGradientButton.h"
|
||||
|
||||
static NSArray* kObservedKeyPaths;
|
||||
|
||||
@interface VUGradientButton ()
|
||||
|
||||
@property (nonatomic, strong) CAGradientLayer* gradientLayer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation VUGradientButton
|
||||
|
||||
+(id)buttonWithType:(UIButtonType)buttonType {
|
||||
UIButton* button = [super buttonWithType:buttonType];
|
||||
[button awakeFromNib];
|
||||
|
||||
if (!kObservedKeyPaths) {
|
||||
kObservedKeyPaths = @[ @"bounds", @"frame", @"highColor", @"lowColor" ];
|
||||
}
|
||||
|
||||
for (NSString* keyPath in kObservedKeyPaths) {
|
||||
[button addObserver:button forKeyPath:keyPath options:0 context:NULL];
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
for (NSString* keyPath in kObservedKeyPaths) {
|
||||
[self removeObserver:self forKeyPath:keyPath];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
-(void)awakeFromNib {
|
||||
self.gradientLayer = [[CAGradientLayer alloc] init];
|
||||
self.gradientLayer.bounds = self.bounds;
|
||||
self.gradientLayer.position = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0);
|
||||
[self.layer insertSublayer:self.gradientLayer atIndex:0];
|
||||
|
||||
self.layer.borderWidth = 1.0f;
|
||||
self.layer.cornerRadius = 8.0f;
|
||||
self.layer.masksToBounds = YES;
|
||||
}
|
||||
|
||||
-(void)drawRect:(CGRect)rect {
|
||||
[self.gradientLayer setColors:@[ (id)[self.highColor CGColor], (id)[self.lowColor CGColor] ]];
|
||||
[super drawRect:rect];
|
||||
}
|
||||
|
||||
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if (![keyPath hasSuffix:@"Color"]) {
|
||||
[self awakeFromNib];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
@end
|
26
clients/ios/Other Sources/VUPinboardActivity/LICENSE
Executable file
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2012 Boris Buegling
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
clients/ios/Other Sources/VUPinboardActivity/README.md
Executable file
|
@ -0,0 +1,6 @@
|
|||
# VUPinboardActivity #
|
||||
|
||||
Really simple (and ugly) UIActivity for [Pinboard][1]. Drop this into
|
||||
your iOS 6 app and get instant support for sendings URLs to Pinboard.
|
||||
|
||||
[1]: http://pinboard.in
|
BIN
clients/ios/Other Sources/VUPinboardActivity/Resources/PinboardActivityImage.png
Executable file
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 3.3 KiB |
|
@ -156,11 +156,13 @@
|
|||
</dict>
|
||||
<dict>
|
||||
<key>Title</key>
|
||||
<string>Send to - Instapaper</string>
|
||||
<string>Sharing services</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>InstapaperActivityIcon</string>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
|
@ -171,6 +173,10 @@
|
|||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>InstapaperActivityIcon</string>
|
||||
<key>IASKTextAlignment</key>
|
||||
<string>IASKUITextAlignmentRight</string>
|
||||
<key>Type</key>
|
||||
<string>PSTextFieldSpecifier</string>
|
||||
<key>Title</key>
|
||||
|
@ -183,6 +189,10 @@
|
|||
<string>no</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>InstapaperActivityIcon</string>
|
||||
<key>IASKTextAlignment</key>
|
||||
<string>IASKUITextAlignmentRight</string>
|
||||
<key>Type</key>
|
||||
<string>PSTextFieldSpecifier</string>
|
||||
<key>Title</key>
|
||||
|
@ -196,6 +206,42 @@
|
|||
<key>AutocorrectionType</key>
|
||||
<string>no</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>PocketActivity</string>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Show Pocket</string>
|
||||
<key>Key</key>
|
||||
<string>enable_pocket</string>
|
||||
<key>DefaultValue</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>Readability-activity-iPhone</string>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Show Readability</string>
|
||||
<key>Key</key>
|
||||
<string>enable_readability</string>
|
||||
<key>DefaultValue</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IASKCellImage</key>
|
||||
<string>PinboardActivityImage</string>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Show Pinboard</string>
|
||||
<key>Key</key>
|
||||
<string>enable_pinboard</string>
|
||||
<key>DefaultValue</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>StringsTable</key>
|
||||
<string>Root</string>
|
||||
|
|