LookinAppInfo.m 8.1 KB

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