Device battery indicators on your Lock Screen
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
13KB

  1. KAIBatteryPlatter *instance;
  2. NSTimer *queueTimer = nil;
  3. NSMutableArray *deviceNames = [[NSMutableArray alloc] init];
  4. NSMutableArray *cellsForDeviceNames = [[NSMutableArray alloc] init];
  5. @implementation KAIBatteryPlatter
  6. - (instancetype)initWithFrame:(CGRect)arg1 {
  7. self = [super initWithFrame:arg1];
  8. instance = self;
  9. if (self) {
  10. self.stackHolder = [[UIView alloc] initWithFrame:arg1];
  11. self.stack = [[KAIStackView alloc] init];
  12. self.stack.axis = kaiAlign == 0 ? 1 : 0;
  13. self.stack.distribution = UIStackViewDistributionFillEqually;
  14. self.stack.spacing = kaiAlign == 0 ? 0 : spacingHorizontal;
  15. self.stack.alignment = 0;
  16. self.oldCountOfDevices = -100;
  17. self.queued = NO;
  18. [self setShowsHorizontalScrollIndicator:NO];
  19. [self setShowsVerticalScrollIndicator:NO];
  20. [self setMinimumZoomScale:1];
  21. [self setMaximumZoomScale:1];
  22. [self addSubview:self.stackHolder];
  23. [self.stackHolder addSubview:self.stack];
  24. [self setContentSize:self.stack.frame.size];
  25. [self resetOffset];
  26. //Add noti observer
  27. [[NSNotificationCenter defaultCenter] addObserver:self
  28. selector:@selector(resetOffset)
  29. name:@"KaiResetOffset"
  30. object:nil];
  31. self.stackHolder.translatesAutoresizingMaskIntoConstraints = NO;
  32. [self.stackHolder.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
  33. [self.stackHolder.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
  34. [self.stackHolder.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;
  35. if (kaiAlign == 0) {
  36. if (bannerAlign == 2) { //center
  37. self.subviewAligner = [self.stack.centerXAnchor constraintEqualToAnchor:self.stackHolder.centerXAnchor constant:horizontalOffset];
  38. } else if (bannerAlign == 1) { //left
  39. self.subviewAligner = [self.stack.leftAnchor constraintEqualToAnchor:self.stackHolder.leftAnchor constant:horizontalOffset];
  40. } else if (bannerAlign == 3) { //right
  41. self.subviewAligner = [self.stack.rightAnchor constraintEqualToAnchor:self.stackHolder.rightAnchor constant:horizontalOffset];
  42. }
  43. self.subviewAligner.active = YES;
  44. }
  45. [self updateBattery];
  46. }
  47. return self;
  48. }
  49. - (void)resetOffset {
  50. if (kaiAlign != 0 && reAlignSelf) {
  51. [UIView animateWithDuration:0.2
  52. animations:^{
  53. if (bannerAlign == 1) { //left
  54. [self setContentOffset:CGPointMake(0 + horizontalOffset, self.contentOffset.y)];
  55. self.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
  56. } else if (bannerAlign == 2) { //center
  57. [self setContentOffset:CGPointMake(((-1 * self.stackHolder.frame.size.width) / 2) + (self.stack.frame.size.width / 2) + horizontalOffset, self.contentOffset.y)];
  58. CGFloat top = 0, left = 0;
  59. if (self.contentSize.width < self.bounds.size.width) {
  60. left = (self.bounds.size.width - self.contentSize.width) * 0.5f;
  61. }
  62. if (self.contentSize.height < self.bounds.size.height) {
  63. top = (self.bounds.size.height - self.contentSize.height) * 0.5f;
  64. }
  65. self.contentInset = UIEdgeInsetsMake(top, left, top, left);
  66. } else if (bannerAlign == 3) { //right
  67. [self setContentOffset:CGPointMake((-1 * self.stackHolder.frame.size.width) + self.stack.frame.size.width + horizontalOffset, self.contentOffset.y)];
  68. CGFloat top = 0, left = 0;
  69. if (self.contentSize.width < self.bounds.size.width) {
  70. left = (self.bounds.size.width - self.contentSize.width);
  71. }
  72. if (self.contentSize.height < self.bounds.size.height) {
  73. top = (self.bounds.size.height - self.contentSize.height);
  74. }
  75. self.contentInset = UIEdgeInsetsMake(top, left, top, left);
  76. }
  77. }];
  78. }
  79. }
  80. - (void)setClipsToBounds:(BOOL)arg1 {
  81. [super setClipsToBounds:NO];
  82. }
  83. - (void)updateBattery {
  84. dispatch_async(dispatch_get_main_queue(), ^{
  85. BCBatteryDeviceController *bcb = [BCBatteryDeviceController sharedInstance];
  86. NSArray *devices = MSHookIvar<NSArray *>(bcb, "_sortedDevices");
  87. if (self.oldCountOfDevices == -100) {
  88. self.oldCountOfDevices = [devices count] + 1;
  89. }
  90. for (KAIBatteryCell *cell in self.stack.subviews) {
  91. [cell updateInfo];
  92. }
  93. if (!self.isUpdating && self.oldCountOfDevices != 0 && ([devices count] + 1 == self.oldCountOfDevices || [devices count] - 1 == self.oldCountOfDevices || [devices count] == self.oldCountOfDevices)) {
  94. //if(!self.isUpdating) {
  95. self.isUpdating = YES;
  96. NSMutableArray *cellsToAdd = [[NSMutableArray alloc] init];
  97. NSMutableArray *cellsToRemove = [[NSMutableArray alloc] init];
  98. for (BCBatteryDevice *device in devices) {
  99. KAIBatteryCell *cell = [device kaiCellForDevice];
  100. BOOL charging = MSHookIvar<long long>(device, "_charging");
  101. BOOL internal = MSHookIvar<BOOL>(device, "_internal");
  102. BOOL shouldAdd = NO;
  103. BOOL fake = MSHookIvar<BOOL>(device, "_fake");
  104. NSString *deviceName = MSHookIvar<NSString *>(device, "_name");
  105. if (!fake) {
  106. if (showAll) {
  107. shouldAdd = YES;
  108. } else if (showAllMinusInternal && !internal) {
  109. shouldAdd = YES;
  110. } else if (!showAll && charging) {
  111. shouldAdd = YES;
  112. }
  113. }
  114. if (!showPhone && internal) {
  115. shouldAdd = NO;
  116. }
  117. if (![self.stack.subviews containsObject:cell] && shouldAdd && [devices containsObject:device]) {
  118. if (![deviceNames containsObject:deviceName]) {
  119. [cellsToAdd addObject:cell];
  120. [deviceNames addObject:deviceName];
  121. [cellsForDeviceNames addObject:cell];
  122. } else {
  123. for (int i = 0; i < [deviceNames count]; i++) {
  124. if ([[deviceNames objectAtIndex:i] isEqualToString:deviceName]) {
  125. KAIBatteryCell *cell = [cellsForDeviceNames objectAtIndex:i];
  126. cell.device = device;
  127. if ([cellsToRemove containsObject:cell])
  128. [cellsToRemove removeObject:cell];
  129. [cell updateInfo];
  130. }
  131. }
  132. }
  133. } else if ([self.stack.subviews containsObject:cell] && !shouldAdd) {
  134. [cellsToRemove addObject:cell];
  135. // this is where i stupidly removed from deviceName names :kekw:
  136. }
  137. }
  138. for (KAIBatteryCell *cell in cellsToAdd) {
  139. if ([cellsToRemove containsObject:cell])
  140. [cellsToRemove removeObject:cell];
  141. if (![self.stack.subviews containsObject:cell] && [devices containsObject:cell.device]) {
  142. // add cell
  143. cell.alpha = 0;
  144. [self.stack addSubview:cell];
  145. [self.stack addArrangedSubview:cell];
  146. [UIView animateWithDuration:0.3
  147. animations:^{
  148. cell.alpha = 1;
  149. }];
  150. }
  151. }
  152. for (KAIBatteryCell *cell in cellsToRemove) {
  153. // remove cell
  154. [UIView animateWithDuration:0.3
  155. animations:^{
  156. cell.alpha = 0;
  157. }
  158. completion:^(BOOL finished) {
  159. [cell removeFromSuperview];
  160. [self.stack removeArrangedSubview:cell];
  161. cell.alpha = 1;
  162. }];
  163. NSString *deviceName = MSHookIvar<NSString *>(cell.device, "_name");
  164. [deviceNames removeObject:deviceName];
  165. [cellsForDeviceNames removeObject:cell];
  166. }
  167. for (KAIBatteryCell *cell in self.stack.subviews) {
  168. if (![devices containsObject:cell.device] || cell.device == nil) { //not existing, remove
  169. NSString *deviceName = cell.label.text;
  170. [UIView animateWithDuration:0.3
  171. animations:^{
  172. cell.alpha = 0;
  173. }
  174. completion:^(BOOL finished) {
  175. [cell removeFromSuperview];
  176. [self.stack removeArrangedSubview:cell];
  177. cell.alpha = 1;
  178. }];
  179. [deviceNames removeObject:deviceName];
  180. [cellsForDeviceNames removeObject:cell];
  181. }
  182. }
  183. queueTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(dispatchQueue) userInfo:nil repeats:NO];
  184. //self.isUpdating = NO;
  185. } else if (self.isUpdating) {
  186. self.queued = YES;
  187. }
  188. self.oldCountOfDevices = [devices count];
  189. [self calculateHeight];
  190. if ([self.superview.superview.superview respondsToSelector:@selector(fixComplicationsViewFrame)]) {
  191. [(NCNotificationListView *)(self.superview.superview.superview) fixComplicationsViewFrame];
  192. }
  193. [self setContentSize:self.stack.frame.size];
  194. [self performSelector:@selector(resetOffset) withObject:self afterDelay:0.2];
  195. });
  196. }
  197. - (void)setContentOffset:(CGPoint)arg1 {
  198. [self setContentSize:self.stack.frame.size]; //sometimes the view gets "stuck", this fixes it
  199. [super setContentOffset:CGPointMake(arg1.x, 0)];
  200. }
  201. - (void)calculateHeight {
  202. self.number = [self.stack.subviews count];
  203. if (self.number == 0) {
  204. UIStackView *s = (UIStackView *)(self.superview);
  205. s.frame = CGRectMake(s.frame.origin.x, s.frame.origin.y, s.frame.size.width, (s.frame.size.height - 1));
  206. [s removeArrangedSubview:self];
  207. [self removeFromSuperview];
  208. } else if (self.number != 0 && self.superview == nil && shouldBeAdded == YES) {
  209. [[[[objc_getClass("CSAdjunctListView") class] sharedListViewForKai] stackView] addArrangedSubview:self];
  210. //[self performSelector:@selector(calculateHeight) withObject:self afterDelay:0.1];
  211. }
  212. [UIView animateWithDuration:0.3
  213. animations:^{
  214. if (!self.heightConstraint) {
  215. int height = (self.number * (bannerHeight + spacing));
  216. if (kaiAlign != 0) {
  217. height = bannerHeight + spacing;
  218. }
  219. if ([self.superview.subviews count] > 1) {
  220. height = (height - spacing) + 1;
  221. }
  222. self.heightConstraint = [self.heightAnchor constraintEqualToConstant:height];
  223. self.stack.heightConstraint = [self.stack.heightAnchor constraintEqualToConstant:height];
  224. self.heightConstraint.active = YES;
  225. self.stack.heightConstraint.active = YES;
  226. [self setContentSize:self.stack.frame.size];
  227. [self resetOffset];
  228. } else {
  229. int height = (self.number * (bannerHeight + spacing));
  230. int extra = extraPaddingAfter ? spacing : 0;
  231. if (kaiAlign == 0) {
  232. //self.stack.widthConstraint.constant = bannerWidthFactor;
  233. } else {
  234. height = bannerHeight + spacing;
  235. }
  236. height = height + extra;
  237. if ([self.superview.subviews count] > 1) {
  238. height = (height - spacing + 1);
  239. }
  240. self.heightConstraint.constant = height;
  241. self.stack.heightConstraint.constant = height - extra; //minus extra because it will stretch cell spacing otherwise
  242. UIStackView *s = (UIStackView *)(self.superview);
  243. s.frame = CGRectMake(s.frame.origin.x, s.frame.origin.y, s.frame.size.width, (s.frame.size.height - 1));
  244. //literally does nothing but makes the stack view lay itself out (doesnt adjust frame because translatesAutoreszingMaskIntoConstraints = NO on stack views)
  245. }
  246. [self setContentSize:self.stack.frame.size];
  247. [self resetOffset];
  248. }];
  249. }
  250. - (void)removeFromSuperview {
  251. [self.superview setNeedsLayout];
  252. [super removeFromSuperview];
  253. }
  254. - (void)refreshForPrefs {
  255. self.stack.spacing = kaiAlign == 0 ? 0 : spacingHorizontal;
  256. [self setContentSize:self.stack.frame.size];
  257. for (UIView *view in self.stack.subviews) {
  258. @try {
  259. [view removeFromSuperview];
  260. } @catch (NSException *exception) {
  261. //Panik
  262. }
  263. }
  264. BCBatteryDeviceController *bcb = [BCBatteryDeviceController sharedInstance];
  265. NSArray *devices = MSHookIvar<NSArray *>(bcb, "_sortedDevices");
  266. for (BCBatteryDevice *device in devices) {
  267. [device resetKaiCellForNewPrefs];
  268. }
  269. if (kaiAlign == 0) {
  270. self.subviewAligner.active = NO;
  271. if (bannerAlign == 2) { //center
  272. self.subviewAligner = [self.stack.centerXAnchor constraintEqualToAnchor:self.stackHolder.centerXAnchor constant:horizontalOffset];
  273. } else if (bannerAlign == 1) { //left
  274. self.subviewAligner = [self.stack.leftAnchor constraintEqualToAnchor:self.stackHolder.leftAnchor constant:horizontalOffset];
  275. } else if (bannerAlign == 3) { //right
  276. self.subviewAligner = [self.stack.rightAnchor constraintEqualToAnchor:self.stackHolder.rightAnchor constant:horizontalOffset];
  277. }
  278. self.subviewAligner.active = YES;
  279. }
  280. [cellsForDeviceNames removeAllObjects];
  281. [deviceNames removeAllObjects];
  282. [self updateBattery];
  283. }
  284. - (void)dispatchQueue {
  285. self.isUpdating = NO;
  286. if (self.queued) {
  287. [self updateBattery];
  288. if ([self.superview.superview.superview respondsToSelector:@selector(fixComplicationsViewFrame)]) {
  289. [(NCNotificationListView *)(self.superview.superview.superview) fixComplicationsViewFrame];
  290. }
  291. self.queued = NO;
  292. }
  293. [queueTimer invalidate];
  294. queueTimer = nil;
  295. }
  296. + (KAIBatteryPlatter *)sharedInstance {
  297. return instance;
  298. }
  299. //This is for compatibility (did i spell that right?)
  300. //basically this fixes it crashing in landscape:
  301. - (void)setSizeToMimic:(CGSize)arg1 {
  302. }
  303. @end