LookinAppInfo.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. //
  3. // LookinAppInfo.m
  4. // qmuidemo
  5. //
  6. // Created by Li Kai on 2018/11/3.
  7. // Copyright © 2018 QMUI Team. All rights reserved.
  8. //
  9. #import "LookinAppInfo.h"
  10. static NSString * const CodingKey_AppIcon = @"1";
  11. static NSString * const CodingKey_Screenshot = @"2";
  12. static NSString * const CodingKey_DeviceDescription = @"3";
  13. static NSString * const CodingKey_OsDescription = @"4";
  14. static NSString * const CodingKey_AppName = @"5";
  15. static NSString * const CodingKey_ScreenWidth = @"6";
  16. static NSString * const CodingKey_ScreenHeight = @"7";
  17. static NSString * const CodingKey_DeviceType = @"8";
  18. @implementation LookinAppInfo
  19. - (id)copyWithZone:(NSZone *)zone {
  20. LookinAppInfo *newAppInfo = [[LookinAppInfo allocWithZone:zone] init];
  21. newAppInfo.appIcon = self.appIcon;
  22. newAppInfo.appName = self.appName;
  23. newAppInfo.deviceDescription = self.deviceDescription;
  24. newAppInfo.osDescription = self.osDescription;
  25. newAppInfo.osMainVersion = self.osMainVersion;
  26. newAppInfo.deviceType = self.deviceType;
  27. newAppInfo.screenWidth = self.screenWidth;
  28. newAppInfo.screenHeight = self.screenHeight;
  29. newAppInfo.screenScale = self.screenScale;
  30. newAppInfo.appInfoIdentifier = self.appInfoIdentifier;
  31. return newAppInfo;
  32. }
  33. - (instancetype)initWithCoder:(NSCoder *)aDecoder {
  34. if (self = [super init]) {
  35. self.serverVersion = [aDecoder decodeIntForKey:@"serverVersion"];
  36. self.serverReadableVersion = [aDecoder decodeObjectForKey:@"serverReadableVersion"];
  37. self.swiftEnabledInLookinServer = [aDecoder decodeIntForKey:@"swiftEnabledInLookinServer"];
  38. NSData *screenshotData = [aDecoder decodeObjectForKey:CodingKey_Screenshot];
  39. self.screenshot = [[LookinImage alloc] initWithData:screenshotData];
  40. NSData *appIconData = [aDecoder decodeObjectForKey:CodingKey_AppIcon];
  41. self.appIcon = [[LookinImage alloc] initWithData:appIconData];
  42. self.appName = [aDecoder decodeObjectForKey:CodingKey_AppName];
  43. self.appBundleIdentifier = [aDecoder decodeObjectForKey:@"appBundleIdentifier"];
  44. self.deviceDescription = [aDecoder decodeObjectForKey:CodingKey_DeviceDescription];
  45. self.osDescription = [aDecoder decodeObjectForKey:CodingKey_OsDescription];
  46. self.osMainVersion = [aDecoder decodeIntegerForKey:@"osMainVersion"];
  47. self.deviceType = [aDecoder decodeIntegerForKey:CodingKey_DeviceType];
  48. self.screenWidth = [aDecoder decodeDoubleForKey:CodingKey_ScreenWidth];
  49. self.screenHeight = [aDecoder decodeDoubleForKey:CodingKey_ScreenHeight];
  50. self.screenScale = [aDecoder decodeDoubleForKey:@"screenScale"];
  51. self.appInfoIdentifier = [aDecoder decodeIntegerForKey:@"appInfoIdentifier"];
  52. self.shouldUseCache = [aDecoder decodeBoolForKey:@"shouldUseCache"];
  53. }
  54. return self;
  55. }
  56. - (void)encodeWithCoder:(NSCoder *)aCoder {
  57. [aCoder encodeInt:self.serverVersion forKey:@"serverVersion"];
  58. [aCoder encodeObject:self.serverReadableVersion forKey:@"serverReadableVersion"];
  59. [aCoder encodeInt:self.swiftEnabledInLookinServer forKey:@"swiftEnabledInLookinServer"];
  60. #if TARGET_OS_IPHONE
  61. NSData *screenshotData = UIImagePNGRepresentation(self.screenshot);
  62. [aCoder encodeObject:screenshotData forKey:CodingKey_Screenshot];
  63. NSData *appIconData = UIImagePNGRepresentation(self.appIcon);
  64. [aCoder encodeObject:appIconData forKey:CodingKey_AppIcon];
  65. #elif TARGET_OS_MAC
  66. NSData *screenshotData = [self.screenshot TIFFRepresentation];
  67. [aCoder encodeObject:screenshotData forKey:CodingKey_Screenshot];
  68. NSData *appIconData = [self.appIcon TIFFRepresentation];
  69. [aCoder encodeObject:appIconData forKey:CodingKey_AppIcon];
  70. #endif
  71. [aCoder encodeObject:self.appName forKey:CodingKey_AppName];
  72. [aCoder encodeObject:self.appBundleIdentifier forKey:@"appBundleIdentifier"];
  73. [aCoder encodeObject:self.deviceDescription forKey:CodingKey_DeviceDescription];
  74. [aCoder encodeObject:self.osDescription forKey:CodingKey_OsDescription];
  75. [aCoder encodeInteger:self.osMainVersion forKey:@"osMainVersion"];
  76. [aCoder encodeInteger:self.deviceType forKey:CodingKey_DeviceType];
  77. [aCoder encodeDouble:self.screenWidth forKey:CodingKey_ScreenWidth];
  78. [aCoder encodeDouble:self.screenHeight forKey:CodingKey_ScreenHeight];
  79. [aCoder encodeDouble:self.screenScale forKey:@"screenScale"];
  80. [aCoder encodeInteger:self.appInfoIdentifier forKey:@"appInfoIdentifier"];
  81. [aCoder encodeBool:self.shouldUseCache forKey:@"shouldUseCache"];
  82. }
  83. + (BOOL)supportsSecureCoding {
  84. return YES;
  85. }
  86. - (BOOL)isEqual:(id)object {
  87. if (self == object) {
  88. return YES;
  89. }
  90. if (![object isKindOfClass:[LookinAppInfo class]]) {
  91. return NO;
  92. }
  93. if ([self isEqualToAppInfo:object]) {
  94. return YES;
  95. }
  96. return NO;
  97. }
  98. - (NSUInteger)hash {
  99. return self.appName.hash ^ self.deviceDescription.hash ^ self.osDescription.hash ^ self.deviceType;
  100. }
  101. - (BOOL)isEqualToAppInfo:(LookinAppInfo *)info {
  102. if (!info) {
  103. return NO;
  104. }
  105. if ([self.appName isEqualToString:info.appName] && [self.deviceDescription isEqualToString:info.deviceDescription] && [self.osDescription isEqualToString:info.osDescription] && self.deviceType == info.deviceType) {
  106. return YES;
  107. }
  108. return NO;
  109. }
  110. #if TARGET_OS_IPHONE
  111. + (LookinAppInfo *)currentInfoWithScreenshot:(BOOL)hasScreenshot icon:(BOOL)hasIcon localIdentifiers:(NSArray<NSNumber *> *)localIdentifiers {
  112. NSInteger selfIdentifier = [self getAppInfoIdentifier];
  113. if ([localIdentifiers containsObject:@(selfIdentifier)]) {
  114. LookinAppInfo *info = [LookinAppInfo new];
  115. info.appInfoIdentifier = selfIdentifier;
  116. info.shouldUseCache = YES;
  117. return info;
  118. }
  119. LookinAppInfo *info = [[LookinAppInfo alloc] init];
  120. info.serverReadableVersion = LOOKIN_SERVER_READABLE_VERSION;
  121. #ifdef LOOKIN_SERVER_SWIFT_ENABLED
  122. info.swiftEnabledInLookinServer = 1;
  123. #else
  124. info.swiftEnabledInLookinServer = -1;
  125. #endif
  126. info.appInfoIdentifier = selfIdentifier;
  127. info.appName = [self appName];
  128. info.deviceDescription = [UIDevice currentDevice].name;
  129. info.appBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
  130. if ([self isSimulator]) {
  131. info.deviceType = LookinAppInfoDeviceSimulator;
  132. } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  133. info.deviceType = LookinAppInfoDeviceIPad;
  134. } else {
  135. info.deviceType = LookinAppInfoDeviceOthers;
  136. }
  137. info.osDescription = [UIDevice currentDevice].systemVersion;
  138. NSString *mainVersionStr = [[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."].firstObject;
  139. info.osMainVersion = [mainVersionStr integerValue];
  140. CGSize screenSize = [UIScreen mainScreen].bounds.size;
  141. info.screenWidth = screenSize.width;
  142. info.screenHeight = screenSize.height;
  143. info.screenScale = [UIScreen mainScreen].scale;
  144. if (hasScreenshot) {
  145. info.screenshot = [self screenshotImage];
  146. }
  147. if (hasIcon) {
  148. info.appIcon = [self appIcon];
  149. }
  150. return info;
  151. }
  152. + (NSString *)appName {
  153. NSDictionary *info = [[NSBundle mainBundle] infoDictionary];
  154. NSString *displayName = [info objectForKey:@"CFBundleDisplayName"];
  155. NSString *name = [info objectForKey:@"CFBundleName"];
  156. return displayName.length ? displayName : name;
  157. }
  158. + (UIImage *)appIcon {
  159. #if TARGET_OS_TV
  160. return nil;
  161. #else
  162. NSString *imageName = [[[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIcons"] objectForKey:@"CFBundlePrimaryIcon"] objectForKey:@"CFBundleIconFiles"] lastObject];
  163. if (!imageName.length) {
  164. // 正常情况下拿到的 name 可能比如 “AppIcon60x60”。但某些情况可能为 nil,此时直接 return 否则 [UIImage imageNamed:nil] 可能导致 console 报 "CUICatalog: Invalid asset name supplied: '(null)'" 的错误信息
  165. return nil;
  166. }
  167. return [UIImage imageNamed:imageName];
  168. #endif
  169. }
  170. + (UIImage *)screenshotImage {
  171. UIWindow *window = [UIApplication sharedApplication].keyWindow;
  172. if (!window) {
  173. return nil;
  174. }
  175. CGSize size = window.bounds.size;
  176. if (size.width <= 0 || size.height <= 0) {
  177. // *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIGraphicsBeginImageContext() failed to allocate CGBitampContext: size={0, 0}, scale=3.000000, bitmapInfo=0x2002. Use UIGraphicsImageRenderer to avoid this assert.'
  178. // https://github.com/hughkli/Lookin/issues/21
  179. return nil;
  180. }
  181. UIGraphicsBeginImageContextWithOptions(size, YES, 0.4);
  182. [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
  183. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  184. UIGraphicsEndImageContext();
  185. return image;
  186. }
  187. + (BOOL)isSimulator {
  188. if (TARGET_OS_SIMULATOR) {
  189. return YES;
  190. }
  191. return NO;
  192. }
  193. #endif
  194. + (NSInteger)getAppInfoIdentifier {
  195. static dispatch_once_t onceToken;
  196. static NSInteger identifier = 0;
  197. dispatch_once(&onceToken,^{
  198. identifier = [[NSDate date] timeIntervalSince1970];
  199. });
  200. return identifier;
  201. }
  202. @end
  203. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */