mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Improve fetching the optimal icon of an app for the update view
This commit is contained in:
@@ -99,25 +99,7 @@
|
||||
}
|
||||
|
||||
- (BOOL)isPreiOS7Environment {
|
||||
static BOOL isPreiOS7Environment = YES;
|
||||
static dispatch_once_t checkOS;
|
||||
|
||||
dispatch_once(&checkOS, ^{
|
||||
// we only perform this runtime check if this is build against at least iOS7 base SDK
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
|
||||
// runtime check according to
|
||||
// https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TransitionGuide/SupportingEarlieriOS.html
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
|
||||
isPreiOS7Environment = YES;
|
||||
} else {
|
||||
isPreiOS7Environment = NO;
|
||||
}
|
||||
#else
|
||||
isPreiOS7Environment = YES;
|
||||
#endif
|
||||
});
|
||||
|
||||
return isPreiOS7Environment;
|
||||
return bit_isPreiOS7Environment();
|
||||
}
|
||||
|
||||
- (NSString *)getDevicePlatform {
|
||||
|
||||
@@ -46,6 +46,10 @@ NSString *bit_appName(NSString *placeHolderString);
|
||||
NSString *bit_UUIDPreiOS6(void);
|
||||
NSString *bit_UUID(void);
|
||||
NSString *bit_appAnonID(void);
|
||||
BOOL bit_isPreiOS7Environment(void);
|
||||
|
||||
NSString *bit_validAppIconStringFromIcons(NSArray *icons);
|
||||
NSString *bit_validAppIconFilename(NSBundle *bundle);
|
||||
|
||||
/* UIImage helpers */
|
||||
UIImage *bit_roundedCornerImage(UIImage *inputImage, NSInteger cornerSize, NSInteger borderSize);
|
||||
|
||||
@@ -216,6 +216,114 @@ NSString *bit_appAnonID(void) {
|
||||
return appAnonID;
|
||||
}
|
||||
|
||||
BOOL bit_isPreiOS7Environment(void) {
|
||||
static BOOL isPreiOS7Environment = YES;
|
||||
static dispatch_once_t checkOS;
|
||||
|
||||
dispatch_once(&checkOS, ^{
|
||||
// we only perform this runtime check if this is build against at least iOS7 base SDK
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
|
||||
// runtime check according to
|
||||
// https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TransitionGuide/SupportingEarlieriOS.html
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
|
||||
isPreiOS7Environment = YES;
|
||||
} else {
|
||||
isPreiOS7Environment = NO;
|
||||
}
|
||||
#else
|
||||
isPreiOS7Environment = YES;
|
||||
#endif
|
||||
});
|
||||
|
||||
return isPreiOS7Environment;
|
||||
}
|
||||
|
||||
/**
|
||||
Find a valid app icon filename that points to a proper app icon image
|
||||
|
||||
@param icons NSArray with app icon filenames
|
||||
|
||||
@return NSString with the valid app icon or nil if none found
|
||||
*/
|
||||
NSString *bit_validAppIconStringFromIcons(NSArray *icons) {
|
||||
if (!icons) return nil;
|
||||
if (![icons isKindOfClass:[NSArray class]]) return nil;
|
||||
|
||||
BOOL useHighResIcon = NO;
|
||||
BOOL useiPadIcon = NO;
|
||||
if ([UIScreen mainScreen].scale == 2.0f) useHighResIcon = YES;
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) useiPadIcon = YES;
|
||||
|
||||
NSString *currentBestMatch = nil;
|
||||
float currentBestMatchHeight = 0;
|
||||
float bestMatchHeight = 0;
|
||||
|
||||
if (bit_isPreiOS7Environment()) {
|
||||
bestMatchHeight = useiPadIcon ? (useHighResIcon ? 144 : 72) : (useHighResIcon ? 114 : 57);
|
||||
} else {
|
||||
bestMatchHeight = useiPadIcon ? (useHighResIcon ? 152 : 76) : 120;
|
||||
}
|
||||
|
||||
for(NSString *icon in icons) {
|
||||
// Don't use imageNamed, otherwise unit tests won't find the fixture icon
|
||||
// and using imageWithContentsOfFile doesn't load @2x files with absolut paths (required in tests)
|
||||
NSData *imgData = [[NSData alloc] initWithContentsOfFile:icon];
|
||||
UIImage *iconImage = [[UIImage alloc] initWithData:imgData];
|
||||
|
||||
if (iconImage) {
|
||||
if (iconImage.size.height == bestMatchHeight) {
|
||||
return icon;
|
||||
} else if (iconImage.size.height < bestMatchHeight &&
|
||||
iconImage.size.height > currentBestMatchHeight) {
|
||||
currentBestMatchHeight = iconImage.size.height;
|
||||
currentBestMatch = icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currentBestMatch;
|
||||
}
|
||||
|
||||
NSString *bit_validAppIconFilename(NSBundle *bundle) {
|
||||
NSString *iconFilename = nil;
|
||||
NSArray *icons = nil;
|
||||
|
||||
icons = [bundle objectForInfoDictionaryKey:@"CFBundleIconFiles"];
|
||||
iconFilename = bit_validAppIconStringFromIcons(icons);
|
||||
|
||||
if (!iconFilename) {
|
||||
icons = [bundle objectForInfoDictionaryKey:@"CFBundleIcons"];
|
||||
if (icons && [icons isKindOfClass:[NSDictionary class]]) {
|
||||
icons = [icons valueForKeyPath:@"CFBundlePrimaryIcon.CFBundleIconFiles"];
|
||||
}
|
||||
iconFilename = bit_validAppIconStringFromIcons(icons);
|
||||
}
|
||||
|
||||
// we test iPad structure anyway and use it if we find a result and don't have another one yet
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||
icons = [bundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"];
|
||||
if (icons && [icons isKindOfClass:[NSDictionary class]]) {
|
||||
icons = [icons valueForKeyPath:@"CFBundlePrimaryIcon.CFBundleIconFiles"];
|
||||
}
|
||||
NSString *iPadIconFilename = bit_validAppIconStringFromIcons(icons);
|
||||
if (iPadIconFilename && !iconFilename) {
|
||||
iconFilename = iPadIconFilename;
|
||||
}
|
||||
}
|
||||
|
||||
if (!iconFilename) {
|
||||
NSString *tempFilename = [bundle objectForInfoDictionaryKey:@"CFBundleIconFile"];
|
||||
if (tempFilename) {
|
||||
iconFilename = bit_validAppIconStringFromIcons(@[tempFilename]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!iconFilename) {
|
||||
iconFilename = bit_validAppIconStringFromIcons(@[@"Icon.png"]);
|
||||
}
|
||||
|
||||
return iconFilename;
|
||||
}
|
||||
|
||||
#pragma mark UIImage private helpers
|
||||
|
||||
|
||||
@@ -294,57 +294,19 @@
|
||||
}
|
||||
[self updateAppStoreHeader];
|
||||
|
||||
NSString *iconString = nil;
|
||||
NSArray *icons = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFiles"];
|
||||
if (!icons) {
|
||||
icons = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIcons"];
|
||||
if ((icons) && ([icons isKindOfClass:[NSDictionary class]])) {
|
||||
icons = [icons valueForKeyPath:@"CFBundlePrimaryIcon.CFBundleIconFiles"];
|
||||
NSString *iconFilename = bit_validAppIconFilename([NSBundle mainBundle]);
|
||||
if (iconFilename) {
|
||||
BOOL addGloss = YES;
|
||||
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
|
||||
if (prerendered) {
|
||||
addGloss = ![prerendered boolValue];
|
||||
}
|
||||
|
||||
if (!icons) {
|
||||
iconString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFile"];
|
||||
if (!iconString) {
|
||||
iconString = @"Icon.png";
|
||||
}
|
||||
if (addGloss && [self.updateManager isPreiOS7Environment]) {
|
||||
_appStoreHeader.iconImage = [self addGlossToImage:[UIImage imageNamed:iconFilename]];
|
||||
} else {
|
||||
_appStoreHeader.iconImage = [UIImage imageNamed:iconFilename];
|
||||
}
|
||||
}
|
||||
|
||||
if (icons) {
|
||||
BOOL useHighResIcon = NO;
|
||||
BOOL useiPadIcon = NO;
|
||||
if ([UIScreen mainScreen].scale == 2.0f) useHighResIcon = YES;
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) useiPadIcon = YES;
|
||||
|
||||
for(NSString *icon in icons) {
|
||||
iconString = icon;
|
||||
UIImage *iconImage = [UIImage imageNamed:icon];
|
||||
|
||||
if (
|
||||
(iconImage.size.height == 57 && !useHighResIcon && !useiPadIcon) ||
|
||||
(iconImage.size.height == 114 && useHighResIcon && !useiPadIcon) ||
|
||||
(iconImage.size.height == 120 && useHighResIcon && !useiPadIcon) ||
|
||||
(iconImage.size.height == 72 && !useHighResIcon && useiPadIcon) ||
|
||||
(iconImage.size.height == 76 && !useHighResIcon && useiPadIcon) ||
|
||||
(iconImage.size.height == 144 && !useHighResIcon && useiPadIcon) ||
|
||||
(iconImage.size.height == 152 && useHighResIcon && useiPadIcon)
|
||||
) {
|
||||
// found!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL addGloss = YES;
|
||||
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
|
||||
if (prerendered) {
|
||||
addGloss = ![prerendered boolValue];
|
||||
}
|
||||
|
||||
if (addGloss && [self.updateManager isPreiOS7Environment]) {
|
||||
_appStoreHeader.iconImage = [self addGlossToImage:[UIImage imageNamed:iconString]];
|
||||
} else {
|
||||
_appStoreHeader.iconImage = [UIImage imageNamed:iconString];
|
||||
}
|
||||
|
||||
self.tableView.tableHeaderView = _appStoreHeader;
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
1E1127C916580C87007067A2 /* buttonRoundedRegular@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127C116580C87007067A2 /* buttonRoundedRegular@2x.png */; };
|
||||
1E1127CA16580C87007067A2 /* buttonRoundedRegularHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127C216580C87007067A2 /* buttonRoundedRegularHighlighted.png */; };
|
||||
1E1127CB16580C87007067A2 /* buttonRoundedRegularHighlighted@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127C316580C87007067A2 /* buttonRoundedRegularHighlighted@2x.png */; };
|
||||
1E494AEC19491943001EFF74 /* AppIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E494AEA19491943001EFF74 /* AppIcon.png */; };
|
||||
1E494AED19491943001EFF74 /* AppIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E494AEB19491943001EFF74 /* AppIcon@2x.png */; };
|
||||
1E49A43C1612223B00463151 /* BITFeedbackComposeViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E49A42D1612223B00463151 /* BITFeedbackComposeViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
1E49A43F1612223B00463151 /* BITFeedbackComposeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E49A42E1612223B00463151 /* BITFeedbackComposeViewController.m */; };
|
||||
1E49A4421612223B00463151 /* BITFeedbackListViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E49A42F1612223B00463151 /* BITFeedbackListViewCell.h */; };
|
||||
@@ -226,6 +228,8 @@
|
||||
1E1127C316580C87007067A2 /* buttonRoundedRegularHighlighted@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "buttonRoundedRegularHighlighted@2x.png"; sourceTree = "<group>"; };
|
||||
1E20A57F181E9D4600D5B770 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/HockeySDK.strings; sourceTree = "<group>"; };
|
||||
1E36D8B816667611000B134C /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/HockeySDK.strings; sourceTree = "<group>"; };
|
||||
1E494AEA19491943001EFF74 /* AppIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AppIcon.png; sourceTree = "<group>"; };
|
||||
1E494AEB19491943001EFF74 /* AppIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIcon@2x.png"; sourceTree = "<group>"; };
|
||||
1E49A42D1612223B00463151 /* BITFeedbackComposeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITFeedbackComposeViewController.h; sourceTree = "<group>"; };
|
||||
1E49A42E1612223B00463151 /* BITFeedbackComposeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITFeedbackComposeViewController.m; sourceTree = "<group>"; };
|
||||
1E49A42F1612223B00463151 /* BITFeedbackListViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITFeedbackListViewCell.h; sourceTree = "<group>"; };
|
||||
@@ -601,6 +605,8 @@
|
||||
1EA1170216F53B49001C015C /* Fixtures */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1E494AEA19491943001EFF74 /* AppIcon.png */,
|
||||
1E494AEB19491943001EFF74 /* AppIcon@2x.png */,
|
||||
1EA1170316F53B49001C015C /* StoreBundleIdentifierUnknown.json */,
|
||||
1EA1170816F53E3A001C015C /* StoreBundleIdentifierKnown.json */,
|
||||
1E70A22F17F2F982001BB32D /* live_report_empty.plcrash */,
|
||||
@@ -949,12 +955,14 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1E494AEC19491943001EFF74 /* AppIcon.png in Resources */,
|
||||
1EA1170C16F54A64001C015C /* HockeySDKResources.bundle in Resources */,
|
||||
1E5A459B16F0DFC200B55C04 /* InfoPlist.strings in Resources */,
|
||||
1E70A23417F2F982001BB32D /* live_report_signal.plcrash in Resources */,
|
||||
1EA1170416F53B49001C015C /* StoreBundleIdentifierUnknown.json in Resources */,
|
||||
1E70A23317F2F982001BB32D /* live_report_exception.plcrash in Resources */,
|
||||
1E70A23217F2F982001BB32D /* live_report_empty.plcrash in Resources */,
|
||||
1E494AED19491943001EFF74 /* AppIcon@2x.png in Resources */,
|
||||
1EA1170916F53E3A001C015C /* StoreBundleIdentifierKnown.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -90,5 +90,63 @@
|
||||
assertThatInteger([resultString length], equalToInteger(36));
|
||||
}
|
||||
|
||||
- (void)testValidAppIconFilename {
|
||||
NSString *resultString = nil;
|
||||
NSBundle *mockBundle = mock([NSBundle class]);
|
||||
NSString *validIconPath = [[NSBundle bundleForClass:self.class] pathForResource:@"AppIcon" ofType:@"png"];
|
||||
NSString *validIconPath2x = [[NSBundle bundleForClass:self.class] pathForResource:@"AppIcon@2x" ofType:@"png"];
|
||||
|
||||
// No valid icons defined at all
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:@"invalidFilename.png"];
|
||||
|
||||
resultString = bit_validAppIconFilename(mockBundle);
|
||||
assertThat(resultString, nilValue());
|
||||
|
||||
// CFBundleIconFiles contains valid filenames
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:@[validIconPath, validIconPath2x]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:nil];
|
||||
|
||||
resultString = bit_validAppIconFilename(mockBundle);
|
||||
assertThat(resultString, notNilValue());
|
||||
|
||||
// CFBundleIcons contains valid dictionary filenames
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:@[@"invalidFilename.png"]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:@{@"CFBundlePrimaryIcon":@{@"CFBundleIconFiles":@[validIconPath, validIconPath2x]}}];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:nil];
|
||||
|
||||
// CFBundleIcons contains valid ipad dictionary and valid default dictionary filenames
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:@[@"invalidFilename.png"]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:@{@"CFBundlePrimaryIcon":@{@"CFBundleIconFiles":@[validIconPath, validIconPath2x]}}];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:@{@"CFBundlePrimaryIcon":@{@"CFBundleIconFiles":@[validIconPath, validIconPath2x]}}];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:nil];
|
||||
|
||||
resultString = bit_validAppIconFilename(mockBundle);
|
||||
assertThat(resultString, notNilValue());
|
||||
|
||||
// CFBundleIcons contains valid filenames
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:@[@"invalidFilename.png"]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:@[validIconPath, validIconPath2x]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:nil];
|
||||
|
||||
resultString = bit_validAppIconFilename(mockBundle);
|
||||
assertThat(resultString, notNilValue());
|
||||
|
||||
// CFBundleIcon contains valid filename
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFiles"]) willReturn:@[@"invalidFilename.png"]];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIcons~ipad"]) willReturn:nil];
|
||||
[given([mockBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]) willReturn:validIconPath];
|
||||
|
||||
resultString = bit_validAppIconFilename(mockBundle);
|
||||
assertThat(resultString, notNilValue());
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
BIN
Support/HockeySDKTests/Fixtures/AppIcon.png
Normal file
BIN
Support/HockeySDKTests/Fixtures/AppIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Support/HockeySDKTests/Fixtures/AppIcon@2x.png
Normal file
BIN
Support/HockeySDKTests/Fixtures/AppIcon@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Reference in New Issue
Block a user