├── .gitattributes
├── NowPlayingIconXVI.plist
├── packages
└── com.nahtedetihw.nowplayingiconxvi_1.0_iphoneos-arm.deb
├── control
├── Makefile
├── ReadMe.md
├── NowPlayingIconXVI.h
└── NowPlayingIconXVI.xm
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/NowPlayingIconXVI.plist:
--------------------------------------------------------------------------------
1 | {
2 | Filter = {
3 | Bundles = (
4 | "com.apple.springboard",
5 | );
6 | };
7 | }
--------------------------------------------------------------------------------
/packages/com.nahtedetihw.nowplayingiconxvi_1.0_iphoneos-arm.deb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nahtedetihw/NowPlayingIconXVI/HEAD/packages/com.nahtedetihw.nowplayingiconxvi_1.0_iphoneos-arm.deb
--------------------------------------------------------------------------------
/control:
--------------------------------------------------------------------------------
1 | Package: com.nahtedetihw.nowplayingiconxvi
2 | Name: NowPlayingIconXVI
3 | Version: 1.0
4 | Architecture: iphoneos-arm
5 | Description: Change your now playing app icon to the album artwork of currently playing music!
6 | Maintainer: Ethan Whited
7 | Author: Ethan Whited
8 | Section: Tweaks
9 | Depends: mobilesubstrate
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TARGET := iphone:clang:latest:13.0
2 | INSTALL_TARGET_PROCESSES = SpringBoard
3 | ARCHS = arm64 arm64e
4 | SYSROOT = $(THEOS)/sdks/iPhoneOS14.2.sdk
5 | PREFIX=$(THEOS)/toolchain/Xcode.xctoolchain/usr/bin/
6 | DEBUG = 0
7 | FINALPACKAGE = 1
8 |
9 | include $(THEOS)/makefiles/common.mk
10 |
11 | TWEAK_NAME = NowPlayingIconXVI
12 |
13 | NowPlayingIconXVI_FILES = NowPlayingIconXVI.xm
14 | NowPlayingIconXVI_CFLAGS = -fobjc-arc
15 | NowPlayingIconXVI_PRIVATE_FRAMEWORKS = MediaRemote
16 |
17 | include $(THEOS_MAKE_PATH)/tweak.mk
18 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | NowPlayingIconXVI
4 |
5 |
6 | 
7 |
8 | ### Change your now playing app icon to the album artwork of currently playing music!
9 | * Tested on Apple Music, Pandora, Spotify
10 |
11 | * No options to configure, should work on iOS 13 - iOS 16
12 |
13 |
14 | Join my Discord today to report any issues: https://discord.gg/64kVRNzKnF
15 |
16 | ### Source Code [https://github.com/nahtedetihw/NowPlayingIconXVI](https://github.com/nahtedetihw/NowPlayingIconXVI)
17 |
18 |
19 | ### Follow
20 |
21 | [Twitter](https://twitter.com/ethanwhited) - follow me for more up to date info, or ask me anything.
22 |
23 | [Reddit](https://www.reddit.com/user/Nahtedetihw) - reach out if you have any questions.
24 |
25 | [Email](mailto:ethanwhited2208@gmail.com) - open to any questions or concerns.
26 |
27 | [Donate](https://paypal.me/nahtdetihw) - buy me a coffee if you like my work.
28 |
29 | [Repo](https://havoc.app) - Repo containing NowPlayingIconXVI
30 |
--------------------------------------------------------------------------------
/NowPlayingIconXVI.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface __NSSingleObjectArrayI : NSArray
4 | @end
5 |
6 | @interface SBIcon : NSObject
7 | -(NSString *)applicationBundleID;
8 | -(id)application;
9 | -(id)parentFolderIcon;
10 | @end
11 |
12 | @interface SBIconView : UIView
13 | @end
14 |
15 | @interface SBIconImageView : UIView
16 | -(SBIcon *)icon;
17 | @end
18 |
19 | @interface SBIconImageCrossfadeView : UIView
20 | @end
21 |
22 | @interface SBApplication : NSObject
23 | @property (nonatomic, readonly) NSString *bundleIdentifier;
24 | @end
25 |
26 | @interface SBApplicationIcon : SBIcon
27 | @end
28 |
29 | @interface SBIconModel : NSObject
30 | -(SBApplicationIcon *)applicationIconForBundleIdentifier:(NSString *)arg1;
31 | @end
32 |
33 | @interface SBMediaController : NSObject
34 | +(id)sharedInstance;
35 | -(SBApplication *)nowPlayingApplication;
36 | @end
37 |
38 | @interface SBHIconImageCache : NSObject
39 | @property (nonatomic, readonly) UIImage *overlayImage;
40 | -(void)cacheImage:(UIImage *)arg1 forIcon:(SBApplicationIcon *)arg2;
41 | -(void)notifyObserversOfUpdateForIcon:(SBApplicationIcon *)arg1;
42 | -(void)purgeCachedImagesForIcons:(id)arg1;
43 | @end
44 |
45 | @interface SBFolderIconImageCache : NSObject
46 | -(void)iconImageCache:(id)arg1 didUpdateImageForIcon:(id)arg2;
47 | @end
48 |
49 | @interface SBHIconManager : NSObject
50 | -(SBHIconImageCache *)iconImageCache;
51 | -(SBFolderIconImageCache *)folderIconImageCache;
52 | @end
53 |
54 | @interface SBIconController : NSObject
55 | @property (nonatomic, readonly) SBHIconManager *iconManager;
56 | @property (nonatomic, readonly) SBHIconImageCache *appSwitcherHeaderIconImageCache;
57 | @property (nonatomic, readonly) SBHIconImageCache *appSwitcherUnmaskedIconImageCache;
58 | @property (nonatomic, readonly) SBHIconImageCache *tableUIIconImageCache;
59 | @property (nonatomic, readonly) SBHIconImageCache *notificationIconImageCache;
60 | @property (nonatomic, retain) SBIconModel *model;
61 | +(instancetype)sharedInstance;
62 |
63 | //NowPlayingIcon
64 | - (void)registerForNowPlayingNotifications;
65 | -(void)setNowPlayingArtworkForApp:(SBApplicationIcon *)appIcon withArtwork:(UIImage *)artwork;
66 | @end
67 |
--------------------------------------------------------------------------------
/NowPlayingIconXVI.xm:
--------------------------------------------------------------------------------
1 | /*
2 | * Tweak.xm
3 | * NowPlayingIcon
4 | *
5 | * Created by Ethan Whited on 12/19/2022.
6 | * Copyright © 2022 Ethan Whited . All rights reserved.
7 | */
8 |
9 | // original tweak https://github.com/LacertosusRepo/Open-Source-Tweaks/tree/master/NowPlayingIcon
10 |
11 | #import "MediaRemote.h"
12 | #import "NowPlayingIconXVI.h"
13 |
14 | NSString *lastNowPlayingBundleID;
15 | UIImage *currentArtwork;
16 | UIImage *currentMaskedArtwork;
17 |
18 | %hook SBMediaController
19 | -(void)_mediaRemoteNowPlayingApplicationDidChange:(id)arg1 {
20 | %orig;
21 | if (arg1 != nil) {
22 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NowPlayingAppChanged" object:nil];
23 | } else {
24 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NowPlayingAppTerminated" object:nil];
25 | }
26 | }
27 |
28 | -(void)_mediaRemoteNowPlayingInfoDidChange:(id)arg1 {
29 | %orig;
30 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NowPlayingInfoChanged" object:nil];
31 | }
32 | %end
33 |
34 | %hook SBIconController
35 | // iOS 13-14
36 | -(instancetype)initWithApplicationController:(id)arg1 applicationPlaceholderController:(id)arg2 userInterfaceController:(id)arg3 policyAggregator:(id)arg4 alertItemsController:(id)arg5 assistantController:(id)arg6 {
37 | [self registerForNowPlayingNotifications];
38 | return %orig;
39 | }
40 |
41 | // iOS 15
42 | -(id)initWithApplicationController:(id)arg1 applicationPlaceholderController:(id)arg2 userInterfaceController:(id)arg3 policyAggregator:(id)arg4 alertItemsController:(id)arg5 assistantController:(id)arg6 powerLogAggregator:(id)arg7 {
43 | [self registerForNowPlayingNotifications];
44 | return %orig;
45 | }
46 |
47 | // iOS 16
48 | - (id)initWithMainDisplayWindowScene:(id)arg1 {
49 | [self registerForNowPlayingNotifications];
50 | return %orig;
51 | }
52 |
53 | %new
54 | - (void)registerForNowPlayingNotifications {
55 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nowPlayingAppDidChange) name:@"NowPlayingAppChanged" object:nil];
56 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nowPlayingAppDidTerminate) name:@"NowPlayingAppTerminated" object:nil];
57 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nowPlayingInfoDidChange) name:@"NowPlayingInfoChanged" object:nil];
58 | }
59 |
60 | %new
61 | -(void)nowPlayingInfoDidChange {
62 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
63 | NSDictionary *nowPlayingInfo = (__bridge NSDictionary *)information;
64 | NSData *artworkData = [nowPlayingInfo objectForKey:(__bridge NSString *)kMRMediaRemoteNowPlayingInfoArtworkData];
65 | if (artworkData) {
66 | NSData *oldArtworkData = UIImageJPEGRepresentation(currentArtwork, 1.0);
67 | NSData *newArtworkData = UIImageJPEGRepresentation([UIImage imageWithData:artworkData], 1.0);
68 | if ([oldArtworkData isEqualToData:newArtworkData]) {
69 | return;
70 | } else {
71 | currentArtwork = [UIImage imageWithData:artworkData];
72 | }
73 |
74 | SBIconController *iconController = [%c(SBIconController) sharedInstance];
75 | SBApplication *nowPlayingApp = [[%c(SBMediaController) sharedInstance] nowPlayingApplication];
76 | NSString *bundleID = nowPlayingApp.bundleIdentifier;
77 | if (bundleID != nil) {
78 | SBApplicationIcon *appIcon = [iconController.model applicationIconForBundleIdentifier:bundleID];
79 |
80 | if (currentArtwork != nil) [self setNowPlayingArtworkForApp:appIcon withArtwork:currentArtwork];
81 | lastNowPlayingBundleID = nowPlayingApp.bundleIdentifier;
82 | }
83 | }
84 | });
85 | }
86 |
87 | %new
88 | -(void)nowPlayingAppDidChange {
89 | SBIconController *iconController = [%c(SBIconController) sharedInstance];
90 | if (lastNowPlayingBundleID != nil) {
91 | SBApplicationIcon *appIcon = [iconController.model applicationIconForBundleIdentifier:lastNowPlayingBundleID];
92 |
93 | if ([appIcon application]) {
94 | dispatch_async(dispatch_get_main_queue(), ^{
95 | __NSSingleObjectArrayI *iconsToPurge = [%c(__NSSingleObjectArrayI) arrayWithObjects:appIcon, nil];
96 | [[iconController.iconManager iconImageCache] purgeCachedImagesForIcons:iconsToPurge];
97 | [[iconController.iconManager iconImageCache] notifyObserversOfUpdateForIcon:appIcon];
98 | });
99 | }
100 | }
101 | }
102 |
103 | %new
104 | -(void)nowPlayingAppDidTerminate {
105 | SBIconController *iconController = [%c(SBIconController) sharedInstance];
106 | if (lastNowPlayingBundleID != nil) {
107 | SBApplicationIcon *appIcon = [iconController.model applicationIconForBundleIdentifier:lastNowPlayingBundleID];
108 |
109 | if ([appIcon application]) {
110 | dispatch_async(dispatch_get_main_queue(), ^{
111 | __NSSingleObjectArrayI *iconsToPurge = [%c(__NSSingleObjectArrayI) arrayWithObjects:appIcon, nil];
112 | [[iconController.iconManager iconImageCache] purgeCachedImagesForIcons:iconsToPurge];
113 | [[iconController.iconManager iconImageCache] notifyObserversOfUpdateForIcon:appIcon];
114 | });
115 | }
116 | }
117 | }
118 |
119 | %new
120 | -(void)setNowPlayingArtworkForApp:(SBApplicationIcon *)appIcon withArtwork:(UIImage *)artwork {
121 | if (appIcon && artwork) {
122 | SBHIconImageCache *imageCacheHS = [self.iconManager iconImageCache];
123 | SBFolderIconImageCache *imageCacheFLDR = [self.iconManager folderIconImageCache];
124 | NSArray *imageCaches = @[imageCacheHS,
125 | self.appSwitcherHeaderIconImageCache,];
126 |
127 | CALayer *maskLayer = [CALayer layer];
128 | maskLayer.frame = CGRectMake(0, 0, artwork.size.width, artwork.size.height);
129 | maskLayer.contents = (id)imageCacheHS.overlayImage.CGImage;
130 |
131 | CALayer *artLayer = [CALayer layer];
132 | artLayer.frame = CGRectMake(0, 0, artwork.size.width, artwork.size.height);
133 | artLayer.contents = (id)artwork.CGImage;
134 | artLayer.masksToBounds = YES;
135 | artLayer.mask = maskLayer;
136 |
137 | UIGraphicsBeginImageContext(artwork.size);
138 | [artLayer renderInContext:UIGraphicsGetCurrentContext()];
139 | currentMaskedArtwork = UIGraphicsGetImageFromCurrentImageContext();
140 | UIGraphicsEndImageContext();
141 |
142 | for (SBHIconImageCache *cache in imageCaches) {
143 | [cache cacheImage:currentMaskedArtwork forIcon:appIcon];
144 | [cache notifyObserversOfUpdateForIcon:appIcon];
145 | }
146 | [imageCacheFLDR iconImageCache:imageCacheHS didUpdateImageForIcon:appIcon];
147 | }
148 | }
149 | %end
150 |
151 | %hook SBIconImageCrossfadeView
152 | //iOS 13
153 | -(instancetype)initWithImageView:(SBIconImageView *)arg1 crossfadeView:(UIView *)arg2 {
154 | SBIcon *icon = [arg1 icon];
155 | SBApplication *nowPlayingApp = [[%c(SBMediaController) sharedInstance] nowPlayingApplication];
156 | NSString *bundleID = nowPlayingApp.bundleIdentifier;
157 | if (bundleID != nil) {
158 | if ([[icon applicationBundleID] isEqualToString:bundleID] && currentMaskedArtwork) {
159 | CATransition *transition = [CATransition animation];
160 | transition.duration = 1.0f;
161 | transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
162 | transition.type = kCATransitionFade;
163 | [arg1.layer addAnimation:transition forKey:nil];
164 |
165 | arg1.layer.contents = (id)currentMaskedArtwork.CGImage;
166 | }
167 | }
168 | return %orig;
169 | }
170 |
171 | //iOS 14+
172 | -(instancetype)initWithSource:(SBIconImageView *)arg1 crossfadeView:(UIView *)arg2 {
173 | SBIcon *icon = [arg1 icon];
174 | SBApplication *nowPlayingApp = [[%c(SBMediaController) sharedInstance] nowPlayingApplication];
175 | NSString *bundleID = nowPlayingApp.bundleIdentifier;
176 | if (bundleID != nil) {
177 | if ([[icon applicationBundleID] isEqualToString:bundleID] && currentMaskedArtwork) {
178 | CATransition *transition = [CATransition animation];
179 | transition.duration = 1.0f;
180 | transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
181 | transition.type = kCATransitionFade;
182 | [arg1.layer addAnimation:transition forKey:nil];
183 |
184 | arg1.layer.contents = (id)currentMaskedArtwork.CGImage;
185 | }
186 | }
187 | return %orig;
188 | }
189 | %end
190 |
--------------------------------------------------------------------------------