-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added version 1.0 of BTManager+ to git
- Loading branch information
0 parents
commit 3a85e27
Showing
30 changed files
with
2,042 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.theos/ | ||
packages/ | ||
.vscode/ | ||
*.temp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ Filter = { Bundles = ( "com.apple.springboard" ); }; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
@class NSString; | ||
|
||
@class NSMutableArray; | ||
|
||
@interface SBBluetoothController : NSObject { | ||
|
||
NSMutableArray* _devices; | ||
BOOL _tetheringConnected; | ||
|
||
} | ||
+(id)sharedInstance; | ||
-(void)startWatchingForDevices; | ||
-(BOOL)tetheringConnected; | ||
-(void)stopWatchingForDevices; | ||
-(id)firstBTDeviceToReportBatteryLevel; | ||
-(void)iapDeviceChanged:(id)arg1 ; | ||
-(void)connectionChanged:(id)arg1 ; | ||
-(void)addDeviceNotification:(id)arg1 ; | ||
-(void)removeDeviceNotification:(id)arg1 ; | ||
-(void)batteryChanged:(id)arg1 ; | ||
-(void)bluetoothDeviceInitiatedVoiceControl:(id)arg1 ; | ||
-(void)bluetoothDeviceEndedVoiceControl:(id)arg1 ; | ||
-(void)noteDevicesChanged; | ||
-(void)updateTetheringConnected; | ||
-(void)updateBattery; | ||
-(BOOL)canReportBatteryLevel; | ||
-(id)deviceForAudioRoute:(id)arg1 ; | ||
-(void)dealloc; | ||
-(int)batteryLevel; | ||
@end | ||
|
||
@interface BluetoothDevice : NSObject | ||
-(id)name; | ||
-(id)address; | ||
@end | ||
|
||
@interface BTSDevice : NSObject | ||
-(id)name; | ||
-(id)identifier; | ||
@end | ||
|
||
@interface BTSDeviceClassic : BTSDevice | ||
@end | ||
|
||
@interface SBApplication: NSObject | ||
@property (nonatomic,readonly) NSString * bundleIdentifier; | ||
@property (nonatomic,readonly) NSString * displayName; | ||
@end | ||
|
||
@interface MPAVRoute : NSObject | ||
@property (nonatomic,readonly) NSString * routeUID; | ||
-(id)routeName; | ||
-(BOOL)isPickedOnPairedDevice; | ||
-(BOOL)isSplitterCapable; | ||
-(BOOL)isShareableRoute; | ||
-(BOOL)isAirpodsRoute; | ||
-(BOOL)isSplitRoute; | ||
@end | ||
|
||
@interface MPAVRoutingController : NSObject | ||
@property (nonatomic, readonly, copy) NSArray *availableRoutes; | ||
@property (nonatomic, readonly) MPAVRoute* pickedRoute; | ||
-(bool)pickRoute:(id)arg1; | ||
-(id)availableRoutes; | ||
@end | ||
|
||
@interface SBMediaController : NSObject { | ||
MPAVRoutingController* _routingController; | ||
} | ||
@property (nonatomic,assign,readonly) SBApplication * nowPlayingApplication; | ||
+(id)sharedInstance; | ||
-(void)routingControllerAvailableRoutesDidChange:(id)arg1; | ||
-(void)_mediaRemoteNowPlayingApplicationDidChange:(id)arg1; | ||
-(BOOL)isPaused; | ||
-(BOOL)isPlaying; | ||
-(BOOL)togglePlayPauseForEventSource:(NSInteger)arg1; | ||
@end | ||
|
||
@interface MRAVConcreteOutputDevice : NSObject | ||
-(id)name; | ||
@end | ||
|
||
@interface MPAVOutputDeviceRoute : MPAVRoute | ||
-(bool)isAppleTVRoute; | ||
-(id)routeUID; | ||
// -(id)routeName; | ||
// -(BOOL)isPickedOnPairedDevice; | ||
@end | ||
|
||
@interface MPAVRoutingViewControllerDelegate | ||
-(void)routingViewController:(id)arg1 didPickRoute:(id)arg2; | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
#import <BTManager.h> | ||
|
||
BOOL enabled = NO; | ||
NSDictionary* deviceOrder; | ||
NSDictionary* deviceSettings; | ||
SBMediaController* mediaController; | ||
MPAVRoutingController* routeController; | ||
NSMutableDictionary* routes; | ||
MPAVRoute* activeRoute; | ||
MPAVRoute* previousRoute; | ||
|
||
SBApplication* nowPlayingApplication; | ||
|
||
void updateAvailableRoutes() { | ||
mediaController = [%c(SBMediaController) sharedInstance]; | ||
routeController = [mediaController valueForKey:@"_routingController"]; | ||
NSArray* availableRoutes = [routeController availableRoutes]; | ||
[routes removeAllObjects]; | ||
for (MPAVOutputDeviceRoute* route in availableRoutes) { | ||
if ([route isAppleTVRoute]) { | ||
continue; | ||
} | ||
NSString* uid = route.routeUID; | ||
[routes setObject:route forKey:uid]; | ||
} | ||
activeRoute = [routeController pickedRoute]; | ||
} | ||
|
||
void updateActiveRoute() { | ||
mediaController = [%c(SBMediaController) sharedInstance]; | ||
routeController = [mediaController valueForKey:@"_routingController"]; | ||
for (NSString* routeAddress in deviceOrder[nowPlayingApplication.bundleIdentifier] ?: deviceOrder[@"SYSTEM"]) { | ||
for (NSString* routeUID in routes) { | ||
if ([routeUID containsString:routeAddress]) { | ||
activeRoute = routes[routeUID]; | ||
[routeController pickRoute:routes[routeUID]]; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
for (NSString* routeUID in routes) { | ||
if ([routeUID isEqual:@"Speaker"]) { | ||
[routeController pickRoute:routes[routeUID]]; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
static void loadPrefs() { | ||
NSMutableDictionary *settings = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.beckettobrien.btmanagerprefs.plist"]; | ||
|
||
enabled = [[settings objectForKey:@"enabled"] boolValue]; | ||
deviceOrder = [settings objectForKey:@"deviceOrder"] ?: [[NSDictionary alloc] init]; | ||
deviceSettings = [settings objectForKey:@"deviceSettings"] ?: [[NSDictionary alloc] init]; | ||
} | ||
|
||
%hook SBBluetoothController | ||
|
||
-(id)deviceForAudioRoute:(id)arg1 { | ||
id out = %orig; | ||
if (enabled == 0) { | ||
return out; | ||
} | ||
|
||
if ([arg1[@"RouteUID"] isEqual:activeRoute.routeUID]) { | ||
return out; | ||
} | ||
|
||
previousRoute = activeRoute; | ||
|
||
updateAvailableRoutes(); | ||
|
||
NSString* mac; | ||
for (NSString* routeAddress in deviceSettings) { | ||
if ([arg1[@"RouteUID"] containsString:routeAddress]) { | ||
mac = routeAddress; | ||
} | ||
} | ||
if (!mac) { | ||
return out; | ||
} | ||
if ([deviceSettings[mac][@"switchConfirm"] isEqual:@0]) { | ||
return out; | ||
} | ||
|
||
if ([[%c(SBMediaController) sharedInstance] isPlaying]) { | ||
[[%c(SBMediaController) sharedInstance] togglePlayPauseForEventSource:0]; | ||
} | ||
|
||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Switch to %@", arg1[@"RouteName"]] message: | ||
@"Confirm to switch to this device, cancel to return to original device if available or switch to speakers and stop." | ||
preferredStyle:UIAlertControllerStyleAlert]; | ||
|
||
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Confirm" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
if ([[%c(SBMediaController) sharedInstance] isPaused]) { | ||
[[%c(SBMediaController) sharedInstance] togglePlayPauseForEventSource:0]; | ||
} | ||
}]; | ||
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { | ||
updateAvailableRoutes(); | ||
MPAVOutputDeviceRoute* route = routes[previousRoute.routeUID] ?: routes[@"Speaker"]; | ||
[[[%c(SBMediaController) sharedInstance] valueForKey:@"_routingController"] pickRoute:route]; | ||
if (![route.routeUID isEqual:@"Speaker"]) { | ||
[[%c(SBMediaController) sharedInstance] togglePlayPauseForEventSource:0]; | ||
} | ||
}]; | ||
[alert addAction:cancel]; | ||
[alert addAction:ok]; | ||
|
||
UIWindow* foundWindow = nil; | ||
for (UIWindow* window in [[UIApplication sharedApplication] windows]) { | ||
if (window.isKeyWindow) { | ||
foundWindow = window; | ||
break; | ||
} | ||
} | ||
[foundWindow.rootViewController presentViewController:alert animated:YES completion:nil]; | ||
|
||
return out; | ||
} | ||
|
||
%end | ||
|
||
%hook SBMediaController | ||
-(void)routingControllerAvailableRoutesDidChange:(id)arg1 { | ||
%orig; | ||
|
||
if (enabled == 0) { | ||
return; | ||
} | ||
|
||
updateAvailableRoutes(); | ||
if (!routes[activeRoute.routeUID]) { | ||
updateActiveRoute(); | ||
} | ||
} | ||
-(void)_mediaRemoteNowPlayingApplicationDidChange:(id)arg1 { | ||
%orig; | ||
if (enabled == 0) { | ||
return; | ||
} | ||
|
||
nowPlayingApplication = [self nowPlayingApplication]; | ||
updateAvailableRoutes(); | ||
updateActiveRoute(); | ||
} | ||
|
||
%end | ||
|
||
%hook MPAVRoutingController | ||
|
||
-(bool)pickRoute:(id)arg1 { | ||
updateAvailableRoutes(); | ||
return %orig; | ||
} | ||
|
||
%end | ||
|
||
%ctor { | ||
routes = [NSMutableDictionary new]; | ||
loadPrefs(); | ||
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)loadPrefs, CFSTR("com.beckettobrien.btmanagerprefs.settingschanged"), NULL, CFNotificationSuspensionBehaviorCoalesce); | ||
} |
Oops, something went wrong.