Browse Source

fix shit code, make cursed code

tags/1.3.1
Burrit0z 3 years ago
commit
b3746a231f
27 changed files with 2153 additions and 0 deletions
  1. +2
    -0
      .gitattributes
  2. +8
    -0
      .gitignore
  3. +48
    -0
      Changelog
  4. +232
    -0
      KAIBatteryCell.mm
  5. +361
    -0
      KAIBatteryPlatter.mm
  6. +74
    -0
      KAIClassHeaders.h
  7. +35
    -0
      KAIStackView.mm
  8. +147
    -0
      Kai.h
  9. +1
    -0
      Kai.plist
  10. +229
    -0
      Kai.xm
  11. +9
    -0
      Layout/DEBIAN/control
  12. +11
    -0
      Layout/DEBIAN/postinst
  13. +40
    -0
      README.md
  14. BIN
      kai depiction screenshots/Screenshot1.png
  15. BIN
      kai depiction screenshots/Screenshot2.png
  16. BIN
      kai depiction screenshots/Screenshot3.png
  17. +38
    -0
      kaiprefs/KAIRootListController.h
  18. +280
    -0
      kaiprefs/KAIRootListController.m
  19. +24
    -0
      kaiprefs/Resources/Info.plist
  20. +593
    -0
      kaiprefs/Resources/Root.plist
  21. BIN
      kaiprefs/Resources/burritoz.jpg
  22. BIN
      kaiprefs/Resources/icon.png
  23. BIN
      kaiprefs/Resources/icon@2x.png
  24. BIN
      kaiprefs/Resources/icon@3x.png
  25. BIN
      kaiprefs/Resources/iconFullSize.png
  26. BIN
      kaiprefs/Resources/thomz.jpg
  27. +21
    -0
      kaiprefs/entry.plist

+ 2
- 0
.gitattributes View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

+ 8
- 0
.gitignore View File

@@ -0,0 +1,8 @@
.DS_store

