Skip to content

Commit

Permalink
Hide the home grabber bar on carplay scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanArbuckle committed Mar 31, 2024
1 parent 2bbc498 commit 25a6d84
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 7 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ after-carplayenable-stage::
cp postinst_postrm $(THEOS_STAGING_DIR)/DEBIAN/postinst
cp postinst_postrm $(THEOS_STAGING_DIR)/DEBIAN/postrm
chmod +x $(THEOS_STAGING_DIR)/DEBIAN/post*
find $(THEOS_STAGING_DIR) -name ".DS_Store" -delete

after-install::
install.exec "killall -9 SpringBoard CarPlay Preferences"
Expand Down
60 changes: 53 additions & 7 deletions src/hooks/SpringBoard.xm
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ When an app icon is tapped on the Carplay dashboard

// Launch the requested app
liveCarplayWindow = [[CRCarPlayWindow alloc] initWithBundleIdentifier:identifier];
objc_setAssociatedObject(self, &kPropertyKey_liveCarplayWindow, liveCarplayWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, &kPropertyKey_liveCarplayWindow, liveCarplayWindow, OBJC_ASSOCIATION_RETAIN);
}
@catch (NSException *exception)
{
Expand All @@ -89,7 +89,7 @@ Invoked when SpringBoard finishes launching
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(carplayIsConnectedChanged) name:@"CarPlayIsConnectedDidChange" object:nil];

NSMutableArray *appIdentifiersToIgnoreLockAssertions = [[NSMutableArray alloc] init];
objc_setAssociatedObject(self, &kPropertyKey_lockAssertionIdentifiers, appIdentifiersToIgnoreLockAssertions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, &kPropertyKey_lockAssertionIdentifiers, appIdentifiersToIgnoreLockAssertions, OBJC_ASSOCIATION_RETAIN);

%orig;

Expand Down Expand Up @@ -183,7 +183,7 @@ happen while the device is in a faceup/facedown orientation.
Called when something is trying to change a scene's settings (including sending it to background/foreground).
Use this to prevent the App from going to sleep when other applications are launched on the main screen.
*/
- (void)_updateSettings:(id)arg1 withTransitionContext:(id)arg2 completion:(void *)arg3
- (void)updateSettings:(id)arg1 withTransitionContext:(id)arg2 completion:(id)arg3
{
LOG_LIFECYCLE_EVENT;
id sceneClient = objcInvoke(self, @"client");
Expand Down Expand Up @@ -259,6 +259,29 @@ Is this a main-screen scene view for an application that is being hosted on the
return NO;
}

/*
* Determine if this sceneview is what is being displayed on the carplay screen
*/
%new
- (BOOL)isHostingContentOnCarplayScreen {
LOG_LIFECYCLE_EVENT;

id liveCarplayWindow = objcInvoke([UIApplication sharedApplication], @"liveCarplayWindow");
if (liveCarplayWindow != nil)
{
id liveAppViewController = [liveCarplayWindow appViewController];
id carplaySceneHandle = objcInvoke(liveAppViewController, @"sceneHandle");
if ([carplaySceneHandle isEqual:objcInvoke(self, @"sceneHandle")])
{
id carplayAppViewController = getIvar(liveAppViewController, @"_deviceAppViewController");
id carplayAppSceneView = objcInvoke(carplayAppViewController, @"_sceneView");
return carplayAppSceneView && [self isEqual:carplayAppSceneView];
}
}

return NO;
}

- (void)layoutSubviews
{
%orig;
Expand All @@ -277,13 +300,15 @@ Is this a main-screen scene view for an application that is being hosted on the
// The placeholder view will try to match the orientation of the application, which if running on Carplay may not match the orientation of the main screen.
// Force the UI to match the main screens orientation. This will end up calling -layoutSubviews again
objcInvoke(self, @"rotateToDeviceOrientation");

UIView *backgroundView = objcInvoke(self, @"backgroundView");
int deviceOrientation = [[UIDevice currentDevice] orientation];
if (backgroundView == nil) {
return;
}

// Draw the Carplay placeholder UI, but only if this view is being layed out in the correct orientation.
// It is expected that this method will be called at least once while the view is in the wrong orientation
BOOL bgIsLandscape = [backgroundView frame].size.width > [backgroundView frame].size.height;
BOOL deviceIsLandscape = UIInterfaceOrientationIsLandscape(deviceOrientation);
BOOL deviceIsLandscape = UIInterfaceOrientationIsLandscape([[UIDevice currentDevice] orientation]);
if (bgIsLandscape == deviceIsLandscape)
{
// Orientation expectation satisfied
Expand All @@ -292,6 +317,22 @@ Is this a main-screen scene view for an application that is being hosted on the
}
}

- (id)homeGrabberView {
LOG_LIFECYCLE_EVENT;

id homeGrabberView = %orig;
if (!homeGrabberView) {
return homeGrabberView;
}

if (objcInvokeT(self, @"isHostingContentOnCarplayScreen", BOOL)) {
objcInvoke_3(homeGrabberView, @"setHidden:forReason:withAnimationSettings:", YES, @"CarPlay", nil);
objcInvoke_1(homeGrabberView, @"setUserInteractionEnabled:", NO);
}

return homeGrabberView;
}

/*
This scene view (and its background view, which the custom ui is drawn on) rotates automatically with the app's orientation changes.
Because the app's orientation may not match the device's real orientation, this view may be layed out incorrectly for the main screen.
Expand Down Expand Up @@ -334,6 +375,11 @@ the carplay screen, the mainscreen will show a blurred background with a label.
// The background view may have already been drawn on. Use an associated object to determine if its already been handled for this orientation
// If it was drawn for a different orientation, start fresh
UIView *backgroundView = objcInvoke(self, @"backgroundView");
if (backgroundView == nil) {
NSLog(@"On-device app scene for a carplay-hosted app has a nil background view");
return;
}

id _drawnForOrientation = objc_getAssociatedObject(backgroundView, &kPropertyKey_didDrawPlaceholder);
int drawnForOrientation = (_drawnForOrientation) ? [_drawnForOrientation intValue] : -1;
if (drawnForOrientation != deviceOrientation)
Expand All @@ -351,7 +397,7 @@ the carplay screen, the mainscreen will show a blurred background with a label.
}

// Set associated object to avoid redrawing if no orientation changes have happened
objc_setAssociatedObject(backgroundView, &kPropertyKey_didDrawPlaceholder, @(deviceOrientation), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(backgroundView, &kPropertyKey_didDrawPlaceholder, @(deviceOrientation), OBJC_ASSOCIATION_RETAIN);

// [[UIScreen mainscreen] bounds] may not be using the correct orientation. Get screen bounds for explicit orientation
CGRect screenBounds = ((CGRect (*)(id, SEL, int))objc_msgSend)([UIScreen mainScreen], NSSelectorFromString(@"boundsForOrientation:"), deviceOrientation);
Expand Down

0 comments on commit 25a6d84

Please sign in to comment.