Skip to content

Commit

Permalink
Add prefs bundle and ability to enable/disable apps
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanArbuckle committed Jan 18, 2021
1 parent 76530e2 commit 254eab8
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 4 deletions.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ after-carplayenable-stage::
cp BLACKLISTED_APPS.plist $(THEOS_STAGING_DIR)/var/mobile/Library/Preferences/com.carplayenable.blacklisted-apps.plist

after-install::
install.exec "killall -9 SpringBoard CarPlay"
install.exec "killall -9 SpringBoard CarPlay Preferences"

test::
install.exec "cycript -p SpringBoard" < tests/springboard_tests.cy
install.exec "cycript -p CarPlay" < tests/carplay_tests.cy
install.exec "cycript -p CarPlay" < tests/carplay_tests.cy

SUBPROJECTS += carplayenableprefs

include $(THEOS_MAKE_PATH)/aggregate.mk
11 changes: 11 additions & 0 deletions carplayenableprefs/CPAppListController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Preferences/PSListController.h>

@interface CPAppListController : PSViewController <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, retain) UITableView *rootTable;
@property (nonatomic, retain) NSArray *appList;
@property (nonatomic, retain) NSDictionary *cachedPreferences;

- (id)initWithAppList:(NSArray *)appList;

@end
135 changes: 135 additions & 0 deletions carplayenableprefs/CPAppListController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include "CPAppListController.h"
#include "../src/common.h"

@implementation CPAppListController

- (id)initWithAppList:(NSArray *)appList
{
if ((self = [super init]))
{
[self setTitle:@"Add or Remove Apps"];
// Sort the list by app name
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:nameDescriptor];
_appList = [appList sortedArrayUsingDescriptors:sortDescriptors];

[self reloadPreferences];

_rootTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height) style:UITableViewStyleGrouped];
[_rootTable setDelegate:self];
[_rootTable setDataSource:self];
[[self view] addSubview:_rootTable];
}

return self;
}

- (void)reloadPreferences
{
if ([[NSFileManager defaultManager] fileExistsAtPath:PREFERENCES_PLIST_PATH])
{
_cachedPreferences = [[NSDictionary alloc] initWithContentsOfFile:PREFERENCES_PLIST_PATH];
}
else {
_cachedPreferences = @{};
}
}

- (BOOL)isAppCarplayEnabled:(NSString *)identifier
{
if (_cachedPreferences && [_cachedPreferences valueForKey:@"excludedApps"])
{
NSArray *excludedIdentifiers = _cachedPreferences[@"excludedApps"];
return [excludedIdentifiers containsObject:identifier] == NO;
}

return YES;
}

- (void)setApp:(NSString *)identifier shouldBeExcluded:(BOOL)shouldExclude
{
NSMutableArray *excludedApps;
if (_cachedPreferences && [_cachedPreferences valueForKey:@"excludedApps"])
{
excludedApps = [_cachedPreferences[@"excludedApps"] mutableCopy];
}
else
{
excludedApps = [[NSMutableArray alloc] init];
}

BOOL alreadyExcluded = [excludedApps containsObject:identifier];
if (shouldExclude && !alreadyExcluded)
{
[excludedApps addObject:identifier];
}
else if (!shouldExclude && alreadyExcluded)
{
[excludedApps removeObject:identifier];
}

NSMutableDictionary *updatedPrefs = [_cachedPreferences mutableCopy];
updatedPrefs[@"excludedApps"] = excludedApps;
_cachedPreferences = updatedPrefs;
[_cachedPreferences writeToFile:PREFERENCES_PLIST_PATH atomically:NO];

// Notify CarPlay of the changes
[[objc_getClass("NSDistributedNotificationCenter") defaultCenter] postNotification:[NSNotification notificationWithName:PREFERENCES_CHANGED_NOTIFICATION object:kPrefsAppLibraryChanged]];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_appList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"com.carplay.rootcell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"com.carplay.rootcell"];
}

// Get info about the current app
NSDictionary *currentApp = _appList[indexPath.row];
NSString *appName = currentApp[@"name"];
NSString *appIdentifier = currentApp[@"bundleID"];

// Setup the cell
[[cell textLabel] setText:appName];
if ([self isAppCarplayEnabled:appIdentifier])
{
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
else
{
[cell setAccessoryType:UITableViewCellAccessoryNone];
}

return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return @"Carplay enabled apps";
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];

NSDictionary *chosenApp = _appList[indexPath.row];
NSString *appIdentifier = chosenApp[@"bundleID"];

BOOL isCurrentlyEnabled = [self isAppCarplayEnabled:appIdentifier];
[self setApp:appIdentifier shouldBeExcluded:isCurrentlyEnabled];