.*/
*.deb
*.zip
.dragon/*
*.ninja
DragonMake

+ 48
- 0
Changelog View File

@@ -0,0 +1,48 @@
1.0.1:
- Added horizontal mode offset option for x axis
- Added horizontal mode alignment with nice little slide animations when re-aligning
- Added hide battery icon option
- In horizontal mode, the "vertical spacing" option now adds padding after kai
- Fixed an issue where cells would not be disposed of properly

1.1.0:
- Fixed cells overlapping in vertical mode
- Added option to add vertical padding after kai
- corrected issue where the "re-align after refreshing" option would be ignored and kai would always re-align
- Renoved apply button, prefs now automatically apply (thanks to gilshahar7 for the idea :)
- Added delete prefs button (again, thanks to gilshahar7 for the suggestions)
- Horizontal scrolling indicator is now hidden
- Added option to completely hide the phone battery in kai
- Helped resolve issues with AirPod device grouping being buggy, and cells would jump around (WIP)

1.1.1:
- Added a label to make it clear when the tweak was not yet activated, and license needed to be downloaded.
- Fixed issues with licenses not activating
- Added @vanwijkdave to credits in prefs for the header cell (sorry for not adding you before, this was overlooked)
- My credit cell now links my Twitter account
- Under the hood optimizations for keeping track of connected devices

1.1.2:
- Fixed cells still showing after device was disconnected (they will now remove as soon as they disappear from stock battery widget)
- Improve memory management within cells
- Fixed issue where the tweak would not disable in 1.1.1 and would always be on

1.1.3:
- Fixed compatibility for my upcoming tweak, Aperio
- Automatic alignment in horizontal mode should feel a lot snappier now

1.1.3-1:
- Compiled with latest DragonBuild
- Removed armv7 slice to reduce binary size

1.2.0:
- Large improvements to kai's auto alignment
- Fixes issues where kai would always re align when opening notification center

1.2.1:
- Add an option to hide kai when media player is showing on the lock screen (kai will be shown again once music player is removed from the lock screen)
- Fixed issues with vertical mode alignment

1.3.0:
- kai is now free!
- General improvements, new prefs banner

+ 232
- 0
KAIBatteryCell.mm View File

@@ -0,0 +1,232 @@
#import "KAIClassHeaders.h"

@implementation KAIBatteryCell

- (instancetype)initWithFrame:(CGRect)arg1 device:(BCBatteryDevice *)device {
self = [super initWithFrame:arg1];
if (self && device != nil) {
self.device = device;

NSString *deviceName = device.name;
double batteryPercentage = device.percentCharge;
BOOL charging = MSHookIvar<long long>(device, "_charging");
BOOL LPM = MSHookIvar<BOOL>(device, "_batterySaverModeActive");

UIView *blur;
UIView *blurPlatter = [[UIView alloc] init];
if (bannerStyle == 1) {
if (kCFCoreFoundationVersionNumber > 1600) {
blur = [[[objc_getClass("MTMaterialView") class] alloc] _initWithRecipe:1 configuration:1 initialWeighting:1 scaleAdjustment:nil];
} else if (kCFCoreFoundationVersionNumber < 1600) {
blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
}
} else if (bannerStyle == 2) {
blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
} else if (bannerStyle == 3) {
blur = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
}
blur.layer.masksToBounds = YES;
blur.layer.continuousCorners = YES;
blur.layer.cornerRadius = cornerRadius;
blurPlatter.alpha = bannerAlpha;

NSString *labelText = [NSString stringWithFormat:@"%@", deviceName];

self.label = [[UILabel alloc] init];
if (!hideDeviceLabel) {
[self.label setFont:[UIFont systemFontOfSize:16]];
} else if (hideDeviceLabel) {
[self.label setFont:[UIFont systemFontOfSize:0]];
}
if (textColor == 1) {
[self.label setTextColor:[UIColor whiteColor]];
} else {
[self.label setTextColor:[UIColor blackColor]];
}
self.label.lineBreakMode = NSLineBreakByWordWrapping;
self.label.numberOfLines = 1;
[self.label setText:labelText];

self.battery = [[_UIBatteryView alloc] init];
self.battery.chargePercent = (batteryPercentage * 0.01);
self.percentLabel = [[UILabel alloc] init];
self.battery.showsPercentage = NO;
if (hidePercent) {
[self.percentLabel setFont:[UIFont systemFontOfSize:0]];
} else {
[self.percentLabel setFont:[UIFont systemFontOfSize:14]];
}
if (textColor == 1) {
[self.percentLabel setTextColor:[UIColor whiteColor]];
} else {
[self.percentLabel setTextColor:[UIColor blackColor]];
}
self.percentLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.percentLabel setTextAlignment:NSTextAlignmentRight];
self.percentLabel.numberOfLines = 1;
[self.percentLabel setText:[NSString stringWithFormat:@"%ld%%", (long)((NSInteger)batteryPercentage)]];
if (charging)
self.battery.chargingState = 1;
self.battery.showsInlineChargingIndicator = YES;
if (LPM)
self.battery.saverModeActive = YES;
if (kCFCoreFoundationVersionNumber > 1600) {
[self.battery setBodyColorAlpha:1.0];
[self.battery setPinColorAlpha:1.0];
}

UIImage *glyph = [device glyph];
self.glyphView = [[UIImageView alloc] init];
self.glyphView.contentMode = UIViewContentModeScaleAspectFit;
[self.glyphView setImage:glyph];

[self addSubview:blurPlatter];
[blurPlatter addSubview:blur];
[self addSubview:self.percentLabel];
[self addSubview:self.label];
[self addSubview:self.battery];
[self addSubview:self.glyphView];

// Blur Platter
blurPlatter.translatesAutoresizingMaskIntoConstraints = NO;
if (bannerAlign == 2) { //center
[blurPlatter.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES;
} else if (bannerAlign == 1) { //left
[blurPlatter.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
} else if (bannerAlign == 3) { //right
[blurPlatter.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
}
[NSLayoutConstraint activateConstraints:@[
[blurPlatter.topAnchor constraintEqualToAnchor:self.topAnchor],
[blurPlatter.widthAnchor constraintEqualToConstant:(([[[objc_getClass("CSAdjunctListView") class] sharedListViewForKai] stackView].frame.size.width - 16) + bannerWidthFactor)],
[blurPlatter.heightAnchor constraintEqualToConstant:bannerHeight]
]];

[self.widthAnchor constraintEqualToAnchor:blurPlatter.widthAnchor].active = YES;

// Blur
blur.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[blur.centerXAnchor constraintEqualToAnchor:blurPlatter.centerXAnchor],
[blur.topAnchor constraintEqualToAnchor:blurPlatter.topAnchor],
[blur.widthAnchor constraintEqualToAnchor:blurPlatter.widthAnchor],
[blur.heightAnchor constraintEqualToAnchor:blurPlatter.heightAnchor]
]];

// Percent label
self.percentLabel.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.percentLabel.centerYAnchor constraintEqualToAnchor:blurPlatter.centerYAnchor],
[self.percentLabel.widthAnchor constraintEqualToConstant:36],
[self.percentLabel.heightAnchor constraintEqualToConstant:12]
]];

// Label
self.label.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.label.leftAnchor constraintEqualToAnchor:self.glyphView.rightAnchor
constant:4.5],
[self.label.centerYAnchor constraintEqualToAnchor:blurPlatter.centerYAnchor],
[self.label.heightAnchor constraintEqualToConstant:25]
]];
if (!hidePercent) {
[self.label.rightAnchor constraintEqualToAnchor:self.percentLabel.leftAnchor constant:-4.5].active = YES;
} else {
[self.label.rightAnchor constraintEqualToAnchor:self.label.leftAnchor].active = YES;
}

// Glyph View
self.glyphView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.glyphView.leftAnchor constraintEqualToAnchor:blurPlatter.leftAnchor
constant:20.5],
[self.glyphView.centerYAnchor constraintEqualToAnchor:blurPlatter.centerYAnchor],
[self.glyphView.widthAnchor constraintEqualToConstant:glyphSize],
[self.glyphView.heightAnchor constraintEqualToConstant:glyphSize]
]];

// Battery
self.battery.translatesAutoresizingMaskIntoConstraints = NO;
if (!hideBatteryIcon) {
[self.battery.widthAnchor constraintEqualToConstant:20].active = YES;
} else {
[self.battery.widthAnchor constraintEqualToConstant:0].active = YES;
self.battery.alpha = 0;
}
[NSLayoutConstraint activateConstraints:@[
[self.battery.centerYAnchor constraintEqualToAnchor:blurPlatter.centerYAnchor],
[self.battery.rightAnchor constraintEqualToAnchor:blurPlatter.rightAnchor
constant:-20.5],
[self.battery.heightAnchor constraintEqualToConstant:10]
]];

if (!hideDeviceLabel) {
[self.percentLabel.rightAnchor constraintEqualToAnchor:self.battery.leftAnchor constant:-4.5].active = YES;
} else if (hideDeviceLabel) {
[self.percentLabel.centerXAnchor constraintEqualToAnchor:blurPlatter.centerXAnchor].active = YES;
}

if (hidePercent) {
[self.label.rightAnchor constraintEqualToAnchor:self.battery.leftAnchor constant:-4.5].active = YES;
}

[self.heightAnchor constraintEqualToConstant:(bannerHeight + spacing)];
}

return self;
}

- (void)traitCollectionDidChange:(id)arg1 {
[super traitCollectionDidChange:arg1];
if (textColor == 0) {
if (@available(iOS 12.0, *)) {
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
[self.label setTextColor:[UIColor whiteColor]];
[self.percentLabel setTextColor:[UIColor whiteColor]];
} else if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {
[self.label setTextColor:[UIColor blackColor]];
[self.percentLabel setTextColor:[UIColor blackColor]];
}
}
}
}

- (void)updateInfo {
if (self.device != nil) {
NSString *deviceName = MSHookIvar<NSString *>(self.device, "_name");
double batteryPercentage = MSHookIvar<long long>(self.device, "_percentCharge");
BOOL charging = MSHookIvar<long long>(self.device, "_charging");
BOOL LPM = MSHookIvar<BOOL>(self.device, "_batterySaverModeActive");

self.label.text = [NSString stringWithFormat:@"%@", deviceName];
[self.percentLabel setText:[NSString stringWithFormat:@"%ld%%", (long)((NSInteger)batteryPercentage)]];
self.battery.chargePercent = (batteryPercentage * 0.01);
if (charging) {
self.battery.chargingState = 1;
} else {
self.battery.chargingState = 0;
}
self.battery.showsInlineChargingIndicator = YES;
if (LPM) {
self.battery.saverModeActive = YES;
} else {
self.battery.saverModeActive = NO;
}
if (kCFCoreFoundationVersionNumber > 1600) {
[self.battery setBodyColorAlpha:1.0];
[self.battery setPinColorAlpha:1.0];
}
[self.percentLabel setText:[NSString stringWithFormat:@"%ld%%", (long)((NSInteger)batteryPercentage)]];
self.battery.chargePercent = (batteryPercentage * 0.01);

[self.glyphView setImage:[self.device glyph]];
} else {
}
}

- (void)removeFromSuperview {
self.device.kaiCell = nil;
[super removeFromSuperview];
}

@end

+ 361
- 0
KAIBatteryPlatter.mm View File

@@ -0,0 +1,361 @@

KAIBatteryPlatter *instance;
NSTimer *queueTimer = nil;
NSMutableArray *deviceNames = [[NSMutableArray alloc] init];
NSMutableArray *cellsForDeviceNames = [[NSMutableArray alloc] init];

@implementation KAIBatteryPlatter

- (instancetype)initWithFrame:(CGRect)arg1 {
self = [super initWithFrame:arg1];
instance = self;

if (self) {
self.stackHolder = [[UIView alloc] initWithFrame:arg1];
self.stack = [[KAIStackView alloc] init];
self.stack.axis = kaiAlign == 0 ? 1 : 0;
self.stack.distribution = UIStackViewDistributionFillEqually;
self.stack.spacing = kaiAlign == 0 ? 0 : spacingHorizontal;
self.stack.alignment = 0;
self.oldCountOfDevices = -100;
self.queued = NO;
[self setShowsHorizontalScrollIndicator:NO];
[self setShowsVerticalScrollIndicator:NO];

[self setMinimumZoomScale:1];
[self setMaximumZoomScale:1];
[self addSubview:self.stackHolder];
[self.stackHolder addSubview:self.stack];
[self setContentSize:self.stack.frame.size];
[self resetOffset];

//Add noti observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resetOffset)
name:@"KaiResetOffset"
object:nil];

self.stackHolder.translatesAutoresizingMaskIntoConstraints = NO;
[self.stackHolder.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
[self.stackHolder.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
[self.stackHolder.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;

if (kaiAlign == 0) {
if (bannerAlign == 2) { //center
self.subviewAligner = [self.stack.centerXAnchor constraintEqualToAnchor:self.stackHolder.centerXAnchor constant:horizontalOffset];
} else if (bannerAlign == 1) { //left
self.subviewAligner = [self.stack.leftAnchor constraintEqualToAnchor:self.stackHolder.leftAnchor constant:horizontalOffset];
} else if (bannerAlign == 3) { //right
self.subviewAligner = [self.stack.rightAnchor constraintEqualToAnchor:self.stackHolder.rightAnchor constant:horizontalOffset];
}

self.subviewAligner.active = YES;
}

[self updateBattery];
}
return self;
}

- (void)resetOffset {
if (kaiAlign != 0 && reAlignSelf) {
[UIView animateWithDuration:0.2
animations:^{
if (bannerAlign == 1) { //left
[self setContentOffset:CGPointMake(0 + horizontalOffset, self.contentOffset.y)];

self.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
} else if (bannerAlign == 2) { //center
[self setContentOffset:CGPointMake(((-1 * self.stackHolder.frame.size.width) / 2) + (self.stack.frame.size.width / 2) + horizontalOffset, self.contentOffset.y)];

CGFloat top = 0, left = 0;
if (self.contentSize.width < self.bounds.size.width) {
left = (self.bounds.size.width - self.contentSize.width) * 0.5f;
}
if (self.contentSize.height < self.bounds.size.height) {
top = (self.bounds.size.height - self.contentSize.height) * 0.5f;
}
self.contentInset = UIEdgeInsetsMake(top, left, top, left);

} else if (bannerAlign == 3) { //right
[self setContentOffset:CGPointMake((-1 * self.stackHolder.frame.size.width) + self.stack.frame.size.width + horizontalOffset, self.contentOffset.y)];

CGFloat top = 0, left = 0;
if (self.contentSize.width < self.bounds.size.width) {
left = (self.bounds.size.width - self.contentSize.width);
}
if (self.contentSize.height < self.bounds.size.height) {
top = (self.bounds.size.height - self.contentSize.height);
}
self.contentInset = UIEdgeInsetsMake(top, left, top, left);
}
}];
}
}

- (void)setClipsToBounds:(BOOL)arg1 {
[super setClipsToBounds:NO];
}

- (void)updateBattery {
dispatch_async(dispatch_get_main_queue(), ^{
BCBatteryDeviceController *bcb = [BCBatteryDeviceController sharedInstance];
NSArray *devices = MSHookIvar<NSArray *>(bcb, "_sortedDevices");

if (self.oldCountOfDevices == -100) {
self.oldCountOfDevices = [devices count] + 1;
}
for (KAIBatteryCell *cell in self.stack.subviews) {
[cell updateInfo];
}

if (!self.isUpdating && self.oldCountOfDevices != 0 && ([devices count] + 1 == self.oldCountOfDevices || [devices count] - 1 == self.oldCountOfDevices || [devices count] == self.oldCountOfDevices)) {
//if(!self.isUpdating) {

self.isUpdating = YES;

NSMutableArray *cellsToAdd = [[NSMutableArray alloc] init];
NSMutableArray *cellsToRemove = [[NSMutableArray alloc] init];

for (BCBatteryDevice *device in devices) {
KAIBatteryCell *cell = [device kaiCellForDevice];
BOOL charging = MSHookIvar<long long>(device, "_charging");
BOOL internal = MSHookIvar<BOOL>(device, "_internal");
BOOL shouldAdd = NO;
BOOL fake = MSHookIvar<BOOL>(device, "_fake");
NSString *deviceName = MSHookIvar<NSString *>(device, "_name");

if (!fake) {
if (showAll) {
shouldAdd = YES;
} else if (showAllMinusInternal && !internal) {
shouldAdd = YES;
} else if (!showAll && charging) {
shouldAdd = YES;
}
}

if (!showPhone && internal) {
shouldAdd = NO;
}

if (![self.stack.subviews containsObject:cell] && shouldAdd && [devices containsObject:device]) {
if (![deviceNames containsObject:deviceName]) {
[cellsToAdd addObject:cell];
[deviceNames addObject:deviceName];
[cellsForDeviceNames addObject:cell];
} else {
for (int i = 0; i < [deviceNames count]; i++) {
if ([[deviceNames objectAtIndex:i] isEqualToString:deviceName]) {
KAIBatteryCell *cell = [cellsForDeviceNames objectAtIndex:i];
cell.device = device;
if ([cellsToRemove containsObject:cell])
[cellsToRemove removeObject:cell];
[cell updateInfo];
}
}
}
} else if ([self.stack.subviews containsObject:cell] && !shouldAdd) {
[cellsToRemove addObject:cell];
// this is where i stupidly removed from deviceName names :kekw:
}
}

for (KAIBatteryCell *cell in cellsToAdd) {
if ([cellsToRemove containsObject:cell])
[cellsToRemove removeObject:cell];
if (![self.stack.subviews containsObject:cell] && [devices containsObject:cell.device]) {
// add cell
cell.alpha = 0;
[self.stack addSubview:cell];
[self.stack addArrangedSubview:cell];
[UIView animateWithDuration:0.3
animations:^{
cell.alpha = 1;
}];
}
}

for (KAIBatteryCell *cell in cellsToRemove) {
// remove cell
[UIView animateWithDuration:0.3
animations:^{
cell.alpha = 0;
}
completion:^(BOOL finished) {
[cell removeFromSuperview];
[self.stack removeArrangedSubview:cell];
cell.alpha = 1;
}];
NSString *deviceName = MSHookIvar<NSString *>(cell.device, "_name");
[deviceNames removeObject:deviceName];
[cellsForDeviceNames removeObject:cell];
}

for (KAIBatteryCell *cell in self.stack.subviews) {
if (![devices containsObject:cell.device] || cell.device == nil) { //not existing, remove
NSString *deviceName = cell.label.text;
[UIView animateWithDuration:0.3
animations:^{
cell.alpha = 0;
}
completion:^(BOOL finished) {
[cell removeFromSuperview];
[self.stack removeArrangedSubview:cell];
cell.alpha = 1;
}];
[deviceNames removeObject:deviceName];
[cellsForDeviceNames removeObject:cell];
}
}

queueTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(dispatchQueue) userInfo:nil repeats:NO];
//self.isUpdating = NO;

} else if (self.isUpdating) {
self.queued = YES;
}

self.oldCountOfDevices = [devices count];

[self calculateHeight];

if ([self.superview.superview.superview respondsToSelector:@selector(fixComplicationsViewFrame)]) {
[(NCNotificationListView *)(self.superview.superview.superview) fixComplicationsViewFrame];
}

[self setContentSize:self.stack.frame.size];
[self performSelector:@selector(resetOffset) withObject:self afterDelay:0.2];
});
}

- (void)setContentOffset:(CGPoint)arg1 {
[self setContentSize:self.stack.frame.size]; //sometimes the view gets "stuck", this fixes it
[super setContentOffset:CGPointMake(arg1.x, 0)];
}

- (void)calculateHeight {
self.number = [self.stack.subviews count];

if (self.number == 0) {
UIStackView *s = (UIStackView *)(self.superview);
s.frame = CGRectMake(s.frame.origin.x, s.frame.origin.y, s.frame.size.width, (s.frame.size.height - 1));
[s removeArrangedSubview:self];
[self removeFromSuperview];
} else if (self.number != 0 && self.superview == nil && shouldBeAdded == YES) {
[[[[objc_getClass("CSAdjunctListView") class] sharedListViewForKai] stackView] addArrangedSubview:self];
//[self performSelector:@selector(calculateHeight) withObject:self afterDelay:0.1];
}

[UIView animateWithDuration:0.3
animations:^{
if (!self.heightConstraint) {
int height = (self.number * (bannerHeight + spacing));
if (kaiAlign != 0) {
height = bannerHeight + spacing;
}

if ([self.superview.subviews count] > 1) {
height = (height - spacing) + 1;
}

self.heightConstraint = [self.heightAnchor constraintEqualToConstant:height];
self.stack.heightConstraint = [self.stack.heightAnchor constraintEqualToConstant:height];
self.heightConstraint.active = YES;
self.stack.heightConstraint.active = YES;
[self setContentSize:self.stack.frame.size];
[self resetOffset];

} else {
int height = (self.number * (bannerHeight + spacing));
int extra = extraPaddingAfter ? spacing : 0;
if (kaiAlign == 0) {
//self.stack.widthConstraint.constant = bannerWidthFactor;
} else {
height = bannerHeight + spacing;
}

height = height + extra;

if ([self.superview.subviews count] > 1) {
height = (height - spacing + 1);
}

self.heightConstraint.constant = height;
self.stack.heightConstraint.constant = height - extra; //minus extra because it will stretch cell spacing otherwise

UIStackView *s = (UIStackView *)(self.superview);
s.frame = CGRectMake(s.frame.origin.x, s.frame.origin.y, s.frame.size.width, (s.frame.size.height - 1));
//literally does nothing but makes the stack view lay itself out (doesnt adjust frame because translatesAutoreszingMaskIntoConstraints = NO on stack views)
}

[self setContentSize:self.stack.frame.size];
[self resetOffset];
}];
}

- (void)removeFromSuperview {
[self.superview setNeedsLayout];
[super removeFromSuperview];
}

- (void)refreshForPrefs {
self.stack.spacing = kaiAlign == 0 ? 0 : spacingHorizontal;
[self setContentSize:self.stack.frame.size];
for (UIView *view in self.stack.subviews) {
@try {
[view removeFromSuperview];
} @catch (NSException *exception) {
//Panik
}
}

BCBatteryDeviceController *bcb = [BCBatteryDeviceController sharedInstance];
NSArray *devices = MSHookIvar<NSArray *>(bcb, "_sortedDevices");
for (BCBatteryDevice *device in devices) {
[device resetKaiCellForNewPrefs];
}

if (kaiAlign == 0) {
self.subviewAligner.active = NO;
if (bannerAlign == 2) { //center
self.subviewAligner = [self.stack.centerXAnchor constraintEqualToAnchor:self.stackHolder.centerXAnchor constant:horizontalOffset];
} else if (bannerAlign == 1) { //left
self.subviewAligner = [self.stack.leftAnchor constraintEqualToAnchor:self.stackHolder.leftAnchor constant:horizontalOffset];
} else if (bannerAlign == 3) { //right
self.subviewAligner = [self.stack.rightAnchor constraintEqualToAnchor:self.stackHolder.rightAnchor constant:horizontalOffset];
}

self.subviewAligner.active = YES;
}

[cellsForDeviceNames removeAllObjects];
[deviceNames removeAllObjects];

[self updateBattery];
}

- (void)dispatchQueue {
self.isUpdating = NO;
if (self.queued) {
[self updateBattery];
if ([self.superview.superview.superview respondsToSelector:@selector(fixComplicationsViewFrame)]) {
[(NCNotificationListView *)(self.superview.superview.superview) fixComplicationsViewFrame];
}
self.queued = NO;
}
[queueTimer invalidate];
queueTimer = nil;
}

+ (KAIBatteryPlatter *)sharedInstance {
return instance;
}

//This is for compatibility (did i spell that right?)
//basically this fixes it crashing in landscape:

- (void)setSizeToMimic:(CGSize)arg1 {
}

@end

+ 74
- 0
KAIClassHeaders.h View File

@@ -0,0 +1,74 @@
#import <UIKit/UIKit.h>
#import <substrate.h>

@interface _UIBatteryView : UIView
@property (nonatomic, assign) CGFloat chargePercent;
@property (nonatomic, assign) CGFloat bodyColorAlpha;
@property (nonatomic, assign) CGFloat pinColorAlpha;
@property (nonatomic, assign) BOOL showsPercentage;
@property (nonatomic, assign) BOOL saverModeActive;
@property (nonatomic, assign) BOOL showsInlineChargingIndicator;
@property (nonatomic, assign) NSInteger chargingState;
@end

@interface MTMaterialView : UIView
@property (nonatomic, assign) BOOL recipeDynamic;
- (id)_initWithRecipe:(NSInteger)arg1 configuration:(NSInteger)arg2 initialWeighting:(CGFloat)arg3 scaleAdjustment:(id)arg4;
+ (id)materialViewWithRecipe:(NSInteger)arg1 options:(NSInteger)arg2 initialWeighting:(CGFloat)arg3 scaleAdjustment:(id)arg4;
@end

@interface BCBatteryDeviceController : NSObject
@property (nonatomic, strong) NSArray *sortedDevices;
- (id)_sortedDevices;
+ (id)sharedInstance;
@end

@interface BCBatteryDevice : NSObject
@property (nonatomic, strong) id kaiCell;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) long long percentCharge;
@property (nonatomic, assign) BOOL charging;
@property (nonatomic, assign) BOOL fake;
@property (nonatomic, assign) BOOL internal;
@property (nonatomic, assign) BOOL batterySaverModeActive;
@property (nonatomic, strong) NSString *identifier;
- (id)glyph;
- (id)kaiCellForDevice;
- (void)resetKaiCellForNewPrefs;
@end

@interface KAIBatteryCell : UIView
@property (nonatomic, weak) BCBatteryDevice *device;
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UILabel *percentLabel;
@property (nonatomic, strong) UIImageView *glyphView;
@property (nonatomic, strong) _UIBatteryView *battery;
- (instancetype)initWithFrame:(CGRect)arg1 device:(BCBatteryDevice *)device;
- (void)updateInfo;
@end

@interface KAIStackView : UIStackView
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end

@interface KAIBatteryPlatter : UIScrollView <UIScrollViewDelegate>
@property (nonatomic, assign) BOOL shouldUpdate;
@property (nonatomic, strong) UIView *stackHolder;
@property (nonatomic, assign) NSInteger number;
@property (nonatomic, assign) NSInteger oldCountOfDevices;
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@property (nonatomic, strong) NSLayoutConstraint *subviewAligner;
@property (nonatomic, strong) KAIStackView *stack;
@property (nonatomic, assign) BOOL isUpdating;
@property (nonatomic, assign) BOOL queued;
+ (KAIBatteryPlatter *)sharedInstance;
- (instancetype)initWithFrame:(CGRect)arg1;
- (void)resetOffset;
- (void)refreshForPrefs;
- (void)updateBattery;
- (void)calculateHeight;
@end

@interface UIView (kai)
- (void)_didRemoveSubview:(UIView *)arg1;
@end

+ 35
- 0
KAIStackView.mm View File

@@ -0,0 +1,35 @@

@implementation KAIStackView

- (id)initWithFrame:(CGRect)arg1 {
self = [super initWithFrame:arg1];
self.translatesAutoresizingMaskIntoConstraints = NO;
return self;
}

- (void)addArrangedSubview:(UIView *)view {
[super addArrangedSubview:view];
[[KAIBatteryPlatter sharedInstance] setContentSize:self.frame.size];

if (textColor == 0 && [view respondsToSelector:@selector(updateInfo)]) {
KAIBatteryCell *cell = (KAIBatteryCell *)view;
if (@available(iOS 12.0, *)) {
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
[cell.label setTextColor:[UIColor whiteColor]];
[cell.percentLabel setTextColor:[UIColor whiteColor]];
} else if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {
[cell.label setTextColor:[UIColor blackColor]];
[cell.percentLabel setTextColor:[UIColor blackColor]];
}
}
}

[[KAIBatteryPlatter sharedInstance] performSelector:@selector(resetOffset) withObject:[KAIBatteryPlatter sharedInstance] afterDelay:0.2];
}

- (void)removeArrangedSubview:(UIView *)subview {
[super removeArrangedSubview:subview];
[[KAIBatteryPlatter sharedInstance] performSelector:@selector(resetOffset) withObject:[KAIBatteryPlatter sharedInstance] afterDelay:0.2];
}

@end

+ 147
- 0
Kai.h View File

@@ -0,0 +1,147 @@
#include <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <objc/runtime.h>

#define KAISelf ((CSAdjunctListView *)self) //for use when calling self in KAITarget
#define afterMusicIndex(cls, obj) [[[cls sharedListViewForKai] stackView].subviews indexOfObject:obj]

@interface CSAdjunctListView : UIView
@property (nonatomic, assign) BOOL hasKai;
- (UIStackView *)stackView;
- (void)_layoutStackView;
- (void)setStackView:(UIStackView *)arg1;
+ (id)sharedListViewForKai;
@end

@interface CALayer (kai)
@property (nonatomic, assign) BOOL continuousCorners;
@end

@interface SBCoverSheetPrimarySlidingViewController : UIViewController
@end

@interface APEPlatter : UIView
@property (nonatomic, assign) BOOL removed;
+ (APEPlatter *)sharedInstance;
@end

@interface NCNotificationListView : UIView
- (void)fixComplicationsViewFrame;
@end

BOOL isUpdating = NO;
BOOL shouldBeAdded = YES;

//prefs
BOOL enabled;
BOOL disableGlyphs;
BOOL hidePercent;
BOOL showAll;
BOOL belowMusic;
BOOL hideDeviceLabel;
BOOL hideChargingAnimation;
BOOL showAllMinusInternal;
BOOL hideBatteryIcon;
BOOL reAlignSelf;
BOOL showPhone;
BOOL removeForMedia;
BOOL extraPaddingAfter;
NSInteger bannerStyle;
NSInteger bannerAlign;
NSInteger textColor;
double spacing;
double glyphSize;
double bannerHeight;
double cornerRadius;
double bannerWidthFactor;
double horizontalOffset;
double bannerAlpha;
double kaiAlign;
double spacingHorizontal;

//by importing here, I can use vars in the .mm files
#import "KAIBatteryCell.mm"
#import "KAIBatteryPlatter.mm"
#import "KAIStackView.mm"

#define PLIST_PATH @"/User/Library/Preferences/com.burritoz.kaiprefs.plist"
#define kIdentifier @"com.burritoz.kaiprefs"
#define kSettingsChangedNotification (CFStringRef) @"com.burritoz.kaiprefs/reload"
#define kSettingsPath @"/var/mobile/Library/Preferences/com.burritoz.kaiprefs.plist"

NSDictionary *prefs = nil;

static void *observer = NULL;

static void reloadPrefs() {
if ([NSHomeDirectory() isEqualToString:@"/var/mobile"]) {
CFArrayRef keyList = CFPreferencesCopyKeyList((CFStringRef)kIdentifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);

if (keyList) {
prefs = (NSDictionary *)CFBridgingRelease(CFPreferencesCopyMultiple(keyList, (CFStringRef)kIdentifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost));

if (!prefs) {
prefs = [NSDictionary new];
}
CFRelease(keyList);
}
} else {
prefs = [NSDictionary dictionaryWithContentsOfFile:kSettingsPath];
}
}

static BOOL boolValueForKey(NSString *key, BOOL defaultValue) {
return (prefs && [prefs objectForKey:key] ? [[prefs objectForKey:key] boolValue] : defaultValue);
}

static double numberForValue(NSString *key, double defaultValue) {
return (prefs && [prefs objectForKey:key] ? [[prefs objectForKey:key] doubleValue] : defaultValue);
}

static void preferencesChanged() {
CFPreferencesAppSynchronize((CFStringRef)kIdentifier);
reloadPrefs();

enabled = boolValueForKey(@"enabled", YES);
spacing = numberForValue(@"spacing", 5);
glyphSize = numberForValue(@"glyphSize", 30);
bannerHeight = numberForValue(@"bannerHeight", 80);
cornerRadius = numberForValue(@"cornerRadius", 13);
disableGlyphs = boolValueForKey(@"disableGlyphs", NO);
hidePercent = boolValueForKey(@"hidePercent", NO);
bannerStyle = numberForValue(@"bannerStyle", 1);
showAll = boolValueForKey(@"showAll", NO);
bannerWidthFactor = numberForValue(@"bannerWidthFactor", 0);
hideDeviceLabel = boolValueForKey(@"hideDeviceLabel", NO);
bannerAlign = numberForValue(@"bannerAlign", 2);
horizontalOffset = numberForValue(@"horizontalOffset", 0);
belowMusic = boolValueForKey(@"belowMusic", NO);
hideChargingAnimation = boolValueForKey(@"hideChargingAnimation", YES);
textColor = numberForValue(@"textColor", 0);
bannerAlpha = numberForValue(@"bannerAlpha", 1);
showAllMinusInternal = boolValueForKey(@"showAllMinusInternal", NO);
kaiAlign = numberForValue(@"kaiAlign", 0);
spacingHorizontal = numberForValue(@"spacingHorizontal", 8);
hideBatteryIcon = boolValueForKey(@"hideBatteryIcon", NO);
reAlignSelf = boolValueForKey(@"reAlignSelf", YES);
extraPaddingAfter = boolValueForKey(@"extraPaddingAfter", NO);
showPhone = boolValueForKey(@"showPhone", YES);
removeForMedia = boolValueForKey(@"removeForMedia", NO);

if (disableGlyphs) {
glyphSize = 0;
}
}

static void applyPrefs() {
preferencesChanged();

isUpdating = YES;

[[KAIBatteryPlatter sharedInstance] refreshForPrefs]; //so hard (not)
[(CSAdjunctListView *)([KAIBatteryPlatter sharedInstance].superview.superview) _layoutStackView];
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiResetOffset" object:nil userInfo:nil];

isUpdating = NO;
}

+ 1
- 0
Kai.plist View File

@@ -0,0 +1 @@
{ Filter = { Bundles = ( "com.apple.springboard" ); }; }

+ 229
- 0
Kai.xm View File

@@ -0,0 +1,229 @@
#import "Kai.h"

CSAdjunctListView *list;
Class mediaClass;

%group main

%hook Media

- (void)dealloc {
%orig;
if(removeForMedia) {
shouldBeAdded = YES;

[[KAIBatteryPlatter sharedInstance] updateBattery];
Class cls = kCFCoreFoundationVersionNumber > 1600 ? ([objc_getClass("CSAdjunctListView") class]) : ([objc_getClass("SBDashBoardAdjunctListView") class]);

if(!belowMusic) { //cursed
[[[cls sharedListViewForKai] stackView] removeArrangedSubview:[KAIBatteryPlatter sharedInstance]];
[[[cls sharedListViewForKai] stackView] insertArrangedSubview:[KAIBatteryPlatter sharedInstance] atIndex:0];
}
}
}

- (void)didMoveToSuperview {
%orig;
Class cls = kCFCoreFoundationVersionNumber > 1600 ? ([objc_getClass("CSAdjunctListView") class]) : ([objc_getClass("SBDashBoardAdjunctListView") class]);

if([[[cls sharedListViewForKai] stackView].arrangedSubviews containsObject:self]) {
if(belowMusic) {//cursed
[[[cls sharedListViewForKai] stackView] removeArrangedSubview:[KAIBatteryPlatter sharedInstance]];
[[[cls sharedListViewForKai] stackView] insertArrangedSubview:[KAIBatteryPlatter sharedInstance] atIndex:afterMusicIndex(cls, self)];
} else {
[[[cls sharedListViewForKai] stackView] removeArrangedSubview:[KAIBatteryPlatter sharedInstance]];
[[[cls sharedListViewForKai] stackView] insertArrangedSubview:[KAIBatteryPlatter sharedInstance] atIndex:0];
}

if(removeForMedia) {
shouldBeAdded = NO;

[[KAIBatteryPlatter sharedInstance] removeFromSuperview];
[[[cls sharedListViewForKai] stackView] removeArrangedSubview:[KAIBatteryPlatter sharedInstance]];
}
}

}

%end

%hook KAITarget //This class is defined in %ctor, KAITarget is not a class name.

%property (nonatomic, assign) BOOL hasKai;

- (void)setClipsToBounds:(BOOL)arg1 {
%orig(YES);
}

- (void)setStackView:(UIStackView *)arg1 {
KAISelf.clipsToBounds = YES;

if(!KAISelf.hasKai) {

list = self;

KAIBatteryPlatter *battery = [[KAIBatteryPlatter alloc] initWithFrame:[self stackView].frame];

//Add noti observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(KaiInfo)
name:@"KaiInfoChanged"
object:nil];
KAISelf.hasKai = YES;

if(![arg1.subviews containsObject:battery]) { //if not added
//add kai to the stack view
[arg1 addArrangedSubview:battery];
}
[battery updateBattery];

//send the adjusted stackview as arg1
%orig(arg1);

}
}

%new
- (void)KaiInfo {

if(!isUpdating) {

isUpdating = YES;

//NSLog(@"kai: kai info will update");
dispatch_async(dispatch_get_main_queue(), ^{

[[KAIBatteryPlatter sharedInstance] updateBattery];
if([KAIBatteryPlatter sharedInstance].number == 0) {
[[KAIBatteryPlatter sharedInstance] removeFromSuperview];
[[self stackView] removeArrangedSubview:[KAIBatteryPlatter sharedInstance]];
} else if(![[self stackView].subviews containsObject:[KAIBatteryPlatter sharedInstance]] && shouldBeAdded) {
[[self stackView] addSubview:[KAIBatteryPlatter sharedInstance]];
[[self stackView] addArrangedSubview:[KAIBatteryPlatter sharedInstance]];
}
if([KAISelf.superview respondsToSelector:@selector(fixComplicationsViewFrame)]) {
[KAISelf.superview performSelector:@selector(fixComplicationsViewFrame) withObject:KAISelf.superview afterDelay:0.35];
}

isUpdating = NO;
});

}

}

%new
+ (id)sharedListViewForKai {
return list;
}

%end

%hook SBCoverSheetPrimarySlidingViewController

- (void)viewDidDisappear:(BOOL)animated {
if(reAlignSelf)
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiResetOffset" object:nil userInfo:nil];
%orig;
}

- (void)viewDidAppear:(BOOL)animated {
if(reAlignSelf)
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiResetOffset" object:nil userInfo:nil];
%orig;
}

%end

%hook BCBatteryDevice
%property (nonatomic, strong) KAIBatteryCell *kaiCell;

- (void)setCharging:(BOOL)arg1 {
//sends the noti to update battery info
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiInfoChanged" object:nil userInfo:nil];
%orig;
}

- (void)setBatterySaverModeActive:(BOOL)arg1 {
//sends the noti to update battery info
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiInfoChanged" object:nil userInfo:nil];
%orig;
}

- (void)setPercentCharge:(NSInteger)arg1 {
//sends the noti to update battery info
if(arg1!=0) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiInfoChanged" object:nil userInfo:nil];
}
%orig;
}

- (void)dealloc {
%orig;
[[NSNotificationCenter defaultCenter] postNotificationName:@"KaiInfoChanged" object:nil userInfo:nil];
}

%new
- (id)kaiCellForDevice {
if(self && self.kaiCell == nil) {
self.kaiCell = [[KAIBatteryCell alloc] initWithFrame:CGRectMake(0,0,[KAIBatteryPlatter sharedInstance].frame.size.width,0) device:self]; }
((KAIBatteryCell *)self.kaiCell).translatesAutoresizingMaskIntoConstraints = NO;
[(KAIBatteryCell *)self.kaiCell updateInfo];

return self.kaiCell;
}

%new
- (void)resetKaiCellForNewPrefs {
self.kaiCell = [[KAIBatteryCell alloc] initWithFrame:CGRectMake(0,0,[KAIBatteryPlatter sharedInstance].frame.size.width,0) device:self];
((KAIBatteryCell *)self.kaiCell).translatesAutoresizingMaskIntoConstraints = NO;
[(KAIBatteryCell *)self.kaiCell updateInfo];
}
%end

%hook KAICSTarget //Again, not a class

- (void)_transitionChargingViewToVisible:(BOOL)arg1 showBattery:(BOOL)arg2 animated:(BOOL)arg3 {
if(hideChargingAnimation) {
//Yeah bro this just makes the method never call to show the charging thing
%orig(NO,NO,NO);
}
}

- (void)_transitionChargingViewToVisible:(BOOL)arg1 showBattery:(BOOL)arg2 animated:(BOOL)arg3 force:(BOOL)arg4 { //might just be ios12
if(hideChargingAnimation) {
//Same idea
%orig(NO,NO,NO,NO);
}
}

%end

%end

%ctor {
preferencesChanged();
CFNotificationCenterAddObserver(
CFNotificationCenterGetDarwinNotifyCenter(),
&observer,
(CFNotificationCallback)applyPrefs,
kSettingsChangedNotification,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately
);

//Bro Muirey helped me figure out a logical way to do this because iOS 12-13 classes have changed

mediaClass = kCFCoreFoundationVersionNumber > 1600 ? ([objc_getClass("CSAdjunctItemView") class]) : ([objc_getClass("SBDashBoardAdjunctItemView") class]);

Class cls = kCFCoreFoundationVersionNumber > 1600 ? ([objc_getClass("CSAdjunctListView") class]) : ([objc_getClass("SBDashBoardAdjunctListView") class]);

Class CSCls = kCFCoreFoundationVersionNumber > 1600 ? ([objc_getClass("CSCoverSheetViewController") class]) : ([objc_getClass("SBDashBoardViewController") class]);

if(enabled) {
%init(main, Media = mediaClass, KAITarget = cls, KAICSTarget = CSCls); //BIG BRAIN BRO!!
}

NSLog(@"[kai]: loaded into %@", [NSBundle mainBundle].bundleIdentifier);
}

+ 9
- 0
Layout/DEBIAN/control View File

@@ -0,0 +1,9 @@
Package: com.burritoz.kai
Name: Kai
Version: 1.3.0
Architecture: iphoneos-arm
Description: Device battery indicators on your lock screen!
Maintainer: burrit0z
Author: burrit0z
Section: Tweaks
Depends: mobilesubstrate (>= 0.9.5000), preferenceloader, ws.hbang.common (>=1.14)

+ 11
- 0
Layout/DEBIAN/postinst View File

@@ -0,0 +1,11 @@
echo ""
echo ""
echo "You've installed kai."
echo "Special thanks to:"
echo "Thomz -- The icon, banner, and for being awesome"
echo "Thenatis -- The banner for the featured package section, and the release banner"
echo "@KorfiDiscord -- Design, and motivating me"
echo "Devvix -- Being there to help"
echo "Esquilli -- Helping improve my design patterns"
echo ""
echo ""

+ 40
- 0
README.md View File

@@ -0,0 +1,40 @@
# kai

## All your batteries, at a glance
kai will show any Bluetooth device that provides battery information, meaning you can check the battery of your phone, your watch, your AirPods, and your AirPods case, all from the lock screen. Quickly and easily.

## Compatibility
kai works with many, many lock screen tweaks, such as Kalm, Grupi, Axon, Quart, Complications, Watermelon, Veza, QuickLS, Jellyfish, and way more!

## Make it yours
kai comes with a multitude of customization options, so you can fine-tune your settings to fit you and your setup.

## Big and Bold, or small and simple
kai offers two main options for displaying battery information. (a) Vertical mode, to make kai fit in with your notifications, and (b) horizontal mode, an unobtrusive, scrollable, and tiny mode that doesn't take up any more space on your lock screen than it needs to.

## Full feature/option list
- Option to hide the large battery view coversheet charging animations on the lock screen
- Show all or just charging devices on kai
- Option to show Bluetooth devices always, and the phone just when charging
- Option to hide device glyphs on kai cells
- Option to hide percent label on kai cells
- Option to hide the device name label on kai cells
- Two-axis options, a vertical mode, or horizontal mode
- Choose between adaptive, light, or dark mode for kai's cells
- Choose between adaptive, white, or black text for labels on kai's cells
- Choose to align kai to the left, right, or center for vertical mode
- Option to show kai above or below music player
- Adjust kai cell height
- Adjust kai banner width
- Adjust the spacing between kai cells vertically
- Adjust the spacing between kai cells horizontally
- Manual offset option for the x-axis
- Adjust device glyph size
- Adjust kai cell corner radius
- Adjust kai cell background blur alpha

## Note
Special thanks to my amazing beta testers in the server I co-own with Thomz. I could not have tested kai so extensively and brought it to where it is today without them. Thanks to Thomz (@Thomzi07 on Twitter) for making kai's icon, and depiction screenshots, and Thenatis (@thenatis1 on Twitter) for helping with design, and for making the banner for kai. Additionally, kai is inspired by LaughingQuoll's Maple tweak series and Apple's AirPower design. kai was built with inspiration from this. However, the main reason I made kai is because the Maple series does not work with notification grouping tweaks like Axon and Grupi. Additionally, kai features a wider range of customization options.

## Socials and Support
If you are encountering issues, or simply wish to reach out, you can contact me at my email (burrit0ztweaks@gmail.com) or join the discord server I co-own with Thomz to get support. Discord server invite link: https://discord.gg/NQ3uXtJ

BIN
kai depiction screenshots/Screenshot1.png View File

Before After
Width: 1080  |  Height: 1920  |  Size: 1.8MB

BIN
kai depiction screenshots/Screenshot2.png View File

Before After
Width: 1080  |  Height: 1920  |  Size: 1.0MB

BIN
kai depiction screenshots/Screenshot3.png View File

Before After
Width: 1080  |  Height: 1920  |  Size: 1.1MB

+ 38
- 0
kaiprefs/KAIRootListController.h View File

@@ -0,0 +1,38 @@
#import <CepheiPrefs/HBRootListController.h>
#import <Foundation/NSUserDefaults.h>
#import <Preferences/PSListController.h>
#import <Preferences/PSListItemsController.h>
#import <Preferences/PSSpecifier.h>
#import <Preferences/PSTableCell.h>

@interface PSListController (kai)
- (void)setFrame:(CGRect)frame;
@end

@interface NSTask : NSObject
@property (copy) NSArray *arguments;
@property (copy) NSString *launchPath;
- (id)init;
- (void)waitUntilExit;
- (void)launch;
@end

@interface KAIRootListController : HBRootListController
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIImageView *iconView;
@end

@interface Thomz_TwitterCell : PSTableCell
@end

@protocol PreferencesTableCustomView
- (id)initWithSpecifier:(id)arg1;
@end

@interface KaiHeaderCell : PSTableCell <PreferencesTableCustomView> {
UIView *bgView;
UILabel *packageNameLabel;
UILabel *developerLabel;
UILabel *versionLabel;
}
@end

+ 280
- 0
kaiprefs/KAIRootListController.m View File

@@ -0,0 +1,280 @@
#include "KAIRootListController.h"

KAIRootListController *controller;
NSBundle *tweakBundle;

//thank god for renai
static inline NSString *getPackageVersion() {
NSString *packageVersion = [NSString stringWithFormat:@"${%@}", @"Version"];
int status;

NSMutableArray<NSString *> *argsv0 = [NSMutableArray array];
for (NSString *string in @[ @"/usr/bin/dpkg-query", @"-Wf", packageVersion, @"com.burritoz.kai" ]) {
[argsv0
addObject:[NSString stringWithFormat:@"'%@'",
[string stringByReplacingOccurrencesOfString:@"'"
withString:@"\\'"
options:NSRegularExpressionSearch
range:NSMakeRange(
0, string.length)]]];
}

NSString *argsv1 = [argsv0 componentsJoinedByString:@" "];
FILE *file = popen(argsv1.UTF8String, "r");
if (!file) {
return nil;
}

char data[1024];
NSMutableString *output = [NSMutableString string];

while (fgets(data, 1024, file) != NULL) {
[output appendString:[NSString stringWithUTF8String:data]];
}

int result = pclose(file);
status = result;

if (status == 0) {
return output ?: @"🏴‍☠️ Pirated";
}

return @"🏴‍☠️ Pirated";
}

////////

static void respringNeeded() {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Respring"
message:@"Changing this requires a respring for it to take effect. Would you like to respring now?"
preferredStyle:UIAlertControllerStyleActionSheet];

UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action){
}];

UIAlertAction *yes = [UIAlertAction actionWithTitle:@"Respring"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
NSTask *t = [[NSTask alloc] init];
[t setLaunchPath:@"usr/bin/killall"];
[t setArguments:[NSArray arrayWithObjects:@"backboardd", nil]];
[t launch];
}];

[alert addAction:defaultAction];
[alert addAction:yes];
[controller presentViewController:alert animated:YES completion:nil];
}

static void applyPrefs() {
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.burritoz.kaiprefs/reload"), nil, nil, true);
}

@implementation KAIRootListController

- (NSArray *)specifiers {
if (!_specifiers) {
_specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
}

return _specifiers;
}

- (void)viewWillAppear:(BOOL)arg1 {
[[UISegmentedControl appearanceWhenContainedInInstancesOfClasses:@[ self.class ]] setTintColor:[UIColor colorWithRed:0.00 green:0.82 blue:1.00 alpha:1.00]];
[[UISwitch appearanceWhenContainedInInstancesOfClasses:@[ self.class ]] setOnTintColor:[UIColor colorWithRed:0.00 green:0.82 blue:1.00 alpha:1.00]];
[[UISlider appearanceWhenContainedInInstancesOfClasses:@[ self.class ]] setTintColor:[UIColor colorWithRed:0.00 green:0.82 blue:1.00 alpha:1.00]];
}

- (void)viewWillDisappear:(BOOL)arg1 {
[super viewWillDisappear:arg1];
}

- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.titleView = [UIView new];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
self.titleLabel.font = [UIFont systemFontOfSize:17.5];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.titleLabel.text = @"kai";
self.titleLabel.alpha = 0.0;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.navigationItem.titleView addSubview:self.titleLabel];

self.iconView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
self.iconView.contentMode = UIViewContentModeScaleAspectFit;
self.iconView.image = [UIImage imageWithContentsOfFile:@"/Library/PreferenceBundles/kaiPrefs.bundle/icon.png"];
self.iconView.translatesAutoresizingMaskIntoConstraints = NO;
self.iconView.alpha = 1.0;
[self.navigationItem.titleView addSubview:self.iconView];

[NSLayoutConstraint activateConstraints:@[
[self.titleLabel.topAnchor constraintEqualToAnchor:self.navigationItem.titleView.topAnchor],
[self.titleLabel.leadingAnchor constraintEqualToAnchor:self.navigationItem.titleView.leadingAnchor],
[self.titleLabel.trailingAnchor constraintEqualToAnchor:self.navigationItem.titleView.trailingAnchor],
[self.titleLabel.bottomAnchor constraintEqualToAnchor:self.navigationItem.titleView.bottomAnchor],
[self.iconView.topAnchor constraintEqualToAnchor:self.navigationItem.titleView.topAnchor],
[self.iconView.leadingAnchor constraintEqualToAnchor:self.navigationItem.titleView.leadingAnchor],
[self.iconView.trailingAnchor constraintEqualToAnchor:self.navigationItem.titleView.trailingAnchor],
[self.iconView.bottomAnchor constraintEqualToAnchor:self.navigationItem.titleView.bottomAnchor],
]];

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Pirated :("
message:@"Please install kai from Chariz repository."
preferredStyle:UIAlertControllerStyleAlert];

if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/lib/dpkg/info/com.burritoz.kai.list"] && [[NSFileManager defaultManager] fileExistsAtPath:@"/var/lib/dpkg/info/com.burritoz.kai.md5sums"]) {
// nothing
} else {
[self presentViewController:alert animated:YES completion:nil];
}

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)respringNeeded, CFSTR("com.burritoz.kaiprefs.respringneeded"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)applyPrefs, CFSTR("com.burritoz.kaiprefs.apply"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);

controller = self;
}

- (void)resetPrefs:(id)sender {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Reset Preferences"
message:@"Are you sure you want to reset all of your preferences? This action CANNOT be undone! Your device will respring."
preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
}];
UIAlertAction *yes = [UIAlertAction actionWithTitle:@"Yes"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
NSUserDefaults *prefs = [[NSUserDefaults standardUserDefaults] init];
[prefs removePersistentDomainForName:@"com.burritoz.kaiprefs"];

NSTask *f = [[NSTask alloc] init];
[f setLaunchPath:@"/usr/bin/killall"];
[f setArguments:[NSArray arrayWithObjects:@"backboardd", nil]];
[f launch];
}];

[alert addAction:defaultAction];
[alert addAction:yes];
[self presentViewController:alert animated:YES completion:nil];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y;

if (offsetY > 120) {
[UIView animateWithDuration:0.2
animations:^{
self.iconView.alpha = 1.0;
self.titleLabel.alpha = 0.0;
}];
} else {
[UIView animateWithDuration:0.2
animations:^{
self.iconView.alpha = 0.0;
self.titleLabel.alpha = 1.0;
}];
}
}

- (void)followMeBurritoz {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://twitter.com/burrit0ztweaks"]];
}

- (void)followMeOnTwitterThomz {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://twitter.com/thomzi07"]];
}

@end

@implementation KaiHeaderCell // Header Cell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(id)reuseIdentifier specifier:(id)specifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier specifier:specifier];

if (self) {
UILabel *tweakLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, self.contentView.bounds.size.width + 30, 50)];
[tweakLabel setTextAlignment:NSTextAlignmentLeft];
[tweakLabel setFont:[UIFont systemFontOfSize:50 weight:UIFontWeightRegular]];
tweakLabel.text = @"kai";

UILabel *devLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 70, self.contentView.bounds.size.width + 30, 50)];
[devLabel setTextAlignment:NSTextAlignmentLeft];
[devLabel setFont:[UIFont systemFontOfSize:20 weight:UIFontWeightMedium]];
devLabel.alpha = 0.8;
devLabel.text = getPackageVersion();

NSBundle *bundle = [[NSBundle alloc] initWithPath:@"/Library/PreferenceBundles/kaiPrefs.bundle"];
UIImage *logo = [UIImage imageWithContentsOfFile:[bundle pathForResource:@"iconFullSize" ofType:@"png"]];
UIImageView *icon = [[UIImageView alloc] initWithImage:logo];
icon.frame = CGRectMake(self.contentView.bounds.size.width - 35, 35, 70, 70);
icon.translatesAutoresizingMaskIntoConstraints = NO;

[self addSubview:tweakLabel];
[self addSubview:devLabel];
[self addSubview:icon];

[icon.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:-20].active = YES;
[icon.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;
[icon.widthAnchor constraintEqualToConstant:70].active = YES;
[icon.heightAnchor constraintEqualToConstant:70].active = YES;

icon.layer.masksToBounds = YES;
icon.layer.cornerRadius = 15;
}

return self;
}

- (instancetype)initWithSpecifier:(PSSpecifier *)specifier {
return [self initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"KaiHeaderCell" specifier:specifier];
}

- (void)setFrame:(CGRect)frame {
frame.origin.x = 0;
[super setFrame:frame];
}

- (CGFloat)preferredHeightForWidth:(CGFloat)arg1 {
return 140.0f;
}

@end

@implementation Thomz_TwitterCell // lil copy of HBTwitterCell from Cephei
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier specifier:specifier];

if (self) {
UILabel *User = [[UILabel alloc] initWithFrame:CGRectMake(70, 15, 200, 20)];
[User setText:specifier.properties[@"user"]];
[User setFont:[User.font fontWithSize:15]];

UILabel *Description = [[UILabel alloc] initWithFrame:CGRectMake(70, 35, 200, 20)];
[Description setText:specifier.properties[@"description"]];
[Description setFont:[Description.font fontWithSize:10]];

NSBundle *bundle = [[NSBundle alloc] initWithPath:@"/Library/PreferenceBundles/kaiPrefs.bundle"];

UIImage *profilePicture;
profilePicture = [UIImage imageWithContentsOfFile:[bundle pathForResource:specifier.properties[@"image"] ofType:@"jpg"]];
UIImageView *profilePictureView = [[UIImageView alloc] initWithImage:profilePicture];
[profilePictureView.layer setMasksToBounds:YES];
[profilePictureView.layer setCornerRadius:20];
[profilePictureView setFrame:CGRectMake(15, 15, 40, 40)];

[self addSubview:User];
[self addSubview:Description];
[self addSubview:profilePictureView];
}

return self;
}

@end

+ 24
- 0
kaiprefs/Resources/Info.plist View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>kaiPrefs</string>
<key>CFBundleIdentifier</key>
<string>com.burritoz.kaiprefs</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSPrincipalClass</key>
<string>KAIRootListController</string>
</dict>
</plist>

+ 593
- 0
kaiprefs/Resources/Root.plist View File

@@ -0,0 +1,593 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>headerCellClass</key>
<string>KaiHeaderCell</string>
<key>height</key>
<integer>175</integer>
</dict>
<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>cellClass</key>
<string>Thomz_TwitterCell</string>
<key>user</key>
<string>Burrit0z</string>
<key>description</key>
<string>Developer</string>
<key>height</key>
<integer>70</integer>
<key>image</key>
<string>burritoz</string>
<key>action</key>
<string>followMeBurritoz</string>
</dict>
<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>cellClass</key>
<string>Thomz_TwitterCell</string>
<key>user</key>
<string>Thomz</string>
<key>description</key>
<string>Prefs banner</string>
<key>height</key>
<integer>70</integer>
<key>image</key>
<string>thomz</string>
<key>action</key>
<string>followMeOnTwitterThomz</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>enabled</string>
<key>label</key>
<string>Enable</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.respringneeded</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>hideChargingAnimation</string>
<key>label</key>
<string>Hide CoverSheet Charging Animations</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.respringneeded</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>removeForMedia</string>
<key>label</key>
<string>Hide when music player showing</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.respringneeded</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>footerText</key>
<string>By having the show non-charging devices option on, all devices, not only devices that are charging, will show. When the always show bluetooth devices option is on, kai will show all connected bluetooth devices if they are charging or not.</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>showAll</string>
<key>label</key>
<string>Show non-charging devices</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>showPhone</string>
<key>label</key>
<string>Show phone battery</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>showAllMinusInternal</string>
<key>label</key>
<string>Always show bluetooth devices</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>disableGlyphs</string>
<key>label</key>
<string>Hide Device Glyphs</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>hidePercent</string>
<key>label</key>
<string>Hide Percent Label</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>hideDeviceLabel</string>
<key>label</key>
<string>Hide Device Name Label</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>hideBatteryIcon</string>
<key>label</key>
<string>Hide Battery Icon</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Alignemnt Axis (Respring Required)</string>
</dict>
<dict>
<key>cell</key>
<string>PSSegmentCell</string>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>default</key>
<string>0</string>
<key>key</key>
<string>kaiAlign</string>
<key>validValues</key>
<array>
<string>0</string>
<string>1</string>
</array>
<key>validTitles</key>
<array>
<string>Vertical</string>
<string>Horizontal</string>
</array>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.respringneeded</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Style</string>
</dict>
<dict>
<key>cell</key>
<string>PSSegmentCell</string>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>default</key>
<string>1</string>
<key>key</key>
<string>bannerStyle</string>
<key>validValues</key>
<array>
<string>1</string>
<string>2</string>
<string>3</string>
</array>
<key>validTitles</key>
<array>
<string>Automatic</string>
<string>Dark</string>
<string>Light</string>
</array>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Text Color</string>
</dict>
<dict>
<key>cell</key>
<string>PSSegmentCell</string>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>default</key>
<string>0</string>
<key>key</key>
<string>textColor</string>
<key>validValues</key>
<array>
<string>0</string>
<string>1</string>
<string>2</string>
</array>
<key>validTitles</key>
<array>
<string>Adaptive</string>
<string>White</string>
<string>Black</string>
</array>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Alignment</string>
</dict>
<dict>
<key>cell</key>
<string>PSSegmentCell</string>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>default</key>
<string>2</string>
<key>key</key>
<string>bannerAlign</string>
<key>validValues</key>
<array>
<string>1</string>
<string>2</string>
<string>3</string>
</array>
<key>validTitles</key>
<array>
<string>Left</string>
<string>Center</string>
<string>Right</string>
</array>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>reAlignSelf</string>
<key>label</key>
<string>Realign after refreshing (horizontal)</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>belowMusic</string>
<key>label</key>
<string>Show kai Below Music</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Height (80)</string>
</dict>
<dict>
<key>default</key>
<real>80</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>20</real>
<key>max</key>
<real>400</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>bannerHeight</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Width Adjustment (0)</string>
</dict>
<dict>
<key>default</key>
<real>0</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>-400</real>
<key>max</key>
<real>400</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>bannerWidthFactor</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Spacing - Vertical (5)</string>
<key>footerText</key>
<string>This is the spacing between cells vertically</string>
</dict>
<dict>
<key>default</key>
<real>5</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>-100.0</real>
<key>max</key>
<real>300</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>spacing</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>default</key>
<false/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>extraPaddingAfter</string>
<key>label</key>
<string>Add padding after kai</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Spacing - Horizontal (8)</string>
<key>footerText</key>
<string>This is the spacing between cells horizontally, for kai's horizontal mode.</string>
</dict>
<dict>
<key>default</key>
<real>8</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>-100.0</real>
<key>max</key>
<real>300</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>spacingHorizontal</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Horizontal Axis Manual Offset (0)</string>
</dict>
<dict>
<key>default</key>
<real>0</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>-300.0</real>
<key>max</key>
<real>300</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>horizontalOffset</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Glyph Size (30)</string>
</dict>
<dict>
<key>default</key>
<real>30</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>10</real>
<key>max</key>
<real>60</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>glyphSize</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Corner Radius (13)</string>
</dict>
<dict>
<key>default</key>
<real>13</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>0.0</real>
<key>max</key>
<real>100</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>cornerRadius</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>Banner Blur Alpha (1)</string>
</dict>
<dict>
<key>default</key>
<real>1</real>
<key>cell</key>
<string>PSSliderCell</string>
<key>min</key>
<real>0.0</real>
<key>max</key>
<real>1</real>
<key>isSegmented</key>
<false/>
<key>showValue</key>
<true/>
<key>defaults</key>
<string>com.burritoz.kaiprefs</string>
<key>key</key>
<string>bannerAlpha</string>
<key>PostNotification</key>
<string>com.burritoz.kaiprefs.apply</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
</dict>
<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>action</key>
<string>resetPrefs:</string>
<key>label</key>
<string>Reset All Preferences</string>
</dict>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
</dict>
</array>
<key>title</key>
<string>kai</string>
</dict>
</plist>

BIN
kaiprefs/Resources/burritoz.jpg View File

Before After
Width: 400  |  Height: 400  |  Size: 13KB

BIN
kaiprefs/Resources/icon.png View File

Before After
Width: 27  |  Height: 27  |  Size: 1.5KB

BIN
kaiprefs/Resources/icon@2x.png View File

Before After
Width: 54  |  Height: 54  |  Size: 3.4KB

BIN
kaiprefs/Resources/icon@3x.png View File

Before After
Width: 87  |  Height: 87  |  Size: 8.3KB

BIN
kaiprefs/Resources/iconFullSize.png View File

Before After
Width: 500  |  Height: 500  |  Size: 120KB

BIN
kaiprefs/Resources/thomz.jpg View File

Before After
Width: 399  |  Height: 399  |  Size: 25KB

+ 21
- 0
kaiprefs/entry.plist View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>entry</key>
<dict>
<key>bundle</key>
<string>kaiPrefs</string>
<key>cell</key>
<string>PSLinkCell</string>
<key>detail</key>
<string>KAIRootListController</string>
<key>icon</key>
<string>icon.png</string>
<key>isController</key>
<true/>
<key>label</key>
<string>kai</string>
</dict>
</dict>
</plist>

Loading…
Cancel
Save