LookinAppInfo.m 9.1 KB

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