[self.rootTable reloadData];
}

@end
9 changes: 9 additions & 0 deletions carplayenableprefs/CRERootListController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import <Preferences/PSListController.h>
#include "CPAppListController.h"

@interface CRERootListController : PSViewController <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, retain) UITableView *rootTable;
@property (nonatomic, retain) CPAppListController *appListController;

@end
144 changes: 144 additions & 0 deletions carplayenableprefs/CRERootListController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include "CRERootListController.h"
#include "../src/common.h"

@implementation CRERootListController

- (id)initForContentSize:(CGSize)size
{
if ((self = [super init]))
{
[self setTitle:@"CarPlayEnable Preferences"];
_rootTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height) style:UITableViewStyleGrouped];
[_rootTable setDelegate:self];
[_rootTable setDataSource:self];
[[self view] addSubview:_rootTable];

// The Preference process does not possess the necessary entitlements to fetch installed apps or icon data. Springboard will handle fetching the data - this process
// will signal and receive the data via notifications
NSOperationQueue *notificationQueue = [[NSOperationQueue alloc] init];
[[objc_getClass("NSDistributedNotificationCenter") defaultCenter] addObserverForName:PREFERENCES_APP_DATA_NOTIFICATION object:kPrefsAppDataReceiving queue:notificationQueue usingBlock:^(NSNotification * _Nonnull note) {
NSDictionary *data = [note userInfo];
// Create AppList controller
_appListController = [[CPAppListController alloc] initWithAppList:data[@"appList"]];
}];

// Ask Springboard for the app data. This may have a little delay, so its being fetched before the user opens the App List controller
[[objc_getClass("NSDistributedNotificationCenter") defaultCenter] postNotification:[NSNotification notificationWithName:PREFERENCES_APP_DATA_NOTIFICATION object:kPrefsAppDataRequesting]];
}

return self;
}

-(void)viewDidLoad
{
[[self view] setBackgroundColor:[UIColor redColor]];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section)
{
case 0:
return 1;
case 1:
return 3;
default:
break;
}
return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"com.carplay.rootcell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"com.carplay.rootcell"];
}

if (indexPath.section == 0)
{
if (indexPath.row == 0)
{
[[cell textLabel] setText:@"Add/Remove Apps"];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
}
else if (indexPath.section == 1)
{
if (indexPath.row == 0)
{
[[cell textLabel] setText:@"Left"];
}
else if (indexPath.row == 1)
{
[[cell textLabel] setText:@"Right"];
}
else if (indexPath.row == 2)
{
[[cell textLabel] setText:@"Auto"];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
}

return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
switch (section)
{
case 0:
return @"CarPlay Dashboard";
case 1:
return @"Dock Alignment";
default:
break;
}
return @"";
}

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
switch (section)
{
case 0:
//return @"";
case 1:
return @"Created by Ethan Arbuckle";
default:
break;
}
return @"";
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
{
case 0:
{
if (!_appListController)
{
// Have not received app data from SpringBoard yet..
break;
}
// Push to the app list controller
[[self navigationController] pushViewController:_appListController animated:YES];
break;
}
case 1:
break;
default:
break;
}

[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

@end
18 changes: 18 additions & 0 deletions carplayenableprefs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
ARCHS = arm64
TARGET = iphone:clang:13.5.1:13.5.1

include $(THEOS)/makefiles/common.mk

BUNDLE_NAME = carplayenableprefs

carplayenableprefs_FILES = $(wildcard *.m)
carplayenableprefs_INSTALL_PATH = /Library/PreferenceBundles
carplayenableprefs_FRAMEWORKS = UIKit
carplayenableprefs_PRIVATE_FRAMEWORKS = Preferences
carplayenableprefs_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/bundle.mk

internal-stage::
$(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END)
$(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/carplayenableprefs.plist$(ECHO_END)
24 changes: 24 additions & 0 deletions carplayenableprefs/Resources/Info.plist
Original file line number Diff line number Diff line change
@@ -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>carplayenableprefs</string>
<key>CFBundleIdentifier</key>
<string>com.ethanarbuckle.carplayprefs</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>CRERootListController</string>
</dict>
</plist>
21 changes: 21 additions & 0 deletions carplayenableprefs/entry.plist
Original file line number Diff line number Diff line change
@@ -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>carplayenableprefs</string>
<key>cell</key>
<string>PSLinkCell</string>
<key>detail</key>
<string>CRERootListController</string>
<key>icon</key>
<string>icon.png</string>
<key>isController</key>
<true/>
<key>label</key>
<string>CarPlayEnable</string>
</dict>
</dict>
</plist>
Loading

0 comments on commit 254eab8

Please sign in to comment.