This repository has been archived by the owner on Jun 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ba624ca
Showing
61 changed files
with
1,817 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,5 @@ | ||
.DS_Store | ||
.theos | ||
packages | ||
|
||
libswiftQuartzCore.dylib |
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,13 @@ | ||
<?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>Filter</key> | ||
<dict> | ||
<key>Bundles</key> | ||
<array> | ||
<string>com.apple.springboard</string> | ||
</array> | ||
</dict> | ||
</dict> | ||
</plist> |
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,12 @@ | ||
#import <BulletinBoard/BBDataProvider.h> | ||
|
||
@class SBApplication; | ||
|
||
@interface HBCZNowPlayingBulletinProvider : BBDataProvider <BBDataProvider> | ||
|
||
+ (instancetype)sharedInstance; | ||
|
||
- (void)postBulletinForApp:(SBApplication *)app title:(NSString *)title artist:(NSString *)artist album:(NSString *)album art:(NSData *)art; | ||
- (void)clearAllBulletins; | ||
|
||
@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,196 @@ | ||
#import "HBCZNowPlayingBulletinProvider.h" | ||
#import "HBCZNowPlayingController.h" | ||
#import "HBCZPreferences.h" | ||
#import <BulletinBoard/BBAction.h> | ||
#import <BulletinBoard/BBAppearance.h> | ||
#import <BulletinBoard/BBBulletinRequest.h> | ||
#import <BulletinBoard/BBDataProviderIdentity.h> | ||
#import <BulletinBoard/BBSectionInfo.h> | ||
#import <BulletinBoard/BBSectionParameters.h> | ||
#import <BulletinBoard/BBSectionSubtypeParameters.h> | ||
#import <BulletinBoard/BBServer.h> | ||
#import <BulletinBoard/BBThumbnailSizeConstraints.h> | ||
#import <SpringBoard/SBApplication.h> | ||
|
||
static NSString *const kHBCZNowPlayingSubsectionIdentifier = @"ws.hbang.canzone.nowplayingsection"; | ||
static NSString *const kHBCZNowPlayingLockSubsectionIdentifier = @"ws.hbang.canzone.nowplayinglocksection"; | ||
static NSString *const kHBCZNowPlayingBulletinRecordIdentifier = @"ws.hbang.canzone.nowplaying"; | ||
static NSString *const kHBCZNowPlayingCategoryIdentifier = @"CanzoneNowPlayingCategory"; | ||
|
||
@interface BBSectionSubtypeParameters () | ||
|
||
@property (nonatomic, copy) NSString *alternateActionLabel; | ||
@property (nonatomic, copy) NSString *secondaryContentRemoteServiceBundleIdentifier; | ||
@property (nonatomic, copy) NSString *secondaryContentRemoteViewControllerClassName; | ||
|
||
@end | ||
|
||
@implementation HBCZNowPlayingBulletinProvider { | ||
HBCZPreferences *_preferences; | ||
|
||
UIImage *_currentArt; | ||
NSMutableSet <BBBulletinRequest *> *_sentBulletins; | ||
} | ||
|
||
#pragma mark - Singleton | ||
|
||
+ (instancetype)sharedInstance { | ||
static HBCZNowPlayingBulletinProvider *sharedInstance; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
sharedInstance = [[self alloc] init]; | ||
}); | ||
|
||
return sharedInstance; | ||
} | ||
|
||
#pragma mark - NSObject | ||
|
||
- (instancetype)init { | ||
self = [super init]; | ||
|
||
if (self) { | ||
// set up variables | ||
_preferences = [HBCZPreferences sharedInstance]; | ||
_currentArt = nil; | ||
_sentBulletins = [NSMutableSet setWithCapacity:1]; | ||
|
||
// construct our data provider identity | ||
BBDataProviderIdentity *identity = [BBDataProviderIdentity identityForDataProvider:self]; | ||
|
||
// give it our identifier and name | ||
identity.sectionIdentifier = kHBCZAppIdentifier; | ||
identity.sectionDisplayName = @"Now Playing"; | ||
|
||
// set ourself as only displaying alerts, not sounds or badges | ||
identity.defaultSectionInfo.pushSettings = BBSectionInfoPushSettingsAlerts; | ||
|
||
// make up our subsection and set them | ||
BBSectionInfo *mainSubsection = [BBSectionInfo defaultSectionInfoForType:2]; | ||
mainSubsection.sectionID = identity.sectionIdentifier; | ||
mainSubsection.subsectionID = kHBCZNowPlayingSubsectionIdentifier; | ||
mainSubsection.allowsAddingToLockScreenWhenUnlocked = YES; | ||
mainSubsection.allowsAutomaticRemovalFromLockScreen = NO; | ||
mainSubsection.prioritizeAtTopOfLockScreen = YES; | ||
|
||
identity.defaultSubsectionInfos = @[ mainSubsection ]; | ||
|
||
// construct our default subtype parameters | ||
BBSectionSubtypeParameters *subtypeParameters = identity.sectionParameters.defaultSubtypeParameters; | ||
subtypeParameters.secondaryContentRemoteServiceBundleIdentifier = @"ws.hbang.canzone.app.notificationcontent"; | ||
subtypeParameters.secondaryContentRemoteViewControllerClassName = @"NotificationViewController"; | ||
|
||
// construct our “replace lock media controls” subtype | ||
BBSectionSubtypeParameters *lockParameters = [[BBSectionSubtypeParameters alloc] init]; | ||
|
||
identity.sectionParameters.allSubtypeParameters = [@{ | ||
@2: lockParameters | ||
} mutableCopy]; | ||
|
||
self.identity = identity; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
#pragma mark - Post bulletin | ||
|
||
- (void)postBulletinForApp:(SBApplication *)app title:(NSString *)title artist:(NSString *)artist album:(NSString *)album art:(NSData *)art { | ||
// if we need to pull the previous bulletins, do that first | ||
if (!_preferences.nowPlayingKeepBulletins) { | ||
[self clearAllBulletins]; | ||
} | ||
|
||
// construct our bulletin | ||
BBBulletinRequest *bulletin = [[BBBulletinRequest alloc] init]; | ||
|
||
// set the basic stuff | ||
bulletin.bulletinID = [NSUUID UUID].UUIDString; | ||
bulletin.sectionID = kHBCZAppIdentifier; | ||
// bulletin.categoryID = kHBCZNowPlayingCategoryIdentifier; | ||
|
||
// set the record id based on the keep all bulletins setting | ||
bulletin.recordID = bulletin.bulletinID; | ||
|
||
// set the subsection based on the hide music controls setting | ||
bulletin.subsectionIDs = [NSSet setWithObject:_preferences.hideLockMusicControls ? kHBCZNowPlayingLockSubsectionIdentifier : kHBCZNowPlayingSubsectionIdentifier]; | ||
|
||
// set the text fields | ||
bulletin.title = title; | ||
|
||
// if we have an album and artist, have them both separated by newline. otherwise, use whichever | ||
// of the two exists (or none!) | ||
if (album && artist) { | ||
bulletin.message = [NSString stringWithFormat:@"%@\n%@", album, artist]; | ||
} else { | ||
bulletin.message = album ?: artist; | ||
} | ||
|
||
// set all the rest | ||
bulletin.date = [NSDate date]; | ||
bulletin.lastInterruptDate = bulletin.date; | ||
bulletin.turnsOnDisplay = _preferences.nowPlayingWakeWhenLocked; | ||
bulletin.primaryAttachmentType = BBAttachmentMetadataTypeImage; | ||
|
||
// set a callback to open the app | ||
bulletin.defaultAction = [BBAction actionWithLaunchBundleID:app.bundleIdentifier callblock:nil]; | ||
|
||
// on apple watch, launch NanoNowPlaying | ||
// TODO: this doesn’t work :( maybe we’ll have to go back to the phone, then have the phone tell | ||
// the watch to open the app | ||
BBAction *watchAction = [BBAction actionWithAppearance:[BBAppearance appearanceWithTitle:@"Open"]]; | ||
watchAction.identifier = @"open-on-watch"; | ||
watchAction.callblock = ^{ HBLogWarn(@"watch out bitchezz"); }; | ||
|
||
// set all our supplementary actions | ||
bulletin.supplementaryActionsByLayout = @{ | ||
@1: @[ watchAction ] | ||
}; | ||
|
||
// get a UIImage of the art and hold onto it | ||
_currentArt = [[UIImage alloc] initWithData:art]; | ||
|
||
// send it! | ||
BBDataProviderAddBulletin(self, bulletin); | ||
[_sentBulletins addObject:bulletin]; | ||
} | ||
|
||
- (void)clearAllBulletins { | ||
// loop and remove all notifications we’ve sent | ||
for (BBBulletinRequest *bulletin in _sentBulletins) { | ||
BBDataProviderWithdrawBulletinsWithRecordID(self, bulletin.recordID); | ||
} | ||
|
||
// empty the set | ||
[_sentBulletins removeAllObjects]; | ||
} | ||
|
||
#pragma mark - BBDataProvider | ||
|
||
- (NSArray *)sortDescriptors { | ||
return @[ [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO] ]; | ||
} | ||
|
||
- (NSData *)attachmentPNGDataForRecordID:(NSString *)recordID sizeConstraints:(BBThumbnailSizeConstraints *)constraints { | ||
// return the current item’s album art. this is only called once when the bulletin is being | ||
// prepared for display; after that it’s stored, well, somewhere | ||
|
||
// no art? nothing for us to do | ||
if (!_currentArt) { | ||
return nil; | ||
} | ||
|
||
// determine the size to use | ||
CGSize size = [constraints sizeFromAspectRatio:1]; | ||
|
||
// render at the new size | ||
UIGraphicsBeginImageContextWithOptions(size, NO, 0); | ||
[_currentArt drawInRect:(CGRect){ CGPointZero, size }]; | ||
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); | ||
UIGraphicsEndImageContext(); | ||
|
||
// turn it back into a png and return it | ||
return UIImagePNGRepresentation(newImage); | ||
} | ||
|
||
@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,7 @@ | ||
static NSString *const kHBCZAppIdentifier = @"ws.hbang.canzone.app"; | ||
|
||
@interface HBCZNowPlayingController : NSObject | ||
|
||
+ (instancetype)sharedInstance; | ||
|
||
@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,114 @@ | ||
#import "HBCZNowPlayingController.h" | ||
#import "HBCZNowPlayingBulletinProvider.h" | ||
#import "HBCZPreferences.h" | ||
#import <MediaRemote/MediaRemote.h> | ||
#import <SpringBoard/SBApplication.h> | ||
#import <SpringBoard/SBMediaController.h> | ||
#import <SpringBoard/SpringBoard.h> | ||
#import <TypeStatusPlusProvider/HBTSPlusProvider.h> | ||
#import <TypeStatusPlusProvider/HBTSPlusProviderController.h> | ||
|
||
@implementation HBCZNowPlayingController { | ||
HBCZPreferences *_preferences; | ||
HBCZNowPlayingBulletinProvider *_bulletinProvider; | ||
|
||
NSString *_lastSongIdentifier; | ||
} | ||
|
||
+ (instancetype)sharedInstance { | ||
static HBCZNowPlayingController *sharedInstance; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
sharedInstance = [[self alloc] init]; | ||
}); | ||
|
||
return sharedInstance; | ||
} | ||
|
||
#pragma mark - NSObject | ||
|
||
- (instancetype)init { | ||
self = [super init]; | ||
|
||
if (self) { | ||
// set up variables | ||
_preferences = [HBCZPreferences sharedInstance]; | ||
_bulletinProvider = [HBCZNowPlayingBulletinProvider sharedInstance]; | ||
_lastSongIdentifier = @""; | ||
|
||
// listen for the now playing change notification | ||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_mediaInfoDidChange:) name:(__bridge NSString *)kMRMediaRemoteNowPlayingInfoDidChangeNotification object:nil]; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
#pragma mark - Notification callbacks | ||
|
||
- (void)_mediaInfoDidChange:(NSNotification *)nsNotification { | ||
MRMediaRemoteGetNowPlayingInfo(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef result) { | ||
// no really, why would you torture yourself and your clients by designing an api that uses | ||
// CF objects? | ||
NSDictionary *dictionary = (__bridge NSDictionary *)result; | ||
NSString *title = dictionary[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoTitle]; | ||
NSString *artist = dictionary[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoArtist]; | ||
NSString *album = dictionary[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoAlbum]; | ||
NSData *art = dictionary[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoArtworkData]; | ||
|
||
// get the now playing app | ||
SBApplication *nowPlayingApp = ((SBMediaController *)[%c(SBMediaController) sharedInstance]).nowPlayingApplication; | ||
|
||
// no title or app? that’s weird. we can’t really do much without those things | ||
if (!title || !nowPlayingApp) { | ||
return; | ||
} | ||
|
||
// construct our internal identifier | ||
NSString *identifier = [NSString stringWithFormat:@"title = %@, artist = %@, album = %@", title, artist, album]; | ||
|
||
// have we just shown one for this? ignore it | ||
if ([_lastSongIdentifier isEqualToString:identifier]) { | ||
return; | ||
} | ||
|
||
// store the identifier | ||
_lastSongIdentifier = identifier; | ||
|
||
// get the frontmost app | ||
SBApplication *frontmostApp = ((SpringBoard *)[UIApplication sharedApplication])._accessibilityFrontMostApplication; | ||
|
||
// if the now playing provider is enabled, and typestatus plus is present | ||
if (_preferences.nowPlayingProvider && %c(HBTSPlusProviderController)) { | ||
// as long as this isn’t coming from the frontmost app | ||
if (![frontmostApp.bundleIdentifier isEqualToString:nowPlayingApp.bundleIdentifier]) { | ||
// post it as a provider notification | ||
[self _postProviderNotificationForApp:nowPlayingApp title:title artist:artist]; | ||
} | ||
} else { | ||
// else, post a bulletin | ||
[_bulletinProvider postBulletinForApp:nowPlayingApp title:title artist:artist album:album art:art]; | ||
} | ||
}); | ||
} | ||
|
||
#pragma mark - TypeStatus Provider | ||
|
||
- (void)_postProviderNotificationForApp:(SBApplication *)app title:(NSString *)title artist:(NSString *)artist { | ||
// if typestatus plus provider api isn’t available, don’t do anything | ||
if (!%c(HBTSPlusProviderController)) { | ||
return; | ||
} | ||
|
||
// construct a notification | ||
HBTSNotification *notification = [[%c(HBTSNotification) alloc] init]; | ||
notification.content = artist ? [NSString stringWithFormat:@"%@ – %@", title, artist] : title; | ||
notification.boldRange = NSMakeRange(0, title.length); | ||
notification.statusBarIconName = @"TypeStatusPlusMusic"; | ||
notification.sourceBundleID = app.bundleIdentifier; | ||
|
||
// grab our provider and show it | ||
HBTSPlusProvider *provider = [[%c(HBTSPlusProviderController) sharedInstance] providerWithAppIdentifier:kHBCZAppIdentifier]; | ||
[provider showNotification:notification]; | ||
} | ||
|
||
@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,13 @@ | ||
#import <Cephei/HBPreferences.h> | ||
|
||
@interface HBCZPreferences : NSObject | ||
|
||
+ (instancetype)sharedInstance; | ||
|
||
@property (readonly) BOOL nowPlayingKeepBulletins, nowPlayingWakeWhenLocked, hideLockMusicControls; | ||
|
||
@property (readonly) BOOL nowPlayingProvider; | ||
|
||
- (void)registerPreferenceChangeBlock:(HBPreferencesChangeCallback)callback; | ||
|
||
@end |
Oops, something went wrong.