From 979b6bbccafbfce6974f8af4d367dd37969f4f2f Mon Sep 17 00:00:00 2001 From: Samuel Clay Date: Wed, 3 Aug 2011 10:00:41 -0700 Subject: [PATCH] Adding icons to main screen and handling html entities on feed detail. --- media/iphone/Base64.h | 15 +++ media/iphone/Base64.m | 106 ++++++++++++++++++ media/iphone/Classes/FeedDetailTableCell.xib | 32 +++--- .../iphone/Classes/FeedDetailViewController.m | 9 +- media/iphone/Classes/NewsBlurViewController.h | 3 + media/iphone/Classes/NewsBlurViewController.m | 22 +++- .../iphone/NewsBlur.xcodeproj/project.pbxproj | 10 ++ media/iphone/NewsBlurViewController.xib | 35 +++++- media/iphone/world.png | Bin 0 -> 888 bytes 9 files changed, 206 insertions(+), 26 deletions(-) create mode 100644 media/iphone/Base64.h create mode 100644 media/iphone/Base64.m create mode 100755 media/iphone/world.png diff --git a/media/iphone/Base64.h b/media/iphone/Base64.h new file mode 100644 index 000000000..883f3c895 --- /dev/null +++ b/media/iphone/Base64.h @@ -0,0 +1,15 @@ +// +// Base64.h +// NewsBlur +// +// Created by Samuel Clay on 8/3/11. +// Copyright 2011 NewsBlur. All rights reserved. +// + + + +@interface NSData (MBBase64) + ++ (id)dataWithBase64EncodedString:(NSString *)string; // Padding '=' characters are optional. Whitespace is ignored. +- (NSString *)base64Encoding; +@end diff --git a/media/iphone/Base64.m b/media/iphone/Base64.m new file mode 100644 index 000000000..e75285871 --- /dev/null +++ b/media/iphone/Base64.m @@ -0,0 +1,106 @@ +#include "Base64.h" + +static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +@implementation NSData (MBBase64) + ++ (id)dataWithBase64EncodedString:(NSString *)string; +{ + if (string == nil) + [NSException raise:NSInvalidArgumentException format:nil]; + if ([string length] == 0) + return [NSData data]; + + static char *decodingTable = NULL; + if (decodingTable == NULL) + { + decodingTable = malloc(256); + if (decodingTable == NULL) + return nil; + memset(decodingTable, CHAR_MAX, 256); + NSUInteger i; + for (i = 0; i < 64; i++) + decodingTable[(short)encodingTable[i]] = i; + } + + const char *characters = [string cStringUsingEncoding:NSASCIIStringEncoding]; + if (characters == NULL) // Not an ASCII string! + return nil; + char *bytes = malloc((([string length] + 3) / 4) * 3); + if (bytes == NULL) + return nil; + NSUInteger length = 0; + + NSUInteger i = 0; + while (YES) + { + char buffer[4]; + short bufferLength; + for (bufferLength = 0; bufferLength < 4; i++) + { + if (characters[i] == '\0') + break; + if (isspace(characters[i]) || characters[i] == '=') + continue; + buffer[bufferLength] = decodingTable[(short)characters[i]]; + if (buffer[bufferLength++] == CHAR_MAX) // Illegal character! + { + free(bytes); + return nil; + } + } + + if (bufferLength == 0) + break; + if (bufferLength == 1) // At least two characters are needed to produce one byte! + { + free(bytes); + return nil; + } + + // Decode the characters in the buffer to bytes. + bytes[length++] = (buffer[0] << 2) | (buffer[1] >> 4); + if (bufferLength > 2) + bytes[length++] = (buffer[1] << 4) | (buffer[2] >> 2); + if (bufferLength > 3) + bytes[length++] = (buffer[2] << 6) | buffer[3]; + } + + realloc(bytes, length); + return [NSData dataWithBytesNoCopy:bytes length:length]; +} + +- (NSString *)base64Encoding; +{ + if ([self length] == 0) + return @""; + + char *characters = malloc((([self length] + 2) / 3) * 4); + if (characters == NULL) + return nil; + NSUInteger length = 0; + + NSUInteger i = 0; + while (i < [self length]) + { + char buffer[3] = {0,0,0}; + short bufferLength = 0; + while (bufferLength < 3 && i < [self length]) + buffer[bufferLength++] = ((char *)[self bytes])[i++]; + + // Encode the bytes in the buffer to four characters, including padding "=" characters if necessary. + characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2]; + characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; + if (bufferLength > 1) + characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; + else characters[length++] = '='; + if (bufferLength > 2) + characters[length++] = encodingTable[buffer[2] & 0x3F]; + else characters[length++] = '='; + } + + return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; +} + +@end diff --git a/media/iphone/Classes/FeedDetailTableCell.xib b/media/iphone/Classes/FeedDetailTableCell.xib index 9468f8ef7..6f112c611 100644 --- a/media/iphone/Classes/FeedDetailTableCell.xib +++ b/media/iphone/Classes/FeedDetailTableCell.xib @@ -2,13 +2,13 @@ 1024 - 10K540 - 1306 - 1038.36 - 461.00 + 11A511 + 1617 + 1138 + 566.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 301 + 534 YES @@ -51,7 +51,7 @@ 292 - {{172, 39}, {119, 15}} + {{172, 44}, {119, 15}} @@ -87,7 +87,7 @@ 292 - {{20, 5}, {271, 31}} + {{20, 7}, {271, 34}} @@ -98,7 +98,7 @@ Long label with a story title the size of Texas. Helvetica-Bold - 12 + 14 16 @@ -118,7 +118,7 @@ 292 - {{20, 39}, {150, 15}} + {{20, 44}, {150, 15}} @@ -141,7 +141,7 @@ 292 - {{2, 13}, {16, 16}} + {{2, 16}, {16, 16}} NO @@ -149,7 +149,7 @@ IBCocoaTouchFramework - {300, 59} + {300, 64} @@ -163,7 +163,7 @@ IBCocoaTouchFramework - {320, 59} + {320, 64} @@ -271,9 +271,10 @@ YES -1.CustomClassName + -1.IBPluginDependency -2.CustomClassName + -2.IBPluginDependency 2.CustomClassName - 2.IBEditorWindowLastContentRect 2.IBPluginDependency 3.IBPluginDependency 5.IBPluginDependency @@ -283,9 +284,10 @@ YES FeedDetailTableCell + com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin FeedDetailTableCell - {{579, 440}, {320, 55}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -378,6 +380,6 @@ YES 3 - 301 + 534 diff --git a/media/iphone/Classes/FeedDetailViewController.m b/media/iphone/Classes/FeedDetailViewController.m index b75a09b9b..5db7c6f69 100644 --- a/media/iphone/Classes/FeedDetailViewController.m +++ b/media/iphone/Classes/FeedDetailViewController.m @@ -10,9 +10,10 @@ #import "NewsBlurAppDelegate.h" #import "FeedDetailTableCell.h" #import "ASIFormDataRequest.h" +#import "GTMNString+HTML.h" #import "JSON.h" -#define kTableViewRowHeight 60; +#define kTableViewRowHeight 65; @implementation FeedDetailViewController @@ -250,7 +251,9 @@ } else { cell.storyAuthor.text = @""; } - cell.storyTitle.text = [story objectForKey:@"story_title"]; + NSString *title = [story objectForKey:@"story_title"]; +// cell.storyTitle.text = [title stringByDecodingHTMLEntities]; + cell.storyTitle.text = title; cell.storyDate.text = [story objectForKey:@"short_parsed_date"]; int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]]; if (score > 0) { @@ -264,7 +267,7 @@ if ([[story objectForKey:@"read_status"] intValue] != 1) { // Unread story cell.storyTitle.textColor = [UIColor colorWithRed:0.1f green:0.1f blue:0.1f alpha:1.0]; - cell.storyTitle.font = [UIFont fontWithName:@"Helvetica-Bold" size:12]; + cell.storyTitle.font = [UIFont fontWithName:@"Helvetica-Bold" size:13]; cell.storyAuthor.textColor = [UIColor colorWithRed:0.58f green:0.58f blue:0.58f alpha:1.0]; cell.storyAuthor.font = [UIFont fontWithName:@"Helvetica-Bold" size:10]; cell.storyDate.textColor = [UIColor colorWithRed:0.14f green:0.18f blue:0.42f alpha:1.0]; diff --git a/media/iphone/Classes/NewsBlurViewController.h b/media/iphone/Classes/NewsBlurViewController.h index 39bc46d72..b8eff09ca 100644 --- a/media/iphone/Classes/NewsBlurViewController.h +++ b/media/iphone/Classes/NewsBlurViewController.h @@ -27,6 +27,7 @@ IBOutlet UISlider * feedScoreSlider; IBOutlet UIBarButtonItem * logoutButton; IBOutlet UISegmentedControl * intelligenceControl; + IBOutlet UIBarButtonItem * sitesButton; } - (void)fetchFeedList; @@ -36,12 +37,14 @@ - (void)updateFeedsWithIntelligence:(int)previousLevel newLevel:(int)newLevel; - (void)calculateFeedLocations; + (int)computeMaxScoreForFeed:(NSDictionary *)feed; +- (IBAction)switchSitesUnread; @property (nonatomic, retain) IBOutlet NewsBlurAppDelegate *appDelegate; @property (nonatomic, retain) IBOutlet UITableView *feedTitlesTable; @property (nonatomic, retain) IBOutlet UIToolbar *feedViewToolbar; @property (nonatomic, retain) IBOutlet UISlider * feedScoreSlider; @property (nonatomic, retain) IBOutlet UIBarButtonItem * logoutButton; +@property (nonatomic, retain) IBOutlet UIBarButtonItem * sitesButton; @property (nonatomic, retain) NSMutableArray *dictFoldersArray; @property (nonatomic, retain) NSMutableDictionary *activeFeedLocations; @property (nonatomic, retain) NSDictionary *dictFolders; diff --git a/media/iphone/Classes/NewsBlurViewController.m b/media/iphone/Classes/NewsBlurViewController.m index d3a951c14..347f8fe7c 100644 --- a/media/iphone/Classes/NewsBlurViewController.m +++ b/media/iphone/Classes/NewsBlurViewController.m @@ -9,6 +9,7 @@ #import "NewsBlurViewController.h" #import "NewsBlurAppDelegate.h" #import "FeedTableCell.h" +#import "Base64.h" #import "JSON.h" #define kTableViewRowHeight 40; @@ -24,6 +25,7 @@ @synthesize logoutButton; @synthesize intelligenceControl; @synthesize activeFeedLocations; +@synthesize sitesButton; @synthesize dictFolders; @synthesize dictFeeds; @@ -111,6 +113,7 @@ [logoutButton release]; [intelligenceControl release]; [activeFeedLocations release]; + [sitesButton release]; [dictFolders release]; [dictFeeds release]; @@ -123,7 +126,7 @@ - (void)fetchFeedList { NSURL *urlFeedList = [NSURL URLWithString:[NSString - stringWithFormat:@"http://www.newsblur.com/reader/feeds?flat=true&favicons=true"]]; + stringWithFormat:@"http://www.newsblur.com/reader/feeds?flat=true&include_favicons=true"]]; responseData = [[NSMutableData data] retain]; NSURLRequest *request = [[NSURLRequest alloc] initWithURL: urlFeedList]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; @@ -206,6 +209,10 @@ [ld release]; } +- (IBAction)switchSitesUnread { + +} + #pragma mark - #pragma mark Table View - Feed List @@ -247,11 +254,16 @@ NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId]; NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr]; cell.feedTitle.text = [feed objectForKey:@"feed_title"]; - NSURL *url = [NSURL URLWithString:[feed objectForKey:@"favicon"]]; - if (url) { - NSData *imageData = [NSData dataWithContentsOfURL:url]; + + NSString *favicon = [feed objectForKey:@"favicon"]; + NSLog(@"Favicon for %@: %d", [feed objectForKey:@"feed_title"], [favicon length]); + if ([favicon length] > 0) { + NSData *imageData = [NSData dataWithBase64EncodedString:favicon]; cell.feedFavicon.image = [UIImage imageWithData:imageData]; + } else { + cell.feedFavicon.image = [UIImage imageNamed:@"world.png"]; } + [cell.feedUnreadView loadHTMLString:[self showUnreadCount:feed] baseURL:nil]; return cell; @@ -371,7 +383,7 @@ } else if (previousLevel == 1) { if (newLevel == 0 && maxScore == 0) { [insertIndexPaths addObject:indexPath]; - } else if (newLevel == -1 && maxScore > -1) { + } else if (newLevel == -1 && (maxScore == -1 || maxScore == 0)) { [insertIndexPaths addObject:indexPath]; } } diff --git a/media/iphone/NewsBlur.xcodeproj/project.pbxproj b/media/iphone/NewsBlur.xcodeproj/project.pbxproj index cea5e2c1a..31b0f384e 100755 --- a/media/iphone/NewsBlur.xcodeproj/project.pbxproj +++ b/media/iphone/NewsBlur.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ FF1C46C213E6003300E7609E /* logo_114.png in Resources */ = {isa = PBXBuildFile; fileRef = FF1C46C113E6003300E7609E /* logo_114.png */; }; FF38679B13D88EEC00F8AB3A /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = FF38679A13D88EEC00F8AB3A /* NSString+HTML.m */; }; FF38679E13D8914A00F8AB3A /* GTMNString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = FF38679D13D8914A00F8AB3A /* GTMNString+HTML.m */; }; + FF435EDB13E9AF4A0083043F /* Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = FF435EDA13E9AF4A0083043F /* Base64.m */; }; + FF435EDE13E9B04E0083043F /* world.png in Resources */ = {isa = PBXBuildFile; fileRef = FF435EDD13E9B04E0083043F /* world.png */; }; FFCE7AE813D49165009A98F6 /* FeedTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFCE7AE613D49165009A98F6 /* FeedTableCell.m */; }; FFCE7AE913D49165009A98F6 /* FeedTableCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = FFCE7AE713D49165009A98F6 /* FeedTableCell.xib */; }; /* End PBXBuildFile section */ @@ -137,6 +139,9 @@ FF38679C13D8914A00F8AB3A /* GTMNString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNString+HTML.h"; sourceTree = ""; }; FF38679D13D8914A00F8AB3A /* GTMNString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNString+HTML.m"; sourceTree = ""; }; FF38679F13D8916E00F8AB3A /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = ""; }; + FF435ED913E9AF4A0083043F /* Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64.h; sourceTree = ""; }; + FF435EDA13E9AF4A0083043F /* Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Base64.m; sourceTree = ""; }; + FF435EDD13E9B04E0083043F /* world.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = world.png; sourceTree = ""; }; FFCE7AE513D49165009A98F6 /* FeedTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FeedTableCell.h; path = ../NewsBlur.xcodeproj/FeedTableCell.h; sourceTree = ""; }; FFCE7AE613D49165009A98F6 /* FeedTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FeedTableCell.m; path = ../NewsBlur.xcodeproj/FeedTableCell.m; sourceTree = ""; }; FFCE7AE713D49165009A98F6 /* FeedTableCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedTableCell.xib; path = ../NewsBlur.xcodeproj/FeedTableCell.xib; sourceTree = ""; }; @@ -222,6 +227,8 @@ FF38679F13D8916E00F8AB3A /* GTMDefines.h */, FF38679D13D8914A00F8AB3A /* GTMNString+HTML.m */, FF38679A13D88EEC00F8AB3A /* NSString+HTML.m */, + FF435ED913E9AF4A0083043F /* Base64.h */, + FF435EDA13E9AF4A0083043F /* Base64.m */, ); name = "Other Sources"; sourceTree = ""; @@ -229,6 +236,7 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + FF435EDD13E9B04E0083043F /* world.png */, FF1C46C113E6003300E7609E /* logo_114.png */, FF1C46BF13E6002300E7609E /* logo_57.png */, FF1C46BD13E5E9BD00E7609E /* Default@2x.png */, @@ -384,6 +392,7 @@ FF1C46BE13E5E9BD00E7609E /* Default@2x.png in Resources */, FF1C46C013E6002300E7609E /* logo_57.png in Resources */, FF1C46C213E6003300E7609E /* logo_114.png in Resources */, + FF435EDE13E9B04E0083043F /* world.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -418,6 +427,7 @@ FFCE7AE813D49165009A98F6 /* FeedTableCell.m in Sources */, FF38679B13D88EEC00F8AB3A /* NSString+HTML.m in Sources */, FF38679E13D8914A00F8AB3A /* GTMNString+HTML.m in Sources */, + FF435EDB13E9AF4A0083043F /* Base64.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/media/iphone/NewsBlurViewController.xib b/media/iphone/NewsBlurViewController.xib index e27758c4e..663df81f7 100644 --- a/media/iphone/NewsBlurViewController.xib +++ b/media/iphone/NewsBlurViewController.xib @@ -107,7 +107,6 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA {{153, 8}, {161, 30}} - IBCocoaTouchFramework 2 3 @@ -259,6 +258,22 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA 66 + + + switchSitesUnread + + + + 67 + + + + sitesButton + + + + 68 + @@ -285,8 +300,8 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA YES - + @@ -378,7 +393,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA - 66 + 68 @@ -640,11 +655,13 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA YES doLogoutButton selectIntelligence + switchSitesUnread YES id id + id @@ -653,6 +670,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA YES doLogoutButton selectIntelligence + switchSitesUnread YES @@ -664,6 +682,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA selectIntelligence id + + switchSitesUnread + id + @@ -676,6 +698,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA feedViewToolbar intelligenceControl logoutButton + sitesButton YES @@ -685,6 +708,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA UIToolbar UISegmentedControl UIBarButtonItem + UIBarButtonItem @@ -697,6 +721,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA feedViewToolbar intelligenceControl logoutButton + sitesButton YES @@ -724,6 +749,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAACAgAAAAAACAAIAAgACAABAAEAAQABA logoutButton UIBarButtonItem + + sitesButton + UIBarButtonItem + diff --git a/media/iphone/world.png b/media/iphone/world.png new file mode 100755 index 0000000000000000000000000000000000000000..0cba3582019e44dd3f5dd0cd4306bb5dbec895e5 GIT binary patch literal 888 zcmV-;1Bd*HP)7)Y-ck&-2MyWHn(VWW+TDrf=)?5UWYD}K`4>1Az~naM2*M>Mwt}4VX>tJ zSt+z-XkUR+D}}bSrBG^N$Q*QI7&qK9%)Wbm$+Cb4pMxj4uj@IvZyB&d+4$*$La(qW z{2}}$tO z0ug#u7+23-V|8TsCbfoXaJccHHR;_)EmBqr9}xat{@sab!_3{M zW`@^Y3_lLjIXBPEPz;563dN9z#z`~E#T!ihY#^;8^~I64506U){pQ3ie7~6K*)XF2 z&5Jh~q}d;#X>uO9X^JANmGW676^RDQ@ z^epqrj1H%A+|X+Z&mMJm5Id|G(@jsN_25{ zqLI3Qj$DnK%$@)ly-S?X#yEP*!{KWqWOi66{H~rB$9X#K^-_UDcG{GYDi&ialr-~& zwU}~S848P?oNg~UeT$sZJ>uAP7pL?pGIhs{&CW`OqV3c< z&r>^Ur9?kLu`xWAFam>yDP@KMz>!cAWt)>s$SI=K{%32*K=ro%Tr*d-Rja_H**EtR_i)#wnNE*q*b zJI0v-l+Icy_(E_9Z)`uN9ricAd*Z#iw};=U*|Q=BH~;fQunD;@&;JYDy}ah@7qXK8 O0000