├── ASSWatchdog
├── ASSWatchdog.plist
├── Makefile
└── Tweak.c
├── .gitignore
├── Music
├── MitsuhaForeverMusic.plist
├── Makefile
├── Tweak.h
└── MusicTweak.xm
├── Spotify
├── MitsuhaForeverSpotify.plist
├── Makefile
├── Tweak.h
└── SpotifyTweak.xm
├── Soundcloud
├── MitsuhaForeverSoundCloud.plist
├── Makefile
├── Tweak.h
└── SCTweak.xm
├── Springboard
├── MitsuhaForeverSpringboard.plist
├── Makefile
├── HomescreenTweak.xm
├── Tweak.h
├── ControlCenterTweak.xm
└── SBTweak.xm
├── Prefs
├── Resources
│ ├── icon.png
│ ├── safari.png
│ ├── icon@2x.png
│ ├── icon@3x.png
│ ├── twitter.png
│ ├── safari@2x.png
│ ├── safari@3x.png
│ ├── twitter@2x.png
│ ├── twitter@3x.png
│ ├── icon_83.5@2x.png
│ ├── Apps
│ │ ├── Music.plist
│ │ ├── HomeScreen.plist
│ │ ├── ControlCenter.plist
│ │ ├── LockScreen.plist
│ │ ├── Springboard.plist
│ │ └── Spotify.plist
│ ├── Prefs.plist
│ └── App.plist
├── entry.plist
├── MSHFAppPrefsListController.h
├── Makefile
├── MSHFPrefsListController.h
├── MSHFAppPrefsListController.m
└── MSHFPrefsListController.m
├── .github
└── FUNDING.yml
├── Makefile
├── control
├── README.md
├── LICENSE
└── DragonMake
/ASSWatchdog/ASSWatchdog.plist:
--------------------------------------------------------------------------------
1 | Filter = { Executables = ("mediaserverd"); };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.theos
2 | obj
3 | .DS_Store
4 | /*.xcodeproj
5 | packages
6 | .dragon
--------------------------------------------------------------------------------
/Music/MitsuhaForeverMusic.plist:
--------------------------------------------------------------------------------
1 | { Filter = { Bundles = ( "com.apple.Music" ); }; }
2 |
--------------------------------------------------------------------------------
/Spotify/MitsuhaForeverSpotify.plist:
--------------------------------------------------------------------------------
1 | { Filter = { Bundles = ( "com.spotify.client" ); }; }
--------------------------------------------------------------------------------
/Soundcloud/MitsuhaForeverSoundCloud.plist:
--------------------------------------------------------------------------------
1 | { Filter = { Bundles = ( "com.soundcloud.TouchApp" ); }; }
--------------------------------------------------------------------------------
/Springboard/MitsuhaForeverSpringboard.plist:
--------------------------------------------------------------------------------
1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; }
2 |
--------------------------------------------------------------------------------
/Prefs/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/icon.png
--------------------------------------------------------------------------------
/Prefs/Resources/safari.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/safari.png
--------------------------------------------------------------------------------
/Prefs/Resources/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/icon@2x.png
--------------------------------------------------------------------------------
/Prefs/Resources/icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/icon@3x.png
--------------------------------------------------------------------------------
/Prefs/Resources/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/twitter.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | ko_fi: https://www.ko-fi.com/ConorTheDev
4 |
--------------------------------------------------------------------------------
/Prefs/Resources/safari@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/safari@2x.png
--------------------------------------------------------------------------------
/Prefs/Resources/safari@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/safari@3x.png
--------------------------------------------------------------------------------
/Prefs/Resources/twitter@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/twitter@2x.png
--------------------------------------------------------------------------------
/Prefs/Resources/twitter@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/twitter@3x.png
--------------------------------------------------------------------------------
/Prefs/Resources/icon_83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryannair05/MitsuhaForever/HEAD/Prefs/Resources/icon_83.5@2x.png
--------------------------------------------------------------------------------
/Prefs/entry.plist:
--------------------------------------------------------------------------------
1 | {
2 | entry = {
3 | bundle = MitsuhaForeverPrefs;
4 | cell = PSLinkCell;
5 | detail = MSHFPrefsListController;
6 | icon = icon.png;
7 | isController = 1;
8 | label = "Mitsuha Forever";
9 | };
10 | }
--------------------------------------------------------------------------------
/Music/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | TWEAK_NAME = MitsuhaForeverMusic
6 | $(TWEAK_NAME)_FILES = MusicTweak.xm
7 | $(TWEAK_NAME)_LIBRARIES += mitsuhaforever
8 |
9 | include $(THEOS_MAKE_PATH)/tweak.mk
--------------------------------------------------------------------------------
/Spotify/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | TWEAK_NAME = MitsuhaForeverSpotify
6 | $(TWEAK_NAME)_FILES = SpotifyTweak.xm
7 | $(TWEAK_NAME)_LIBRARIES += mitsuhaforever
8 |
9 | include $(THEOS_MAKE_PATH)/tweak.mk
--------------------------------------------------------------------------------
/ASSWatchdog/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | TWEAK_NAME = ASSWatchdog
6 | $(TWEAK_NAME)_FILES = Tweak.c
7 | $(TWEAK_NAME)_WEAK_LIBRARIES = $(THEOS_LIBRARY_PATH)/libhooker.dylib
8 |
9 | include $(THEOS_MAKE_PATH)/tweak.mk
--------------------------------------------------------------------------------
/Soundcloud/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | TWEAK_NAME = MitsuhaForeverSoundCloud
6 | $(TWEAK_NAME)_FILES = SCTweak.xm
7 | $(TWEAK_NAME)_PRIVATE_FRAMEWORKS += MediaRemote
8 | $(TWEAK_NAME)_LIBRARIES += mitsuhaforever
9 |
10 | include $(THEOS_MAKE_PATH)/tweak.mk
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/Music.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | Music
7 | title
8 | Apple Music
9 |
10 |
--------------------------------------------------------------------------------
/Springboard/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | TWEAK_NAME = MitsuhaForeverSpringboard
6 | $(TWEAK_NAME)_FILES = SBTweak.xm HomescreenTweak.xm ControlCenterTweak.xm
7 | $(TWEAK_NAME)_PRIVATE_FRAMEWORKS += MediaRemote
8 | $(TWEAK_NAME)_LIBRARIES += mitsuhaforever
9 |
10 | include $(THEOS_MAKE_PATH)/tweak.mk
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/HomeScreen.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | HomeScreen
7 | title
8 | Homescreen
9 |
10 |
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/ControlCenter.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | ControlCenter
7 | title
8 | Control Center
9 |
10 |
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/LockScreen.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | LockScreen
7 | title
8 | Lockscreen Background
9 |
10 |
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/Springboard.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | Springboard
7 | title
8 | Lockscreen Notification
9 |
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | FINALPACKAGE = 1
2 |
3 | PREFIX = $(THEOS)/toolchain/Xcode.xctoolchain/usr/bin/
4 |
5 | export ADDITIONAL_CFLAGS = -DTHEOS_LEAN_AND_MEAN -fobjc-arc -O3
6 | export TARGET = iphone:13.5:12.0
7 |
8 | include $(THEOS)/makefiles/common.mk
9 |
10 | SUBPROJECTS += ASSWatchdog Music Prefs Spotify Springboard
11 |
12 | after-install::
13 | install.exec "killall -9 SpringBoard"
14 |
15 | include $(THEOS_MAKE_PATH)/aggregate.mk
--------------------------------------------------------------------------------
/Prefs/MSHFAppPrefsListController.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | @interface PSListController (Method)
6 | -(BOOL)containsSpecifier:(id)arg1;
7 | @end
8 |
9 | @interface MSHFAppPrefsListController : PSListController
10 | @property (nonatomic, retain) NSMutableDictionary *savedSpecifiers;
11 | @property (nonatomic, retain) NSString *appName;
12 | @end
13 |
--------------------------------------------------------------------------------
/control:
--------------------------------------------------------------------------------
1 | Package: com.ryannair05.mitsuhaforever
2 | Name: Mitsuha Forever
3 | Depends: mobilesubstrate, preferenceloader, com.ryannair05.libmitsuhaforever (>=1.4.0), ws.hbang.alderis, firmware (>=11.0)
4 | Replaces: io.c0ldra1n.mitsuha, io.ominousness.mitsuhaxi, me.nepeta.mitsuhainfinity, me.nepeta.mitsuhainfinity-lockscreen
5 | Version: 1.4.3
6 | Architecture: iphoneos-arm
7 | Description: Universal audio visualizer for iOS
8 | Maintainer: Ryan Nair
9 | Author: Ryan Nair, ConorTheDev, and Nepeta
10 | Section: Tweaks
--------------------------------------------------------------------------------
/Prefs/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS = arm64 arm64e
2 |
3 | include $(THEOS_MAKE_PATH)/common.mk
4 |
5 | BUNDLE_NAME = MitsuhaForeverPrefs
6 | $(BUNDLE_NAME)_FILES = MSHFPrefsListController.m MSHFAppPrefsListController.m
7 | $(BUNDLE_NAME)_INSTALL_PATH = /Library/PreferenceBundles
8 | $(BUNDLE_NAME)_LIBRARIES = colorpicker mitsuhaforever
9 | $(BUNDLE_NAME)_PRIVATE_FRAMEWORKS = Preferences
10 |
11 | include $(THEOS_MAKE_PATH)/bundle.mk
12 |
13 | internal-stage::
14 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END)
15 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(BUNDLE_NAME).plist$(ECHO_END)
--------------------------------------------------------------------------------
/Music/Tweak.h:
--------------------------------------------------------------------------------
1 | //
2 | // Tweak.h
3 | // Mitsuha2
4 | //
5 | // Created by c0ldra1n on 12/10/17.
6 | // Copyright © 2017 c0ldra1n. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MusicArtworkComponentImageView : UIImageView
12 | @end
13 |
14 | @interface MusicNowPlayingControlsViewController : UIViewController
15 | @property(retain, nonatomic) MSHFView *mshfView;
16 | @end
17 |
18 | @interface CFWPrefsManager : NSObject
19 | + (id)sharedInstance;
20 | @end
21 |
22 | @interface CFWColorInfo : NSObject
23 | @property (nonatomic, strong, readwrite) UIColor *primaryColor;
24 | @end
25 |
26 | @interface CFWMusicStateManager : NSObject
27 | @property (atomic, strong, readonly) CFWColorInfo *colorInfo;
28 | + (id)sharedInstance;
29 | @end
30 |
--------------------------------------------------------------------------------
/Soundcloud/Tweak.h:
--------------------------------------------------------------------------------
1 | //
2 | // Tweak.h
3 | // Mitsuha2
4 | //
5 | // Created by c0ldra1n on 12/10/17.
6 | // Copyright © 2017 c0ldra1n. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PlayerArtworkView : UIView
12 |
13 | @property(retain, nonatomic) UIImage *artworkImage;
14 | @property(retain, nonatomic) UIImageView *artworkImageView;
15 | - (void)observeValueForKeyPath:(NSString *)keyPath
16 | ofObject:(id)object
17 | change:(NSDictionary *)change
18 | context:(void *)context;
19 | - (void)readjustWaveColor;
20 |
21 | @end
22 |
23 | @interface WaveFormController : UIView
24 |
25 | @end
26 |
27 | @interface TrackPlayerViewController : UIViewController
28 |
29 | @property(retain, nonatomic) MSHFView *mshfView;
30 |
31 | @end
--------------------------------------------------------------------------------
/Prefs/Resources/Apps/Spotify.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | Spotify
7 | items
8 |
9 |
10 | cell
11 | PSSwitchCell
12 | default
13 |
14 | defaults
15 | com.ryannair05.mitsuhaforever
16 | label
17 | Ignore ColorFlow
18 | key
19 | MSHFSpotifyIgnoreColorFlow
20 | alternateColors
21 |
22 |
23 |
24 | cell
25 | PSSwitchCell
26 | default
27 |
28 | defaults
29 | com.ryannair05.mitsuhaforever
30 | label
31 | Cover art bug fix
32 | key
33 | MSHFSpotifyEnableCoverArtBugFix
34 | alternateColors
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Prefs/MSHFPrefsListController.h:
--------------------------------------------------------------------------------
1 | #import "MSHFAppPrefsListController.h"
2 | #import
3 | #import
4 | #import
5 | #import
6 |
7 | @interface MSHFPrefsListController : PSListController
8 | - (void)resetPrefs:(id)sender;
9 | - (void)respring:(id)sender;
10 | - (void)restartmsd:(id)sender;
11 | @end
12 |
13 | @interface MSHFTintedTableCell : PSTableCell
14 | @end
15 |
16 | @interface MSHFLinkTableCell : MSHFTintedTableCell
17 | @property (nonatomic, readonly) BOOL isBig;
18 | @property (nonatomic, retain, readonly) UIView *avatarView;
19 | @property (nonatomic, retain, readonly) UIImageView *avatarImageView;
20 | @property (nonatomic, retain) UIImage *avatarImage;
21 | @property (nonatomic, retain) NSURL *avatarURL;
22 | @property (nonatomic, readonly) BOOL isAvatarCircular;
23 | - (void)loadAvatarIfNeeded;
24 | - (BOOL)shouldShowAvatar;
25 | @end
26 |
27 | @interface MSHFTwitterCell : MSHFLinkTableCell
28 | @end
29 |
30 | @interface MSHFTwitterCell () {
31 | NSString *_user;
32 | }
33 | @end
34 |
35 | @interface MSHFPackageNameHeaderCell : PSTableCell
36 | @end
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mitsuha Forever
2 |
3 | This is a fork of a Nepeta's excellent tweak - [MitsuhaInfinity](https://github.com/Nepeta/MitsuhaInfinity) with support for iOS 13, bug fixes and new features
4 |
5 | For support / to report a bug [join my discord server](https://discord.gg/J2Tmaqy)
6 |
7 | ## Installation
8 |
9 | 1. Add this repository to your package manager: https://repo.chariz.com
10 | 2. Install **Mitsuha Forever**.
11 |
12 | ## Compatibility
13 |
14 | ### Supported apps
15 |
16 | * Apple Music
17 | * Spotify
18 | * iOS audio player notification (global)
19 |
20 | ### Tweaks
21 |
22 | Should be compatible with Eclipse and similar tweaks.
23 | Works with Artsy.
24 |
25 | ## Bugs
26 |
27 | For support / to report a bug you may open an issue here on Github
28 |
29 | ## Donations
30 |
31 | Support the original developer, c0ldra1n, not me, here: https://www.paypal.me/c0ldra1n
32 |
33 | ## Credits
34 |
35 | * [c0ldra1n](https://github.com/c0ldra1n/) - Developed the original tweak for iOS 10 and earlier.
36 | * Nepeta - Developed the original Mitsuha Infinity.
37 | * [cbyrne](https://github.com/conorthedev) - Originally developed this tweak.
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Andy Shin
4 | Copyright (c) 2019 Nepeta
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/DragonMake:
--------------------------------------------------------------------------------
1 | ---
2 | name: Mitsuha Forever
3 | icmd: killall -9 SpringBoard
4 |
5 | all:
6 | targetvers: 11.0
7 | archs:
8 | - arm64
9 | - arm64e
10 | optim: 3
11 |
12 | ASSWatchdog:
13 | dir: ASSWatchdog
14 | type: tweak
15 | c_files:
16 | - "Tweak.c"
17 | MitsuhaForeverHomeScreen:
18 | dir: Homescreen
19 | type: tweak
20 | logos_files:
21 | - "HomescreenTweak.xm"
22 | libs:
23 | - MitsuhaForever
24 | frameworks:
25 | - MediaRemote
26 | MitsuhaForeverMusic:
27 | dir: Music
28 | type: tweak
29 | logos_files:
30 | - "MusicTweak.xm"
31 | libs:
32 | - MitsuhaForever
33 | MitsuhaForeverSpotify:
34 | dir: Spotify
35 | type: tweak
36 | logos_files:
37 | - "SpotifyTweak.xm"
38 | libs:
39 | - MitsuhaForever
40 | MitsuhaForeverSpringboardLS:
41 | dir: SpringboardLS
42 | type: tweak
43 | logos_files:
44 | - "SBTweak.xm"
45 | libs:
46 | - MitsuhaForever
47 | frameworks:
48 | - MediaRemote
49 | MitsuhaForeverSpringboardLSBackground:
50 | dir: SpringboardLSBackground
51 | type: tweak
52 | logos_files:
53 | - "SBLSTweak.xm"
54 | libs:
55 | - MitsuhaForever
56 | frameworks:
57 | - MediaRemote
58 |
59 | MitsuhaForeverPrefs:
60 | dir: Prefs
61 | type: prefs
62 | objc_files:
63 | - MSHFAppPrefsListController.m
64 | - MSHFPrefsListController.m
65 | libs:
66 | - colorpicker
67 | - mitsuhaforever
--------------------------------------------------------------------------------
/ASSWatchdog/Tweak.c:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #include
4 | #include
5 | #include "libhooker.h"
6 |
7 | #define ASSPort 44333
8 |
9 | bool hasConnected;
10 | const int one = 1;
11 | int connfd;
12 |
13 | OSStatus (*_origAudioQueueStart)(AudioQueueRef inAQ, const AudioTimeStamp *inStartTime);
14 | OSStatus _functionAudioQueueStart(AudioQueueRef inAQ, const AudioTimeStamp *inStartTime) {
15 |
16 | os_log(OS_LOG_DEFAULT, "[ASSWatchdog] checking for ASS");
17 | if (!hasConnected) {
18 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
19 | struct sockaddr_in remote;
20 | memset(&remote, 0, sizeof(struct sockaddr_in));
21 | remote.sin_family = PF_INET;
22 | remote.sin_port = htons(ASSPort);
23 | inet_aton("127.0.0.1", &remote.sin_addr);
24 |
25 | os_log(OS_LOG_DEFAULT, "[ASSWatchdog] Connecting to ASS");
26 | connfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
27 |
28 | setsockopt(connfd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
29 |
30 | if (connfd <= 0) {
31 | os_log(OS_LOG_DEFAULT, "[ASSWatchdog] ASS not running.");
32 | }
33 | else {
34 | os_log(OS_LOG_DEFAULT, "[ASSWatchdog] Connected.");
35 | hasConnected = true;
36 | close(connfd);
37 | }
38 | });
39 | } else {
40 | os_log(OS_LOG_DEFAULT, "[ASSWatchdog] already has connected...");
41 | }
42 |
43 | return _origAudioQueueStart(inAQ, inStartTime);
44 | }
45 |
46 | static __attribute__((constructor)) void Init() {
47 |
48 | hasConnected = false;
49 |
50 | if (access("/usr/lib/libhooker.dylib", F_OK) == 0) {
51 | const struct LHFunctionHook hook[1] = {{(void *)AudioQueueStart, (void **)&_functionAudioQueueStart, (void **)&_origAudioQueueStart}};
52 | LHHookFunctions(hook, 1);
53 | }
54 | else {
55 | MSHookFunction((void *)AudioQueueStart, (void *)&_functionAudioQueueStart, (void **)&_origAudioQueueStart);
56 | }
57 | }
--------------------------------------------------------------------------------
/Soundcloud/SCTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 |
3 | // bool MSHFNeedsFirstPlayerInit = YES;
4 |
5 | %group MitsuhaVisuals
6 |
7 | MSHFConfig *config = NULL;
8 |
9 | WaveFormController *waveController;
10 |
11 | %hook PlayerArtworkView
12 |
13 | -(void)layoutSubviews{
14 | %orig;
15 | if ([config view] == NULL) return;
16 | if (!self.superview) return;
17 | if (!self.superview.nextResponder) return;
18 | if (![NSStringFromClass([self.superview.nextResponder class]) isEqualToString:@"TrackPlayerViewController"]) return;
19 |
20 | if (config.colorMode != 2) {
21 | [self readjustWaveColor];
22 | }
23 |
24 | [self addObserver:self forKeyPath:@"artworkImage" options:NSKeyValueObservingOptionNew context:NULL];
25 | }
26 |
27 | %new;
28 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
29 | if ([keyPath isEqualToString:@"artworkImage"] && config.colorMode != 2) {
30 | [self readjustWaveColor];
31 | }
32 | }
33 |
34 | %new;
35 | -(void)readjustWaveColor{
36 | [config colorizeView:((PlayerArtworkView*)self).artworkImage];
37 | }
38 |
39 | %end
40 |
41 | %hook TrackPlayerViewController
42 |
43 | %property (retain,nonatomic) MSHFView *mshfView;
44 |
45 | -(void)loadView {
46 | %orig;
47 |
48 | NSLog(@"[Mitsuha]: viewLoaded");
49 |
50 | CGFloat width = CGRectGetWidth(self.view.bounds);
51 | CGFloat height = CGRectGetHeight(self.view.bounds);
52 |
53 | [config initializeViewWithFrame:CGRectMake(0, height + config.waveOffset, width, height)];
54 |
55 | self.mshfView = [config view];
56 | [self.view insertSubview:self.mshfView atIndex:20];
57 |
58 | }
59 |
60 | -(void)viewWillAppear:(BOOL)animated{
61 | NSLog(@"[Mitsuha]: viewWillAppear");
62 |
63 | %orig;
64 |
65 | self.view.clipsToBounds = 1;
66 |
67 | [self.mshfView start];
68 | }
69 |
70 | %end
71 |
72 | %end
73 |
74 | %ctor{
75 | config = [MSHFConfig loadConfigForApplication:@"SoundCloud"];
76 | config.waveOffsetOffset = -130;
77 |
78 | if(config.enabled){
79 | %init(MitsuhaVisuals);
80 | }
81 | }
--------------------------------------------------------------------------------
/Spotify/Tweak.h:
--------------------------------------------------------------------------------
1 | //
2 | // Tweak.h
3 | // Mitsuha
4 | //
5 | // Created by c0ldra1n on 2/17/17.
6 | // Copyright © 2017 c0ldra1n. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface SPTNowPlayingCoverArtImageView : UIImageView
13 | -(void)setImage:(UIImage *)image;
14 | -(void)readjustWaveColor;
15 |
16 | @end
17 |
18 | @interface SPTNowPlayingContentCell : UIView
19 |
20 | @property(retain, nonatomic) UIImage *cellContentRepresentation;
21 |
22 | @end
23 |
24 | @interface SPTNowPlayingCoverArtView : UIView
25 |
26 | @end
27 |
28 | @interface SPTNowPlayingCarouselContentUnitView : UIView
29 |
30 | @property(retain, nonatomic) SPTNowPlayingCoverArtView *coverArtView; // @synthesize coverArtView=_coverArtView;
31 |
32 | @end
33 |
34 | @interface SPTNowPlayingCarouselAreaViewController : UIViewController
35 |
36 | @property(retain, nonatomic) SPTNowPlayingCarouselContentUnitView *view; // @dynamic view;
37 |
38 | @end
39 |
40 | @interface SPTNowPlayingModel : NSObject
41 | - (void)player:(id)arg1 stateDidChange:(id)arg2 fromState:(id)arg3;
42 | - (void)updateWithPlayerState:(id)arg1;
43 |
44 | -(void)applyColorChange;
45 |
46 | @end
47 |
48 | @interface CFWColorInfo : NSObject
49 |
50 | + (id)colorInfoWithAnalyzedInfo:(struct AnalyzedInfo)arg1;
51 | @property(nonatomic, getter=isBackgroundDark) _Bool backgroundDark; // @synthesize backgroundDark=_backgroundDark;
52 | @property(retain, nonatomic) UIColor *secondaryColor; // @synthesize secondaryColor=_secondaryColor;
53 | @property(retain, nonatomic) UIColor *primaryColor; // @synthesize primaryColor=_primaryColor;
54 | @property(retain, nonatomic) UIColor *backgroundColor; // @synthesize backgroundColor=_backgroundColor;
55 | - (id)initWithAnalyzedInfo:(struct AnalyzedInfo)arg1;
56 |
57 | @end
58 |
59 | @interface CFWSpotifyStateManager : NSObject
60 |
61 | + (id)sharedManager;
62 |
63 | @property(readonly, retain, nonatomic) CFWColorInfo *mainColorInfo; // @synthesize mainColorInfo=_mainColorInfo;
64 |
65 | @end
66 |
67 | @interface CFWPrefsManager : NSObject
68 |
69 | +(id)sharedInstance;
70 |
71 | @end
72 |
73 | @interface SPTNowPlayingViewController : UIViewController
74 | @property (retain,nonatomic) MSHFView *mshfview;
75 | @end
76 |
77 | @interface SPTVideoDisplayView : UIView
78 | @property (nonatomic, strong, readwrite) AVPlayer *player;
79 | @property(nonatomic) _Bool playbackReady;
80 | @end
--------------------------------------------------------------------------------
/Springboard/HomescreenTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 | #import
3 | #import
4 |
5 | static MSHFConfig *mshConfig;
6 |
7 | %group SBMediaHook
8 | %hook SBMediaController
9 |
10 | -(void)setNowPlayingInfo:(id)arg1 {
11 | %orig;
12 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
13 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
14 | [mshConfig colorizeView:[UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)]];
15 | }
16 | });
17 | }
18 | %end
19 | %end
20 |
21 | %hook SBIconController
22 |
23 | %property (strong,nonatomic) MSHFView *mshfView;
24 |
25 | -(void)loadView{
26 | %orig;
27 | mshConfig.waveOffsetOffset = self.view.bounds.size.height - 200;
28 | if (![mshConfig view]) [mshConfig initializeViewWithFrame:self.view.bounds];
29 | self.mshfView = [mshConfig view];
30 |
31 | [[self view] insertSubview:self.mshfView atIndex:1];
32 |
33 | self.mshfView.translatesAutoresizingMaskIntoConstraints = NO;
34 | [self.mshfView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
35 | [self.mshfView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
36 | [self.mshfView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
37 | [self.mshfView.heightAnchor constraintEqualToConstant:self.mshfView.frame.size.height].active = YES;
38 | }
39 |
40 | -(void)viewWillAppear:(BOOL)animated{
41 | %orig;
42 | [self.mshfView start];
43 | }
44 |
45 | -(void)viewWillDisappear:(BOOL)animated{
46 | %orig;
47 | [self.mshfView stop];
48 | }
49 |
50 | %end
51 |
52 | static void screenDisplayStatus(CFNotificationCenterRef center, void* o, CFStringRef name, const void* object, CFDictionaryRef userInfo) {
53 | if(@available(iOS 13.0, *)) {
54 | [[mshConfig view] stop];
55 | }
56 | else if ([[%c(SBMediaController) sharedInstance] isPlaying]) {
57 | uint64_t state;
58 | int token;
59 | notify_register_check("com.apple.iokit.hid.displayStatus", &token);
60 | notify_get_state(token, &state);
61 | notify_cancel(token);
62 | if ([mshConfig view]) {
63 | if (state) {
64 | [[mshConfig view] start];
65 | } else {
66 | [[mshConfig view] stop];
67 | }
68 | }
69 | }
70 | else {
71 | [[mshConfig view] stop];
72 | }
73 | }
74 |
75 | %ctor{
76 | mshConfig = [MSHFConfig loadConfigForApplication:@"HomeScreen"];
77 | if(mshConfig.enabled){
78 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)screenDisplayStatus, (CFStringRef)@"com.apple.iokit.hid.displayStatus", NULL, (CFNotificationSuspensionBehavior)kNilOptions);
79 |
80 | %init;
81 | %init(SBMediaHook);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Springboard/Tweak.h:
--------------------------------------------------------------------------------
1 | //
2 | // Tweak.h
3 | // Mitsuha2
4 | //
5 | // Created by c0ldra1n on 12/10/17.
6 | // Copyright © 2017 c0ldra1n. All rights reserved.
7 | //
8 |
9 |
10 | #import
11 |
12 | #define ArtsyTweakDylibFile \
13 | @"/Library/MobileSubstrate/DynamicLibraries/Artsy.dylib"
14 | #define PrysmTweakDylibFile \
15 | @"/Library/MobileSubstrate/DynamicLibraries/Prysm.dylib"
16 | #define QuartTweakDylibFile \
17 | @"/Library/MobileSubstrate/DynamicLibraries/Quart.dylib"
18 | #define FlowTweakDylibFile \
19 | @"/Library/MobileSubstrate/DynamicLibraries/Flow.dylib"
20 | #define OrionTweakDylibFile \
21 | "/Library/MobileSubstrate/DynamicLibraries/OrionSpringboard.dylib"
22 | #define ArtsyPreferencesFile \
23 | @"/var/mobile/Library/Preferences/ch.mdaus.artsy.plist"
24 | #define QuartPreferencesFile \
25 | @"/var/mobile/Library/Preferences/com.laughingquoll.quartprefs.plist"
26 | #define PrysmPreferencesFile \
27 | @"/var/mobile/Library/Preferences/com.laughingquoll.prysmprefs.plist"
28 |
29 | @interface MRUCoverSheetViewController : UIViewController
30 | @property(retain, nonatomic) MSHFView *mshfView;
31 | @end
32 |
33 | @interface CSMediaControlsViewController : UIViewController
34 | @property(retain, nonatomic) MSHFView *mshfView;
35 | @end
36 |
37 | @interface MRPlatterViewController : UIViewController
38 | @property(retain, nonatomic) MSHFView *mshfView;
39 | @end
40 |
41 | @interface MRUNowPlayingViewController : UIViewController
42 | @property(retain, nonatomic) MSHFView *mshfView;
43 | @end
44 |
45 | @interface MediaControlsPanelViewController : UIViewController
46 | @property(retain, nonatomic) MSHFView *mshfView;
47 | @end
48 |
49 | @interface MediaControlsInteractionRecognizer : UIGestureRecognizer
50 | @end
51 |
52 | @interface SBDashBoardMediaControlsViewController : UIViewController
53 | @property(retain, nonatomic) MSHFView *mshfView;
54 | @end
55 |
56 | @interface QRTMediaModuleViewController : UIViewController
57 | @property(strong, nonatomic) MSHFView *mshfView;
58 | @property(strong, nonatomic) UIImageView *artworkView;
59 | @end
60 |
61 | @interface SBDashBoardFixedFooterViewController : UIViewController
62 |
63 | @property(strong, nonatomic) MSHFView *mshfview;
64 |
65 | @end
66 |
67 | @interface CSFixedFooterViewController : UIViewController
68 |
69 | @property(strong, nonatomic) MSHFView *mshfview;
70 |
71 | @end
72 |
73 | @interface CFWPrefsManager : NSObject
74 | + (id)sharedInstance;
75 | - (BOOL)lockScreenFullScreenEnabled;
76 | @end
77 |
78 | @interface OrionColorizer : NSObject
79 | @property (nonatomic, strong, readonly) UIColor *secondaryColor;
80 | + (id)sharedInstance;
81 | @end
82 |
83 | @interface CFWColorInfo : NSObject
84 | @property (nonatomic, strong, readwrite) UIColor *primaryColor;
85 | @end
86 |
87 | @interface CFWMusicStateManager : NSObject
88 | @property (atomic, strong, readonly) CFWColorInfo *colorInfo;
89 | + (id)sharedInstance;
90 | @end
91 |
92 | @interface SBIconController : UIViewController
93 | @property (nonatomic,retain) MSHFView * mshfView;
94 | @end
95 |
96 | @interface MRUControlCenterViewController : UIViewController
97 | @property (nonatomic,retain) MSHFView * mshfView;
98 | @end
99 |
100 | @interface PrysmMediaModuleViewController : UIViewController
101 | @property (nonatomic,retain) MSHFView * mshfView;
102 | @end
--------------------------------------------------------------------------------
/Prefs/Resources/Prefs.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | items
6 |
7 |
8 | cell
9 | PSGroupCell
10 | headerCellClass
11 | MSHFPackageNameHeaderCell
12 | packageIdentifier
13 | com.ryannair05.mitsuhaforever
14 |
16 | titleColor
17 | #ffffff
18 | subtitleColor
19 | #e0e0e0
20 | backgroundGradientColors
21 |
22 | #ee645c
23 | #ee645c
24 |
25 |
26 |
27 |
28 |
29 | cell
30 | PSGroupCell
31 | label
32 | Configuration
33 | isStaticText
34 |
35 | id
36 | apps
37 |
38 |
39 | cell
40 | PSSwitchCell
41 | default
42 |
43 | defaults
44 | com.ryannair05.mitsuhaforever
45 | label
46 | Enable sensitivity boost (AirPods 2 + Pro)
47 | key
48 | MSHFAirpodsSensBoost
49 | alternateColors
50 |
51 |
52 |
53 |
54 |
55 | cell
56 | PSGroupCell
57 | label
58 | Other
59 |
60 |
61 | action
62 | respring:
63 | cell
64 | PSButtonCell
65 | cellClass
66 | MSHFTintedTableCell
67 | label
68 | Respring
69 |
70 |
71 | action
72 | restartmsd:
73 | cell
74 | PSButtonCell
75 | cellClass
76 | MSHFTintedTableCell
77 | label
78 | Restart mediaserverd
79 |
80 |
81 | action
82 | resetPrefs:
83 | cell
84 | PSButtonCell
85 | cellClass
86 | MSHFTintedTableCell
87 | isDestructive
88 |
89 | confirmation
90 |
91 | prompt
92 | Are you sure you want to reset all your Mitsuha Forever preferences? This can not be undone.
93 | title
94 | OK
95 | okTitle
96 | OK
97 | cancelTitle
98 | Cancel
99 |
100 | label
101 | Restore default settings and respring
102 |
103 |
104 | cellClass
105 | MSHFTwitterCell
106 | label
107 | Ryan Nair
108 | subtitle
109 | Current Developer of Mitsuha Forever
110 | user
111 | ryannair05
112 |
113 |
114 | cellClass
115 | MSHFLinkTableCell
116 | label
117 | Github
118 | subtitle
119 | Source code
120 | url
121 | https://github.com/ryannair05/MitsuhaForever
122 |
123 |
124 | cellClass
125 | MSHFLinkTableCell
126 | label
127 | Donate to c0ldra1n
128 | subtitle
129 | Original developer of Mitsuha for iOS 9 and 10
130 | url
131 | https://www.paypal.me/c0ldra1n
132 |
133 |
134 | title
135 | Mitsuha Forever
136 |
137 |
--------------------------------------------------------------------------------
/Springboard/ControlCenterTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 | #import
3 | #import
4 | #import
5 |
6 | static MSHFConfig *mshConfig;
7 |
8 | %group SBMediaHook
9 | %hook SBMediaController
10 |
11 | -(void)setNowPlayingInfo:(id)arg1 {
12 | %orig;
13 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
14 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
15 | [mshConfig colorizeView:[UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)]];
16 | }
17 | });
18 | }
19 | %end
20 | %end
21 |
22 | %group Prysm
23 |
24 | %hook PrysmMainPageViewController
25 | - (void)setIsPresented:(BOOL)isPresented {
26 | %orig;
27 | if (isPresented) {
28 | [mshConfig.view start];
29 | }
30 | else {
31 | [mshConfig.view stop];
32 | }
33 | }
34 | %end
35 |
36 | %hook PrysmMediaModuleViewController
37 |
38 | %property (strong,nonatomic) MSHFView *mshfView;
39 |
40 | -(void)viewDidLoad {
41 |
42 | %orig;
43 |
44 | if (![mshConfig view]) [mshConfig initializeViewWithFrame:self.view.bounds];
45 | self.mshfView = [mshConfig view];
46 |
47 | [self.view addSubview:self.mshfView];
48 | [self.view bringSubviewToFront:self.mshfView];
49 |
50 | self.mshfView.translatesAutoresizingMaskIntoConstraints = NO;
51 | [self.mshfView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
52 | [self.mshfView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
53 | [self.mshfView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
54 | [NSLayoutConstraint constraintWithItem:self.mshfView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5 constant:-mshConfig.waveOffset].active = YES;
55 | }
56 | %end
57 | %end
58 |
59 | %group ControlCenter
60 |
61 | %hook MRUControlCenterViewController
62 |
63 | %property (strong,nonatomic) MSHFView *mshfView;
64 |
65 | -(void)loadView{
66 | %orig;
67 | if (![mshConfig view]) [mshConfig initializeViewWithFrame:self.view.subviews[2].bounds];
68 | self.mshfView = [mshConfig view];
69 |
70 | self.mshfView.layer.cornerRadius = 19;
71 |
72 | #pragma clang diagnostic push
73 | #pragma clang diagnostic ignored "-Wunguarded-availability-new"
74 | self.mshfView.layer.cornerCurve = kCACornerCurveCircular;
75 | #pragma clang diagnostic pop
76 |
77 | self.mshfView.layer.maskedCorners = kCALayerMaxXMaxYCorner | kCALayerMinXMaxYCorner;
78 | self.mshfView.layer.masksToBounds = TRUE;
79 |
80 | [self.view.subviews[2] addSubview:self.mshfView];
81 | [self.view.subviews[2] bringSubviewToFront:self.mshfView];
82 |
83 | self.mshfView.translatesAutoresizingMaskIntoConstraints = NO;
84 | [self.mshfView.leadingAnchor constraintEqualToAnchor:self.view.subviews[2].leadingAnchor].active = YES;
85 | [self.mshfView.trailingAnchor constraintEqualToAnchor:self.view.subviews[2].trailingAnchor].active = YES;
86 | [self.mshfView.bottomAnchor constraintEqualToAnchor:self.view.subviews[2].bottomAnchor].active = YES;
87 | [NSLayoutConstraint constraintWithItem:self.mshfView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view.subviews[2] attribute:NSLayoutAttributeHeight multiplier:0.5 constant:-mshConfig.waveOffset].active = YES;
88 | }
89 | - (void)setState:(NSInteger)arg1 {
90 | %orig;
91 | if (arg1 == 1) {
92 | self.mshfView.layer.cornerRadius = 38;
93 | }
94 | else {
95 | self.mshfView.layer.cornerRadius = 19;
96 | }
97 |
98 | }
99 | -(void)viewWillAppear:(BOOL)animated {
100 | %orig;
101 | [[self mshfView] start];
102 | }
103 | %end
104 |
105 | %hook SBControlCenterController
106 | -(void)_didDismiss {
107 | %orig;
108 | [mshConfig.view stop];
109 | }
110 | %end
111 | %end
112 |
113 | %ctor{
114 |
115 | bool prysmEnabled;
116 |
117 | if ([[NSFileManager defaultManager] fileExistsAtPath:PrysmTweakDylibFile]) {
118 | mshConfig = [MSHFConfig loadConfigForApplication:@"ControlCenter"];
119 | prysmEnabled = TRUE;
120 |
121 | NSDictionary *prysmPrefs = [[NSDictionary alloc] initWithContentsOfFile:PrysmPreferencesFile];
122 | if (prysmPrefs) {
123 | if (![([prysmPrefs objectForKey:@"enable"] ?: @(YES)) boolValue]) {
124 | prysmEnabled = false;
125 | }
126 | }
127 | }
128 | else if (@available(iOS 14.2, *)) {
129 | mshConfig = [MSHFConfig loadConfigForApplication:@"ControlCenter"];
130 | }
131 |
132 | if (mshConfig && mshConfig.enabled) {
133 | %init(SBMediaHook);
134 |
135 | if (prysmEnabled) {
136 | dlopen("/Library/Prysm/Bundles/com.laughingquoll.prysm.PrysmMedia.bundle/PrysmMedia", RTLD_NOW);
137 | dlopen("/Library/MobileSubstrate/DynamicLibraries/Prysm.dylib", RTLD_NOW);
138 | %init(Prysm);
139 | }
140 | else %init(ControlCenter);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/Music/MusicTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 |
3 | static MSHFConfig *config = NULL;
4 | static bool const colorflow = [%c(CFWPrefsManager) class] && MSHookIvar([%c(CFWPrefsManager) sharedInstance], "_musicEnabled");
5 |
6 | %group MitsuhaVisuals
7 |
8 | %hook MusicArtworkComponentImageView
9 |
10 | -(void)setImage:(id)arg1 {
11 | %orig;
12 | if ([config view] == NULL) return;
13 |
14 | NSString *musicString;
15 |
16 | if (@available(iOS 13.0, *))
17 | musicString = @"MusicApplication.NowPlayingContentView";
18 | else
19 | musicString = @"Music.NowPlayingContentView";
20 |
21 | UIView *me = (UIView *)self;
22 |
23 | if ([NSStringFromClass([me.superview class]) isEqualToString:musicString]) {
24 | if (colorflow && config.colorMode == 0) {
25 | UIColor *color = [[[%c(CFWMusicStateManager) sharedInstance] colorInfo] primaryColor];
26 | [[config view] updateWaveColor:color subwaveColor:color];
27 | }
28 | else if (config.colorMode != 2) {
29 | [config colorizeView:((MusicArtworkComponentImageView*)self).image];
30 | }
31 |
32 | }
33 |
34 | }
35 |
36 | %end
37 |
38 | %hook MusicNowPlayingControlsViewController
39 | %property (retain,nonatomic) MSHFView *mshfView;
40 |
41 | - (instancetype)init{
42 |
43 | self = %orig;
44 |
45 | if (self) {
46 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMitsuhaApplicationState:) name:UIApplicationDidBecomeActiveNotification object:nil];
47 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMitsuhaApplicationState:) name:UIApplicationDidEnterBackgroundNotification object:nil];
48 | }
49 |
50 | return self;
51 | }
52 |
53 | %new
54 | - (void)handleMitsuhaApplicationState:(NSNotification *)notification{
55 | if ([notification.name isEqualToString:UIApplicationDidBecomeActiveNotification]) {
56 | [[config view] start];
57 |
58 | } else {
59 | [[config view] stop];
60 | }
61 | }
62 |
63 | - (void)dealloc{
64 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
65 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
66 | %orig;
67 | }
68 |
69 | -(void)viewDidLoad{
70 | %orig;
71 |
72 | if(colorflow) {
73 | self.view.subviews[3].clipsToBounds = 1;
74 | [config initializeViewWithFrame:CGRectMake(0, -150, self.view.frame.size.width, (self.view.frame.size.height / 2) - 100)];
75 |
76 | self.mshfView = [config view];
77 | [self.view.subviews[3] addSubview:[config view]];
78 | [self.view.subviews[3] sendSubviewToBack:[config view]];
79 |
80 | if(self.mshfView.superview == NULL) {
81 | self.mshfView = [config view];
82 | [self.view addSubview:[config view]];
83 | [self.view sendSubviewToBack:[config view]];
84 | }
85 | } else {
86 | CGSize const screenSize = [[UIScreen mainScreen] bounds].size;
87 |
88 | self.view.clipsToBounds = 1;
89 |
90 | [config initializeViewWithFrame:CGRectMake(0, 0, screenSize.width, screenSize.height)];
91 |
92 | self.mshfView = [config view];
93 | [self.view addSubview:[config view]];
94 | [self.view sendSubviewToBack:[config view]];
95 |
96 | if (@available(iOS 14.0, *)) {
97 | return;
98 | }
99 |
100 | self.view.subviews[3].backgroundColor = [UIColor clearColor];
101 | self.view.subviews[4].backgroundColor = [UIColor clearColor];
102 | }
103 | }
104 |
105 | -(void)viewWillAppear:(BOOL)animated{
106 | %orig;
107 | [[config view] start];
108 | [config view].center = CGPointMake([config view].center.x, [config view].frame.size.height*2);
109 | }
110 |
111 | -(void)viewDidAppear:(BOOL)animated{
112 | %orig;
113 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
114 | if(colorflow) {
115 | [config view].center = CGPointMake([config view].center.x, 150);
116 | } else {
117 | [config view].center = CGPointMake([config view].center.x, [config view].frame.size.height);
118 | }
119 | } completion:nil];
120 | }
121 |
122 | -(void)viewWillDisappear:(BOOL)animated{
123 | %orig;
124 | [[config view] stop];
125 | }
126 |
127 | -(void)viewDidLayoutSubviews {
128 | %orig;
129 | if (@available(iOS 14.0, *)) {
130 | return;
131 | }
132 | if(!colorflow) {
133 | self.view.subviews[3].backgroundColor = [UIColor clearColor];
134 | self.view.subviews[4].backgroundColor = [UIColor clearColor];
135 | }
136 | }
137 |
138 | %end
139 |
140 | %end
141 |
142 | %ctor{
143 | config = [MSHFConfig loadConfigForApplication:@"Music"];
144 | config.waveOffsetOffset = 70;
145 | if(config.enabled){
146 | NSString *classString = nil;
147 | if(@available(iOS 13.0, *)) {
148 | classString = @"MusicApplication.ArtworkComponentImageView";
149 | } else {
150 | classString = @"Music.ArtworkComponentImageView";
151 | }
152 |
153 | %init(MitsuhaVisuals, MusicArtworkComponentImageView = NSClassFromString(classString));
154 | }
155 | }
--------------------------------------------------------------------------------
/Spotify/SpotifyTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 | #define CFWBackgroundViewTagNumber 896541
3 |
4 | bool MSHFColorFlowSpotifyEnabled = NO;
5 |
6 | %group MitsuhaVisuals
7 |
8 | MSHFConfig *config = NULL;
9 |
10 | %hook SPTNowPlayingCoverArtImageView
11 |
12 | -(void)setImage:(UIImage*)image {
13 | %orig;
14 | [config colorizeView:image];
15 | }
16 |
17 | %end
18 |
19 | %hook SPTVideoDisplayView
20 | - (void)refreshVideoRect {
21 | %orig;
22 |
23 | AVPlayer *displayView = [self player];
24 | AVAsset *asset = displayView.currentItem.asset;
25 |
26 | AVAssetImageGenerator* generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
27 | generator.appliesPreferredTrackTransform = YES;
28 | UIImage* image = [UIImage imageWithCGImage:[generator copyCGImageAtTime:CMTimeMake(0, 1) actualTime:nil error:nil]];
29 | if (image) [config colorizeView:image];
30 | }
31 |
32 | %end
33 |
34 | %hook SPTNowPlayingViewController
35 |
36 | %property (retain,nonatomic) MSHFView *mshfview;
37 |
38 | -(void)viewDidLoad{
39 | %orig;
40 |
41 | NSLog(@"[Mitsuha]: viewDidLoad");
42 |
43 | if (![config view]) [config initializeViewWithFrame:CGRectMake(0, config.waveOffset, self.view.bounds.size.width, self.view.bounds.size.height)];
44 | self.mshfview = [config view];
45 | [self.mshfview setUserInteractionEnabled:NO];
46 |
47 | [self.view insertSubview:self.mshfview atIndex:1];
48 |
49 | self.mshfview.translatesAutoresizingMaskIntoConstraints = NO;
50 | [self.mshfview.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
51 | [self.mshfview.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
52 | [self.mshfview.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
53 | [self.mshfview.heightAnchor constraintEqualToConstant:self.mshfview.frame.size.height].active = YES;
54 |
55 | }
56 |
57 | -(void)viewWillAppear:(BOOL)animated{
58 | [[config view] start];
59 | %orig;
60 | }
61 |
62 | -(void)viewDidAppear:(BOOL)animated{
63 | %orig;
64 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
65 |
66 | [config view].center = CGPointMake([config view].center.x, [config view].frame.size.height/2 + config.waveOffset);
67 |
68 | } completion:nil];
69 |
70 | [[config view] resetWaveLayers];
71 |
72 | if (config.colorMode == 1) {
73 | [config colorizeView:nil];
74 | }
75 | // Copied from NowPlayingImpl
76 | else if(MSHFColorFlowSpotifyEnabled){
77 | CFWSpotifyStateManager *stateManager = [%c(CFWSpotifyStateManager) sharedManager];
78 | UIColor *backgroundColor = [stateManager.mainColorInfo.backgroundColor colorWithAlphaComponent:0.5];
79 | [[config view] updateWaveColor:backgroundColor subwaveColor:backgroundColor];
80 | }
81 | }
82 |
83 | -(void)viewWillDisappear:(BOOL)animated{
84 | %orig;
85 | [[config view] stop];
86 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
87 | [config view].center = CGPointMake([config view].center.x, [config view].frame.size.height + config.waveOffset);
88 | } completion:^(BOOL finished){
89 | }];
90 | }
91 |
92 | %end
93 | %end
94 |
95 | %group MitsuhaSpotifyCoverArtFix
96 |
97 | %hook SPTNowPlayingCarouselAreaViewController
98 |
99 | static CGFloat originalCenterY = 0;
100 |
101 | -(void)viewWillAppear:(BOOL)animated{
102 | %orig;
103 |
104 | NSLog(@"[Mitsuha]: originalCenterY: %lf", originalCenterY);
105 |
106 | CGPoint center = self.view.coverArtView.center;
107 |
108 | self.view.coverArtView.alpha = 0;
109 | self.view.coverArtView.center = CGPointMake(center.x, originalCenterY);
110 | }
111 |
112 | -(void)viewDidAppear:(BOOL)animated{
113 | %orig;
114 |
115 | NSLog(@"[Mitsuha]: viewDidAppear");
116 |
117 | CGPoint center = self.view.coverArtView.center;
118 |
119 | if(originalCenterY == 0){
120 | originalCenterY = center.y;
121 | }
122 |
123 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
124 | self.view.coverArtView.alpha = 1.0;
125 | self.view.coverArtView.center = CGPointMake(center.x, originalCenterY * 0.8);
126 | } completion:^(BOOL finished){
127 | if(self.view.coverArtView.center.y != originalCenterY * 0.8){ // For some reason I can't explain
128 | [UIView animateWithDuration:0.25 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
129 | self.view.coverArtView.center = CGPointMake(center.x, originalCenterY * 0.8);
130 | } completion:nil];
131 | }
132 | }];
133 | }
134 |
135 | -(void)viewWillDisappear:(BOOL)animated{
136 | %orig;
137 |
138 | CGPoint center = self.view.coverArtView.center;
139 |
140 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:3.5 initialSpringVelocity:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
141 | self.view.coverArtView.alpha = 0;
142 | self.view.coverArtView.center = CGPointMake(center.x, originalCenterY);
143 | } completion:nil];
144 | }
145 |
146 | %end
147 |
148 | %end
149 |
150 | %ctor{
151 | config = [MSHFConfig loadConfigForApplication:@"Spotify"];
152 |
153 | if(config.enabled){
154 | config.waveOffsetOffset = 520;
155 |
156 | if ([%c(CFWPrefsManager) class] && MSHookIvar([%c(CFWPrefsManager) sharedInstance], "_spotifyEnabled") && !config.ignoreColorFlow) {
157 | MSHFColorFlowSpotifyEnabled = YES;
158 | }
159 | %init(MitsuhaVisuals);
160 | if (config.enableCoverArtBugFix) %init(MitsuhaSpotifyCoverArtFix)
161 |
162 | }
163 | }
--------------------------------------------------------------------------------
/Prefs/MSHFAppPrefsListController.m:
--------------------------------------------------------------------------------
1 | #import "MSHFAppPrefsListController.h"
2 |
3 | @implementation MSHFAppPrefsListController
4 |
5 | - (NSArray *)specifiers {
6 | return _specifiers;
7 | }
8 |
9 | - (void)setSpecifier:(PSSpecifier *)specifier {
10 | [super setSpecifier:specifier];
11 |
12 | self.appName = [specifier propertyForKey:@"MSHFApp"];
13 | if (!self.appName) return;
14 | NSString *prefix = [@"MSHF" stringByAppendingString:self.appName];
15 | NSString *title = [specifier name];
16 | self.savedSpecifiers = [[NSMutableDictionary alloc] init];
17 |
18 | _specifiers = [self loadSpecifiersFromPlistName:@"App" target:self];
19 |
20 | for (PSSpecifier *specifier in _specifiers) {
21 | NSString *key = [specifier propertyForKey:@"key"];
22 | if (key) {
23 | [specifier setProperty:[prefix stringByAppendingString:key] forKey:@"key"];
24 | }
25 |
26 | if ([specifier.name isEqualToString:@"%APP_NAME%"]) {
27 | specifier.name = title;
28 | }
29 |
30 | else if ([specifier propertyForKey:@"id"]) {
31 | [self.savedSpecifiers setObject:specifier forKey:[specifier propertyForKey:@"id"]];
32 | }
33 | }
34 |
35 | NSArray *extra = [self loadSpecifiersFromPlistName:[NSString stringWithFormat:@"Apps/%@", self.appName] target:self];
36 | if (extra) {
37 | for (PSSpecifier *specifier in extra) {
38 | [self insertSpecifier:specifier afterSpecifierID:@"otherSettings"];
39 | }
40 | }
41 |
42 | [self setTitle:title];
43 | }
44 |
45 | -(void)removeBarText:(bool)animated {
46 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"BarText"]] animated:animated];
47 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"BarSpacingText"]] animated:animated];
48 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"BarSpacing"]] animated:animated];
49 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"BarRadiusText"]] animated:animated];
50 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"BarRadius"]] animated:animated];
51 | }
52 | -(void)removeLineText:(bool)animated {
53 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"LineText"]] animated:animated];
54 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"LineThicknessText"]] animated:animated];
55 | [self removeContiguousSpecifiers:@[self.savedSpecifiers[@"LineThickness"]] animated:animated];
56 | }
57 |
58 | -(void)viewDidLoad {
59 | [super viewDidLoad];
60 |
61 | MSHFConfig *config = [MSHFConfig loadConfigForApplication:self.appName];
62 |
63 | if (config.style != 1) {
64 | [self removeBarText:NO];
65 | if (config.style != 2) {
66 | [self removeLineText:NO];
67 | }
68 | } else {
69 | [self removeLineText:NO];
70 | }
71 | }
72 |
73 | - (void)viewDidAppear:(BOOL)animated {
74 | [super viewDidAppear:animated];
75 | self.table.separatorColor = [UIColor colorWithWhite:0 alpha:0];
76 |
77 | UIWindow *keyWindow = [[[UIApplication sharedApplication] windows] firstObject];
78 |
79 | if ([keyWindow respondsToSelector:@selector(setTintColor:)]) {
80 | keyWindow.tintColor = [UIColor colorWithRed:238.0f / 255.0f
81 | green:100.0f / 255.0f
82 | blue:92.0f / 255.0f
83 | alpha:1];
84 | }
85 |
86 | [UISwitch appearanceWhenContainedInInstancesOfClasses:@[self.class]].onTintColor = [UIColor colorWithRed:238.0f / 255.0f
87 | green:100.0f / 255.0f
88 | blue:92.0f / 255.0f
89 | alpha:1];
90 |
91 |
92 | if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) {
93 | self.edgesForExtendedLayout = UIRectEdgeNone;
94 | }
95 | }
96 |
97 | - (id)readPreferenceValue:(PSSpecifier*)specifier {
98 | NSString *path = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", specifier.properties[@"defaults"]];
99 | NSMutableDictionary *settings = [NSMutableDictionary dictionary];
100 | [settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
101 |
102 | return ([settings objectForKey:specifier.properties[@"key"]]) ?: specifier.properties[@"default"];
103 | }
104 |
105 | - (void)setPreferenceValue:(id)value specifier:(PSSpecifier*)specifier {
106 |
107 | NSString *path = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", specifier.properties[@"defaults"]];
108 | NSMutableDictionary *settings = [NSMutableDictionary dictionary];
109 | [settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
110 |
111 | [settings setObject:value forKey:specifier.properties[@"key"]];
112 | [settings writeToFile:path atomically:YES];
113 |
114 | CFStringRef notificationName = (__bridge CFStringRef) specifier.properties[@"PostNotification"];
115 | if (notificationName) {
116 | // [[NSNotificationCenter defaultCenter] postNotificationName:@"com.ryannair05.mitsuhaforever/ReloadPrefs" object:nil];
117 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), notificationName, NULL, NULL, YES);
118 | }
119 |
120 | NSString const *key = [specifier propertyForKey:@"key"];
121 |
122 | if ([key containsString:@"Style"]){
123 | if ([value integerValue] == 1) {
124 | if (![self containsSpecifier:self.savedSpecifiers[@"BarText"]]) {
125 |
126 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"BarText"]] afterSpecifierID:@"NumberOfPoints" animated:YES];
127 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"BarSpacingText"]] afterSpecifierID:@"BarText" animated:YES];
128 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"BarSpacing"]] afterSpecifierID:@"BarSpacingText" animated:YES];
129 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"BarRadiusText"]] afterSpecifierID:@"BarSpacing" animated:YES];
130 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"BarRadius"]] afterSpecifierID:@"BarRadiusText" animated:YES];
131 |
132 | if ([self containsSpecifier:self.savedSpecifiers[@"LineText"]]) {
133 | [self removeLineText:YES];
134 | }
135 | }
136 | }
137 | else if ([value integerValue] == 2) {
138 |
139 | if (![self containsSpecifier:self.savedSpecifiers[@"LineText"]]) {
140 |
141 | if ([self containsSpecifier:self.savedSpecifiers[@"BarText"]]) {
142 | [self removeBarText:YES];
143 | }
144 |
145 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"LineText"]] afterSpecifierID:@"NumberOfPoints" animated:YES];
146 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"LineThicknessText"]] afterSpecifierID:@"LineText" animated:YES];
147 | [self insertContiguousSpecifiers:@[self.savedSpecifiers[@"LineThickness"]] afterSpecifierID:@"LineThicknessText" animated:YES];
148 |
149 | }
150 | }
151 | else if ([self containsSpecifier:self.savedSpecifiers[@"BarText"]]) {
152 | [self removeBarText:YES];
153 | }
154 | else if ([self containsSpecifier:self.savedSpecifiers[@"LineText"]]) {
155 | [self removeLineText:YES];
156 | }
157 | }
158 |
159 | }
160 |
161 | - (bool)shouldReloadSpecifiersOnResume {
162 | return NO;
163 | }
164 | @end
165 |
--------------------------------------------------------------------------------
/Prefs/Resources/App.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | items
6 |
7 |
8 | cell
9 | PSGroupCell
10 | label
11 | %APP_NAME%
12 | isStaticText
13 |
14 |
15 |
16 | cell
17 | PSSwitchCell
18 | default
19 |
20 | defaults
21 | com.ryannair05.mitsuhaforever
22 | label
23 | Enabled
24 | key
25 | Enabled
26 | alternateColors
27 |
28 |
29 |
30 | cell
31 | PSSwitchCell
32 | default
33 |
34 | defaults
35 | com.ryannair05.mitsuhaforever
36 | label
37 | FFT (frequency instead of amplitude)
38 | key
39 | EnableFFT
40 | alternateColors
41 |
42 | PostNotification
43 | com.ryannair05.mitsuhaforever/ReloadPrefs
44 |
45 |
46 | cell
47 | PSSwitchCell
48 | default
49 |
50 | defaults
51 | com.ryannair05.mitsuhaforever
52 | label
53 | Automatically hide the visualizer
54 | key
55 | EnableAutoHide
56 | alternateColors
57 |
58 | PostNotification
59 | com.ryannair05.mitsuhaforever/ReloadPrefs
60 |
61 |
62 | cell
63 | PSGroupCell
64 | label
65 | Style
66 | isStaticText
67 |
68 |
69 |
70 | cell
71 | PSSegmentCell
72 | default
73 | 0
74 | defaults
75 | com.ryannair05.mitsuhaforever
76 | key
77 | Style
78 | validValues
79 |
80 | 0
81 | 1
82 | 2
83 | 3
84 | 4
85 |
86 | validTitles
87 |
88 | Wave
89 | Bar
90 | Line
91 | Dot
92 | Siri
93 |
94 | alignment
95 | 5
96 | PostNotification
97 | com.ryannair05.mitsuhaforever/ReloadPrefs
98 |
99 |
100 | cell
101 | PSStaticTextCell
102 | label
103 | Number of points
104 |
105 |
106 | cell
107 | PSSliderCell
108 | label
109 | Number of points
110 | default
111 | 8
112 | defaults
113 | com.ryannair05.mitsuhaforever
114 | key
115 | NumberOfPoints
116 | min
117 | 4
118 | max
119 | 32
120 | showValue
121 |
122 | isSegmented
123 |
124 | id
125 | NumberOfPoints
126 | PostNotification
127 | com.ryannair05.mitsuhaforever/ReloadPrefs
128 |
129 |
130 | cell
131 | PSGroupCell
132 | label
133 | Bar
134 | isStaticText
135 |
136 | id
137 | BarText
138 |
139 |
140 | cell
141 | PSStaticTextCell
142 | label
143 | Bar spacing
144 | id
145 | BarSpacingText
146 |
147 |
148 | cell
149 | PSSliderCell
150 | label
151 | Bar spacing
152 | default
153 | 5
154 | defaults
155 | com.ryannair05.mitsuhaforever
156 | key
157 | BarSpacing
158 | min
159 | 1
160 | max
161 | 10
162 | showValue
163 |
164 | isSegmented
165 |
166 | id
167 | BarSpacing
168 | PostNotification
169 | com.ryannair05.mitsuhaforever/ReloadPrefs
170 |
171 |
172 | cell
173 | PSStaticTextCell
174 | label
175 | Bar corner radius
176 | id
177 | BarRadiusText
178 |
179 |
180 | cell
181 | PSSliderCell
182 | label
183 | Bar corner radius
184 | default
185 | 0
186 | defaults
187 | com.ryannair05.mitsuhaforever
188 | key
189 | BarCornerRadius
190 | min
191 | 0
192 | max
193 | 30
194 | showValue
195 |
196 | isSegmented
197 |
198 | id
199 | BarRadius
200 | PostNotification
201 | com.ryannair05.mitsuhaforever/ReloadPrefs
202 |
203 |
204 | cell
205 | PSGroupCell
206 | label
207 | Line
208 | isStaticText
209 |
210 | id
211 | LineText
212 |
213 |
214 | cell
215 | PSStaticTextCell
216 | label
217 | Line thickness
218 | id
219 | LineThicknessText
220 |
221 |
222 | cell
223 | PSSliderCell
224 | label
225 | Line thickness
226 | default
227 | 5
228 | defaults
229 | com.ryannair05.mitsuhaforever
230 | key
231 | LineThickness
232 | min
233 | 1
234 | max
235 | 10
236 | showValue
237 |
238 | isSegmented
239 |
240 | id
241 | LineThickness
242 | PostNotification
243 | com.ryannair05.mitsuhaforever/ReloadPrefs
244 |
245 |
246 | cell
247 | PSGroupCell
248 | label
249 | Color
250 | isStaticText
251 |
252 | footerText
253 | Siri Style and Color, credit to biD3V for development
254 |
255 |
256 | cell
257 | PSSegmentCell
258 | default
259 | 0
260 | defaults
261 | com.ryannair05.mitsuhaforever
262 | key
263 | ColorMode
264 | validValues
265 |
266 | 0
267 | 1
268 | 2
269 |
270 | validTitles
271 |
272 | Dynamic
273 | Siri
274 | Custom
275 |
276 | alignment
277 | 2
278 | PostNotification
279 | com.ryannair05.mitsuhaforever/ReloadPrefs
280 |
281 |
282 | cell
283 | PSStaticTextCell
284 | label
285 | Dynamic color alpha
286 |
287 |
288 | cell
289 | PSSliderCell
290 | label
291 | Dynamic color alpha
292 | default
293 | 0.6
294 | defaults
295 | com.ryannair05.mitsuhaforever
296 | key
297 | DynamicColorAlpha
298 | min
299 | 0.1
300 | max
301 | 1
302 | showValue
303 |
304 | isSegmented
305 |
306 |
307 |
308 | cell
309 | PSLinkCell
310 | cellClass
311 | HBColorPickerTableCell
312 | defaults
313 | com.ryannair05.mitsuhaforever
314 | default
315 | #000000:0.5
316 | key
317 | WaveColor
318 | label
319 | Wave Color
320 | showAlphaSlider
321 |
322 | PostNotification
323 | com.ryannair05.mitsuhaforever/ReloadPrefs
324 |
325 |
326 | cell
327 | PSGroupCell
328 | label
329 | Other
330 | isStaticText
331 |
332 | id
333 | otherSettings
334 |
335 |
336 | cell
337 | PSStaticTextCell
338 | label
339 | Wave offset (top)
340 |
341 |
342 | cell
343 | PSSliderCell
344 | label
345 | Wave offset (top)
346 | defaults
347 | com.ryannair05.mitsuhaforever
348 | key
349 | WaveOffset
350 | min
351 | -150
352 | max
353 | 150
354 | showValue
355 |
356 | isSegmented
357 |
358 | PostNotification
359 | com.ryannair05.mitsuhaforever/ReloadPrefs
360 |
361 |
362 | cell
363 | PSStaticTextCell
364 | label
365 | Visualizer speed/frames per second
366 |
367 |
368 | cell
369 | PSSliderCell
370 | label
371 | Visualizer speed/frames per second
372 | default
373 | 24
374 | defaults
375 | com.ryannair05.mitsuhaforever
376 | key
377 | Fps
378 | min
379 | 5
380 | max
381 | 120
382 | showValue
383 |
384 | isSegmented
385 |
386 | PostNotification
387 | com.ryannair05.mitsuhaforever/ReloadPrefs
388 |
389 |
390 | cell
391 | PSStaticTextCell
392 | label
393 | Sensitivity
394 |
395 |
396 | cell
397 | PSSliderCell
398 | label
399 | Sensitivity
400 | default
401 | 1
402 | defaults
403 | com.ryannair05.mitsuhaforever
404 | key
405 | Sensitivity
406 | min
407 | 0.25
408 | max
409 | 4
410 | showValue
411 |
412 | isSegmented
413 |
414 | PostNotification
415 | com.ryannair05.mitsuhaforever/ReloadPrefs
416 |
417 |
418 | cell
419 | PSSwitchCell
420 | default
421 |
422 | defaults
423 | com.ryannair05.mitsuhaforever
424 | label
425 | Disable battery saver
426 | key
427 | DisableBatterySaver
428 | alternateColors
429 |
430 | PostNotification
431 | com.ryannair05.mitsuhaforever/ReloadPrefs
432 |
433 |
434 |
435 | cell
436 | PSGroupCell
437 | footerText
438 | Mitsuha, Mitsuha2 by c0ldra1n
439 | MitsuhaXI, Mitsuha Infinity by Nepeta
440 | Mitsuha Forever by ConorTheDev
441 | Mitsuha Forever updated by Ryan Nair
442 | isStaticText
443 |
444 |
445 |
446 |
447 |
448 |
--------------------------------------------------------------------------------
/Springboard/SBTweak.xm:
--------------------------------------------------------------------------------
1 | #import "Tweak.h"
2 | #import
3 | #import
4 | #import
5 |
6 | bool moveIntoPanel = false;
7 | static MSHFConfig *SBconfig = NULL;
8 | static MSHFConfig *SBLSconfig = NULL;
9 |
10 | %group ColorFlowMitsuhaVisualsNotification
11 |
12 | %hook CFWSBMediaController
13 |
14 | -(void)setColorInfo:(CFWColorInfo *)colorInfo {
15 | %orig;
16 |
17 | UIColor *backgroundColor = [colorInfo.primaryColor colorWithAlphaComponent:0.5];
18 |
19 | if (SBconfig.colorMode == 0) {
20 | [[SBconfig view] updateWaveColor:backgroundColor subwaveColor:backgroundColor];
21 | }
22 |
23 | if (SBLSconfig.colorMode == 0) {
24 | [[SBLSconfig view] updateWaveColor:backgroundColor subwaveColor:backgroundColor];
25 | }
26 |
27 | }
28 |
29 | %end
30 |
31 | %end
32 |
33 | %group MitsuhaVisualsNotification
34 |
35 | %hook SBMediaController
36 |
37 | -(void)setNowPlayingInfo:(NSDictionary *)arg1 {
38 | %orig;
39 |
40 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
41 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
42 | UIImage *imageToColor = [UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)];
43 |
44 | [SBconfig colorizeView:imageToColor];
45 | [SBLSconfig colorizeView:imageToColor];
46 | }
47 | });
48 | }
49 |
50 | %end
51 |
52 | %end
53 |
54 | %group Quart
55 |
56 | %hook QRTMediaModuleViewController
57 | %property (retain,nonatomic) MSHFView *mshfView;
58 |
59 | -(void)loadView {
60 | %orig;
61 |
62 | if (![SBconfig view]) [SBconfig initializeViewWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
63 | self.mshfView = [SBconfig view];
64 |
65 | [self.view addSubview:self.mshfView];
66 | [self.view sendSubviewToBack:self.mshfView];
67 | }
68 |
69 | -(void)setArtworkContainer:(id)arg1 {
70 |
71 | %orig;
72 | [[SBconfig view] start];
73 | [SBconfig view].center = CGPointMake([SBconfig view].center.x, SBconfig.waveOffset);
74 |
75 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
76 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
77 | [SBconfig colorizeView:[UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)]];
78 | }
79 | });
80 | }
81 |
82 | -(void)viewDidDisappear:(BOOL)animated{
83 | %orig;
84 | [[SBconfig view] stop];
85 | }
86 | %end
87 |
88 | %end
89 |
90 | #import
91 |
92 | %group ios13SB
93 |
94 | %hook CSMediaControlsViewController
95 |
96 | %property (retain,nonatomic) MSHFView *mshfView;
97 |
98 | -(void)loadView{
99 | %orig;
100 | self.view.clipsToBounds = 1;
101 |
102 | MRPlatterViewController *pvc = nil;
103 |
104 | if (@available(iOS 15.0, *)) {
105 | pvc = object_getIvar(self, class_getInstanceVariable([self class], "_mediaRemoteViewController"));
106 | }
107 | else {
108 | pvc = object_getIvar(self, class_getInstanceVariable([self class], "_platterViewController"));
109 | }
110 |
111 | if (![SBconfig view]) [SBconfig initializeViewWithFrame:CGRectMake(-4, 0, self.view.frame.size.width - 8, self.view.frame.size.height)];
112 | self.mshfView = [SBconfig view];
113 |
114 | [pvc.view insertSubview:self.mshfView atIndex:0];
115 |
116 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
117 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
118 | [SBconfig colorizeView:[UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)]];
119 | }
120 | });
121 | }
122 |
123 | -(void)viewWillAppear:(BOOL)animated{
124 | %orig;
125 | self.view.superview.layer.cornerRadius = 13;
126 | #pragma clang diagnostic push
127 | #pragma clang diagnostic ignored "-Wunguarded-availability-new"
128 | self.view.superview.layer.cornerCurve = kCACornerCurveContinuous;
129 | #pragma clang diagnostic pop
130 | self.view.superview.layer.masksToBounds = TRUE;
131 | }
132 |
133 | -(void)viewDidAppear:(BOOL)animated {
134 | %orig;
135 | [[SBconfig view] start];
136 | [SBconfig view].center = CGPointMake([SBconfig view].center.x, SBconfig.waveOffset);
137 | }
138 |
139 | -(void)viewDidDisappear:(BOOL)animated{
140 | %orig;
141 | [[SBconfig view] stop];
142 | }
143 |
144 | - (void)platterViewController:(id)arg1 didReceiveInteractionEvent:(MediaControlsInteractionRecognizer *)arg2 {
145 | %orig;
146 |
147 | if (arg2.state == UIGestureRecognizerStateEnded) {
148 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
149 | if ([[%c(SBMediaController) sharedInstance] isPlaying]) {
150 | [[SBconfig view] start];
151 | }
152 | else {
153 | [[SBconfig view] stop];
154 | }
155 | });
156 | }
157 | }
158 | %end
159 |
160 | %end
161 |
162 | %group oldSB
163 | %hook SBDashBoardMediaControlsViewController
164 |
165 | %property (retain,nonatomic) MSHFView *mshfView;
166 |
167 | %new
168 | -(id)valueForUndefinedKey:(NSString *)key {
169 | return nil;
170 | }
171 |
172 | -(void)loadView{
173 | %orig;
174 | self.view.clipsToBounds = 1;
175 |
176 | MediaControlsPanelViewController *mcpvc = (MediaControlsPanelViewController*)[self valueForKey:@"_mediaControlsPanelViewController"];
177 |
178 | if (!mcpvc && [self valueForKey:@"_platterViewController"]) {
179 | mcpvc = (MediaControlsPanelViewController*)[self valueForKey:@"_platterViewController"];
180 | }
181 |
182 | if (!mcpvc) return;
183 |
184 | if (![SBconfig view]) [SBconfig initializeViewWithFrame:CGRectMake(-4, 0, self.view.frame.size.width - 8, self.view.frame.size.height)];
185 | self.mshfView = [SBconfig view];
186 |
187 | if (!moveIntoPanel) {
188 | [self.view addSubview:self.mshfView];
189 | [self.view sendSubviewToBack:self.mshfView];
190 | } else {
191 | [mcpvc.view insertSubview:self.mshfView atIndex:1];
192 | }
193 |
194 | MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef information) {
195 | if (information && CFDictionaryContainsKey(information, kMRMediaRemoteNowPlayingInfoArtworkData)) {
196 | [SBconfig colorizeView:[UIImage imageWithData:(__bridge NSData*)CFDictionaryGetValue(information, kMRMediaRemoteNowPlayingInfoArtworkData)]];
197 | }
198 | });
199 | }
200 |
201 | -(void)viewWillAppear:(BOOL)animated{
202 | %orig;
203 | self.view.superview.layer.cornerRadius = 13;
204 | self.view.superview.layer.masksToBounds = TRUE;
205 |
206 | [[SBconfig view] start];
207 |
208 | [SBconfig view].center = CGPointMake([SBconfig view].center.x, SBconfig.waveOffset);
209 | }
210 |
211 | // -(void)viewDidAppear:(BOOL)animated {
212 | // [[SBconfig view] start];
213 | // }
214 |
215 | -(void)viewDidDisappear:(BOOL)animated{
216 | %orig;
217 | [[SBconfig view] stop];
218 | }
219 |
220 | %end
221 |
222 | %end
223 |
224 | %group ios13SBLS
225 |
226 | %hook CSFixedFooterViewController
227 |
228 | %property (strong,nonatomic) MSHFView *mshfview;
229 |
230 | -(void)loadView{
231 | %orig;
232 | SBLSconfig.waveOffsetOffset = self.view.bounds.size.height - 200;
233 |
234 | if (![SBLSconfig view]) [SBLSconfig initializeViewWithFrame:self.view.bounds];
235 | self.mshfview = [SBLSconfig view];
236 |
237 | [self.view addSubview:self.mshfview];
238 | [self.view bringSubviewToFront:self.mshfview];
239 |
240 | self.mshfview.translatesAutoresizingMaskIntoConstraints = NO;
241 | [self.mshfview.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
242 | [self.mshfview.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
243 | [self.mshfview.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
244 | [self.mshfview.heightAnchor constraintEqualToConstant:self.mshfview.frame.size.height].active = YES;
245 | }
246 |
247 | -(void)viewWillAppear:(BOOL)animated{
248 | %orig;
249 | if([SBLSconfig view] && [[%c(SBMediaController) sharedInstance] isPlaying]) {
250 | [self.mshfview start];
251 | }
252 | }
253 |
254 | -(void)viewWillDisappear:(BOOL)animated{
255 | %orig;
256 | if([SBLSconfig view]) {
257 | [self.mshfview stop];
258 | }
259 | }
260 |
261 | %end
262 |
263 | %end
264 |
265 |
266 | %group oldSBLS
267 |
268 | %hook SBDashBoardFixedFooterViewController
269 |
270 | %property (strong,nonatomic) MSHFView *mshfview;
271 |
272 | -(void)loadView{
273 | %orig;
274 | SBLSconfig.waveOffsetOffset = self.view.bounds.size.height - 200;
275 |
276 | if (![SBLSconfig view]) [SBLSconfig initializeViewWithFrame:self.view.bounds];
277 | self.mshfview = [SBLSconfig view];
278 |
279 | [self.view addSubview:self.mshfview];
280 | [self.view bringSubviewToFront:self.mshfview];
281 |
282 | self.mshfview.translatesAutoresizingMaskIntoConstraints = NO;
283 | [self.mshfview.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
284 | [self.mshfview.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
285 | [self.mshfview.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
286 | [self.mshfview.heightAnchor constraintEqualToConstant:self.mshfview.frame.size.height].active = YES;
287 | }
288 |
289 | -(void)viewWillAppear:(BOOL)animated{
290 | %orig;
291 | if([SBLSconfig view] && [[%c(SBMediaController) sharedInstance] isPlaying]) {
292 | [self.mshfview start];
293 | }
294 | }
295 |
296 | -(void)viewWillDisappear:(BOOL)animated{
297 | %orig;
298 | if([SBLSconfig view]) {
299 | [self.mshfview stop];
300 | }
301 | }
302 |
303 | %end
304 |
305 | %end
306 |
307 | static void screenDisplayStatus(CFNotificationCenterRef center, void* o, CFStringRef name, const void* object, CFDictionaryRef userInfo) {
308 | uint64_t state;
309 | int token;
310 | notify_register_check("com.apple.iokit.hid.displayStatus", &token);
311 | notify_get_state(token, &state);
312 | notify_cancel(token);
313 | if (![[%c(SBMediaController) sharedInstance] isPlaying]) {
314 | state = false;
315 | }
316 | if (SBLSconfig.enabled) {
317 | if (state) {
318 | [[SBLSconfig view] start];
319 | } else {
320 | [[SBLSconfig view] stop];
321 | }
322 | }
323 | if (SBconfig.enabled) {
324 | if (state) {
325 | [[SBconfig view] start];
326 | } else {
327 | [[SBconfig view] stop];
328 | }
329 | }
330 | }
331 |
332 | static void loadPrefs() {
333 | [SBLSconfig reload];
334 | [SBconfig reload];
335 | }
336 |
337 |
338 | %ctor{
339 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)screenDisplayStatus, (CFStringRef)@"com.apple.iokit.hid.displayStatus", NULL, (CFNotificationSuspensionBehavior)kNilOptions);
340 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)loadPrefs, CFSTR("com.ryannair05.mitsuhaforever/ReloadPrefs"), NULL, CFNotificationSuspensionBehaviorCoalesce);
341 |
342 | SBLSconfig = [MSHFConfig loadConfigForApplication:@"LockScreen"];
343 | SBconfig = [MSHFConfig loadConfigForApplication:@"Springboard"];
344 |
345 | if (SBLSconfig.enabled || SBconfig.enabled) {
346 | if ([%c(CFWPrefsManager) class] && MSHookIvar([%c(CFWPrefsManager) sharedInstance], "_lockScreenEnabled")) %init(ColorFlowMitsuhaVisualsNotification);
347 | else if (access(OrionTweakDylibFile, F_OK) == 0) {
348 | [[NSNotificationCenter defaultCenter] addObserverForName:@"OrionMusicArtworkChanged" object:nil queue:nil usingBlock:^(NSNotification *n){
349 |
350 | UIColor *backgroundColor = [[[%c(OrionColorizer) sharedInstance] primaryColor] colorWithAlphaComponent:0.5];
351 |
352 | if (SBconfig.colorMode == 0) {
353 | [[SBconfig view] updateWaveColor:backgroundColor subwaveColor:backgroundColor];
354 | }
355 |
356 | if (SBLSconfig.colorMode == 0) {
357 | [[SBLSconfig view] updateWaveColor:backgroundColor subwaveColor:backgroundColor];
358 | }
359 |
360 | }];
361 | }
362 | else %init(MitsuhaVisualsNotification);
363 | }
364 | else return;
365 |
366 | if(SBLSconfig.enabled){
367 | if(@available(iOS 13.0, *)) {
368 | %init(ios13SBLS)
369 | } else {
370 | %init(oldSBLS)
371 | }
372 | }
373 |
374 | if(SBconfig.enabled){
375 | NSFileManager *fileManager = [NSFileManager defaultManager];
376 |
377 | bool const flowPresent = [fileManager fileExistsAtPath: FlowTweakDylibFile];
378 | if(flowPresent) {
379 | return;
380 | }
381 |
382 | bool quartPresent = [fileManager fileExistsAtPath: QuartTweakDylibFile];
383 |
384 | if (quartPresent) {
385 |
386 | NSDictionary *quartPrefs = [[NSDictionary alloc] initWithContentsOfFile: QuartPreferencesFile];
387 | if (quartPrefs) {
388 | quartPresent = [([quartPrefs objectForKey:@"enable"] ?: @(YES)) boolValue];
389 | if (quartPresent)
390 | quartPresent = [([quartPrefs objectForKey:@"enableMedia"] ?: @(YES)) boolValue];
391 | }
392 |
393 | if (quartPresent) {
394 | void *Quart = dlopen("/Library/MobileSubstrate/DynamicLibraries/Quart.dylib", RTLD_LAZY);
395 | if ([([quartPrefs objectForKey:@"largerMedia"] ?: @(NO)) boolValue]) {
396 | SBconfig.waveOffsetOffset = 385;
397 | }
398 | else {
399 | SBconfig.waveOffsetOffset = 375;
400 | }
401 | %init(Quart);
402 | dlclose(Quart);
403 | return;
404 | }
405 | }
406 |
407 | if (@available(iOS 13.0, *)) {
408 | SBconfig.waveOffsetOffset = 500;
409 | %init(ios13SB)
410 | }
411 | else {
412 | bool const artsyPresent = [fileManager fileExistsAtPath: ArtsyTweakDylibFile]; // Check if Artsy is installed
413 |
414 | if (artsyPresent) {
415 | NSLog(@"[MitsuhaForever] Artsy found");
416 |
417 | NSDictionary *artsyPrefs = [[NSDictionary alloc] initWithContentsOfFile:ArtsyPreferencesFile];
418 | if (artsyPrefs) { //Check if Artsy is enabled
419 | bool const artsyEnabled = [([artsyPrefs objectForKey:@"enabled"] ?: @(YES)) boolValue];
420 | if (artsyEnabled)
421 | moveIntoPanel = [([artsyPrefs objectForKey:@"lsEnabled"] ?: @(YES)) boolValue];
422 | }
423 | else { //It's enabled by default when Artsy is installed
424 | NSLog(@"[MitsuhaForever: ARTSY] lsEnabled = true");
425 | moveIntoPanel = true;
426 | }
427 | }
428 |
429 | SBconfig.waveOffsetOffset = 500;
430 | %init(oldSB)
431 | }
432 | }
433 | }
--------------------------------------------------------------------------------
/Prefs/MSHFPrefsListController.m:
--------------------------------------------------------------------------------
1 | #import "MSHFPrefsListController.h"
2 |
3 | @implementation MSHFPrefsListController
4 | - (instancetype)init {
5 | self = [super init];
6 |
7 | if (self) {
8 | UIBarButtonItem *respringItem =
9 | [[UIBarButtonItem alloc] initWithTitle:@"Apply"
10 | style:UIBarButtonItemStylePlain
11 | target:self
12 | action:@selector(respring:)];
13 | self.navigationItem.rightBarButtonItem = respringItem;
14 | }
15 |
16 | return self;
17 | }
18 |
19 | - (NSArray *)specifiers {
20 | if(!_specifiers) {
21 | _specifiers = [self loadSpecifiersFromPlistName:@"Prefs" target:self];
22 | }
23 | return _specifiers;
24 | }
25 |
26 | - (void)loadFromSpecifier:(PSSpecifier *)specifier {
27 | _specifiers = [self loadSpecifiersFromPlistName:@"Prefs" target:self];
28 |
29 | NSFileManager *manager = [NSFileManager defaultManager];
30 | NSString *directory = MSHFAppSpecifiersDirectory;
31 | NSArray *appPlists = [manager contentsOfDirectoryAtPath:directory error:nil];
32 | NSMutableArray *appSpecifiers = [NSMutableArray new];
33 |
34 | for (NSString *filename in appPlists) {
35 | NSString *path = [directory stringByAppendingPathComponent:filename];
36 | NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:path];
37 |
38 | if (plist) {
39 | NSString *name = plist[@"name"] ?: [filename stringByReplacingOccurrencesOfString:@".plist" withString:@""];
40 | NSString *title = plist[@"title"] ?: name;
41 | PSSpecifier *spec = [PSSpecifier preferenceSpecifierNamed:title target:nil set:nil get:nil detail:[MSHFAppPrefsListController class] cell:2 edit:nil];
42 | [spec setProperty:name forKey:@"MSHFApp"];
43 |
44 | if (plist[@"important"]) {
45 | [appSpecifiers insertObject:spec atIndex:0];
46 | } else {
47 | [appSpecifiers addObject:spec];
48 | }
49 | }
50 | }
51 |
52 | for (PSSpecifier *spec in [appSpecifiers reverseObjectEnumerator]) {
53 | [self insertSpecifier:spec afterSpecifierID:@"apps"];
54 | }
55 |
56 | [self setTitle:@"Mitsuha Forever"];
57 | [self.navigationItem setTitle:@"Mitsuha Forever"];
58 | }
59 |
60 | - (id)readPreferenceValue:(PSSpecifier*)specifier {
61 | NSString *path = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", specifier.properties[@"defaults"]];
62 | NSMutableDictionary *settings = [NSMutableDictionary dictionary];
63 | [settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
64 |
65 | return ([settings objectForKey:specifier.properties[@"key"]]) ?: specifier.properties[@"default"];
66 | }
67 |
68 | - (void)setPreferenceValue:(id)value specifier:(PSSpecifier*)specifier {
69 | NSString *path = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", specifier.properties[@"defaults"]];
70 | NSMutableDictionary *settings = [NSMutableDictionary dictionary];
71 | [settings addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
72 |
73 | [settings setObject:value forKey:specifier.properties[@"key"]];
74 | [settings writeToFile:path atomically:YES];
75 | }
76 |
77 | - (void)setSpecifier:(PSSpecifier *)specifier {
78 | [self loadFromSpecifier:specifier];
79 | [super setSpecifier:specifier];
80 | }
81 |
82 | - (void)viewWillAppear:(BOOL)animated {
83 | [super viewWillAppear:animated];
84 |
85 | self.table.separatorColor = [UIColor colorWithWhite:0 alpha:0];
86 |
87 | if ([self.view respondsToSelector:@selector(setTintColor:)]) {
88 |
89 | UIWindow *keyWindow = [[[UIApplication sharedApplication] windows] firstObject];
90 |
91 | keyWindow.tintColor = [UIColor colorWithRed:238.0f / 255.0f
92 | green:100.0f / 255.0f
93 | blue:92.0f / 255.0f
94 | alpha:1];
95 | }
96 |
97 | [UISwitch appearanceWhenContainedInInstancesOfClasses:@[self.class]].onTintColor = [UIColor colorWithRed:238.0f / 255.0f
98 | green:100.0f / 255.0f
99 | blue:92.0f / 255.0f
100 | alpha:1];
101 |
102 |
103 | if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) {
104 | self.edgesForExtendedLayout = UIRectEdgeNone;
105 | }
106 | }
107 |
108 | - (void)viewDidDisappear:(BOOL)animated {
109 | [super viewDidDisappear:animated];
110 |
111 | if ([self.view respondsToSelector:@selector(setTintColor:)]) {
112 | UIWindow *keyWindow = [[[UIApplication sharedApplication] windows] firstObject];
113 | keyWindow.tintColor = nil;
114 | }
115 | }
116 |
117 | - (void)resetPrefs:(id)sender {
118 |
119 | NSString *plistPath = @"/User/Library/Preferences/com.ryannair05.mitsuhaforever.plist";
120 |
121 | if([[NSFileManager defaultManager] fileExistsAtPath:plistPath]){
122 | NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
123 | [prefs removeAllObjects];
124 | [prefs writeToFile:plistPath atomically:YES];
125 | }
126 |
127 | [self respring:sender];
128 | }
129 |
130 | - (bool)shouldReloadSpecifiersOnResume {
131 | return NO;
132 | }
133 |
134 | - (void)respring:(id)sender {
135 | pid_t pid;
136 | const char* args[] = {"killall", "backboardd", NULL};
137 | posix_spawn(&pid, "/usr/bin/killall", NULL, NULL, (char* const*)args, NULL);
138 | }
139 |
140 | - (void)restartmsd:(id)sender {
141 | pid_t pid;
142 | const char* args[] = {"killall", "mediaserverd", NULL};
143 | posix_spawn(&pid, "/usr/bin/killall", NULL, NULL, (char* const*)args, NULL);
144 | }
145 | @end
146 |
147 |
148 | @implementation MSHFTintedTableCell
149 |
150 | - (void)tintColorDidChange {
151 | [super tintColorDidChange];
152 |
153 | self.textLabel.textColor = [UIColor colorWithRed:238.0f / 255.0f
154 | green:100.0f / 255.0f
155 | blue:92.0f / 255.0f
156 | alpha:1];
157 | self.textLabel.highlightedTextColor = [UIColor colorWithRed:238.0f / 255.0f
158 | green:100.0f / 255.0f
159 | blue:92.0f / 255.0f
160 | alpha:1];
161 | }
162 |
163 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier {
164 | [super refreshCellContentsWithSpecifier:specifier];
165 |
166 | if ([self respondsToSelector:@selector(tintColor)]) {
167 | self.textLabel.textColor = [UIColor colorWithRed:238.0f / 255.0f
168 | green:100.0f / 255.0f
169 | blue:92.0f / 255.0f
170 | alpha:1];
171 | self.textLabel.highlightedTextColor = [UIColor colorWithRed:238.0f / 255.0f
172 | green:100.0f / 255.0f
173 | blue:92.0f / 255.0f
174 | alpha:1];
175 | }
176 | }
177 |
178 | @end
179 |
180 | @implementation MSHFLinkTableCell
181 |
182 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier {
183 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier specifier:specifier];
184 |
185 | if (self) {
186 | _isBig = specifier.properties[@"big"] && ((NSNumber *)specifier.properties[@"big"]).boolValue;
187 | _isAvatarCircular = specifier.properties[@"avatarCircular"] && ((NSNumber *)specifier.properties[@"avatarCircular"]).boolValue;
188 | _avatarURL = [NSURL URLWithString:specifier.properties[@"avatarURL"]];
189 |
190 | self.selectionStyle = UITableViewCellSelectionStyleBlue;
191 |
192 | //UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"safari" inBundle:globalBundle]];
193 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"/Library/PreferenceBundles/MitsuhaForeverPrefs.bundle/safari.png"]]];
194 | imageView.image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
195 | if (@available(iOS 13.0, *)) {
196 | imageView.tintColor = [UIColor systemGray3Color];
197 | }
198 | self.accessoryView = imageView;
199 |
200 | self.detailTextLabel.numberOfLines = _isBig ? 0 : 1;
201 | self.detailTextLabel.text = specifier.properties[@"subtitle"] ?: @"";
202 | if (@available(iOS 13.0, *)) {
203 | self.detailTextLabel.textColor = [UIColor secondaryLabelColor];
204 | } else {
205 | self.detailTextLabel.textColor = [UIColor systemGrayColor];
206 | }
207 |
208 | self.specifier = specifier;
209 | if (self.shouldShowAvatar) {NSLog(@"avatar? %i %@", self.shouldShowAvatar, self.specifier.properties);
210 | CGFloat size = _isBig ? 38.f : 29.f;
211 |
212 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, [UIScreen mainScreen].scale);
213 | specifier.properties[@"iconImage"] = UIGraphicsGetImageFromCurrentImageContext();
214 | UIGraphicsEndImageContext();
215 |
216 | _avatarView = [[UIView alloc] initWithFrame:self.imageView.bounds];
217 | _avatarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
218 | _avatarView.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1];
219 | _avatarView.userInteractionEnabled = NO;
220 | _avatarView.clipsToBounds = YES;
221 | [self.imageView addSubview:_avatarView];
222 |
223 | if (specifier.properties[@"initials"]) {
224 | _avatarView.backgroundColor = [UIColor colorWithWhite:0.8f alpha:1];
225 |
226 | UILabel *label = [[UILabel alloc] initWithFrame:_avatarView.bounds];
227 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
228 | label.font = [UIFont systemFontOfSize:13.f];
229 | label.textAlignment = NSTextAlignmentCenter;
230 | label.textColor = [UIColor whiteColor];
231 | label.text = specifier.properties[@"initials"];
232 | [_avatarView addSubview:label];
233 | } else {
234 | _avatarImageView = [[UIImageView alloc] initWithFrame:_avatarView.bounds];
235 | _avatarImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
236 | _avatarImageView.alpha = 0;
237 | _avatarImageView.userInteractionEnabled = NO;
238 | _avatarImageView.layer.minificationFilter = kCAFilterTrilinear;
239 | [_avatarView addSubview:_avatarImageView];
240 |
241 | if (_avatarURL != nil) {
242 | if (specifier.properties[@"avatarCircular"] == nil) {
243 | _isAvatarCircular = YES;
244 | }
245 | }
246 |
247 | [self loadAvatarIfNeeded];
248 | }
249 |
250 | _avatarView.layer.cornerRadius = size / 2;
251 | }
252 | }
253 |
254 | return self;
255 | }
256 |
257 | #pragma mark - Avatar
258 |
259 | - (UIImage *)avatarImage {
260 | return _avatarImageView.image;
261 | }
262 |
263 | - (void)setAvatarImage:(UIImage *)avatarImage {
264 | _avatarImageView.image = avatarImage;
265 |
266 | // Fade in if we haven’t yet
267 | if (_avatarImageView.alpha == 0) {
268 | [UIView animateWithDuration:0.15 animations:^{
269 | _avatarImageView.alpha = 1;
270 | }];
271 | }
272 | }
273 |
274 | - (BOOL)shouldShowAvatar {
275 | // If we were explicitly told to show an avatar, or if we have an avatar URL or initials
276 | return (self.specifier.properties[@"showAvatar"] && ((NSNumber *)self.specifier.properties[@"showAvatar"]).boolValue)
277 | || self.specifier.properties[@"avatarURL"] != nil || self.specifier.properties[@"initials"] != nil;
278 | }
279 |
280 | - (void)loadAvatarIfNeeded {
281 | if (_avatarURL == nil || self.avatarImage != nil) {
282 | return;
283 | }
284 | }
285 | - (void)setSelected:(BOOL)arg1 animated:(BOOL)arg2
286 | {
287 | if (arg1) [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.specifier.properties[@"url"]] options:@{} completionHandler:nil];
288 | }
289 | @end
290 |
291 |
292 | @implementation MSHFTwitterCell
293 |
294 | + (NSString *)_urlForUsername:(NSString *)user {
295 |
296 | /* if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"aphelion://"]]) {
297 | return [@"aphelion://profile/" stringByAppendingString:user];
298 | } else */ if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tweetbot://"]]) {
299 | return [@"tweetbot:///user_profile/" stringByAppendingString:user];
300 | } else if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"twitterrific://"]]) {
301 | return [@"twitterrific:///profile?screen_name=" stringByAppendingString:user];
302 | } else if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tweetings://"]]) {
303 | return [@"tweetings:///user?screen_name=" stringByAppendingString:user];
304 | } else {
305 | return [@"https://mobile.twitter.com/" stringByAppendingString:user];
306 | }
307 | }
308 |
309 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier {
310 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier specifier:specifier];
311 |
312 | if (self) {
313 | UIImageView *imageView = (UIImageView *)self.accessoryView;
314 | imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"/Library/PreferenceBundles/MitsuhaForeverPrefs.bundle/twitter.png"]];
315 | imageView.image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
316 | [imageView sizeToFit];
317 |
318 | _user = [specifier.properties[@"user"] copy];
319 | NSAssert(_user, @"User name not provided");
320 |
321 | specifier.properties[@"url"] = [self.class _urlForUsername:_user];
322 |
323 | self.detailTextLabel.text = [@"@" stringByAppendingString:_user];
324 |
325 | [self loadAvatarIfNeeded];
326 | }
327 |
328 | return self;
329 | }
330 |
331 | #pragma mark - Avatar
332 |
333 | - (BOOL)shouldShowAvatar {
334 | // HBLinkTableCell doesn’t want avatars by default, but we do. override its check method so that
335 | // if showAvatar is unset, we return YES
336 | return self.specifier.properties[@"showAvatar"] ? [super shouldShowAvatar] : YES;
337 | }
338 |
339 | - (void)loadAvatarIfNeeded {
340 | if (!_user || self.avatarImage) {
341 | return;
342 | }
343 |
344 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
345 |
346 | // NSString *size = [UIScreen mainScreen].scale > 2 ? @"original" : @"bigger";
347 | NSError __block *err = NULL;
348 | NSData __block *data;
349 | BOOL __block reqProcessed = false;
350 |
351 | // NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://mobile.twitter.com/%@/profile_image?size=%@", _user, size]]];
352 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://pbs.twimg.com/profile_images/1161080936836018176/4GUKuGlb_200x200.jpg"]]];
353 |
354 | [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *_data, NSURLResponse *_response, NSError *_error) {
355 | err = _error;
356 | data = _data;
357 | reqProcessed = true;
358 | }] resume];
359 |
360 | while (!reqProcessed) {
361 | [NSThread sleepForTimeInterval:0];
362 | }
363 |
364 | if (err)
365 | return;
366 |
367 | UIImage *image = [UIImage imageWithData:data];
368 |
369 | dispatch_async(dispatch_get_main_queue(), ^{
370 | self.avatarImage = image;
371 | });
372 | });
373 | }
374 |
375 | @end
376 |
--------------------------------------------------------------------------------