├── .gitignore ├── layout ├── DEBIAN │ ├── postinst │ └── postrm └── Library │ └── Application Support │ └── Choicy.bundle │ ├── AppLaunchIcon@2x.png │ ├── AppLaunchIcon@3x.png │ ├── AppLaunchIcon_Big@2x.png │ ├── AppLaunchIcon_Big@3x.png │ ├── AppLaunchIcon_Crossed@2x.png │ ├── AppLaunchIcon_Crossed@3x.png │ ├── AppLaunchIcon_Crossed_Big@2x.png │ ├── AppLaunchIcon_Crossed_Big@3x.png │ ├── el.lproj │ └── Localizable.strings │ ├── zh_TW.lproj │ └── Localizable.strings │ ├── zh_CN.lproj │ └── Localizable.strings │ ├── zh_Hans.lproj │ └── Localizable.strings │ ├── he.lproj │ └── Localizable.strings │ ├── ar.lproj │ └── Localizable.strings │ ├── en.lproj │ └── Localizable.strings │ ├── ru.lproj │ └── Localizable.strings │ └── de.lproj │ └── Localizable.strings ├── ChoicyPrefs ├── Resources │ ├── Credits@2x.png │ ├── Credits@3x.png │ ├── Donate@2x.png │ ├── Donate@3x.png │ ├── GitHub@2x.png │ ├── GitHub@3x.png │ ├── Mastodon@2x.png │ ├── Mastodon@3x.png │ ├── ChoicyIcon@2x.png │ ├── ChoicyIcon@3x.png │ ├── GlobalTweakConfiguration.plist │ ├── Info.plist │ ├── ApplicationDaemon.plist │ ├── Credits.plist │ └── Root.plist ├── CHPSubtitleSwitch.h ├── CHPPreferences.h ├── CHPSubtitleSwitch.m ├── CoreServices.h ├── entry.plist ├── Makefile ├── CHPBlackTextTableCell.h ├── CHPDestructiveTableCell.h ├── CHPCreditsListController.h ├── CHPDaemonListObserver.h ├── CHPRootListController.h ├── CHPApplicationListSubcontrollerController.h ├── CHPApplicationPlugInsListController.h ├── CHPGlobalTweakConfigurationController.h ├── CHPDaemonListController.h ├── CHPDaemonInfo.h ├── CHPDestructiveTableCell.m ├── CHPTweakInfo.h ├── CHPAdditionalExecutablesListController.h ├── CHPPackageInfo.h ├── CHPDaemonInfo.m ├── CHPTweakTroubleshootListController.h ├── CHPBlackTextTableCell.m ├── CHPMachoParser.h ├── CHPDaemonList.h ├── CHPTweakList.h ├── CHPListController.h ├── CHPApplicationListSubcontrollerController.m ├── CHPProcessConfigurationListController.h ├── CHPCreditsListController.m ├── CHPTweakInfo.m ├── CHPApplicationPlugInsListController.m ├── CHPPackageInfo.m ├── CHPGlobalTweakConfigurationController.m ├── CHPDaemonListController.m ├── CHPTweakList.m ├── CHPAdditionalExecutablesListController.m ├── CHPListController.m └── CHPMachoParser.m ├── nextstep_plist.h ├── .gitmodules ├── dyld_interpose.h ├── release_build.sh ├── gen_asm.sh ├── ChoicySB ├── Makefile ├── runningboardd.x ├── ChoicySB.plist ├── ChoicySB.h ├── RunningBoard.h ├── ChoicyOverrideProvider.h ├── ChoicyOverrideManager.h ├── SpringBoard.h ├── ChoicyOverrideManager.m ├── Tweak.x └── SpringBoard.x ├── control ├── README.md ├── Choicy.plist ├── Tweak.s ├── update_localizations.sh ├── Makefile ├── gen.c ├── LICENSE.md ├── HBLogWeak.h ├── ChoicyPrefsMigrator.h ├── Shared.m ├── ChoicyPrefsMigrator.m ├── Shared.h └── nextstep_plist.c /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/ 2 | .DS_Store 3 | packages/ 4 | gen.*.s -------------------------------------------------------------------------------- /layout/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | killall runningboardd 2> /dev/null 4 | exit 0 -------------------------------------------------------------------------------- /layout/DEBIAN/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | killall runningboardd 2> /dev/null 4 | exit 0 -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Credits@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Credits@2x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Credits@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Credits@3x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Donate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Donate@2x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Donate@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Donate@3x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/GitHub@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/GitHub@2x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/GitHub@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/GitHub@3x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Mastodon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Mastodon@2x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Mastodon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/Mastodon@3x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/ChoicyIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/ChoicyIcon@2x.png -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/ChoicyIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/ChoicyPrefs/Resources/ChoicyIcon@3x.png -------------------------------------------------------------------------------- /ChoicyPrefs/CHPSubtitleSwitch.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface CHPSubtitleSwitch : PSSwitchTableCell 4 | @end -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon@3x.png -------------------------------------------------------------------------------- /nextstep_plist.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | uint32_t index; 3 | size_t size; 4 | char *data; 5 | } nextstep_plist_t; 6 | 7 | xpc_object_t nxp_parse_object(nextstep_plist_t *plist); -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Big@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Big@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Big@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Big@3x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed@3x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed_Big@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed_Big@2x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed_Big@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opa334/Choicy/HEAD/layout/Library/Application Support/Choicy.bundle/AppLaunchIcon_Crossed_Big@3x.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/litehook"] 2 | path = external/litehook 3 | url = https://github.com/opa334/litehook 4 | [submodule "external/ChOma"] 5 | path = external/ChOma 6 | url = https://github.com/opa334/ChOma 7 | -------------------------------------------------------------------------------- /dyld_interpose.h: -------------------------------------------------------------------------------- 1 | struct dyld_interpose_tuple { 2 | const void* replacement; 3 | const void* replacee; 4 | }; 5 | extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count); -------------------------------------------------------------------------------- /release_build.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | set -e 3 | 4 | export PREFIX=$THEOS/toolchain/Xcode11.xctoolchain/usr/bin/ 5 | 6 | make clean 7 | make package FINALPACKAGE=1 8 | 9 | export -n PREFIX 10 | 11 | make clean 12 | make package FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless -------------------------------------------------------------------------------- /gen_asm.sh: -------------------------------------------------------------------------------- 1 | CFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=8.0" 2 | 3 | clang $CFLAGS -S -arch armv7 gen.c -o gen.armv7.s 4 | clang $CFLAGS -S -arch arm64 gen.c -o gen.arm64.s 5 | clang $CFLAGS -S -arch arm64e gen.c -o gen.arm64e.s -fno-ptrauth-abi-version -------------------------------------------------------------------------------- /ChoicyPrefs/CHPPreferences.h: -------------------------------------------------------------------------------- 1 | extern NSArray *dylibsBeforeChoicy; 2 | extern NSDictionary *preferences; 3 | extern NSMutableDictionary *preferencesForWriting(); 4 | extern void writePreferences(NSMutableDictionary *mutablePrefs); 5 | extern void presentNotLoadingFirstWarning(PSListController *plc, BOOL showDontShowAgainOption); -------------------------------------------------------------------------------- /ChoicySB/Makefile: -------------------------------------------------------------------------------- 1 | INSTALL_TARGET_PROCESSES = SpringBoard 2 | 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | TWEAK_NAME = ChoicySB 6 | 7 | ChoicySB_FILES = $(wildcard *.x) $(wildcard *.m) ../Shared.m ../ChoicyPrefsMigrator.m 8 | ChoicySB_CFLAGS = -fobjc-arc -Wno-unguarded-availability-new 9 | ChoicySB_PRIVATE_FRAMEWORKS = BackBoardServices 10 | 11 | include $(THEOS_MAKE_PATH)/tweak.mk 12 | -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/GlobalTweakConfiguration.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.opa334.choicy 2 | Name: Choicy 3 | Depends: mobilesubstrate, com.opa334.altlist (>=1.0.4), preferenceloader 4 | Version: 1.5.3-2 5 | Architecture: iphoneos-arm 6 | Description: Advanced Tweak Configuration! 7 | Depiction: https://opa334.github.io/depic/Choicy/index.html 8 | Maintainer: opa334 9 | Author: opa334 10 | Section: Tweaks 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Choicy 2 | 3 | An advanced tweak configurator! 4 | 5 | ## Features 6 | • Disable tweak injection individually for every process 7 | 8 | • Configure each tweak dylib individually for every process 9 | 10 | • Disable tweaks globally (with the ability to set exceptions for individual 11 | processes) 12 | 13 | • Option for an application shortcut to launch the application with or without tweaks once -------------------------------------------------------------------------------- /Choicy.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Bundles 8 | 9 | com.apple.Security 10 | 11 | 12 | IsTweakManager 13 | 14 | 15 | -------------------------------------------------------------------------------- /ChoicySB/runningboardd.x: -------------------------------------------------------------------------------- 1 | #import 2 | #import "../Shared.h" 3 | #import "ChoicyOverrideManager.h" 4 | #import "ChoicySB.h" 5 | #import "RunningBoard.h" 6 | 7 | %hook RBProcessManager 8 | 9 | - (id)executeLaunchRequest:(RBSLaunchRequest *)launchRequest withError:(NSError **)errorOut 10 | { 11 | choicy_applyEnvironmentChangesToLaunchContext(launchRequest.context); 12 | return %orig; 13 | } 14 | 15 | %end 16 | 17 | void choicy_initRunningBoardd(void) 18 | { 19 | %init(); 20 | } -------------------------------------------------------------------------------- /ChoicySB/ChoicySB.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Bundles 8 | 9 | com.apple.springboard 10 | 11 | Executables 12 | 13 | runningboardd 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Tweak.s: -------------------------------------------------------------------------------- 1 | // Include the right generated assembly file based on the architecture 2 | 3 | #if !__has_include("gen.arm64e.s") || !__has_include("gen.arm64.s") || !__has_include("gen.armv7.s") 4 | #error "Generated asssembly files not found, please run gen_asm.sh with a recent enough clang to support __attribute__((musttail))" 5 | #endif 6 | 7 | #ifdef __arm64__ 8 | 9 | #ifdef __arm64e__ 10 | #include "gen.arm64e.s" 11 | #else 12 | #include "gen.arm64.s" 13 | #endif 14 | 15 | #else 16 | 17 | #include "gen.armv7.s" 18 | 19 | #endif -------------------------------------------------------------------------------- /ChoicySB/ChoicySB.h: -------------------------------------------------------------------------------- 1 | #import "SpringBoard.h" 2 | 3 | extern NSDictionary *preferences; 4 | 5 | void choicy_reloadPreferences(void); 6 | BOOL choicy_shouldDisableTweakInjectionForApplication(NSString *applicationID); 7 | NSDictionary *choicy_applyEnvironmentChanges(NSDictionary *originalEnvironment, NSString *bundleIdentifier); 8 | void choicy_applyEnvironmentChangesToLaunchContext(RBSLaunchContext *launchContext); 9 | void choicy_applyEnvironmentChangesToExecutionContext(FBProcessExecutionContext *executionContext, NSString *bundleIdentifier); -------------------------------------------------------------------------------- /ChoicyPrefs/CHPSubtitleSwitch.m: -------------------------------------------------------------------------------- 1 | #import "CHPSubtitleSwitch.h" 2 | 3 | #import 4 | 5 | @implementation CHPSubtitleSwitch 6 | 7 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier 8 | { 9 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier specifier:specifier]; 10 | if (self) { 11 | self.detailTextLabel.text = [specifier propertyForKey:@"subtitle"]; 12 | } 13 | return self; 14 | } 15 | 16 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CoreServices.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface LSApplicationProxy () 4 | + (instancetype)applicationProxyForIdentifier:(NSString *)identifier; 5 | @property (nonatomic,readonly) NSString *canonicalExecutablePath; 6 | @property (nonatomic,readonly) NSURL *bundleURL; 7 | @property (nonatomic,readonly) NSString *bundleExecutable; 8 | @property (nonatomic,readonly) NSArray *VPNPlugins; 9 | @end 10 | 11 | @interface LSBundleRecord : NSObject 12 | @property (readonly) NSURL *executableURL; 13 | @end 14 | 15 | @interface LSApplicationExtensionRecord : LSBundleRecord 16 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/entry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | ChoicyPrefs 9 | cell 10 | PSLinkCell 11 | detail 12 | CHPRootListController 13 | icon 14 | ChoicyIcon 15 | isController 16 | 17 | label 18 | Choicy 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ChoicySB/RunningBoard.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RBSProcessIdentity : NSObject 4 | @property(readonly, copy, nonatomic) NSString *executablePath; 5 | @property(readonly, copy, nonatomic) NSString *embeddedApplicationIdentifier; 6 | @end 7 | 8 | @interface RBSLaunchContext : NSObject 9 | @property (setter=_setAdditionalEnvironment:,nonatomic,copy) NSDictionary *_additionalEnvironment; 10 | @property (nonatomic,copy) RBSProcessIdentity *identity; 11 | @property (nonatomic,copy) NSString *bundleIdentifier; 12 | @end 13 | 14 | @interface RBSLaunchRequest : NSObject 15 | @property (nonatomic,readonly) RBSLaunchContext *context; 16 | @end 17 | -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ChoicyPrefs 9 | CFBundleIdentifier 10 | com.opa334.choicyprefs 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | CHPRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /ChoicyPrefs/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | BUNDLE_NAME = ChoicyPrefs 4 | 5 | ChoicyPrefs_FILES = $(wildcard *.m) $(wildcard *.x) ../Shared.m ../ChoicyPrefsMigrator.m $(wildcard ../external/litehook/src/*.c) $(wildcard ../external/ChOma/src/*.c) 6 | ChoicyPrefs_INSTALL_PATH = /Library/PreferenceBundles 7 | ChoicyPrefs_FRAMEWORKS = UIKit 8 | ChoicyPrefs_PRIVATE_FRAMEWORKS = Preferences MobileCoreServices 9 | ChoicyPrefs_EXTRA_FRAMEWORKS = AltList 10 | ChoicyPrefs_CFLAGS = -fobjc-arc -Wno-deprecated-declarations -I../external/ChOma/src -I../external/litehook/src 11 | 12 | include $(THEOS_MAKE_PATH)/bundle.mk 13 | 14 | internal-stage:: 15 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 16 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/ChoicyPrefs.plist$(ECHO_END) 17 | -------------------------------------------------------------------------------- /update_localizations.sh: -------------------------------------------------------------------------------- 1 | localisort -t ./layout/Library/Application\ Support/Choicy.bundle/en.lproj/Localizable.strings -i ./layout/Library/Application\ Support/Choicy.bundle/de.lproj/Localizable.strings -r 2 | localisort -t ./layout/Library/Application\ Support/Choicy.bundle/en.lproj/Localizable.strings -i ./layout/Library/Application\ Support/Choicy.bundle/el.lproj/Localizable.strings -r 3 | localisort -t ./layout/Library/Application\ Support/Choicy.bundle/en.lproj/Localizable.strings -i ./layout/Library/Application\ Support/Choicy.bundle/he.lproj/Localizable.strings -r 4 | localisort -t ./layout/Library/Application\ Support/Choicy.bundle/en.lproj/Localizable.strings -i ./layout/Library/Application\ Support/Choicy.bundle/zh_Hans.lproj/Localizable.strings -r 5 | localisort -t ./layout/Library/Application\ Support/Choicy.bundle/en.lproj/Localizable.strings -i ./layout/Library/Application\ Support/Choicy.bundle/zh_TW.lproj/Localizable.strings -r 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(THEOS_PACKAGE_SCHEME),rootless) 2 | TARGET = iphone:clang:16.5:15.0 3 | else 4 | TARGET = iphone:clang:13.7:8.0 5 | endif 6 | 7 | include $(THEOS)/makefiles/common.mk 8 | 9 | TWEAK_NAME = Choicy 10 | 11 | Choicy_FILES = Tweak.c Tweak.s nextstep_plist.c $(wildcard external/litehook/src/*.c) 12 | Choicy_CFLAGS = -DTHEOS_LEAN_AND_MEAN -I./external/litehook/src -I./external/litehook/external/include 13 | 14 | include $(THEOS_MAKE_PATH)/tweak.mk 15 | SUBPROJECTS += ChoicyPrefs 16 | SUBPROJECTS += ChoicySB 17 | include $(THEOS_MAKE_PATH)/aggregate.mk 18 | 19 | internal-stage:: 20 | $(ECHO_NOTHING)mv "$(THEOS_STAGING_DIR)/Library/MobileSubstrate/DynamicLibraries/Choicy.dylib" "$(THEOS_STAGING_DIR)/Library/MobileSubstrate/DynamicLibraries/ Choicy.dylib" $(ECHO_END) 21 | $(ECHO_NOTHING)mv "$(THEOS_STAGING_DIR)/Library/MobileSubstrate/DynamicLibraries/Choicy.plist" "$(THEOS_STAGING_DIR)/Library/MobileSubstrate/DynamicLibraries/ Choicy.plist" $(ECHO_END) -------------------------------------------------------------------------------- /gen.c: -------------------------------------------------------------------------------- 1 | // We need to compile fucking old ABI with Xcode 11 2 | // Xcode 11 however does not fucking support __attribute__((musttail)) 3 | // So what we do instead is compile this C code into assembly with latest clang using gen_asm.sh 4 | // The right assembly file will then be included in Tweak.s 5 | 6 | #include 7 | #include 8 | 9 | extern void *(*dlopen_orig)(const char*, int); 10 | extern void *(*dyld_dlopen_orig)(const void *, const char*, int); 11 | 12 | bool should_load_dylib(const char *dylibPath); 13 | 14 | void *dlopen_hook(const char *path, int mode) 15 | { 16 | if (path) { 17 | if (!should_load_dylib(path)) { 18 | return NULL; 19 | } 20 | } 21 | __attribute__((musttail)) return dlopen_orig(path, mode); 22 | } 23 | 24 | void *dyld_dlopen_hook(const void *dyld, const char *path, int mode) 25 | { 26 | if (path) { 27 | if (!should_load_dylib(path)) { 28 | return NULL; 29 | } 30 | } 31 | __attribute__((musttail)) return dyld_dlopen_orig(dyld, path, mode); 32 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Lars Fröder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPBlackTextTableCell.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPBlackTextTableCell : PSTableCell 24 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDestructiveTableCell.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPDestructiveTableCell : PSTableCell 24 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPCreditsListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | 23 | @interface CHPCreditsListController : CHPListController 24 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonListObserver.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | @class CHPDaemonList; 22 | 23 | @protocol CHPDaemonListObserver 24 | @required 25 | - (void)daemonListDidUpdate:(CHPDaemonList *)list; 26 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPRootListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | 23 | @interface CHPRootListController : CHPListController 24 | - (void)openTwitterWithUsername:(NSString *)username; 25 | @end -------------------------------------------------------------------------------- /HBLogWeak.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | // HBLogDebugWeak is ommited from release builds 22 | 23 | #import 24 | #ifdef __DEBUG__ 25 | #define HBLogDebugWeak(args ...) HBLogDebug(args) 26 | #else 27 | #define HBLogDebugWeak(...) 28 | #endif -------------------------------------------------------------------------------- /ChoicyPrefs/CHPApplicationListSubcontrollerController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPApplicationListSubcontrollerController : ATLApplicationListSubcontrollerController 24 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPApplicationPlugInsListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | @class PSSpecifier, LSPlugInKitProxy; 23 | 24 | @interface CHPApplicationPlugInsListController : PSListController { 25 | NSArray *_appPlugIns; 26 | } 27 | 28 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPGlobalTweakConfigurationController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | 23 | @interface CHPGlobalTweakConfigurationController : CHPListController { 24 | NSMutableArray *_globalDeniedTweaks; 25 | } 26 | 27 | - (void)loadGlobalTweakBlacklist; 28 | - (void)saveGlobalTweakBlacklist; 29 | 30 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | 23 | #import "CHPDaemonListObserver.h" 24 | 25 | @interface CHPDaemonListController : CHPListController { 26 | NSSet *_suggestedDaemons; 27 | BOOL _showsAllDaemons; 28 | } 29 | - (void)updateSuggestedDaemons; 30 | @end 31 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonInfo.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPDaemonInfo : NSObject 24 | 25 | @property(nonatomic) NSString *executablePath; 26 | @property(nonatomic) NSString *plistIdentifier; 27 | @property(nonatomic) NSSet *linkedFrameworkIdentifiers; 28 | 29 | - (NSString *)executableName; 30 | 31 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDestructiveTableCell.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPDestructiveTableCell.h" 22 | 23 | @implementation CHPDestructiveTableCell 24 | 25 | - (void)layoutSubviews 26 | { 27 | [super layoutSubviews]; 28 | self.textLabel.textColor = [UIColor systemRedColor]; 29 | self.textLabel.highlightedTextColor = [UIColor systemRedColor]; 30 | } 31 | 32 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPTweakInfo.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPTweakInfo : NSObject 24 | @property (nonatomic) NSString *dylibName; 25 | @property (nonatomic) NSArray *filterBundles; 26 | @property (nonatomic) NSArray *filterExecutables; 27 | - (instancetype)initWithDylibPath:(NSString *)dylibPath plistPath:(NSString *)plistPath; 28 | @end -------------------------------------------------------------------------------- /ChoicyPrefsMigrator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "Shared.h" 22 | #import 23 | 24 | @interface ChoicyPrefsMigrator : NSObject 25 | 26 | + (BOOL)preferencesNeedMigration:(NSDictionary *)preferences; 27 | + (void)migratePreferences:(NSMutableDictionary *)preferences; 28 | + (void)updatePreferenceVersion:(NSMutableDictionary *)preferences; 29 | 30 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPAdditionalExecutablesListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface PSEditableListController : PSListController 24 | - (BOOL)performDeletionActionForSpecifier:(id)arg1; 25 | @end 26 | 27 | @interface CHPAdditionalExecutablesListController : PSEditableListController { 28 | NSMutableArray *_additionalExecutables; 29 | } 30 | 31 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPPackageInfo.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @interface CHPPackageInfo : NSObject 24 | @property (nonatomic, readonly) NSString *identifier; 25 | @property (nonatomic, readonly) NSString *name; 26 | @property (nonatomic, readonly) NSArray *tweakDylibs; 27 | + (NSArray *)allInstalledPackages; 28 | - (instancetype)initWithPackageIdentifier:(NSString *)packageID; 29 | + (instancetype)fetchPackageInfoForDylibName:(NSString *)dylibName; 30 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonInfo.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPDaemonInfo.h" 22 | 23 | @implementation CHPDaemonInfo 24 | 25 | - (NSString *)executableName 26 | { 27 | return [self.executablePath lastPathComponent]; 28 | } 29 | 30 | - (NSString *)description 31 | { 32 | return [NSString stringWithFormat:@"", self.executablePath, self.plistIdentifier, self.linkedFrameworkIdentifiers]; 33 | } 34 | 35 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPTweakTroubleshootListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | #import "CHPDaemonListObserver.h" 23 | 24 | @class CHPTweakInfo, CHPPackageInfo; 25 | 26 | @interface CHPTweakTroubleshootListController : CHPListController { 27 | NSArray *_packageList; 28 | CHPPackageInfo *_selectedPackageWhileWaitingOnLoad; 29 | UIAlertController *_loadingAlertController; 30 | } 31 | 32 | - (PSSpecifier *)newSpecifierForPackage:(CHPPackageInfo *)packageInfo; 33 | 34 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPBlackTextTableCell.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPBlackTextTableCell.h" 22 | 23 | //Button with black text color (white in dark mode) 24 | 25 | @implementation CHPBlackTextTableCell 26 | 27 | - (void)layoutSubviews 28 | { 29 | [super layoutSubviews]; 30 | if (@available(iOS 13, *)) { 31 | self.textLabel.textColor = [UIColor labelColor]; 32 | self.textLabel.highlightedTextColor = [UIColor labelColor]; 33 | } 34 | else { 35 | self.textLabel.textColor = [UIColor blackColor]; 36 | self.textLabel.highlightedTextColor = [UIColor blackColor]; 37 | } 38 | } 39 | 40 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPMachoParser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | #import 24 | 25 | @interface CHPMachoParser : NSObject 26 | { 27 | NSMutableDictionary *_bundleIdentifierCache; 28 | NSMutableDictionary *_dependencyPathCache; 29 | DyldSharedCache *_sharedCache; 30 | } 31 | 32 | + (instancetype)sharedInstance; 33 | + (BOOL)isMachoAtPath:(NSString *)path; 34 | 35 | - (NSSet *)dependencyPathsForMachoAtPath:(NSString *)path; 36 | - (NSSet *)frameworkBundleIdentifiersForMachoAtPath:(NSString *)path; 37 | 38 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonList.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPDaemonListObserver.h" 22 | #import 23 | 24 | @interface CHPDaemonList : NSObject { 25 | NSHashTable *_observers; 26 | } 27 | @property(nonatomic,readonly) BOOL loaded; 28 | @property(nonatomic,readonly) BOOL loading; 29 | @property(nonatomic,readonly) NSArray *daemonList; 30 | + (instancetype)sharedInstance; 31 | - (BOOL)daemonList:(NSArray *)daemonList containsExecutableName:(NSString *)executableName; 32 | - (void)updateDaemonListIfNeeded; 33 | - (void)addObserver:(id)observer; 34 | - (void)removeObserver:(id)observer; 35 | - (void)sendReloadToObservers; 36 | - (NSString *)executablePathForDaemonName:(NSString *)daemonName; 37 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPTweakList.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | @class CHPDaemonInfo; 24 | @class CHPTweakInfo; 25 | 26 | @interface CHPTweakList : NSObject 27 | @property (nonatomic) NSArray *tweakList; 28 | + (NSArray *)possibleInjectionLibrariesPaths; 29 | + (NSString *)injectionLibrariesPath; 30 | + (BOOL)isTweakLibraryPath:(NSString *)path; 31 | + (NSURL *)injectionLibrariesURL; 32 | + (instancetype)sharedInstance; 33 | - (void)updateTweakList; 34 | - (NSArray *)tweakListForExecutableAtPath:(NSString *)executablePath; 35 | - (BOOL)oneOrMoreTweaksInjectIntoExecutableAtPath:(NSString *)executablePath; 36 | - (BOOL)isTweak:(CHPTweakInfo *)tweak hiddenForApplicationWithIdentifier:(NSString *)applicationID; 37 | - (BOOL)isTweakHiddenForAnyProcess:(CHPTweakInfo *)tweakName; 38 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/ApplicationDaemon.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | 11 | 12 | PostNotification 13 | com.opa334.choicyprefs/ReloadPrefs 14 | cell 15 | PSSwitchCell 16 | default 17 | 18 | defaults 19 | com.opa334.choicyprefs 20 | key 21 | overwriteGlobalTweakConfiguration 22 | label 23 | OVERWRITE_GLOBAL_TWEAK_CONFIGURATION 24 | 25 | 26 | cell 27 | PSGroupCell 28 | 29 | 30 | PostNotification 31 | com.opa334.choicyprefs/ReloadPrefs 32 | cell 33 | PSSwitchCell 34 | default 35 | 36 | defaults 37 | com.opa334.choicyprefs 38 | key 39 | tweakInjectionDisabled 40 | label 41 | DISABLE_TWEAK_INJECTION 42 | 43 | 44 | cell 45 | PSGroupCell 46 | 47 | 48 | PostNotification 49 | com.opa334.choicyprefs/ReloadPrefs 50 | cell 51 | PSSwitchCell 52 | default 53 | 54 | defaults 55 | com.opa334.choicyprefs 56 | key 57 | customTweakConfigurationEnabled 58 | label 59 | CUSTOM_TWEAK_CONFIGURATION 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | #import 23 | 24 | @interface CHPListController : PSListController { 25 | NSString *_searchKey; 26 | UISearchController *_searchController; 27 | } 28 | + (void)sendPostNotificationForSpecifier:(PSSpecifier *)specifier; 29 | + (void)sendChoicyPrefsPostNotification; 30 | + (NSString *)previewStringForProcessPreferences:(NSDictionary *)processPreferences; 31 | + (NSString *)previewStringForSpecifier:(PSSpecifier *)specifier; 32 | + (PSSpecifier *)createSpecifierForExecutable:(NSString *)executablePath named:(NSString *)name; 33 | - (void)applySearchControllerHideWhileScrolling:(BOOL)hideWhileScrolling; 34 | - (NSString *)topTitle; 35 | - (NSString *)plistName; 36 | - (void)parseLocalizationsForSpecifiers:(NSArray *)specifiers; 37 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPApplicationListSubcontrollerController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPApplicationListSubcontrollerController.h" 22 | #import "../Shared.h" 23 | #import "CHPPreferences.h" 24 | #import "CHPListController.h" 25 | 26 | @implementation CHPApplicationListSubcontrollerController 27 | 28 | - (NSString *)previewStringForApplicationWithIdentifier:(NSString *)applicationID 29 | { 30 | NSDictionary *appSettings = [preferences objectForKey:kChoicyPrefsKeyAppSettings]; 31 | NSDictionary *settingsForApplication = [appSettings objectForKey:applicationID]; 32 | return [CHPListController previewStringForProcessPreferences:settingsForApplication]; 33 | } 34 | 35 | // Older Crane versions depend on this 36 | + (NSString *)previewStringForProcessPreferences:(NSDictionary *)processPreferences 37 | { 38 | return [CHPListController previewStringForProcessPreferences:processPreferences]; 39 | } 40 | 41 | @end -------------------------------------------------------------------------------- /ChoicySB/ChoicyOverrideProvider.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | 23 | enum { 24 | Choicy_Override_DisableTweakInjection = 1 << 0, 25 | Choicy_Override_CustomTweakConfiguration = 1 << 1, 26 | Choicy_Override_OverrideGlobalConfiguration = 1 << 2 27 | }; 28 | 29 | @protocol ChoicyOverrideProvider 30 | 31 | @required 32 | - (uint32_t)providedOverridesForApplication:(NSString *)applicationID; 33 | 34 | @optional 35 | - (BOOL)disableTweakInjectionOverrideForApplication:(NSString *)applicationID; 36 | - (BOOL)customTweakConfigurationEnabledOverrideForApplication:(NSString *)applicationID; 37 | - (BOOL)customTweakConfigurationAllowDenyModeOverrideForApplication:(NSString *)applicationID; 38 | - (NSArray *)customTweakConfigurationAllowOrDenyListOverrideForApplication:(NSString *)applicationID; 39 | - (BOOL)overwriteGlobalConfigurationOverrideForApplication:(NSString *)applicationID; 40 | 41 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPProcessConfigurationListController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | @class PSSpecifier, LSPlugInKitProxy, LSBundleProxy; 23 | 24 | @interface CHPProcessConfigurationListController : CHPListController { 25 | NSString *_appIdentifier; 26 | NSString *_pluginIdentifier; 27 | NSString *_executablePath; 28 | 29 | LSBundleProxy *_bundleProxy; 30 | BOOL _isSpringboard; 31 | NSMutableArray *_customConfigurationSpecifiers; 32 | PSSpecifier *_segmentSpecifier; 33 | NSMutableArray *_allowedTweaks; 34 | NSMutableArray *_deniedTweaks; 35 | NSInteger _customTweakConfigurationSection; 36 | } 37 | @property (nonatomic) NSMutableDictionary *processPreferences; 38 | + (NSString *)executablePathForBundleProxy:(LSBundleProxy *)bundleProxy; 39 | - (void)readAppDaemonSettingsFromMainPropertyList; 40 | - (void)writeAppDaemonSettingsToMainPropertyList; 41 | - (void)updateSwitchesAvailability; 42 | - (NSString *)dictionaryName; 43 | @end 44 | -------------------------------------------------------------------------------- /ChoicySB/ChoicyOverrideManager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "ChoicyOverrideProvider.h" 22 | #import 23 | 24 | @interface ChoicyOverrideManager : NSObject { 25 | NSMutableArray *_overrideProviders; 26 | } 27 | 28 | + (instancetype)sharedManager; 29 | - (void)registerOverrideProvider:(id)provider; 30 | - (void)unregisterOverrideProvider:(id)provider; 31 | 32 | - (BOOL)disableTweakInjectionOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists; 33 | - (BOOL)customTweakConfigurationEnabledOverwriteForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists; 34 | - (BOOL)customTweakConfigurationAllowDenyModeOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists; // YES: Deny, NO: ALLOW 35 | - (NSArray *)customTweakConfigurationAllowOrDenyListOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists; 36 | - (BOOL)overwriteGlobalConfigurationOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists; 37 | 38 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPCreditsListController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPCreditsListController.h" 22 | #import "../Shared.h" 23 | 24 | @implementation CHPCreditsListController 25 | 26 | - (NSString *)plistName 27 | { 28 | return @"Credits"; 29 | } 30 | 31 | - (NSString *)topTitle 32 | { 33 | return localize(@"CREDITS"); 34 | } 35 | 36 | - (void)openURL:(NSURL *)URL 37 | { 38 | if (@available(iOS 10, *)) { 39 | [[UIApplication sharedApplication] openURL:URL options:@{} completionHandler:nil]; 40 | } 41 | else { 42 | [[UIApplication sharedApplication] openURL:URL]; 43 | } 44 | } 45 | 46 | - (void)openQuickPrefs 47 | { 48 | [self openURL:[NSURL URLWithString:@"https://github.com/AnthoPakPak/QuickPrefs"]]; 49 | } 50 | 51 | - (void)openTweakConfigurator 52 | { 53 | [self openURL:[NSURL URLWithString:@"https://github.com/pixelomer/TweakConfigurator"]]; 54 | } 55 | 56 | - (void)openUnSub 57 | { 58 | [self openURL:[NSURL URLWithString:@"https://github.com/NepetaDev/UnSub"]]; 59 | } 60 | 61 | - (void)openBrendonjkding 62 | { 63 | [self openURL:[NSURL URLWithString:@"https://github.com/brendonjkding"]]; 64 | } 65 | 66 | - (void)openStaturnz 67 | { 68 | [self openURL:[NSURL URLWithString:@"https://github.com/staturnzz"]]; 69 | } 70 | 71 | - (void)openAlfie 72 | { 73 | [self openURL:[NSURL URLWithString:@"https://github.com/alfiecg24"]]; 74 | } 75 | 76 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Credits.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | footerText 11 | No code was directly taken from the credited repos. They still helped with the development, that's why they are mentioned here. 12 | 13 | 14 | action 15 | openQuickPrefs 16 | cell 17 | PSButtonCell 18 | icon 19 | GitHub 20 | label 21 | QuickPrefs (AnthoPakPak) 22 | 23 | 24 | action 25 | openTweakConfigurator 26 | cell 27 | PSButtonCell 28 | icon 29 | GitHub 30 | label 31 | TweakConfigurator (pixelomer) 32 | 33 | 34 | action 35 | openUnSub 36 | cell 37 | PSButtonCell 38 | icon 39 | GitHub 40 | label 41 | UnSub (Nepeta) 42 | 43 | 44 | cell 45 | PSGroupCell 46 | footerText 47 | Search related contributions on GitHub 48 | 49 | 50 | action 51 | openBrendonjkding 52 | cell 53 | PSButtonCell 54 | icon 55 | GitHub 56 | label 57 | brendonjkding 58 | 59 | 60 | cell 61 | PSGroupCell 62 | footerText 63 | NeXTSTEP plist parser 64 | 65 | 66 | action 67 | openStaturnz 68 | cell 69 | PSButtonCell 70 | icon 71 | GitHub 72 | label 73 | staturnz 74 | 75 | 76 | cell 77 | PSGroupCell 78 | footerText 79 | ChOma 80 | 81 | 82 | action 83 | openAlfie 84 | cell 85 | PSButtonCell 86 | icon 87 | GitHub 88 | label 89 | alfiecg24 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPTweakInfo.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPTweakInfo.h" 22 | 23 | @implementation CHPTweakInfo 24 | 25 | - (instancetype)initWithDylibPath:(NSString *)dylibPath plistPath:(NSString *)plistPath 26 | { 27 | self = [super init]; 28 | 29 | self.dylibName = [[dylibPath lastPathComponent] stringByDeletingPathExtension]; 30 | 31 | NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistPath]; 32 | 33 | NSDictionary *filter = [plist objectForKey:@"Filter"]; 34 | 35 | if (filter) { 36 | self.filterBundles = [filter objectForKey:@"Bundles"]; 37 | self.filterExecutables = [filter objectForKey:@"Executables"]; 38 | 39 | //If a plist filters classes, treat it as Security (not very accurate, but better safe than sorry) 40 | NSArray *classes = [filter objectForKey:@"Classes"]; 41 | if (classes && classes.count > 0 && ![self.filterBundles containsObject:@"com.apple.Security"]) { 42 | if (!self.filterBundles) { 43 | self.filterBundles = @[@"com.apple.Security"]; 44 | } 45 | else { 46 | self.filterBundles = [self.filterBundles arrayByAddingObject:@"com.apple.Security"]; 47 | } 48 | } 49 | } 50 | 51 | return self; 52 | } 53 | 54 | - (NSComparisonResult)caseInsensitiveCompare:(CHPTweakInfo *)info 55 | { 56 | return [self.dylibName caseInsensitiveCompare:info.dylibName]; 57 | } 58 | 59 | - (NSString *)description 60 | { 61 | return [NSString stringWithFormat:@"", self.dylibName, self.filterBundles, self.filterExecutables]; 62 | } 63 | 64 | @end -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/el.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "Διαδικασία διαμόρφωσης"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "Διαδικασία διαμόρφωσης footer"; 3 | "APPLICATIONS" = "Εφαρμογές"; 4 | "DAEMONS" = "Υπηρεσία συστήματος"; 5 | /*ADDITIONAL_EXECUTABLES*/ 6 | /*OVERWRITE_GLOBAL_TWEAK_CONFIGURATION*/ 7 | "DISABLE_TWEAK_INJECTION" = "Απενεργοποίηση διακοπής tweak"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "Λειτουργία χωρίς tweaks"; 9 | "LAUNCH_WITH_TWEAKS" = "Λειτουργία με tweaks"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "Λειτουργία χωρίς επιλογή tweaks"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "Λειτουργία με επιλογή tweaks"; 12 | "APP_OPTIONS" = "Επιλογές εφαρμογής"; 13 | "APP_OPTIONS_FOOTER" = "Επιλογές εφαρμογής footer"; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "Προσαρμοσμένη διαμόρφωση tweak"; 15 | /*ALLOW*/ 16 | /*DENY*/ 17 | /*GREYED_OUT_ENTRIES*/ 18 | /*APP_PLUGINS*/ 19 | "TWEAKS_DISABLED" = "Απενεργοποίηση tweaks"; 20 | "CUSTOM" = "Προσαρμοσμένο"; 21 | /*GLOBAL_OVERWRITE*/ 22 | "SHOW_RECOMMENDED_DAEMONS" = "Προειδοποίηση υπηρεσίας συστήματος"; 23 | "SHOW_ALL_DAEMONS" = "Προβολή όλων των υπηρεσιών συστημάτων"; 24 | "SYSTEM_APPLICATIONS" = "Σύστημα εφαρμογών"; 25 | "USER_APPLICATIONS" = "Εφαρμογές χρήστη"; 26 | "HIDDEN_APPLICATIONS" = "Κρυμμένες εφαρμοφές"; 27 | "GLOBAL_CONFIGURATION" = "Διεθνής διαμόρφωση"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "Διεθνής διαμόρφωση tweak"; 29 | /*ADDITIONAL_EXECUTABLES_FOOTER*/ 30 | /*SELECT_EXECUTABLE*/ 31 | /*SELECT_EXECUTABLE_MESSAGE*/ 32 | /*ERROR*/ 33 | /*ERROR_FILE_NOT_FOUND*/ 34 | /*ERROR_FILE_NO_EXECUTABLE*/ 35 | /*ERROR_NO_TWEAKS_INJECT*/ 36 | /*ADD*/ 37 | /*PATH*/ 38 | /*TWEAKS*/ 39 | /*GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE*/ 40 | "OTHER" = "Άλλα"; 41 | "FOLLOW_ME_ON_MASTODON" = "Ακολουθήστε με στο mastodon"; 42 | "DONATE" = "Δωρεά"; 43 | "SOURCE_CODE" = "Κωδικός πηγής"; 44 | "CREDITS" = "Πιστώσεις"; 45 | "RESPRING" = "Επανεκκίνηση/Επαναφορά"; 46 | "WARNING_ALERT_TITLE" = "Προειδοποίηση τίτλου"; 47 | "THE_INJECTION_PLATFORM" = "Διακοπή πλατφορμας"; 48 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "Μήνυμα προειδοποίησης"; 49 | "CHOICYLOADER_ADVICE" = "Συμβουλές επιλογής φορτίων"; 50 | "OPEN_REPO" = "Άνοιγμα repo"; 51 | "DONT_SHOW_AGAIN" = "Να μην εμφανιστεί ξανά"; 52 | "CLOSE" = "Κλείστε"; 53 | "DAEMON_LIST_BOTTOM_NOTICE" = "Υπηρεσία συστήματος γνωστοποίησης"; 54 | /*PACKAGE*/ 55 | /*TROUBLESHOOTING*/ 56 | /*TWEAK_TROUBLESHOOTING*/ 57 | /*TWEAK_TROUBLESHOOTING_FOOTER*/ 58 | /*PACKAGES*/ 59 | /*RESULTS*/ 60 | /*RESULTS_GLOBAL_DENIED*/ 61 | /*RESULTS_PROCESS*/ 62 | /*RESULTS_APPLICATION*/ 63 | /*NOTHING_FOUND_MESSAGE*/ 64 | /*FIX*/ 65 | /*CANCEL*/ 66 | /*TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL*/ 67 | /*TROUBLESHOOT_LOG_DISABLED_TO_ALLOW*/ 68 | /*TROUBLESHOOT_LOG_ADDED_TO_ALLOW*/ 69 | /*TROUBLESHOOT_LOG_REMOVED_FROM_DENY*/ 70 | /*APPLIED_CHANGES*/ 71 | /*RESET_PREFERENCES*/ 72 | /*RESET_PREFERENCES_MESSAGE*/ 73 | /*CONTINUE*/ 74 | 75 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/zh_TW.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "行程設定"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "在變更設定時,應用程式會自動重新啟動。在更改某些守護進程設定時可能需要重新啟動主畫面,或甚至重新啟動使用者空間。"; 3 | "APPLICATIONS" = "應用程式"; 4 | "DAEMONS" = "守護進程"; 5 | "ADDITIONAL_EXECUTABLES" = "額外可執行檔案"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "覆寫全域插件設定"; 7 | "DISABLE_TWEAK_INJECTION" = "停用插件"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "不載入插件"; 9 | "LAUNCH_WITH_TWEAKS" = "載入插件"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "「不載入插件」選項"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "「載入插件」選項"; 12 | "APP_OPTIONS" = "應用程式設定"; 13 | "APP_OPTIONS_FOOTER" = "在 3D Touch 內新增「不載入插件」選項。若啟用此選項 3D Touch 項目將改為「載入套件」"; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "自訂插件設定"; 15 | "ALLOW" = "白名單"; 16 | "DENY" = "黑名單"; 17 | "GREYED_OUT_ENTRIES" = "無法設定此選項?"; 18 | "APP_PLUGINS" = "應用程式擴充功能"; 19 | "TWEAKS_DISABLED" = "插件已停用"; 20 | "CUSTOM" = "自訂"; 21 | "GLOBAL_OVERWRITE" = "全域覆寫"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "顯示建議守護進程"; 23 | "SHOW_ALL_DAEMONS" = "顯示全部守護進程"; 24 | "SYSTEM_APPLICATIONS" = "系統應用程式"; 25 | "USER_APPLICATIONS" = "使用者應用程式"; 26 | "HIDDEN_APPLICATIONS" = "隱形應用程式"; 27 | "GLOBAL_CONFIGURATION" = "全域設定"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "全域插件設定"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "可在此導入可執行檔(非守護進程)並變更插件設定\n\n警告:此區域建議給有經驗者使用,更改設定前請三思"; 30 | "SELECT_EXECUTABLE" = "選擇可執行檔案"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "指定可執行檔路徑"; 32 | "ERROR" = "錯誤"; 33 | "ERROR_FILE_NOT_FOUND" = "在指定位置中找不到檔案"; 34 | "ERROR_FILE_NO_EXECUTABLE" = "此檔案非可執行檔"; 35 | "ERROR_NO_TWEAKS_INJECT" = "此可執行檔因未啟用插件而被停用。"; 36 | "ADD" = "新增"; 37 | "PATH" = "路徑"; 38 | "TWEAKS" = "插件"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "開啟「覆寫全域插件設定」 選項可以自訂每個行程的全域設定。"; 40 | "OTHER" = "更多"; 41 | "DONATE" = "捐款"; 42 | "SOURCE_CODE" = "原始碼"; 43 | "CREDITS" = "貢獻"; 44 | "RESPRING" = "重新啟動主畫面"; 45 | "WARNING_ALERT_TITLE" = "警告"; 46 | "THE_INJECTION_PLATFORM" = "載入插件平台"; 47 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "停用某些插件同時將「停用插件」選項開啟時可能因 %@ 於 Choicy 載入前載入而發生問題,故無法設定此插件。"; 48 | "CHOICYLOADER_ADVICE" = "從軟體源 https://opa334.github.io 安裝 ChoicyLoader 即可暫時解決此問題"; 49 | "OPEN_REPO" = "軟體源"; 50 | "DONT_SHOW_AGAIN" = "不要再顯示"; 51 | "CLOSE" = "關閉"; 52 | "DAEMON_LIST_BOTTOM_NOTICE" = "未啟用插件的守護進程會自動隱藏"; 53 | "PACKAGE" = "Package"; 54 | "TROUBLESHOOTING" = "錯誤排除"; 55 | "TWEAK_TROUBLESHOOTING" = "插件錯誤排除"; 56 | "TWEAK_TROUBLESHOOTING_FOOTER" = "如果您認為 Choicy 讓插件載入發生問題,您可以選取它以找出哪些插件會防止被停用"; 57 | "PACKAGES" = "套件"; 58 | "RESULTS" = "結果"; 59 | "RESULTS_GLOBAL_DENIED" = "這些插件在全域設定中被停用:"; 60 | "RESULTS_PROCESS" = "「%@.dylib」被 %@ 阻擋"; 61 | "RESULTS_APPLICATION" = "「%@.dylib」被 %@ 阻擋"; 62 | "NOTHING_FOUND_MESSAGE" = "Choicy 無法影響此套件故未做出任何更改。"; 63 | "FIX" = "修復"; 64 | "CANCEL" = "取消"; 65 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "已於全域設定啟用「%@.dylib」"; 66 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "「%@」已停用「停用插件」選項,「自訂插件設定」已啟用。 將開啟「允許」選項,而「%@.dylib」於白名單內"; 67 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "已將「%@.dylib」加入到「%@」的白名單。"; 68 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "已從 \"%@\" 的黑名單移除「%@.dylib」。"; 69 | "APPLIED_CHANGES" = "已變更的設定"; 70 | "RESET_PREFERENCES" = "重設設定"; 71 | "RESET_PREFERENCES_MESSAGE" = "即將把所有設定調回預設,確定繼續?"; 72 | "CONTINUE" = "繼續"; 73 | -------------------------------------------------------------------------------- /ChoicySB/SpringBoard.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | #import "RunningBoard.h" 23 | 24 | @interface FBSystemService : NSObject 25 | + (id)sharedInstance; 26 | - (void)exitAndRelaunch:(BOOL)arg1; 27 | @end 28 | 29 | @interface FBProcessExecutionContext : NSObject 30 | @property (nonatomic,copy) NSDictionary *environment; 31 | @property (nonatomic,copy) RBSProcessIdentity *identity; 32 | @end 33 | 34 | @interface FBProcessManager : NSObject 35 | - (void)choicy_handleEnvironmentChangesForExecutionContext:(FBProcessExecutionContext *)executionContext forAppWithBundleIdentifier:(NSString *)bundleIdentifier; 36 | @end 37 | 38 | @interface SBApplicationInfo : NSObject 39 | @property (nonatomic,readonly) NSURL *executableURL; 40 | @property (nonatomic,readonly) BOOL hasHiddenTag; 41 | @property (nonatomic,retain,readonly) NSArray *tags; 42 | @end 43 | 44 | @interface SBApplication : NSObject 45 | - (SBApplicationInfo *)_appInfo; 46 | @end 47 | 48 | @interface SBSApplicationShortcutIcon : NSObject 49 | @end 50 | 51 | @interface SBSApplicationShortcutCustomImageIcon : SBSApplicationShortcutIcon 52 | - (id)initWithImageData:(id)arg1 dataType:(long long)arg2 isTemplate:(bool)arg3; 53 | @end 54 | 55 | @interface SBSApplicationShortcutItem : NSObject 56 | @property (nonatomic,copy) NSString *type; 57 | @property (nonatomic,copy) NSString *localizedTitle; 58 | @property (nonatomic,copy) NSString *localizedSubtitle; 59 | @property (nonatomic,copy) SBSApplicationShortcutIcon *icon; 60 | @property (nonatomic,copy) NSDictionary *userInfo; 61 | @property (assign,nonatomic) NSUInteger activationMode; 62 | @property (nonatomic,copy) NSString *bundleIdentifierToLaunch; 63 | @end 64 | 65 | @interface SBIconView : NSObject 66 | - (NSString *)applicationBundleIdentifier; 67 | - (NSString *)applicationBundleIdentifierForShortcuts; 68 | @end 69 | 70 | @interface SBUIAppIconForceTouchControllerDataProvider : NSObject 71 | - (NSString *)applicationBundleIdentifier; 72 | @end 73 | -------------------------------------------------------------------------------- /Shared.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "Shared.h" 22 | #import "libroot.h" 23 | 24 | BOOL parseNumberBool(id number, BOOL default_) 25 | { 26 | if (!number) return default_; 27 | if (![number isKindOfClass:[NSNumber class]]) return default_; 28 | 29 | NSNumber *numberNum = number; 30 | return numberNum.boolValue; 31 | } 32 | 33 | NSInteger parseNumberInteger(id number, NSInteger default_) 34 | { 35 | if (!number) return default_; 36 | if (![number isKindOfClass:[NSNumber class]]) return default_; 37 | 38 | NSNumber *numberNum = number; 39 | return numberNum.integerValue; 40 | } 41 | 42 | NSString *localize(NSString *key) 43 | { 44 | static NSBundle *CHBundle; 45 | static NSDictionary *englishLocalizations; 46 | 47 | if (key == nil) { 48 | return nil; 49 | } 50 | 51 | if (!CHBundle) { 52 | CHBundle = [NSBundle bundleWithPath:JBROOT_PATH_NSSTRING(@"/Library/Application Support/Choicy.bundle")]; 53 | } 54 | 55 | NSString *localizedString = [CHBundle localizedStringForKey:key value:key table:nil]; 56 | 57 | if ([localizedString isEqualToString:key]) { 58 | if (!englishLocalizations) { 59 | englishLocalizations = [NSDictionary dictionaryWithContentsOfFile:[CHBundle pathForResource:@"Localizable" ofType:@"strings" inDirectory:@"en.lproj"]]; 60 | } 61 | 62 | //If no localization was found, fallback to english 63 | NSString *engString = [englishLocalizations objectForKey:key]; 64 | 65 | if (engString) { 66 | return engString; 67 | } 68 | else { 69 | //If an english localization was not found, just return the key itself 70 | return key; 71 | } 72 | } 73 | 74 | return localizedString; 75 | } 76 | 77 | NSDictionary *processPreferencesForApplication(NSDictionary *preferences, NSString *applicationID) 78 | { 79 | NSDictionary *appSettings = [preferences objectForKey:kChoicyPrefsKeyAppSettings]; 80 | return [appSettings objectForKey:applicationID]; 81 | } 82 | 83 | NSDictionary *processPreferencesForDaemon(NSDictionary *preferences, NSString *daemonDisplayName) 84 | { 85 | NSDictionary *daemonSettings = [preferences objectForKey:kChoicyPrefsKeyDaemonSettings]; 86 | return [daemonSettings objectForKey:daemonDisplayName]; 87 | } -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/zh_CN.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "进程配置"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "当应用程序的配置更改时,它们会自动重新启动。要应用 SpringBoard 的配置,需要手动注销。要将配置应用于某些守护进程,可能需要进行(用户空间)重启。"; 3 | "APPLICATIONS" = "应用程序"; 4 | "DAEMONS" = "守护进程"; 5 | "ADDITIONAL_EXECUTABLES" = "额外的可执行文件"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "覆盖全局插件配置"; 7 | "DISABLE_TWEAK_INJECTION" = "禁用插件注入"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "无插件启动"; 9 | "LAUNCH_WITH_TWEAKS" = "带插件启动"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "「无插件启动」选项"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "「带插件启动」选项"; 12 | "APP_OPTIONS" = "应用选项"; 13 | "APP_OPTIONS_FOOTER" = "在应用程序的 3D/Haptic Touch 菜单中添加一个选项以便一次性地无插件启动该应用。如果启用,当应用程序的进程配置中禁用插件注入时,该选项将变为「带插件启动」。"; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "自定义插件配置"; 15 | "ALLOW" = "允许"; 16 | "DENY" = "拒绝"; 17 | "GREYED_OUT_ENTRIES" = "灰色条目"; 18 | "APP_PLUGINS" = "应用插件"; 19 | "TWEAKS_DISABLED" = "插件已禁用"; 20 | "CUSTOM" = "自定义"; 21 | "GLOBAL_OVERWRITE" = "全局覆盖"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "显示推荐守护进程"; 23 | "SHOW_ALL_DAEMONS" = "显示所有守护进程"; 24 | "SYSTEM_APPLICATIONS" = "系统应用程序"; 25 | "USER_APPLICATIONS" = "用户应用程序"; 26 | "HIDDEN_APPLICATIONS" = "隐藏应用程序"; 27 | "GLOBAL_CONFIGURATION" = "全局配置"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "全局插件配置"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "在此处添加额外的可执行文件(非守护进程),并对它们应用插件配置。\n\n警告:此部分适用于高级用户,请只在您知道自己在做什么的情况下使用。"; 30 | "SELECT_EXECUTABLE" = "选择可执行文件"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "指定要添加到列表中的可执行文件路径。"; 32 | "ERROR" = "错误"; 33 | "ERROR_FILE_NOT_FOUND" = "所选路径上找不到文件。"; 34 | "ERROR_FILE_NO_EXECUTABLE" = "所选路径上的文件似乎不是可执行文件。"; 35 | "ERROR_NO_TWEAKS_INJECT" = "添加失败:没有插件尝试诸如您添加的可执行文件。"; 36 | "ADD" = "添加"; 37 | "PATH" = "路径"; 38 | "TWEAKS" = "插件"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "此全局配置可以通过在进程的首选项页面中启用「覆盖全局插件配置」开关来进行逐个进程的覆盖。"; 40 | "OTHER" = "其他"; 41 | "FOLLOW_ME_ON_MASTODON" = "在 Mastodon 上关注我"; 42 | "DONATE" = "捐赠"; 43 | "SOURCE_CODE" = "源代码"; 44 | "CREDITS" = "致谢"; 45 | "RESPRING" = "注销"; 46 | "WARNING_ALERT_TITLE" = "警告"; 47 | "THE_INJECTION_PLATFORM" = "注入平台"; 48 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "已确定在应用上除了使用「禁用插件注入」开关之外禁用某些插件将无效,因为 %@ 在 Choicy 之前加载插件。这些插件已灰显受以避免引起困惑。"; 49 | "CHOICYLOADER_ADVICE" = "如果您想解决此问题,请从 `https://opa334.github.io` 仓库安装 ChoicyLoader。"; 50 | "OPEN_REPO" = "打开仓库"; 51 | "DONT_SHOW_AGAIN" = "不再显示"; 52 | "CLOSE" = "关闭"; 53 | "DAEMON_LIST_BOTTOM_NOTICE" = "没有插件尝试注入的守护进程会自动隐藏"; 54 | "PACKAGE" = "软件包"; 55 | "TROUBLESHOOTING" = "故障排除"; 56 | "TWEAK_TROUBLESHOOTING" = "插件故障排除"; 57 | "TWEAK_TROUBLESHOOTING_FOOTER" = "如果您怀疑 Choicy 可能阻止某个插件正常工作,您可以在此部分中选择该插件以查看 Choicy 是否有禁止插件中的 dylib。"; 58 | "PACKAGES" = "软件包"; 59 | "RESULTS" = "结果"; 60 | "RESULTS_GLOBAL_DENIED" = "以下插件 dylib 已在全局插件配置中禁用:"; 61 | "RESULTS_PROCESS" = "「%@.dylib」已被禁止注入以下进程:\n%@"; 62 | "RESULTS_APPLICATION" = "「%@.dylib」已被禁止注入以下应用程序:\n%@"; 63 | "NOTHING_FOUND_MESSAGE" = "Choicy 似乎不会影响此软件包安装的插件 dylib。无需采取任何操作。"; 64 | "FIX" = "修复"; 65 | "CANCEL" = "取消"; 66 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "「%@.dylib」已在全局插件配置中启用。"; 67 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "已禁用「禁用插件注入」以允许「%@」,已启用「自定义插件配置」,设置为「允许」,并将「%@.dylib」添加到允许列表中。"; 68 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "已将「%@.dylib」添加到「%@」的允许列表中。"; 69 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "已将「%@.dylib」从「%@」的拒绝列表中移除。"; 70 | "APPLIED_CHANGES" = "已应用更改"; 71 | "RESET_PREFERENCES" = "重置首选项"; 72 | "RESET_PREFERENCES_MESSAGE" = "您将永久重置 Choicy 首选项。是否要继续?"; 73 | "CONTINUE" = "继续"; -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/zh_Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "进程配置"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "当应用程序的配置更改时,它们会自动重新启动。要应用 SpringBoard 的配置,需要手动注销。要将配置应用于某些守护进程,可能需要进行(用户空间)重启。"; 3 | "APPLICATIONS" = "应用程序"; 4 | "DAEMONS" = "守护进程"; 5 | "ADDITIONAL_EXECUTABLES" = "额外的可执行文件"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "覆盖全局插件配置"; 7 | "DISABLE_TWEAK_INJECTION" = "禁用插件注入"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "无插件启动"; 9 | "LAUNCH_WITH_TWEAKS" = "带插件启动"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "「无插件启动」选项"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "「带插件启动」选项"; 12 | "APP_OPTIONS" = "应用选项"; 13 | "APP_OPTIONS_FOOTER" = "在应用程序的 3D/Haptic Touch 菜单中添加一个选项以便一次性地无插件启动该应用。如果启用,当应用程序的进程配置中禁用插件注入时,该选项将变为「带插件启动」。"; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "自定义插件配置"; 15 | "ALLOW" = "允许"; 16 | "DENY" = "拒绝"; 17 | "GREYED_OUT_ENTRIES" = "灰色条目"; 18 | "APP_PLUGINS" = "应用插件"; 19 | "TWEAKS_DISABLED" = "插件已禁用"; 20 | "CUSTOM" = "自定义"; 21 | "GLOBAL_OVERWRITE" = "全局覆盖"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "显示推荐守护进程"; 23 | "SHOW_ALL_DAEMONS" = "显示所有守护进程"; 24 | "SYSTEM_APPLICATIONS" = "系统应用程序"; 25 | "USER_APPLICATIONS" = "用户应用程序"; 26 | "HIDDEN_APPLICATIONS" = "隐藏应用程序"; 27 | "GLOBAL_CONFIGURATION" = "全局配置"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "全局插件配置"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "在此处添加额外的可执行文件(非守护进程),并对它们应用插件配置。\n\n警告:此部分适用于高级用户,请只在您知道自己在做什么的情况下使用。"; 30 | "SELECT_EXECUTABLE" = "选择可执行文件"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "指定要添加到列表中的可执行文件路径。"; 32 | "ERROR" = "错误"; 33 | "ERROR_FILE_NOT_FOUND" = "所选路径上找不到文件。"; 34 | "ERROR_FILE_NO_EXECUTABLE" = "所选路径上的文件似乎不是可执行文件。"; 35 | "ERROR_NO_TWEAKS_INJECT" = "添加失败:没有插件尝试诸如您添加的可执行文件。"; 36 | "ADD" = "添加"; 37 | "PATH" = "路径"; 38 | "TWEAKS" = "插件"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "此全局配置可以通过在进程的首选项页面中启用「覆盖全局插件配置」开关来进行逐个进程的覆盖。"; 40 | "OTHER" = "其他"; 41 | "FOLLOW_ME_ON_MASTODON" = "在 Mastodon 上关注我"; 42 | "DONATE" = "捐赠"; 43 | "SOURCE_CODE" = "源代码"; 44 | "CREDITS" = "致谢"; 45 | "RESPRING" = "注销"; 46 | "WARNING_ALERT_TITLE" = "警告"; 47 | "THE_INJECTION_PLATFORM" = "注入平台"; 48 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "已确定在应用上除了使用「禁用插件注入」开关之外禁用某些插件将无效,因为 %@ 在 Choicy 之前加载插件。这些插件已灰显受以避免引起困惑。"; 49 | "CHOICYLOADER_ADVICE" = "如果您想解决此问题,请从 `https://opa334.github.io` 仓库安装 ChoicyLoader。"; 50 | "OPEN_REPO" = "打开仓库"; 51 | "DONT_SHOW_AGAIN" = "不再显示"; 52 | "CLOSE" = "关闭"; 53 | "DAEMON_LIST_BOTTOM_NOTICE" = "没有插件尝试注入的守护进程会自动隐藏"; 54 | "PACKAGE" = "软件包"; 55 | "TROUBLESHOOTING" = "故障排除"; 56 | "TWEAK_TROUBLESHOOTING" = "插件故障排除"; 57 | "TWEAK_TROUBLESHOOTING_FOOTER" = "如果您怀疑 Choicy 可能阻止某个插件正常工作,您可以在此部分中选择该插件以查看 Choicy 是否有禁止插件中的 dylib。"; 58 | "PACKAGES" = "软件包"; 59 | "RESULTS" = "结果"; 60 | "RESULTS_GLOBAL_DENIED" = "以下插件 dylib 已在全局插件配置中禁用:"; 61 | "RESULTS_PROCESS" = "「%@.dylib」已被禁止注入以下进程:\n%@"; 62 | "RESULTS_APPLICATION" = "「%@.dylib」已被禁止注入以下应用程序:\n%@"; 63 | "NOTHING_FOUND_MESSAGE" = "Choicy 似乎不会影响此软件包安装的插件 dylib。无需采取任何操作。"; 64 | "FIX" = "修复"; 65 | "CANCEL" = "取消"; 66 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "「%@.dylib」已在全局插件配置中启用。"; 67 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "已禁用「禁用插件注入」以允许「%@」,已启用「自定义插件配置」,设置为「允许」,并将「%@.dylib」添加到允许列表中。"; 68 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "已将「%@.dylib」添加到「%@」的允许列表中。"; 69 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "已将「%@.dylib」从「%@」的拒绝列表中移除。"; 70 | "APPLIED_CHANGES" = "已应用更改"; 71 | "RESET_PREFERENCES" = "重置首选项"; 72 | "RESET_PREFERENCES_MESSAGE" = "您将永久重置 Choicy 首选项。是否要继续?"; 73 | "CONTINUE" = "继续"; 74 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPApplicationPlugInsListController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPApplicationPlugInsListController.h" 22 | #import "CoreServices.h" 23 | #import 24 | #import 25 | #import "CHPProcessConfigurationListController.h" 26 | #import "../Shared.h" 27 | #import "CHPPreferences.h" 28 | #import "CHPApplicationListSubcontrollerController.h" 29 | #import "CHPListController.h" 30 | 31 | @implementation CHPApplicationPlugInsListController 32 | 33 | - (void)loadPlugIns 34 | { 35 | NSString *applicationID = [self appIdentifier]; 36 | LSApplicationProxy *appProxy = [LSApplicationProxy applicationProxyForIdentifier:applicationID]; 37 | 38 | NSMutableArray *appPlugIns = [NSMutableArray new]; 39 | [appPlugIns addObjectsFromArray:[appProxy plugInKitPlugins]]; 40 | [appPlugIns addObjectsFromArray:[appProxy VPNPlugins]]; 41 | 42 | _appPlugIns = [appPlugIns sortedArrayUsingComparator:^NSComparisonResult(LSPlugInKitProxy *p1, LSPlugInKitProxy *p2) { 43 | NSString *p1Name = p1.infoPlist[@"CFBundleExecutable"]; 44 | NSString *p2Name = p2.infoPlist[@"CFBundleExecutable"]; 45 | return [p1Name caseInsensitiveCompare:p2Name]; 46 | }]; 47 | } 48 | 49 | - (PSSpecifier *)newSpecifierForPlugIn:(LSPlugInKitProxy *)plugInProxy 50 | { 51 | NSString *plugInName = plugInProxy.infoPlist[@"CFBundleExecutable"]; 52 | // alternative on iOS 14+ 53 | /*else if (NSClassFromString(@"LSApplicationExtensionRecord")) { 54 | LSApplicationExtensionRecord *appexRecord = [plugInProxy valueForKey:@"_appexRecord"]; 55 | plugInName = appexRecord.executableURL.lastPathComponent; 56 | }*/ 57 | 58 | PSSpecifier *specifier = [PSSpecifier preferenceSpecifierNamed:plugInName 59 | target:self 60 | set:nil 61 | get:@selector(previewStringForSpecifier:) 62 | detail:[CHPProcessConfigurationListController class] 63 | cell:PSLinkListCell 64 | edit:nil]; 65 | 66 | [specifier setProperty:@YES forKey:@"enabled"]; 67 | [specifier setProperty:plugInProxy.bundleIdentifier forKey:@"pluginIdentifier"]; 68 | 69 | return specifier; 70 | } 71 | 72 | - (NSString *)appIdentifier 73 | { 74 | return [[self specifier] propertyForKey:@"applicationIdentifier"]; 75 | } 76 | 77 | - (NSMutableArray *)specifiers 78 | { 79 | if (!_specifiers) { 80 | _specifiers = [NSMutableArray new]; 81 | 82 | [self loadPlugIns]; 83 | 84 | [_appPlugIns enumerateObjectsUsingBlock:^(LSPlugInKitProxy *plugInProxy, NSUInteger idx, BOOL *stop) { 85 | PSSpecifier *plugInSpecifier = [self newSpecifierForPlugIn:plugInProxy]; 86 | [_specifiers addObject:plugInSpecifier]; 87 | }]; 88 | } 89 | 90 | return _specifiers; 91 | } 92 | 93 | - (NSString *)previewStringForSpecifier:(PSSpecifier *)specifier 94 | { 95 | NSString *plugInID = [specifier propertyForKey:@"pluginIdentifier"]; 96 | NSDictionary *appSettings = [preferences objectForKey:kChoicyPrefsKeyAppSettings]; 97 | NSDictionary *settingsForApplication = [appSettings objectForKey:plugInID]; 98 | return [CHPListController previewStringForProcessPreferences:settingsForApplication]; 99 | } 100 | 101 | @end -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/he.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "תצורת תהליך"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "האפליקציות מופעלות מחדש אוטומטית כאשר ההגדרות שלהן משתנות. כדי לבצע תצורה של SpringBoard נדרשת הקפצה ידנית. על מנת להשתמש בתצורה של שירותי רקע מסוימים, יש צורך בהפעלה מחדש או הפעלה מחדש של המכשיר."; 3 | "APPLICATIONS" = "אפליקציות"; 4 | "DAEMONS" = "שירותי רקע"; 5 | "ADDITIONAL_EXECUTABLES" = "קובצי הפעלה נוספים"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "החלף תצורת טוויקים גלובלית"; 7 | "DISABLE_TWEAK_INJECTION" = "השבת הזרקת טוויקים"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "הפעל ללא טוויקים"; 9 | "LAUNCH_WITH_TWEAKS" = "הפעל עם טוויקים"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "אפשרות \"הפעל ללא טוויקים\""; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "אפשרות \"הפעל עם טוויקים\""; 12 | "APP_OPTIONS" = "אפשרויות אפליקציה"; 13 | "APP_OPTIONS_FOOTER" = "מוסיף אפשרות 'הפעל ללא טוויקים' לתפריט 3D Touch / Haptic טביעת אצבע של אפליקציה. אם היא מופעלת, האפשרות הופכת לאפשרות 'הפעל עם טוויקים' אם כבר הושבתו טוויקים עבור האפליקציה המתאימה בתצורה."; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "תצורה מותאמת אישית של טוויק"; 15 | "ALLOW" = "אפשר"; 16 | "DENY" = "לא מעוניין"; 17 | "GREYED_OUT_ENTRIES" = "ערכים אפורים?"; 18 | "APP_PLUGINS" = "תוספים לאפליקציה"; 19 | "TWEAKS_DISABLED" = "טוויקים מושבתים"; 20 | "CUSTOM" = "התאמה אישית"; 21 | "GLOBAL_OVERWRITE" = "תצורה עולמית"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "הצג שירותי רקע מומלצים"; 23 | "SHOW_ALL_DAEMONS" = "הצג את כל שירותי הרקע"; 24 | "SYSTEM_APPLICATIONS" = "אפליקציות מערכת"; 25 | "USER_APPLICATIONS" = "אפליקציות משתמש"; 26 | "HIDDEN_APPLICATIONS" = "הסתרת אפליקציות"; 27 | "GLOBAL_CONFIGURATION" = "תצורה גלובלית"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "תצורה גלובלית של טוויקים"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "כאן אתה יכול להוסיף קובצי הפעלה נוספים (לא שירותי רקע) ולהחיל עליהם תצורות כוונון.\n\nאזהרה: סעיף זה מיועד למשתמשים מתקדמים, השתמש בו רק אם אתה יודע מה אתה עושה."; 30 | "SELECT_EXECUTABLE" = "בחר בר הפעלה"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "ציין את הנתיב של קובץ ההפעלה להוספה לרשימה."; 32 | "ERROR" = "שגיאה"; 33 | "ERROR_FILE_NOT_FOUND" = "לא נמצא קובץ בנתיב שנבחר."; 34 | "ERROR_FILE_NO_EXECUTABLE" = "הקובץ בנתיב שנבחר אינו נראה כקובץ הפעלה."; 35 | "ERROR_NO_TWEAKS_INJECT" = "לקובץ ההפעלה שניסית להוסיף אין שום טוויקים שמזריקים לתוכו, הוספתו נמנעה מכיוון שהוא לא יועיל."; 36 | "ADD" = "הוסף"; 37 | "PATH" = "נתיב"; 38 | "TWEAKS" = "טוויקים"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "ניתן להחליף תצורה גלובלית זו על בסיס כל תהליך על ידי הפעלת הלחצן \"תצורה גלובלית של טוויקים\" בדף ההעדפות של התהליך."; 40 | "OTHER" = "אחר"; 41 | "DONATE" = "תרומה"; 42 | "SOURCE_CODE" = "קוד מקור"; 43 | "CREDITS" = "קרדיטים"; 44 | "RESPRING" = "ריספינג"; 45 | "WARNING_ALERT_TITLE" = "אזהרה"; 46 | "THE_INJECTION_PLATFORM" = "פלטפורמת ההזרקה"; 47 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "נקבע כי השבתת כיווצים מסוימים עם כל דבר מלבד הלחצן \"השבת הזרקת טוויקים\" באפליקציות לא תעבוד עקב\n %@ טעינת השינויים לפני Choicy, השינויים המושפעים הופיעו באפור כדי למנוע בלבול."; 48 | "CHOICYLOADER_ADVICE" = "כפתרון לעקיפת הבעיה, ניתן להוריד את ChoicyLoader מהמקור\n https://opa334.github.io להתקנה"; 49 | "OPEN_REPO" = "קוד פתוח"; 50 | "DONT_SHOW_AGAIN" = "אל תציג לי שוב"; 51 | "CLOSE" = "סגור"; 52 | "DAEMON_LIST_BOTTOM_NOTICE" = "שרותי רקע ששום תיקון לא מזריק אליהם מוסתרים באופן אוטומטי"; 53 | "PACKAGE" = "חבילה"; 54 | "TROUBLESHOOTING" = "פתרון תקלות"; 55 | "TWEAK_TROUBLESHOOTING" = "פתרון בעיות טוויקים"; 56 | "TWEAK_TROUBLESHOOTING_FOOTER" = "אם אתה חושד שייתכן ש-Choicy מונע מטוויק לפעול כראוי, תוכל לבחור בו בסעיף זה כדי לברר אם דחיקה של הזרקה של כל אחד \nמהטוויקים.\n\nתרגום לשפה העברית מאת עומרי גז\nHebrew language by Omri Guez\n"; 57 | "PACKAGES" = "חבילות"; 58 | "RESULTS" = "תוצאות"; 59 | "RESULTS_GLOBAL_DENIED" = "הטוויק dylibs הבא הושבת בתצורת טוויקים גלובלית:"; 60 | "RESULTS_PROCESS" = "ה-\"%@.dylib\" נמנעת מהזרקה לתהליכים הבאים:\n%@"; 61 | "RESULTS_APPLICATION" = "ה- \"%@.dylib\" נמנעת מלהזריק לאפליקציות הבאות:\n%@"; 62 | "NOTHING_FOUND_MESSAGE" = "נראה ש-choicy לא משפיע על ה-dylibs המותקן על ידי חבילה זו. לא צריך לעשות שום פעולה."; 63 | "FIX" = "תיקון"; 64 | "CANCEL" = "ביטול"; 65 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "\"%@.dylib\" has been enabled inside global tweak configuration."; 66 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "\"השבת הזרקת טוויקים\" הושבת עבור \"%@\", \"תצורת טוויקים מותאמת אישית\" הופעל, הוגדר ל \"אפשר\" ו \"%@.dylib\" התווסף להרשאה ברשימה."; 67 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "ה-\"%@.dylib\" התווסף לרשימת ההיתרים של \"%@\"."; 68 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "ה-\"%@.dylib\" הוסר מרשימת הדחייה של\"%@\"."; 69 | "APPLIED_CHANGES" = "שינויים שהוחלו"; 70 | "RESET_PREFERENCES" = "אפס העדפות"; 71 | "RESET_PREFERENCES_MESSAGE" = "אתה עומד לאפס באופן בלתי הפיך את העדפות של Choicy. האם אתה רוצה להמשיך?"; 72 | "CONTINUE" = "המשך"; 73 | -------------------------------------------------------------------------------- /ChoicyPrefsMigrator.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "ChoicyPrefsMigrator.h" 22 | #import "Shared.h" 23 | 24 | void renameKey(NSMutableDictionary *dict, NSString *key, NSString *newKey) 25 | { 26 | if (!dict || !key || !newKey) return; 27 | NSObject *value = dict[key]; 28 | if (!value) return; 29 | [dict removeObjectForKey:key]; 30 | dict[newKey] = value; 31 | } 32 | 33 | void renameKeys(NSMutableDictionary *dict, NSDictionary *keyChanges) 34 | { 35 | if (!dict || !keyChanges) return; 36 | 37 | [keyChanges enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *newKey, BOOL *stop) { 38 | renameKey(dict, key, newKey); 39 | }]; 40 | } 41 | 42 | void removeKeys(NSMutableDictionary *dict, NSArray *keys) 43 | { 44 | if (!dict || !keys) return; 45 | 46 | [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) { 47 | [dict removeObjectForKey:key]; 48 | }]; 49 | } 50 | 51 | @implementation ChoicyPrefsMigrator 52 | 53 | + (BOOL)preferencesNeedMigration:(NSDictionary *)prefs 54 | { 55 | NSInteger preferenceVersion = parseNumberInteger(prefs[kChoicyPrefsVersionKey], 0); 56 | return preferenceVersion < kChoicyPrefsCurrentVersion; 57 | } 58 | 59 | + (void)migratePreferences:(NSMutableDictionary *)prefs 60 | { 61 | NSInteger preferenceVersion = parseNumberInteger(prefs[kChoicyPrefsVersionKey], 0); 62 | 63 | // pre 1.4 -> 1.4 64 | if (preferenceVersion == 0) { 65 | BOOL prev_allowBlacklistOverwrites = parseNumberBool(prefs[@"allowBlacklistOverwrites"], NO); 66 | BOOL prev_allowWhitelistOverwrites = parseNumberBool(prefs[@"allowWhitelistOverwrites"], NO); 67 | 68 | renameKeys(prefs, kChoicyPrefMigration1_4_ChangedKeys); 69 | removeKeys(prefs, kChoicyPrefMigration1_4_RemovedKeys); 70 | 71 | void (^processPrefsHandler)(NSMutableDictionary*, NSString*, NSDictionary*, BOOL *) = ^(NSMutableDictionary *sourceDict, NSString *key, NSDictionary *processPrefs, BOOL *stop) { 72 | NSMutableDictionary *processPrefsM = processPrefs.mutableCopy; 73 | renameKeys(processPrefsM, kChoicyProcessPrefMigration1_4_ChangedKeys); 74 | 75 | BOOL customTweakConfigurationEnabled = parseNumberBool(processPrefsM[kChoicyProcessPrefsKeyCustomTweakConfigurationEnabled], NO); 76 | if (customTweakConfigurationEnabled) { 77 | NSInteger allowDenyMode = parseNumberInteger(processPrefsM[kChoicyProcessPrefsKeyAllowDenyMode], 1); 78 | if ((allowDenyMode == 1 && prev_allowWhitelistOverwrites) || (allowDenyMode == 2 && prev_allowBlacklistOverwrites)) { // ALLOW 79 | processPrefsM[kChoicyProcessPrefsKeyOverwriteGlobalTweakConfiguration] = @YES; 80 | } 81 | } 82 | 83 | sourceDict[key] = processPrefsM.copy; 84 | }; 85 | 86 | NSMutableDictionary *appSettings = [prefs[kChoicyPrefsKeyAppSettings] mutableCopy]; 87 | NSMutableDictionary *daemonSettings = [prefs[kChoicyPrefsKeyDaemonSettings] mutableCopy]; 88 | 89 | if (appSettings) { 90 | [appSettings enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *processPrefs, BOOL *stop) { 91 | processPrefsHandler(appSettings, key, processPrefs, stop); 92 | }]; 93 | prefs[kChoicyPrefsKeyAppSettings] = appSettings.copy; 94 | } 95 | 96 | if (daemonSettings) { 97 | [daemonSettings enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *processPrefs, BOOL *stop) { 98 | processPrefsHandler(daemonSettings, key, processPrefs, stop); 99 | }]; 100 | prefs[kChoicyPrefsKeyDaemonSettings] = daemonSettings.copy; 101 | } 102 | } 103 | } 104 | 105 | + (void)updatePreferenceVersion:(NSMutableDictionary *)prefs 106 | { 107 | prefs[kChoicyPrefsVersionKey] = @kChoicyPrefsCurrentVersion; 108 | } 109 | 110 | @end -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/ar.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "إعداد العمليات"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "يتم إعادة تشغيل التطبيقات تلقائيًا عندما يتغير إعدادها. لتطبيق إعداد على واجهة النظام، يتطلب عمل إعادة تشغيل سريع يدويًا. لتطبيق التغييرات على بعض الملفات الخفية، يتطلب إعادة تشغيل userspace."; 3 | "APPLICATIONS" = "التطبيقات"; 4 | "DAEMONS" = "الملفات الخفية"; 5 | "ADDITIONAL_EXECUTABLES" = "الملفات التنفيذية الإضافية"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "التنفيذ فوق الإعداد العام للأدوات"; 7 | "DISABLE_TWEAK_INJECTION" = "تعطيل وصول الأدوات"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "تشغيل بدون الأدوات"; 9 | "LAUNCH_WITH_TWEAKS" = "تشغيل بالأدوات"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "خيار \"تشغيل بدون الأدوات\""; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "خيار \"تشغيل بالأدوات\""; 12 | "APP_OPTIONS" = "خيارات التطبيق"; 13 | "APP_OPTIONS_FOOTER" = "إضافة خيار إلى قائمة 3D اللمسية وقائمة اللمس الحسية الخاصة بالتطبيقات لتشغيل التطبيق مرة واحدة بدون أدوات. إذا تم التفعيل، فسيتحول الخيار إلى \"تشغيل بالأدوات\" عند تعطيل وصول الأدوات داخل إعداد عملية التطبيق."; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "إعداد مخصص للأدوات"; 15 | "ALLOW" = "السماح"; 16 | "DENY" = "عدم السماح"; 17 | "GREYED_OUT_ENTRIES" = "تأكيد الاستبعاد؟"; 18 | "APP_PLUGINS" = "إضافات التطبيق"; 19 | "TWEAKS_DISABLED" = "تعطيل الأدوات"; 20 | "CUSTOM" = "تخصيص"; 21 | "GLOBAL_OVERWRITE" = "التنفيذ فوق الإعداد العام"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "إظهار الملفات الخفية المهمة"; 23 | "SHOW_ALL_DAEMONS" = "إظهار كل الملفات الخفية"; 24 | "SYSTEM_APPLICATIONS" = "تطبيقات النظام"; 25 | "USER_APPLICATIONS" = "تطبيقات النظام"; 26 | "HIDDEN_APPLICATIONS" = "التطبيقات المخفية"; 27 | "GLOBAL_CONFIGURATION" = "الإعداد العام"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "الإعداد العام للأدوات"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "يمكنك هنا إضافة ملفات تنفيذية إضافية (غير الملفات الخفية) وتطبيق إعداد الأدوات عليها.\n\nتحذير: هذا القسم مخصص للمستخدمين الخبراء، استخدمه فقط إذا كنت تعرف مالذي تفعله."; 30 | "SELECT_EXECUTABLE" = "حدد ملفًا تنفيذيًا"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "حدد مسار الملف القابل للتنفيذ لإضافته إلى القائمة."; 32 | "ERROR" = "خطأ"; 33 | "ERROR_FILE_NOT_FOUND" = "لم يتم العثور على ملف في المسار المحدد."; 34 | "ERROR_FILE_NO_EXECUTABLE" = "يبدو أن الملف الموجود في المسار المحدد ليس ملفًا تنفيذيًا."; 35 | "ERROR_NO_TWEAKS_INJECT" = "لا يحتوي الملف التنفيذي الذي تحاول إضافته على أدوات تصل إليه، لذلك تم منع إضافته لأنه لا فائدة من ذلك."; 36 | "ADD" = "إضافة"; 37 | "PATH" = "المسار"; 38 | "TWEAKS" = "الأدوات"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "يمكن التنفيذ فوق هذا الإعداد العام لكل عملية بناءً على تفعيل خيار \"التنفيذ فوق الإعداد العام للأدوات\" في صفحة الإعدادات للعملية."; 40 | "OTHER" = "أخرى"; 41 | "DONATE" = "التبرع"; 42 | "SOURCE_CODE" = "شفرة المصدر"; 43 | "CREDITS" = "الاعتمادات"; 44 | "RESPRING" = "إعادة تشغيل سريع"; 45 | "WARNING_ALERT_TITLE" = "تحذير"; 46 | "THE_INJECTION_PLATFORM" = "منصة الوصول"; 47 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "لقد اتضح أن تعطيل بعض الأدوات على التطبيقات بأي طريقة غير خيار \"تعطيل وصول الأدوات\" لن يعمل بسبب أن %@ يتم تحميله قبل Choicy، تم استبعاد الأدوات المتأثرة لتجنب حدوث تعارض."; 48 | "CHOICYLOADER_ADVICE" = "إذا كنت ترغب في محاولة حل هذه المشكلة، قم بتثبيت ChoicyLoader من المستودع https://opa334.github.io"; 49 | "OPEN_REPO" = "فتح المستودع"; 50 | "DONT_SHOW_AGAIN" = "عدم الإظهار مجددًا"; 51 | "CLOSE" = "إغلاق"; 52 | "DAEMON_LIST_BOTTOM_NOTICE" = "الملفات الخفية التي لا تحمل وصول للأدوات تم إخفاؤها تلقائيًا"; 53 | "PACKAGE" = "الحزمة"; 54 | "TROUBLESHOOTING" = "استكشاف الأخطاء وإصلاحها"; 55 | "TWEAK_TROUBLESHOOTING" = "استكشاف أخطاء الأداة وإصلاحها"; 56 | "TWEAK_TROUBLESHOOTING_FOOTER" = "إذا كنت تعتقد أن Choicy قد تمنع أداةً ما من العمل بشكلٍ صحيح، فيمكنك تحديدها في هذا القسم لمعرفة ما إذا كان يتم رفض أي من dylib الأدوات من الوصول."; 57 | "PACKAGES" = "الحزم"; 58 | "RESULTS" = "النتائج"; 59 | "RESULTS_GLOBAL_DENIED" = "تم تعطيل dylib الأدوات التالية داخل الإعداد العام للأدوات:"; 60 | "RESULTS_PROCESS" = "تم منع \"%@.dylib\" من الوصول إلى العمليات التالية:\n%@"; 61 | "RESULTS_APPLICATION" = "تم منع \"%@.dylib\" من الوصول إلى التطبيقات التالية:\n%@"; 62 | "NOTHING_FOUND_MESSAGE" = "لا يبدو أن Choicy يؤثر على ملفات dylib المثبتة بواسطة هذه الحزمة. لا يلزم اتخاذ أي إجراء."; 63 | "FIX" = "إصلاح"; 64 | "CANCEL" = "إلغاء"; 65 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "تم تفعيل \"%@.dylib\" داخل الإعداد العام للأدوات."; 66 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "تم تعطيل \"تعطيل وصول الأدوات\" لـ \"%@\"، وتم تفعيل \"الإعداد المخصص للأدوات\" وتعيينه على \"السماح\" وأُضيف \"%@.dylib\" إلى قائمة السماح."; 67 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "تم إضافة \"%@.dylib\" إلى قائمة السماح \"%@\"."; 68 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "تم إزالة \"%@.dylib\" من قائمة عدم السماح \"%@\"."; 69 | "APPLIED_CHANGES" = "تطبيق التغييرات"; 70 | "RESET_PREFERENCES" = "إعادة تعيين الإعدادات"; 71 | "RESET_PREFERENCES_MESSAGE" = "أنت على وشك إعادة تعيين إعدادات Choicy بشكلٍ لا رجعة فيه. هل تريد الاستمرار؟"; 72 | "CONTINUE" = "استمرار"; 73 | -------------------------------------------------------------------------------- /Shared.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import 22 | #import 23 | #import 24 | #import 25 | 26 | extern NSBundle *CHBundle; 27 | extern NSString *localize(NSString *key); 28 | extern NSDictionary *processPreferencesForApplication(NSDictionary *preferences, NSString *applicationID); 29 | extern NSDictionary *processPreferencesForDaemon(NSDictionary *preferences, NSString *daemonDisplayName); 30 | 31 | extern BOOL parseNumberBool(id number, BOOL default_); 32 | extern NSInteger parseNumberInteger(id number, NSInteger default_); 33 | 34 | #define kChoicyPrefsPlistPath JBROOT_PATH(@"/var/mobile/Library/Preferences/com.opa334.choicyprefs.plist") 35 | #define kChoicyDylibName @" Choicy" 36 | 37 | #define kChoicyPrefsKeyGlobalDeniedTweaks @"globalDeniedTweaks" 38 | #define kChoicyPrefsKeyAppSettings @"appSettings" 39 | #define kChoicyPrefsKeyDaemonSettings @"daemonSettings" 40 | #define kChoicyPrefsKeyAdditionalExecutables @"additionalExecutables" 41 | #define kChoicyProcessPrefsKeyTweakInjectionDisabled @"tweakInjectionDisabled" 42 | #define kChoicyProcessPrefsKeyCustomTweakConfigurationEnabled @"customTweakConfigurationEnabled" 43 | #define kChoicyProcessPrefsKeyAllowDenyMode @"allowDenyMode" 44 | #define kChoicyProcessPrefsKeyDeniedTweaks @"deniedTweaks" 45 | #define kChoicyProcessPrefsKeyAllowedTweaks @"allowedTweaks" 46 | #define kChoicyProcessPrefsKeyOverwriteGlobalTweakConfiguration @"overwriteGlobalTweakConfiguration" 47 | 48 | // pre 1.4 keys 49 | #define kChoicyPrefsKeyGlobalDeniedTweaks_LEGACY @"globalTweakBlacklist" 50 | #define kChoicyProcessPrefsKeyAllowDenyMode_LEGACY @"whitelistBlacklistSegment" 51 | #define kChoicyProcessPrefsKeyDeniedTweaks_LEGACY @"tweakBlacklist" 52 | #define kChoicyProcessPrefsKeyAllowedTweaks_LEGACY @"tweakWhitelist" 53 | 54 | // pref migration 55 | #define kChoicyPrefMigration1_4_ChangedKeys @{kChoicyPrefsKeyGlobalDeniedTweaks_LEGACY : kChoicyPrefsKeyGlobalDeniedTweaks} 56 | #define kChoicyPrefMigration1_4_RemovedKeys @[@"allowBlacklistOverwrites", @"allowWhitelistOverwrites"] 57 | #define kChoicyProcessPrefMigration1_4_ChangedKeys @{kChoicyProcessPrefsKeyAllowDenyMode_LEGACY : kChoicyProcessPrefsKeyAllowDenyMode, kChoicyProcessPrefsKeyDeniedTweaks_LEGACY : kChoicyProcessPrefsKeyDeniedTweaks, kChoicyProcessPrefsKeyAllowedTweaks_LEGACY : kChoicyProcessPrefsKeyAllowedTweaks} 58 | 59 | #define kPreferencesBundleID @"com.apple.Preferences" 60 | #define kSpringboardBundleID @"com.apple.springboard" 61 | 62 | #define kEnvDeniedTweaksOverride "CHOICY_DENIED_TWEAKS_OVERRIDE" 63 | #define kEnvAllowedTweaksOverride "CHOICY_ALLOWED_TWEAKS_OVERRIDE" 64 | #define kEnvOverwriteGlobalConfigurationOverride "CHOICY_OVERWRITE_GLOBAL_TWEAK_CONFIGURATION_OVERRIDE" 65 | 66 | #define kNoDisableTweakInjectionToggle @[kPreferencesBundleID] 67 | #define kAlwaysInjectGlobal @[kChoicyDylibName, @"MobileSafety"] 68 | #define kAlwaysInjectSpringboard @[@"ChoicySB"] 69 | #define kAlwaysInjectPreferences @[@"PreferenceLoader", @"preferred"] 70 | 71 | #define kChoicyPrefsCurrentVersion 1 72 | #define kChoicyPrefsVersionKey @"preferenceVersion" 73 | 74 | extern void BKSTerminateApplicationForReasonAndReportWithDescription(NSString *bundleID, int reasonID, bool report, NSString *description); 75 | 76 | #ifndef kCFCoreFoundationVersionNumber_iOS_11_0 77 | #define kCFCoreFoundationVersionNumber_iOS_11_0 1443.00 78 | #endif 79 | 80 | #ifndef kCFCoreFoundationVersionNumber_iOS_13_0 81 | #define kCFCoreFoundationVersionNumber_iOS_13_0 1665.15 82 | #endif 83 | 84 | #ifndef kCFCoreFoundationVersionNumber_iOS_14_0 85 | #define kCFCoreFoundationVersionNumber_iOS_14_0 1740.00 86 | #endif 87 | 88 | #ifndef kCFCoreFoundationVersionNumber_iOS_14_1 89 | #define kCFCoreFoundationVersionNumber_iOS_14_1 1751.108 90 | #endif 91 | 92 | #ifndef kCFCoreFoundationVersionNumber_iOS_15_0 93 | #define kCFCoreFoundationVersionNumber_iOS_15_0 1854 94 | #endif -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "Process Configuration"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "Applications are automatically restarted when their configuration changes. To apply the SpringBoard configuration, a manual respring is needed. To apply the configuration to some daemons, it might be needed to do a (userspace) reboot."; 3 | "APPLICATIONS" = "Applications"; 4 | "DAEMONS" = "Daemons"; 5 | "ADDITIONAL_EXECUTABLES" = "Additional Executables"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "Overwrite Global Tweak Configuration"; 7 | "DISABLE_TWEAK_INJECTION" = "Disable Tweak Injection"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "Launch Without Tweaks"; 9 | "LAUNCH_WITH_TWEAKS" = "Launch With Tweaks"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "\"Launch Without Tweaks\" Option"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "\"Launch With Tweaks\" Option"; 12 | "APP_OPTIONS" = "App Options"; 13 | "APP_OPTIONS_FOOTER" = "Adds an option to the 3D touch / haptic touch menu of applications with the ability to launch the application once without tweaks. If enabled, the option will turn into \"Launch with Tweaks\" when tweak injection has been disabled inside the process configuration of the application."; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "Custom Tweak Configuration"; 15 | "ALLOW" = "Allow"; 16 | "DENY" = "Deny"; 17 | "GREYED_OUT_ENTRIES" = "Greyed Out Entries?"; 18 | "APP_PLUGINS" = "App Plugins"; 19 | "TWEAKS_DISABLED" = "Tweaks Disabled"; 20 | "CUSTOM" = "Custom"; 21 | "GLOBAL_OVERWRITE" = "Global Overwrite"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "Show Recommended Daemons"; 23 | "SHOW_ALL_DAEMONS" = "Show All Daemons"; 24 | "SYSTEM_APPLICATIONS" = "System Applications"; 25 | "USER_APPLICATIONS" = "User Applications"; 26 | "HIDDEN_APPLICATIONS" = "Hidden Applications"; 27 | "GLOBAL_CONFIGURATION" = "Global Configuration"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "Global Tweak Configuration"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "Here you can add additional executables (non daemons) and apply tweak configurations to them.\n\nWARNING: This section is for power users, only use it if you know what you are doing."; 30 | "SELECT_EXECUTABLE" = "Select Executable"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "Specify the path of the executable to add to the list."; 32 | "ERROR" = "Error"; 33 | "ERROR_FILE_NOT_FOUND" = "No file found at selected path."; 34 | "ERROR_FILE_NO_EXECUTABLE" = "The file at the selected path does not seem to be an executable."; 35 | "ERROR_NO_TWEAKS_INJECT" = "The executable you tried to add has no tweaks injecting into it, adding it has been prevented as it would be of no use."; 36 | "ADD" = "Add"; 37 | "PATH" = "Path"; 38 | "TWEAKS" = "Tweaks"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "This global configuration can be overwritten on a per process basis by enabling the \"Overwrite Global Tweak Configuration\" toggle in the preference page of a process."; 40 | "OTHER" = "Other"; 41 | "FOLLOW_ME_ON_MASTODON" = "Follow me on Mastodon"; 42 | "DONATE" = "Donate"; 43 | "SOURCE_CODE" = "Source Code"; 44 | "CREDITS" = "Credits"; 45 | "RESPRING" = "Respring"; 46 | "WARNING_ALERT_TITLE" = "Warning"; 47 | "THE_INJECTION_PLATFORM" = "the injection platform"; 48 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "It was determined that disabling certain tweaks with anything but the \"Disable Tweak Injection\" toggle on apps will not work due to %@ loading the tweaks before Choicy, the affected tweaks have been greyed out to avoid confusion."; 49 | "CHOICYLOADER_ADVICE" = "If you want to work around this issue, install ChoicyLoader from the repo https://opa334.github.io"; 50 | "OPEN_REPO" = "Open Repo"; 51 | "DONT_SHOW_AGAIN" = "Don't Show Again"; 52 | "CLOSE" = "Close"; 53 | "DAEMON_LIST_BOTTOM_NOTICE" = "Daemons that no tweak injects into are automatically hidden"; 54 | "PACKAGE" = "Package"; 55 | "TROUBLESHOOTING" = "Troubleshooting"; 56 | "TWEAK_TROUBLESHOOTING" = "Tweak Troubleshooting"; 57 | "TWEAK_TROUBLESHOOTING_FOOTER" = "If you suspect that Choicy may be preventing a tweak from working correctly, you can select it in this section to find out if any of the tweaks dylibs are being denied from injecting."; 58 | "PACKAGES" = "Packages"; 59 | "RESULTS" = "Results"; 60 | "RESULTS_GLOBAL_DENIED" = "The following tweak dylibs have been disabled inside global tweak configuration:"; 61 | "RESULTS_PROCESS" = "\"%@.dylib\" is being denied from injecting into the following processes:\n%@"; 62 | "RESULTS_APPLICATION" = "\"%@.dylib\" is being denied from injecting into the following applications:\n%@"; 63 | "NOTHING_FOUND_MESSAGE" = "Choicy does not seem to impact the dylibs installed by this package. No action needs to be done."; 64 | "FIX" = "Fix"; 65 | "CANCEL" = "Cancel"; 66 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "\"%@.dylib\" has been enabled inside global tweak configuration."; 67 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "\"Disable Tweak Injection\" has been disabled for \"%@\", \"Custom Tweak Configuration\" has been enabled, set to \"Allow\" and \"%@.dylib\" has been added to the allow list."; 68 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "\"%@.dylib\" has been added to the allow list of \"%@\"."; 69 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "\"%@.dylib\" has been removed from the deny list of \"%@\"."; 70 | "APPLIED_CHANGES" = "Applied Changes"; 71 | "RESET_PREFERENCES" = "Reset Preferences"; 72 | "RESET_PREFERENCES_MESSAGE" = "You are about to irreversibly reset the Choicy preferences. Do you want to continue?"; 73 | "CONTINUE" = "Continue"; 74 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPPackageInfo.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPPackageInfo.h" 22 | #import "CHPTweakList.h" 23 | #import 24 | 25 | NSDictionary *g_packageNamesByIdentifier; 26 | NSArray *g_packageInfos; 27 | 28 | @implementation CHPPackageInfo 29 | 30 | + (void)load 31 | { 32 | [self loadPackageNames]; 33 | [self loadAvailablePackages]; 34 | } 35 | 36 | + (void)loadPackageNames 37 | { 38 | NSMutableDictionary *packageNamesByIdentifierM = [NSMutableDictionary new]; 39 | 40 | NSString *status = [NSString stringWithContentsOfFile:JBROOT_PATH_NSSTRING(@"/var/lib/dpkg/status") encoding:NSUTF8StringEncoding error:nil]; 41 | NSArray *statusSections = [status componentsSeparatedByString:@"\n\n"]; 42 | [statusSections enumerateObjectsUsingBlock:^(NSString *packageInfoStr, NSUInteger idx, BOOL *stop) { 43 | if ([packageInfoStr hasPrefix:@"Package: "]) { 44 | NSArray *packageLines = [packageInfoStr componentsSeparatedByString:@"\n"]; 45 | 46 | NSString *packageIDLine = packageLines.firstObject; 47 | NSString *packageID = [packageIDLine substringWithRange:NSMakeRange(9, packageIDLine.length-9)]; 48 | __block NSString *packageName; 49 | 50 | [packageLines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL *stop) { 51 | if ([line hasPrefix:@"Name: "]) { 52 | packageName = [line substringWithRange:NSMakeRange(6, line.length-6)]; 53 | *stop = YES; 54 | } 55 | }]; 56 | 57 | packageNamesByIdentifierM[packageID] = packageName; 58 | } 59 | }]; 60 | 61 | g_packageNamesByIdentifier = packageNamesByIdentifierM.copy; 62 | } 63 | 64 | + (void)loadAvailablePackages 65 | { 66 | //Load all packages that have a dylib into g_packageInfos 67 | NSMutableArray *packageInfos = [NSMutableArray new]; 68 | 69 | NSString *dirPath = JBROOT_PATH_NSSTRING(@"/var/lib/dpkg/info"); 70 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:dirPath]; 71 | 72 | NSString *filename; 73 | while (filename = [dirEnum nextObject]) { 74 | if ([filename hasSuffix:@".list"]) { 75 | NSString *packageID = [filename stringByDeletingPathExtension]; 76 | CHPPackageInfo *packageInfo = [[CHPPackageInfo alloc] initWithPackageIdentifier:packageID]; 77 | // Filter out packages that do not have tweaks associated 78 | if (packageInfo.tweakDylibs.count) { 79 | [packageInfos addObject:packageInfo]; 80 | } 81 | } 82 | } 83 | 84 | g_packageInfos = packageInfos.copy; 85 | } 86 | 87 | + (instancetype)fetchPackageInfoForDylibName:(NSString *)dylibName 88 | { 89 | __block CHPPackageInfo *result = nil; 90 | [g_packageInfos enumerateObjectsUsingBlock:^(CHPPackageInfo *info, NSUInteger idx, BOOL *stop) { 91 | if ([info.tweakDylibs containsObject:dylibName]) { 92 | result = info; 93 | *stop = YES; 94 | } 95 | }]; 96 | return result; 97 | } 98 | 99 | + (NSArray *)allInstalledPackages 100 | { 101 | NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; 102 | return [g_packageInfos sortedArrayUsingDescriptors:@[nameSortDescriptor]]; 103 | } 104 | 105 | - (instancetype)initWithPackageIdentifier:(NSString *)packageID 106 | { 107 | self = [super init]; 108 | if (self) { 109 | _identifier = packageID; 110 | _name = g_packageNamesByIdentifier[packageID]; 111 | [self loadTweakDylibs]; 112 | } 113 | return self; 114 | } 115 | 116 | - (void)loadTweakDylibs 117 | { 118 | NSString *dpkgInfoPath = [NSString stringWithFormat:JBROOT_PATH_NSSTRING(@"/var/lib/dpkg/info/%@.list"), _identifier]; 119 | NSString *dpkgInfo = [NSString stringWithContentsOfFile:dpkgInfoPath encoding:NSUTF8StringEncoding error:nil]; 120 | 121 | NSMutableArray *tweakDylibsM = [NSMutableArray new]; 122 | 123 | if (dpkgInfo) { 124 | NSArray *infoLines = [dpkgInfo componentsSeparatedByString:@"\n"]; 125 | [infoLines enumerateObjectsUsingBlock:^(NSString *infoLine, NSUInteger idx, BOOL *stop) { 126 | if ([CHPTweakList isTweakLibraryPath:infoLine]) { 127 | NSString *dylibName = infoLine.lastPathComponent.stringByDeletingPathExtension; 128 | [tweakDylibsM addObject:dylibName]; 129 | } 130 | }]; 131 | } 132 | 133 | _tweakDylibs = tweakDylibsM.copy; 134 | } 135 | 136 | @end -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/ru.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "ADD"="Добавить"; 2 | "ADDITIONAL_EXECUTABLES"="Дополнительно"; 3 | "ADDITIONAL_EXECUTABLES_FOOTER"="Здесь можно добавить какие-либо иные исполняемые файлы, отсутствующие в других списках, для последующей их настройки. Допускаются программы и библиотеки, но НЕ СЛУЖБЫ!\n\nИ ПОМНИ, что раздел сей предназначен\nДля тех, кто ведает своих деяний суть!\nКоль ты дурак, поверивший в удачу —\nРасплата ждёт и чует глупость за версту."; 4 | "ALLOW"="Разрешить"; 5 | "APPLICATIONS"="Приложения"; 6 | "APPLIED_CHANGES"="Внесенные изменения"; 7 | "APP_OPTIONS"="Меню действий с приложением"; 8 | "APP_OPTIONS_FOOTER"="Добавляет дополнительные пункты в меню действий (долгое нажатие на значке приложения): в случае, когда загрузка сторонних модулей в приложение запрещена настройками — пункт «Запуск с модулями», позволяющий однократно (без внесения изменений в настройки) запустить приложение с модулями; в ином случае — пункт «Запуск без модулей», работающий по аналогии."; 9 | "APP_PLUGINS"="Расширения и плагины"; 10 | "CANCEL"="Отмена"; 11 | "CHOICYLOADER_ADVICE"="Можно избавиться от этой проблемы, установив ChoicyLoader из репозитория https:\/\/opa334.github.io"; 12 | "CLOSE"="Закрыть"; 13 | "CONTINUE"="Продолжить"; 14 | "CREDITS"="Благодарности"; 15 | "CUSTOM"="Особые"; 16 | "CUSTOM_TWEAK_CONFIGURATION"="Особые настройки модулей"; 17 | "DAEMONS"="Службы"; 18 | "DAEMON_LIST_BOTTOM_NOTICE"="Автоматически скрываются те службы, модули для загрузки в которые в системе не найдены"; 19 | "DENY"="Запретить"; 20 | "DISABLE_TWEAK_INJECTION"="Отключить все модули"; 21 | "DONATE"="Пожертвовать"; 22 | "DONT_SHOW_AGAIN"="Больше не показывать"; 23 | "ERROR"="Ошибка"; 24 | "ERROR_FILE_NOT_FOUND"="Указанный путь не содержит файла."; 25 | "ERROR_FILE_NO_EXECUTABLE"="Выбранный файл не является исполняемым."; 26 | "ERROR_NO_TWEAKS_INJECT"="Выбранный файл не был добавлен, потому как твиков, загружаемых в него, не установлено. Присутствие записи об этом файле здесь попросту бессмысленно."; 27 | "FIX"="Исправить"; 28 | "FOLLOW_ME_ON_MASTODON"="Подписаться на Mastodon"; 29 | "GLOBAL_CONFIGURATION"="Общие настройки"; 30 | "GLOBAL_OVERWRITE"="Переопределять Общие"; 31 | "GLOBAL_TWEAK_CONFIGURATION"="Общие настройки модулей"; 32 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE"="Общие настройки можно переопределять для конкретных приложений и служб, используя переключатель «Переопределять Общие», доступный на странице настроек каждого приложения и службы."; 33 | "GREYED_OUT_ENTRIES"="Заблокировать элементы, связанные с этими модулями?"; 34 | "HIDDEN_APPLICATIONS"="Скрытые приложения"; 35 | "LAUNCH_WITHOUT_TWEAKS"="Запуск без модулей"; 36 | "LAUNCH_WITHOUT_TWEAKS_OPTION"="Пункт «Запуск без модулей»"; 37 | "LAUNCH_WITH_TWEAKS"="Запуск с модулями"; 38 | "LAUNCH_WITH_TWEAKS_OPTION"="Пункт «Запуск с модулями»"; 39 | "NOTHING_FOUND_MESSAGE"="Похоже, что Choicy никак не препятствует запуску и загрузке исполняемых файлов, установленных данным пакетом. Исправлять нечего, действий не требуется."; 40 | "OPEN_REPO"="Открыть репозиторий"; 41 | "OTHER"="Прочее"; 42 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION"="Переопределять Общие"; 43 | "PACKAGE"="Пакет"; 44 | "PACKAGES"="Пакеты"; 45 | "PATH"="Путь"; 46 | "PROCESS_CONFIGURATION"="Настройки отдельных программ"; 47 | "PROCESS_CONFIGURATION_FOOTER"="При изменении настроек приложения, оно автоматически перезапускается и настройки вступают в силу. В случае с домашним экраном (springboard), для применения настроек перезапуск нужно осуществить самостоятельно (respring). Некоторые службы могут потребовать перезапуска всей пользовательской среды (ldrestart)."; 48 | "RESET_PREFERENCES"="Сброс настроек"; 49 | "RESET_PREFERENCES_MESSAGE"="Удаление всех настроек и восстановление значений по умолчанию отменить нельзя. Уверены, что хотите продолжить?"; 50 | "RESPRING"="Перезапустить SB"; 51 | "RESULTS"="Отчет"; 52 | "RESULTS_APPLICATION"="Список программ, в которые не удалось загрузить «%@.dylib»:\n%@"; 53 | "RESULTS_GLOBAL_DENIED"="Список модулей, загрузка которых запрещена в Общих настройках:"; 54 | "RESULTS_PROCESS"="Список программ, загрузка «%@.dylib» в которые заблокирована их настройками:\n%@"; 55 | "SELECT_EXECUTABLE"="Выбрать файл"; 56 | "SELECT_EXECUTABLE_MESSAGE"="Укажите путь к исполняемому файлу, который нужно добавить в список."; 57 | "SHOW_ALL_DAEMONS"="Показать все службы"; 58 | "SHOW_RECOMMENDED_DAEMONS"="Только рекомендованные"; 59 | "SOURCE_CODE"="Исходный код"; 60 | "SYSTEM_APPLICATIONS"="Системные"; 61 | "SpringBoard"="Домашний экран"; 62 | "THE_INJECTION_PLATFORM"="механизм загрузки"; 63 | "TROUBLESHOOTING"="Решение проблем"; 64 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW"="Загрузка «%@.dylib» включена в настройках «%@»"; 65 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW"="Настройки «%@» изменены: «Отключить все модули» - выкл., «Особые» - вкл. в режиме «Разрешить», загрузка «%@.dylib» включена."; 66 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL"="Загрузка «%@.dylib» включена в Общих настройках."; 67 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY"="Запрет на загрузку «%@.dylib» отключен в настройках «%@»."; 68 | "TWEAKS"="Сторонние модули"; 69 | "TWEAKS_DISABLED"="Отключены"; 70 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE"="Обнаружены модули, блокировка которых возможна лишь отключением загрузки всех модулей в настройках приложения. Всему виной — «%@», загружающий некоторые модули раньше, чем загрузится Choicy. Во избежание конфликтов, пункты настроек, относящиеся к этим модулям, будут доступны только для чтения."; 71 | "TWEAK_TROUBLESHOOTING"="Диагностика проблем с модулями"; 72 | "TWEAK_TROUBLESHOOTING_FOOTER"="Проверка Choicy на причастность к проблемам, связанным с загрузкой или работоспособностью какого-либо модуля. Выбираем модуль -> запускаем диагностику -> выбираем решение."; 73 | "USER_APPLICATIONS"="Пользовательские"; 74 | "WARNING_ALERT_TITLE"="Внимание"; 75 | -------------------------------------------------------------------------------- /layout/Library/Application Support/Choicy.bundle/de.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROCESS_CONFIGURATION" = "Prozess Konfiguration"; 2 | "PROCESS_CONFIGURATION_FOOTER" = "Applikationen werden automatisch neu gestartet, wenn sich ihre Einstellungen ändern. Um die Konfiguration von SpringBoard anzuwenden ist ein manueller Respring nötig. Um die Konfiguration von einigen Hintergrunddiensten anzuwenden, ist ein Userspace Reboot oder ein Neustart des Gerätes notwendig."; 3 | "APPLICATIONS" = "Applikationen"; 4 | "DAEMONS" = "Hintergrunddienste"; 5 | "ADDITIONAL_EXECUTABLES" = "Zusätzliche ausführbare Dateien"; 6 | "OVERWRITE_GLOBAL_TWEAK_CONFIGURATION" = "Globale Tweak Konfiguration überschreiben"; 7 | "DISABLE_TWEAK_INJECTION" = "Tweaks deaktivieren"; 8 | "LAUNCH_WITHOUT_TWEAKS" = "Ohne Tweaks starten"; 9 | "LAUNCH_WITH_TWEAKS" = "Mit Tweaks starten"; 10 | "LAUNCH_WITHOUT_TWEAKS_OPTION" = "\"Ohne Tweaks starten\" Option"; 11 | "LAUNCH_WITH_TWEAKS_OPTION" = "\"Mit Tweaks starten\" Option"; 12 | "APP_OPTIONS" = "App Optionen"; 13 | "APP_OPTIONS_FOOTER" = "Fügt eine \"Ohne Tweaks starten\" Option zum zum 3D Touch / Haptic Touch Menü einer App hinzu. Wenn aktiviert wird die Option zu einer \"Mit Tweaks starten\" Option, falls Tweaks für die jeweilige App in der Konfiguration bereits deaktiviert wurden."; 14 | "CUSTOM_TWEAK_CONFIGURATION" = "Benutzerdefinierte Tweak Konfiguration"; 15 | "ALLOW" = "Erlauben"; 16 | "DENY" = "Verweigern"; 17 | "GREYED_OUT_ENTRIES" = "Ausgegraute Einträge?"; 18 | "APP_PLUGINS" = "App Plugins"; 19 | "TWEAKS_DISABLED" = "Tweaks deaktiviert"; 20 | "CUSTOM" = "Benutzerdefiniert"; 21 | "GLOBAL_OVERWRITE" = "Globale Überschreibung"; 22 | "SHOW_RECOMMENDED_DAEMONS" = "Empfohlene Hintergrunddienste anzeigen"; 23 | "SHOW_ALL_DAEMONS" = "Alle Hintergrunddienste anzeigen"; 24 | "SYSTEM_APPLICATIONS" = "Systemapplikationen"; 25 | "USER_APPLICATIONS" = "Benutzerapplikationen"; 26 | "HIDDEN_APPLICATIONS" = "Versteckte Applikationen"; 27 | "GLOBAL_CONFIGURATION" = "Globale Konfiguration"; 28 | "GLOBAL_TWEAK_CONFIGURATION" = "Globale Tweak Konfiguration"; 29 | "ADDITIONAL_EXECUTABLES_FOOTER" = "Hier können zusätzliche ausführbare Dateien (nicht-Hintergrunddienste) hinzugefügt werden und Tweak Konfigurationen auf diese angewendet werden.\n\nWARNUNG: Diese Sektion ist für Power User, benutze sie nur wenn du weißt was du tuhst."; 30 | "SELECT_EXECUTABLE" = "Ausführbare Datei auswählen"; 31 | "SELECT_EXECUTABLE_MESSAGE" = "Gebe den Pfad zur ausführbaren Datei an, welche zur Liste hinzugefügt werden soll."; 32 | "ERROR" = "Fehler"; 33 | "ERROR_FILE_NOT_FOUND" = "Unter dem angegeben Pfad wurde keine Datei gefunden."; 34 | "ERROR_FILE_NO_EXECUTABLE" = "Die Datei an dem angegebenen Pfad ist nicht ausführbar."; 35 | "ERROR_NO_TWEAKS_INJECT" = "In die ausgwählte ausführbare Datei laden keine Tweaks. Das Hinzufügen wurde daher verhindert, da es keinen Nutzen hätte."; 36 | "ADD" = "Hinzufügen"; 37 | "PATH" = "Pfad"; 38 | "TWEAKS" = "Tweaks"; 39 | "GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE" = "Diese Globale Konfiguration kann per Prozess überschrieben werden indem die \"Globale Konfiguration überschreiben\" Option in den Einstellungen eines Prozesses aktiviert wird."; 40 | "OTHER" = "Anderes"; 41 | "FOLLOW_ME_ON_MASTODON" = "Folge mir auf Mastodon"; 42 | "DONATE" = "Spenden"; 43 | "SOURCE_CODE" = "Quellcode"; 44 | "CREDITS" = "Credits"; 45 | "RESPRING" = "Respring"; 46 | "WARNING_ALERT_TITLE" = "Warnung"; 47 | "THE_INJECTION_PLATFORM" = "die Injektionsplattform"; 48 | "TWEAKS_LOADING_BEFORE_CHOICY_ALERT_MESSAGE" = "Es wurde festgestellt, dass einige Tweaks außer über die \"Tweaks deaktivieren\" Option bei Apps nicht deaktiviert werden können, da %@ die betroffenen Tweaks vor Choicy lädt. Diese werden daher ausgegraut um Verwirrung zu vermeiden."; 49 | "CHOICYLOADER_ADVICE" = "Zur Umgehung des Problems kann ChoicyLoader von der Quelle https://opa334.github.io installiert werden."; 50 | "OPEN_REPO" = "Quelle öffnen"; 51 | "DONT_SHOW_AGAIN" = "Nie wieder anzeigen"; 52 | "CLOSE" = "Schließen"; 53 | "DAEMON_LIST_BOTTOM_NOTICE" = "Hintergrunddienste in welche kein Tweak geladen wird, werden automatisch ausgeblendet"; 54 | "PACKAGE" = "Packet"; 55 | "TROUBLESHOOTING" = "Fehlerbehebung"; 56 | "TWEAK_TROUBLESHOOTING" = "Tweak Fehlerbehebung"; 57 | "TWEAK_TROUBLESHOOTING_FOOTER" = "Wenn der Verdacht besteht, dass Choicy einen Tweak davon abhält korrekt zu funktionieren, kann der Tweak in dieser Sektion ausgewählt werden um herrauszufinden, ob einer Dylib des Tweaks die Injektion verweigert wird."; 58 | "PACKAGES" = "Pakete"; 59 | "RESULTS" = "Ergebnis"; 60 | "RESULTS_GLOBAL_DENIED" = "Die folgenden Tweak Dylibs wurden in der Globalen Tweak Konfiguration deaktiviert:"; 61 | "RESULTS_PROCESS" = "\"%@.dylib\" wird die Injektion in die folgenden Prozesse verweigert:\n%@"; 62 | "RESULTS_APPLICATION" = "\"%@.dylib\" wird die Injektion in die folgenden Apps verweigert:\n%@"; 63 | "NOTHING_FOUND_MESSAGE" = "Choicy hat keinen Einfluss auf die Dylibs dieses Pakets. Es muss nichts weiter getan werden."; 64 | "FIX" = "Beheben"; 65 | "CANCEL" = "Abbrechen"; 66 | "TROUBLESHOOT_LOG_ENABLED_IN_GLOBAL" = "\"%@.dylib\" wurde in der Globalen Tweak Konfiguration aktiviert."; 67 | "TROUBLESHOOT_LOG_DISABLED_TO_ALLOW" = "\"Tweaks deaktivieren\" wurde für \"%@\" deaktiviert, \"Benutzerdefinierte Tweak Konfiguration\" wurde aktiviert und auf \"Erlauben\" gesetzt, \"%@.dylib\" wurde der Liste an erlaubten Tweaks hinzugefügt."; 68 | "TROUBLESHOOT_LOG_ADDED_TO_ALLOW" = "\"%@.dylib\" wurde der Liste an erlaubten Tweaks von \"%@\" hinzugefügt."; 69 | "TROUBLESHOOT_LOG_REMOVED_FROM_DENY" = "\"%@.dylib\" wurde aus der Liste an verweigerten Tweaks von \"%@\" entfernt."; 70 | "APPLIED_CHANGES" = "Angewandte Änderungen"; 71 | "RESET_PREFERENCES" = "Einstellungen zurücksetzen"; 72 | "RESET_PREFERENCES_MESSAGE" = "Du bist dabei die Choicy Einstellungen zurückzusetzen, dies kann nicht rückgangig gemacht werden. Soll der Vorgang fortgesetzt werden?"; 73 | "CONTINUE" = "Fortsetzen"; 74 | 75 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPGlobalTweakConfigurationController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPGlobalTweakConfigurationController.h" 22 | #import "CHPTweakList.h" 23 | #import "CHPTweakInfo.h" 24 | #import "CHPRootListController.h" 25 | #import "CHPPackageInfo.h" 26 | #import "../Shared.h" 27 | #import "CHPPreferences.h" 28 | #import "../ChoicyPrefsMigrator.h" 29 | 30 | @implementation CHPGlobalTweakConfigurationController 31 | 32 | - (NSString *)topTitle 33 | { 34 | return localize(@"GLOBAL_TWEAK_CONFIGURATION"); 35 | } 36 | 37 | - (NSString *)plistName 38 | { 39 | return @"GlobalTweakConfiguration"; 40 | } 41 | 42 | - (void)viewDidLoad 43 | { 44 | [self applySearchControllerHideWhileScrolling:YES]; 45 | [super viewDidLoad]; 46 | } 47 | 48 | - (NSMutableArray *)specifiers 49 | { 50 | if (!_specifiers) { 51 | _specifiers = [super specifiers]; 52 | 53 | [self loadGlobalTweakBlacklist]; 54 | 55 | PSSpecifier *groupSpecifier = [PSSpecifier emptyGroupSpecifier]; 56 | groupSpecifier.name = localize(@"TWEAKS"); 57 | [groupSpecifier setProperty:localize(@"GLOBAL_TWEAK_CONFIGURATION_BOTTOM_NOTICE") forKey:@"footerText"]; 58 | 59 | [_specifiers addObject:groupSpecifier]; 60 | 61 | CHPTweakList *sharedTweakList = [CHPTweakList sharedInstance]; 62 | 63 | __block BOOL atLeastOneTweakDisabled = NO; 64 | 65 | [sharedTweakList.tweakList enumerateObjectsUsingBlock:^(CHPTweakInfo *tweakInfo, NSUInteger idx, BOOL *stop) { 66 | if ([sharedTweakList isTweakHiddenForAnyProcess:tweakInfo]) return; 67 | 68 | if (_searchKey && ![_searchKey isEqualToString:@""]) { 69 | if (![tweakInfo.dylibName localizedStandardContainsString:_searchKey]) { 70 | return; 71 | } 72 | } 73 | 74 | PSSpecifier *tweakSpecifier = [PSSpecifier preferenceSpecifierNamed:tweakInfo.dylibName 75 | target:self 76 | set:@selector(setPreferenceValue:forTweakWithSpecifier:) 77 | get:@selector(readValueForTweakWithSpecifier:) 78 | detail:nil 79 | cell:PSSwitchCell 80 | edit:nil]; 81 | 82 | BOOL enabled = ![dylibsBeforeChoicy containsObject:tweakInfo.dylibName]; 83 | if (!enabled) { 84 | atLeastOneTweakDisabled = YES; 85 | } 86 | 87 | [tweakSpecifier setProperty:NSClassFromString(@"CHPSubtitleSwitch") forKey:@"cellClass"]; 88 | [tweakSpecifier setProperty:@(enabled) forKey:@"enabled"]; 89 | [tweakSpecifier setProperty:tweakInfo.dylibName forKey:@"key"]; 90 | [tweakSpecifier setProperty:@YES forKey:@"default"]; 91 | 92 | CHPPackageInfo *packageInfo = [CHPPackageInfo fetchPackageInfoForDylibName:tweakInfo.dylibName]; 93 | if (packageInfo) { 94 | [tweakSpecifier setProperty:[NSString stringWithFormat:@"%@: %@", localize(@"PACKAGE"), packageInfo.name] forKey:@"subtitle"]; 95 | } 96 | 97 | [_specifiers addObject:tweakSpecifier]; 98 | }]; 99 | 100 | if (atLeastOneTweakDisabled) { 101 | PSSpecifier *greyedOutInfoSpecifier = [PSSpecifier preferenceSpecifierNamed:localize(@"GREYED_OUT_ENTRIES") 102 | target:self 103 | set:nil 104 | get:nil 105 | detail:nil 106 | cell:PSButtonCell 107 | edit:nil]; 108 | 109 | [greyedOutInfoSpecifier setProperty:@YES forKey:@"enabled"]; 110 | greyedOutInfoSpecifier.buttonAction = @selector(presentNotLoadingFirstWarning); 111 | [_specifiers addObject:greyedOutInfoSpecifier]; 112 | } 113 | } 114 | 115 | return _specifiers; 116 | } 117 | 118 | - (void)presentNotLoadingFirstWarning 119 | { 120 | presentNotLoadingFirstWarning(self, NO); 121 | } 122 | 123 | - (void)setPreferenceValue:(id)value forTweakWithSpecifier:(PSSpecifier *)specifier 124 | { 125 | NSNumber *numberValue = value; 126 | 127 | if (numberValue.boolValue) { 128 | [_globalDeniedTweaks removeObject:[specifier propertyForKey:@"key"]]; 129 | } 130 | else { 131 | [_globalDeniedTweaks addObject:[specifier propertyForKey:@"key"]]; 132 | } 133 | 134 | [self saveGlobalTweakBlacklist]; 135 | } 136 | 137 | - (id)readValueForTweakWithSpecifier:(PSSpecifier *)specifier 138 | { 139 | NSString *key = [specifier propertyForKey:@"key"]; 140 | 141 | if ([dylibsBeforeChoicy containsObject:key]) { 142 | return @1; 143 | } 144 | 145 | if ([_globalDeniedTweaks containsObject:key]) { 146 | return @0; 147 | } 148 | else { 149 | return @1; 150 | } 151 | } 152 | 153 | - (void)loadGlobalTweakBlacklist 154 | { 155 | _globalDeniedTweaks = [[preferences objectForKey:kChoicyPrefsKeyGlobalDeniedTweaks] mutableCopy] ?: [NSMutableArray new]; 156 | } 157 | 158 | - (void)saveGlobalTweakBlacklist 159 | { 160 | NSMutableDictionary *mutablePrefs = preferencesForWriting(); 161 | mutablePrefs[kChoicyPrefsKeyGlobalDeniedTweaks] = [_globalDeniedTweaks copy]; 162 | writePreferences(mutablePrefs); 163 | } 164 | 165 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPDaemonListController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPDaemonListController.h" 22 | #import "CHPPreferences.h" 23 | 24 | #import "../Shared.h" 25 | 26 | #import "CHPDaemonInfo.h" 27 | #import "CHPDaemonList.h" 28 | #import "CHPProcessConfigurationListController.h" 29 | #import "CHPApplicationListSubcontrollerController.h" 30 | 31 | @interface PSListController() 32 | - (id)controllerForSpecifier:(PSSpecifier *)specifier; 33 | @end 34 | 35 | @implementation CHPDaemonListController 36 | 37 | - (void)viewDidLoad 38 | { 39 | [self applySearchControllerHideWhileScrolling:NO]; 40 | [[CHPDaemonList sharedInstance] addObserver:self]; 41 | 42 | if (![CHPDaemonList sharedInstance].loaded) { 43 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { 44 | [[CHPDaemonList sharedInstance] updateDaemonListIfNeeded]; 45 | }); 46 | } 47 | else { 48 | [self updateSuggestedDaemons]; 49 | } 50 | 51 | [super viewDidLoad]; 52 | } 53 | 54 | - (NSString *)topTitle 55 | { 56 | return localize(@"DAEMONS"); 57 | } 58 | 59 | - (NSString *)plistName 60 | { 61 | return nil; 62 | } 63 | 64 | - (NSMutableArray *)specifiers 65 | { 66 | if (!_specifiers) { 67 | _specifiers = [NSMutableArray new]; 68 | 69 | if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_11_0) { 70 | [_specifiers addObject:[PSSpecifier emptyGroupSpecifier]]; 71 | [_specifiers addObject:[PSSpecifier emptyGroupSpecifier]]; 72 | } 73 | 74 | if (![CHPDaemonList sharedInstance].loaded) { 75 | PSSpecifier *loadingIndicator = [PSSpecifier preferenceSpecifierNamed:@"" 76 | target:self 77 | set:nil 78 | get:nil 79 | detail:nil 80 | cell:[PSTableCell cellTypeFromString:@"PSSpinnerCell"] 81 | edit:nil]; 82 | 83 | [_specifiers addObject:loadingIndicator]; 84 | } 85 | else { 86 | NSString *toggleName; 87 | 88 | if (_showsAllDaemons) { 89 | toggleName = localize(@"SHOW_RECOMMENDED_DAEMONS"); 90 | } 91 | else { 92 | toggleName = localize(@"SHOW_ALL_DAEMONS"); 93 | } 94 | 95 | PSSpecifier *daemonToggleSpecifier = [PSSpecifier preferenceSpecifierNamed:toggleName 96 | target:self 97 | set:nil 98 | get:nil 99 | detail:nil 100 | cell:PSButtonCell 101 | edit:nil]; 102 | 103 | [daemonToggleSpecifier setProperty:@YES forKey:@"enabled"]; 104 | daemonToggleSpecifier.buttonAction = @selector(daemonTogglePressed:); 105 | [_specifiers addObject:daemonToggleSpecifier]; 106 | 107 | PSSpecifier *daemonsGroup = [PSSpecifier emptyGroupSpecifier]; 108 | [daemonsGroup setProperty:localize(@"DAEMON_LIST_BOTTOM_NOTICE") forKey:@"footerText"]; 109 | [_specifiers addObject:daemonsGroup]; 110 | 111 | NSArray *daemonList = [CHPDaemonList sharedInstance].daemonList; 112 | 113 | for (CHPDaemonInfo *info in daemonList) { 114 | if (_showsAllDaemons || [_suggestedDaemons containsObject:[info executableName]]) { 115 | if (_searchKey && ![_searchKey isEqualToString:@""]) { 116 | if (![[info executableName] localizedStandardContainsString:_searchKey]) { 117 | continue; 118 | } 119 | } 120 | 121 | PSSpecifier *specifier = [CHPListController createSpecifierForExecutable:info.executablePath named:info.executableName]; 122 | [_specifiers addObject:specifier]; 123 | } 124 | } 125 | } 126 | } 127 | 128 | return _specifiers; 129 | } 130 | 131 | - (id)previewStringForSpecifier:(PSSpecifier *)specifier 132 | { 133 | return [CHPListController previewStringForSpecifier:specifier]; 134 | } 135 | 136 | - (void)daemonTogglePressed:(PSSpecifier *)specifier 137 | { 138 | _showsAllDaemons = !_showsAllDaemons; 139 | 140 | [self reloadSpecifiers]; 141 | } 142 | 143 | - (void)reloadValueOfSelectedSpecifier 144 | { 145 | UITableView *tableView = [self valueForKey:@"_table"]; 146 | for (NSIndexPath *selectedIndexPath in tableView.indexPathsForSelectedRows) { 147 | PSSpecifier *specifier = [self specifierAtIndex:[self indexForIndexPath:selectedIndexPath]]; 148 | [self reloadSpecifier:specifier]; 149 | } 150 | } 151 | 152 | - (void)updateSuggestedDaemons 153 | { 154 | NSMutableSet *suggestedDaemons = [NSMutableSet new]; 155 | 156 | for (CHPDaemonInfo *info in [CHPDaemonList sharedInstance].daemonList) { 157 | if ([info.linkedFrameworkIdentifiers containsObject:@"com.apple.UIKit"]) { 158 | [suggestedDaemons addObject:[info executableName]]; 159 | } 160 | } 161 | 162 | _suggestedDaemons = [suggestedDaemons copy]; 163 | } 164 | 165 | - (void)daemonListDidUpdate:(CHPDaemonList *)list 166 | { 167 | [self updateSuggestedDaemons]; 168 | [self reloadSpecifiers]; 169 | } 170 | 171 | - (id)controllerForSpecifier:(PSSpecifier *)specifier 172 | { 173 | if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_11_0) { 174 | [UIView performWithoutAnimation:^ { 175 | _searchController.active = NO; 176 | }]; 177 | } 178 | 179 | return [super controllerForSpecifier:specifier]; 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /ChoicyPrefs/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | label 11 | GLOBAL_CONFIGURATION 12 | 13 | 14 | cell 15 | PSLinkCell 16 | label 17 | GLOBAL_TWEAK_CONFIGURATION 18 | detail 19 | CHPGlobalTweakConfigurationController 20 | isController 21 | 22 | 23 | 24 | cell 25 | PSGroupCell 26 | label 27 | PROCESS_CONFIGURATION 28 | footerText 29 | PROCESS_CONFIGURATION_FOOTER 30 | 31 | 32 | cell 33 | PSLinkCell 34 | detail 35 | CHPProcessConfigurationListController 36 | isController 37 | 38 | label 39 | SpringBoard 40 | applicationIdentifier 41 | com.apple.springboard 42 | 43 | 44 | cell 45 | PSLinkListCell 46 | label 47 | APPLICATIONS 48 | detail 49 | CHPApplicationListSubcontrollerController 50 | subcontrollerClass 51 | CHPProcessConfigurationListController 52 | sections 53 | 54 | 55 | sectionType 56 | Visible 57 | 58 | 59 | sectionType 60 | Hidden 61 | 62 | 63 | useSearchBar 64 | 65 | showIdentifiersAsSubtitle 66 | 67 | includeIdentifiersInSearch 68 | 69 | 70 | 71 | cell 72 | PSLinkCell 73 | label 74 | DAEMONS 75 | detail 76 | CHPDaemonListController 77 | isController 78 | 79 | 80 | 81 | cell 82 | PSLinkCell 83 | label 84 | ADDITIONAL_EXECUTABLES 85 | detail 86 | CHPAdditionalExecutablesListController 87 | isController 88 | 89 | 90 | 91 | cell 92 | PSGroupCell 93 | label 94 | APP_OPTIONS 95 | footerText 96 | APP_OPTIONS_FOOTER 97 | 98 | 99 | cell 100 | PSSwitchCell 101 | PostNotification 102 | com.opa334.choicyprefs/ReloadPrefs 103 | default 104 | 105 | key 106 | launchWithoutTweaksOptionEnabled 107 | label 108 | LAUNCH_WITHOUT_TWEAKS_OPTION 109 | 110 | 111 | cell 112 | PSSwitchCell 113 | PostNotification 114 | com.opa334.choicyprefs/ReloadPrefs 115 | default 116 | 117 | key 118 | launchWithTweaksOptionEnabled 119 | label 120 | LAUNCH_WITH_TWEAKS_OPTION 121 | 122 | 123 | cell 124 | PSGroupCell 125 | label 126 | TROUBLESHOOTING 127 | footerText 128 | TWEAK_TROUBLESHOOTING_FOOTER 129 | 130 | 131 | cell 132 | PSLinkCell 133 | label 134 | TWEAK_TROUBLESHOOTING 135 | detail 136 | CHPTweakTroubleshootListController 137 | isController 138 | 139 | 140 | 141 | cell 142 | PSGroupCell 143 | label 144 | OTHER 145 | 146 | 147 | action 148 | sourceLink 149 | cell 150 | PSButtonCell 151 | icon 152 | GitHub 153 | label 154 | SOURCE_CODE 155 | 156 | 157 | action 158 | openMastodon 159 | cell 160 | PSButtonCell 161 | icon 162 | Mastodon 163 | label 164 | FOLLOW_ME_ON_MASTODON 165 | 166 | 167 | action 168 | donationLink 169 | cell 170 | PSButtonCell 171 | icon 172 | Donate 173 | label 174 | DONATE 175 | 176 | 177 | cell 178 | PSLinkListCell 179 | icon 180 | Credits 181 | defaults 182 | com.opa334.safariplusprefs 183 | detail 184 | CHPCreditsListController 185 | label 186 | CREDITS 187 | 188 | 189 | cell 190 | PSGroupCell 191 | 192 | 193 | action 194 | resetPreferences 195 | cell 196 | PSButtonCell 197 | label 198 | RESET_PREFERENCES 199 | cellClass 200 | CHPDestructiveTableCell 201 | 202 | 203 | cell 204 | PSGroupCell 205 | footerText 206 | © 2019-2024 Lars Fröder (opa334) 207 | 208 | 209 | title 210 | Choicy 211 | 212 | 213 | -------------------------------------------------------------------------------- /ChoicyPrefs/CHPTweakList.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPTweakList.h" 22 | #import "CHPTweakInfo.h" 23 | #import "CHPDaemonInfo.h" 24 | #import "CHPMachoParser.h" 25 | #import "../Shared.h" 26 | #import "../HBLogWeak.h" 27 | #import 28 | 29 | @implementation CHPTweakList 30 | 31 | + (instancetype)sharedInstance 32 | { 33 | static CHPTweakList *sharedInstance = nil; 34 | static dispatch_once_t onceToken; 35 | dispatch_once(&onceToken, ^ { 36 | //Initialise instance 37 | sharedInstance = [[CHPTweakList alloc] init]; 38 | }); 39 | return sharedInstance; 40 | } 41 | 42 | + (NSArray *)possibleInjectionLibrariesPaths 43 | { 44 | // /Library and /usr always gets converted to rootless paths on xina, so this workaround is neccessary 45 | return @[[@"/" stringByAppendingString:@"Library/MobileSubstrate/DynamicLibraries"], [@"/" stringByAppendingString:@"usr/lib/TweakInject"], @"/var/jb/Library/MobileSubstrate/DynamicLibraries", @"/var/jb/usr/lib/TweakInject"]; 46 | } 47 | 48 | + (NSString *)injectionLibrariesPath 49 | { 50 | for (NSString *possibleInjectionLibrariesPath in [self possibleInjectionLibrariesPaths]) { 51 | if ([[NSFileManager defaultManager] fileExistsAtPath:possibleInjectionLibrariesPath]) { 52 | return possibleInjectionLibrariesPath; 53 | } 54 | } 55 | 56 | @throw [[NSException alloc] initWithName:@"TweakDirectoryException" reason:[NSString stringWithFormat:@"Unable to locate tweak installation directory"] userInfo:nil]; 57 | } 58 | 59 | + (BOOL)isTweakLibraryPath:(NSString *)path 60 | { 61 | if (![path.pathExtension isEqualToString:@"dylib"]) return NO; 62 | 63 | for (NSString *possibleInjectionLibrariesPath in [self possibleInjectionLibrariesPaths]) { 64 | if ([path hasPrefix:possibleInjectionLibrariesPath]) return YES; 65 | } 66 | 67 | return NO; 68 | } 69 | 70 | + (NSURL *)injectionLibrariesURL 71 | { 72 | return [NSURL fileURLWithPath:[self injectionLibrariesPath]].URLByResolvingSymlinksInPath; 73 | } 74 | 75 | - (instancetype)init 76 | { 77 | self = [super init]; 78 | if (self) { 79 | [self updateTweakList]; 80 | } 81 | return self; 82 | } 83 | 84 | - (void)updateTweakList 85 | { 86 | NSMutableArray *tweakListM = [NSMutableArray new]; 87 | NSArray *dynamicLibraries = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[CHPTweakList injectionLibrariesURL] includingPropertiesForKeys:nil options:0 error:nil]; 88 | 89 | for (NSURL *URL in dynamicLibraries) { 90 | if ([[URL pathExtension] isEqualToString:@"plist"]) { 91 | NSURL *dylibURL = [[URL URLByDeletingPathExtension] URLByAppendingPathExtension:@"dylib"]; 92 | if ([dylibURL checkResourceIsReachableAndReturnError:nil]) { 93 | CHPTweakInfo *tweakInfo = [[CHPTweakInfo alloc] initWithDylibPath:dylibURL.path plistPath:URL.path]; 94 | [tweakListM addObject:tweakInfo]; 95 | } 96 | } 97 | } 98 | 99 | [tweakListM sortUsingSelector:@selector(caseInsensitiveCompare:)]; 100 | 101 | self.tweakList = [tweakListM copy]; 102 | } 103 | 104 | - (NSArray *)tweakListForExecutableAtPath:(NSString *)executablePath 105 | { 106 | HBLogDebugWeak(@"tweakListForExecutableAtPath:%@", executablePath); 107 | if (!executablePath) return nil; 108 | 109 | NSString *bundleID = [NSBundle bundleWithPath:executablePath.stringByDeletingLastPathComponent].bundleIdentifier; 110 | NSString *executableName = executablePath.lastPathComponent; 111 | NSSet *linkedFrameworks = [[CHPMachoParser sharedInstance] frameworkBundleIdentifiersForMachoAtPath:executablePath]; 112 | 113 | NSMutableArray *tweakListForExecutable = [NSMutableArray new]; 114 | [self.tweakList enumerateObjectsUsingBlock:^(CHPTweakInfo *tweakInfo, NSUInteger idx, BOOL *stop) { 115 | if (bundleID) { 116 | if ([tweakInfo.filterBundles containsObject:bundleID]) { 117 | [tweakListForExecutable addObject:tweakInfo]; 118 | return; 119 | } 120 | } 121 | 122 | if (executableName) { 123 | if ([tweakInfo.filterExecutables containsObject:executableName]) { 124 | [tweakListForExecutable addObject:tweakInfo]; 125 | return; 126 | } 127 | } 128 | 129 | if (linkedFrameworks) { 130 | [linkedFrameworks enumerateObjectsUsingBlock:^(NSString *frameworkID, BOOL *stop) { 131 | if ([tweakInfo.filterBundles containsObject:frameworkID]) { 132 | [tweakListForExecutable addObject:tweakInfo]; 133 | *stop = YES; 134 | } 135 | }]; 136 | } 137 | }]; 138 | 139 | return tweakListForExecutable; 140 | } 141 | 142 | - (BOOL)oneOrMoreTweaksInjectIntoExecutableAtPath:(NSString *)executablePath 143 | { 144 | for (CHPTweakInfo *tweakInfo in [self tweakListForExecutableAtPath:executablePath]) { 145 | if ([tweakInfo.dylibName containsString:@"Choicy"]) { 146 | continue; 147 | } 148 | 149 | return YES; 150 | } 151 | 152 | return NO; 153 | } 154 | 155 | - (BOOL)isTweak:(CHPTweakInfo *)tweak hiddenForApplicationWithIdentifier:(NSString *)applicationID 156 | { 157 | if ([applicationID isEqualToString:kSpringboardBundleID]) { 158 | if ([kAlwaysInjectSpringboard containsObject:tweak.dylibName]) { 159 | return YES; 160 | } 161 | } 162 | 163 | if ([applicationID isEqualToString:kPreferencesBundleID]) { 164 | if ([kAlwaysInjectPreferences containsObject:tweak.dylibName]) { 165 | return YES; 166 | } 167 | } 168 | 169 | return [kAlwaysInjectGlobal containsObject:tweak.dylibName]; 170 | } 171 | 172 | - (BOOL)isTweakHiddenForAnyProcess:(CHPTweakInfo *)tweak 173 | { 174 | if ([self isTweak:tweak hiddenForApplicationWithIdentifier:kSpringboardBundleID]) { 175 | return YES; 176 | } 177 | 178 | if ([self isTweak:tweak hiddenForApplicationWithIdentifier:kPreferencesBundleID]) { 179 | return YES; 180 | } 181 | 182 | return NO; 183 | } 184 | 185 | @end -------------------------------------------------------------------------------- /ChoicyPrefs/CHPAdditionalExecutablesListController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPAdditionalExecutablesListController.h" 22 | #import 23 | #import "CHPPreferences.h" 24 | #import "../Shared.h" 25 | #import "CHPProcessConfigurationListController.h" 26 | #import "CHPMachoParser.h" 27 | #import "CHPApplicationListSubcontrollerController.h" 28 | #import "CHPTweakList.h" 29 | 30 | @implementation CHPAdditionalExecutablesListController 31 | 32 | // iOS 16+ 33 | - (UIBarButtonItem *)editBarButtonItem 34 | { 35 | return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonPressed)]; 36 | } 37 | 38 | // iOS 11-15 39 | - (id)_editButtonBarItem 40 | { 41 | return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonPressed)]; 42 | } 43 | 44 | - (id)previewStringForSpecifier:(PSSpecifier *)specifier 45 | { 46 | return [CHPListController previewStringForSpecifier:specifier]; 47 | } 48 | 49 | - (void)loadAdditionalExecutables 50 | { 51 | NSArray *additionalExecutables = preferences[kChoicyPrefsKeyAdditionalExecutables]; 52 | if (additionalExecutables) { 53 | _additionalExecutables = additionalExecutables.mutableCopy; 54 | } 55 | else { 56 | _additionalExecutables = [NSMutableArray new]; 57 | } 58 | } 59 | 60 | - (void)saveAdditionalExecutables 61 | { 62 | NSMutableDictionary *mutablePrefs = preferencesForWriting(); 63 | mutablePrefs[kChoicyPrefsKeyAdditionalExecutables] = _additionalExecutables.copy; 64 | writePreferences(mutablePrefs); 65 | } 66 | 67 | - (void)addButtonPressed 68 | { 69 | UIAlertController *executableAlert = [UIAlertController alertControllerWithTitle:localize(@"SELECT_EXECUTABLE") message:localize(@"SELECT_EXECUTABLE_MESSAGE") preferredStyle:UIAlertControllerStyleAlert]; 70 | 71 | [executableAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) { 72 | textField.placeholder = localize(@"PATH"); 73 | if (@available(iOS 13, *)) { 74 | textField.textColor = [UIColor labelColor]; 75 | } 76 | else { 77 | textField.textColor = [UIColor blackColor]; 78 | } 79 | textField.keyboardType = UIKeyboardTypeDefault; 80 | textField.clearButtonMode = UITextFieldViewModeWhileEditing; 81 | textField.borderStyle = UITextBorderStyleNone; 82 | }]; 83 | 84 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:localize(@"CANCEL") style:UIAlertActionStyleCancel handler:nil]; 85 | [executableAlert addAction:cancelAction]; 86 | 87 | UIAlertAction *addAction = [UIAlertAction actionWithTitle:localize(@"ADD") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 88 | UITextField *pathField = executableAlert.textFields[0]; 89 | [self addExecutableAtPath:pathField.text]; 90 | }]; 91 | [executableAlert addAction:addAction]; 92 | 93 | [self presentViewController:executableAlert animated:YES completion:nil]; 94 | } 95 | 96 | - (void)showErrorMessage:(NSString *)message 97 | { 98 | UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:localize(@"ERROR") message:message preferredStyle:UIAlertControllerStyleAlert]; 99 | UIAlertAction *closeAction = [UIAlertAction actionWithTitle:localize(@"CLOSE") style:UIAlertActionStyleDefault handler:nil]; 100 | [errorAlert addAction:closeAction]; 101 | 102 | [self presentViewController:errorAlert animated:YES completion:nil]; 103 | } 104 | 105 | - (void)addExecutableAtPath:(NSString *)executablePath 106 | { 107 | if ([_additionalExecutables containsObject:executablePath]) return; 108 | 109 | if (![[NSFileManager defaultManager] fileExistsAtPath:executablePath]) { 110 | [self showErrorMessage:localize(@"ERROR_FILE_NOT_FOUND")]; 111 | return; 112 | } 113 | 114 | if ([CHPMachoParser isMachoAtPath:executablePath]) { 115 | [self showErrorMessage:localize(@"ERROR_FILE_NO_EXECUTABLE")]; 116 | return; 117 | } 118 | 119 | if (![[CHPTweakList sharedInstance] oneOrMoreTweaksInjectIntoExecutableAtPath:executablePath]) { 120 | [self showErrorMessage:localize(@"ERROR_NO_TWEAKS_INJECT")]; 121 | return; 122 | } 123 | 124 | [_additionalExecutables addObject:executablePath]; 125 | [self saveAdditionalExecutables]; 126 | 127 | PSSpecifier *specifierToInsert = [CHPListController createSpecifierForExecutable:executablePath named:executablePath]; 128 | [self insertSpecifier:specifierToInsert atEndOfGroup:0 animated:YES]; 129 | } 130 | 131 | - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath 132 | { 133 | return UITableViewCellEditingStyleDelete; 134 | } 135 | 136 | - (BOOL)performDeletionActionForSpecifier:(PSSpecifier *)specifier 137 | { 138 | BOOL orig = [super performDeletionActionForSpecifier:specifier]; 139 | 140 | NSString *executablePath = [specifier propertyForKey:@"executablePath"]; 141 | [_additionalExecutables removeObject:executablePath]; 142 | [self saveAdditionalExecutables]; 143 | 144 | return orig; 145 | } 146 | 147 | - (NSMutableArray *)specifiers 148 | { 149 | if (!_specifiers) { 150 | _specifiers = [NSMutableArray new]; 151 | 152 | [self loadAdditionalExecutables]; 153 | 154 | PSSpecifier *groupSpecifier = [PSSpecifier emptyGroupSpecifier]; 155 | [groupSpecifier setProperty:localize(@"ADDITIONAL_EXECUTABLES_FOOTER") forKey:@"footerText"]; 156 | 157 | [_specifiers addObject:groupSpecifier]; 158 | 159 | [_additionalExecutables enumerateObjectsUsingBlock:^(NSString *executablePath, NSUInteger idx, BOOL *stop) { 160 | [_specifiers addObject:[CHPListController createSpecifierForExecutable:executablePath named:executablePath]]; 161 | }]; 162 | } 163 | 164 | return _specifiers; 165 | } 166 | 167 | @end -------------------------------------------------------------------------------- /ChoicySB/ChoicyOverrideManager.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "ChoicyOverrideManager.h" 22 | 23 | @implementation ChoicyOverrideManager 24 | 25 | + (instancetype)sharedManager 26 | { 27 | static ChoicyOverrideManager *sharedManager = nil; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^ { 30 | sharedManager = [[ChoicyOverrideManager alloc] init]; 31 | }); 32 | return sharedManager; 33 | } 34 | 35 | - (instancetype)init 36 | { 37 | self = [super init]; 38 | 39 | _overrideProviders = [NSMutableArray new]; 40 | 41 | return self; 42 | } 43 | 44 | - (void)registerOverrideProvider:(NSObject *)provider 45 | { 46 | [_overrideProviders addObject:provider]; 47 | } 48 | 49 | - (void)unregisterOverrideProvider:(NSObject *)provider 50 | { 51 | [_overrideProviders removeObject:provider]; 52 | } 53 | 54 | - (BOOL)disableTweakInjectionOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists 55 | { 56 | __block BOOL overrideExists_ = NO; 57 | __block BOOL overrideValue = NO; 58 | 59 | [_overrideProviders enumerateObjectsUsingBlock:^(NSObject *overrideProvider, NSUInteger idx, BOOL *stop) { 60 | uint32_t providedOverrides = [overrideProvider providedOverridesForApplication:applicationID]; 61 | if ((providedOverrides & Choicy_Override_DisableTweakInjection) == Choicy_Override_DisableTweakInjection) { 62 | overrideExists_ = YES; 63 | if ([overrideProvider respondsToSelector:@selector(disableTweakInjectionOverrideForApplication:)]) { 64 | overrideValue = [overrideProvider disableTweakInjectionOverrideForApplication:applicationID]; 65 | *stop = YES; 66 | } 67 | } 68 | }]; 69 | 70 | if (overrideExists) *overrideExists = overrideExists_; 71 | return overrideValue; 72 | } 73 | 74 | - (BOOL)customTweakConfigurationEnabledOverwriteForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists 75 | { 76 | __block BOOL overrideExists_ = NO; 77 | __block BOOL overrideValue = NO; 78 | 79 | [_overrideProviders enumerateObjectsUsingBlock:^(NSObject *overrideProvider, NSUInteger idx, BOOL *stop) { 80 | uint32_t providedOverrides = [overrideProvider providedOverridesForApplication:applicationID]; 81 | if ((providedOverrides & Choicy_Override_CustomTweakConfiguration) == Choicy_Override_CustomTweakConfiguration) { 82 | overrideExists_ = YES; 83 | if ([overrideProvider respondsToSelector:@selector(customTweakConfigurationEnabledOverrideForApplication:)]) { 84 | overrideValue = [overrideProvider customTweakConfigurationEnabledOverrideForApplication:applicationID]; 85 | *stop = YES; 86 | } 87 | } 88 | }]; 89 | 90 | if (overrideExists) *overrideExists = overrideExists_; 91 | return overrideValue; 92 | } 93 | 94 | - (BOOL)customTweakConfigurationAllowDenyModeOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists 95 | { 96 | __block BOOL overrideExists_ = NO; 97 | __block BOOL overrideValue = NO; 98 | 99 | [_overrideProviders enumerateObjectsUsingBlock:^(NSObject *overrideProvider, NSUInteger idx, BOOL *stop) { 100 | uint32_t providedOverrides = [overrideProvider providedOverridesForApplication:applicationID]; 101 | if ((providedOverrides & Choicy_Override_CustomTweakConfiguration) == Choicy_Override_CustomTweakConfiguration) { 102 | overrideExists_ = YES; 103 | if ([overrideProvider respondsToSelector:@selector(customTweakConfigurationAllowDenyModeOverrideForApplication:)]) { 104 | overrideValue = [overrideProvider customTweakConfigurationAllowDenyModeOverrideForApplication:applicationID]; 105 | *stop = YES; 106 | } 107 | } 108 | }]; 109 | 110 | if (overrideExists) *overrideExists = overrideExists_; 111 | return overrideValue; 112 | } 113 | 114 | - (NSArray *)customTweakConfigurationAllowOrDenyListOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists 115 | { 116 | __block BOOL overrideExists_ = NO; 117 | __block NSArray *overrideValue = nil; 118 | 119 | [_overrideProviders enumerateObjectsUsingBlock:^(NSObject *overrideProvider, NSUInteger idx, BOOL *stop) { 120 | uint32_t providedOverrides = [overrideProvider providedOverridesForApplication:applicationID]; 121 | if ((providedOverrides & Choicy_Override_CustomTweakConfiguration) == Choicy_Override_CustomTweakConfiguration) { 122 | overrideExists_ = YES; 123 | if ([overrideProvider respondsToSelector:@selector(customTweakConfigurationAllowOrDenyListOverrideForApplication:)]) { 124 | overrideValue = [overrideProvider customTweakConfigurationAllowOrDenyListOverrideForApplication:applicationID]; 125 | *stop = YES; 126 | } 127 | } 128 | }]; 129 | 130 | if (overrideExists) *overrideExists = overrideExists_; 131 | return overrideValue; 132 | } 133 | 134 | - (BOOL)overwriteGlobalConfigurationOverrideForApplication:(NSString *)applicationID overrideExists:(BOOL *)overrideExists 135 | { 136 | __block BOOL overrideExists_ = NO; 137 | __block BOOL overrideValue = NO; 138 | 139 | [_overrideProviders enumerateObjectsUsingBlock:^(NSObject *overrideProvider, NSUInteger idx, BOOL *stop) { 140 | uint32_t providedOverrides = [overrideProvider providedOverridesForApplication:applicationID]; 141 | if ((providedOverrides & Choicy_Override_OverrideGlobalConfiguration) == Choicy_Override_OverrideGlobalConfiguration) { 142 | overrideExists_ = YES; 143 | if ([overrideProvider respondsToSelector:@selector(overwriteGlobalConfigurationOverrideForApplication:)]) { 144 | overrideValue = [overrideProvider overwriteGlobalConfigurationOverrideForApplication:applicationID]; 145 | *stop = YES; 146 | } 147 | } 148 | }]; 149 | 150 | if (overrideExists) *overrideExists = overrideExists_; 151 | return overrideValue; 152 | } 153 | 154 | @end -------------------------------------------------------------------------------- /nextstep_plist.c: -------------------------------------------------------------------------------- 1 | // Credits: https://github.com/staturnzz 2 | 3 | #include 4 | #include 5 | #include "nextstep_plist.h" 6 | 7 | #define current_char(plist) (plist->data[plist->index]) 8 | 9 | static int find_next_token(nextstep_plist_t *plist) { 10 | while (plist->index < plist->size) { 11 | char current = current_char(plist); 12 | plist->index++; 13 | 14 | switch (current) { 15 | case '\t' ... '\f': 16 | case ' ': continue; 17 | case '/': { 18 | if (plist->index < plist->size) { 19 | plist->index--; 20 | return 0; 21 | } 22 | 23 | switch (current_char(plist)) { 24 | case '/': { 25 | plist->index++; 26 | while (plist->index < plist->size) { 27 | if (current_char(plist) == '\n' || current_char(plist) == 'r') break; 28 | plist->index++; 29 | } 30 | } break; 31 | case '*': { 32 | plist->index++; 33 | while (plist->index < plist->size) { 34 | current = current_char(plist); 35 | plist->index++; 36 | 37 | if (current == '*' && plist->index < plist->size) { 38 | if (current_char(plist) == '/') { 39 | plist->index++; 40 | break; 41 | } 42 | } 43 | } 44 | } break; 45 | default: { 46 | plist->index--; 47 | return 0; 48 | } break; 49 | } 50 | } break; 51 | default: { 52 | plist->index--; 53 | return 0; 54 | } break; 55 | } 56 | } 57 | return -1; 58 | } 59 | 60 | static int validate_char(char current) { 61 | if ((current >= 'a' && current <= 'z') || 62 | (current >= 'A' && current <= 'Z') || 63 | (current >= '0' && current <= '9')) return 0; 64 | 65 | switch (current) { 66 | case '$': 67 | case '/': 68 | case ':': 69 | case '.': 70 | case '-': 71 | case '_': return 0; 72 | default: break; 73 | } 74 | return -1; 75 | } 76 | 77 | static char *nxp_parse_string(nextstep_plist_t *plist) { 78 | if (find_next_token(plist) != 0) return NULL; 79 | char current = current_char(plist); 80 | bool quoted = (current == '\'' || current == '\"'); 81 | if (!quoted && (validate_char(current) != 0)) return NULL; 82 | 83 | char buf[plist->size]; 84 | bzero(buf, plist->size); 85 | char *str = NULL; 86 | int copied = 0; 87 | 88 | if (quoted) plist->index++; 89 | for (; plist->index < plist->size; plist->index++) { 90 | current = current_char(plist); 91 | if ((quoted && (current == '\'' || current == '\"')) || 92 | (!quoted && (current == ' ' || current == '\0'))) { 93 | buf[copied] = '\0'; 94 | str = strdup(buf); 95 | break; 96 | } 97 | buf[copied++] = current; 98 | } 99 | return str; 100 | } 101 | 102 | static xpc_object_t nxp_parse_array(nextstep_plist_t *plist) { 103 | xpc_object_t array = xpc_array_create(NULL, 0); 104 | xpc_object_t entry = nxp_parse_object(plist); 105 | 106 | while (entry != NULL) { 107 | xpc_array_append_value(array, entry); 108 | xpc_release(entry); 109 | entry = NULL; 110 | 111 | if (find_next_token(plist) != 0) { 112 | xpc_release(array); 113 | return NULL; 114 | } 115 | 116 | if (current_char(plist) == ',') { 117 | plist->index++; 118 | entry = nxp_parse_object(plist); 119 | } 120 | } 121 | 122 | if (find_next_token(plist) != 0 || current_char(plist) != ')') { 123 | xpc_release(array); 124 | return NULL; 125 | } 126 | 127 | plist->index++; 128 | return array; 129 | } 130 | 131 | static xpc_object_t nxp_parse_dict(nextstep_plist_t *plist) { 132 | char *key = nxp_parse_string(plist); 133 | if (key == NULL) return NULL; 134 | xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); 135 | 136 | while (key != NULL) { 137 | if (find_next_token(plist) != 0) { 138 | xpc_release(dict); 139 | free(key); 140 | return NULL; 141 | } 142 | 143 | char current = current_char(plist); 144 | xpc_object_t value = NULL; 145 | 146 | if (current == ';') { 147 | value = xpc_string_create(key); 148 | } else if (current == '=') { 149 | plist->index++; 150 | value = nxp_parse_object(plist); 151 | } 152 | 153 | if (value == NULL) { 154 | xpc_release(dict); 155 | free(key); 156 | return NULL; 157 | } 158 | 159 | xpc_dictionary_set_value(dict, key, value); 160 | xpc_release(value); value = NULL; 161 | free(key); key = NULL; 162 | 163 | if (find_next_token(plist) != 0) { 164 | xpc_release(dict); 165 | return NULL; 166 | } 167 | 168 | if (current_char(plist) == ';') { 169 | plist->index++; 170 | key = nxp_parse_string(plist); 171 | } else if (current_char(plist) == '}') { 172 | plist->index++; 173 | bool found_end = true; 174 | for (int i = plist->index; i < plist->size; i++) { 175 | if (plist->data[i] == ';') { 176 | found_end = false; 177 | break; 178 | } 179 | } 180 | if (found_end) return dict; 181 | } 182 | } 183 | return dict; 184 | } 185 | 186 | xpc_object_t nxp_parse_object(nextstep_plist_t *plist) { 187 | if (find_next_token(plist) != 0) return NULL; 188 | char current = current_char(plist); 189 | plist->index++; 190 | 191 | switch (current) { 192 | case '{': return nxp_parse_dict(plist); 193 | case '(': return nxp_parse_array(plist); 194 | case '\'': 195 | case '\"': { 196 | plist->index--; 197 | if (validate_char(current) == 0) { 198 | return nxp_parse_dict(plist); 199 | } else { 200 | char *str = nxp_parse_string(plist); 201 | plist->index++; 202 | if (str == NULL) return NULL; 203 | return xpc_string_create(str); 204 | } 205 | } 206 | default: { 207 | plist->index--; 208 | if (validate_char(current) == 0) { 209 | if (plist->index <= 1) { 210 | return nxp_parse_dict(plist); 211 | } else { 212 | char *str = nxp_parse_string(plist); 213 | if (str == NULL) return NULL; 214 | return xpc_string_create(str); 215 | } 216 | } 217 | } 218 | } 219 | return NULL; 220 | } 221 | -------------------------------------------------------------------------------- /ChoicySB/Tweak.x: -------------------------------------------------------------------------------- 1 | #import 2 | #import "../Shared.h" 3 | #import "../ChoicyPrefsMigrator.h" 4 | #import "ChoicyOverrideManager.h" 5 | #import "ChoicySB.h" 6 | 7 | NSDictionary *preferences; 8 | BOOL gIsSpringBoard = NO; 9 | 10 | extern void choicy_initSpringBoard(void); 11 | extern void choicy_initRunningBoardd(void); 12 | 13 | extern char** *_NSGetArgv(); 14 | NSString *safe_getExecutablePath() 15 | { 16 | char *executablePathC = **_NSGetArgv(); 17 | return [NSString stringWithUTF8String:executablePathC]; 18 | } 19 | 20 | void choicy_reloadPreferences(void) 21 | { 22 | if (preferences) { 23 | NSDictionary *oldPreferences = [preferences copy]; 24 | preferences = [NSDictionary dictionaryWithContentsOfFile:kChoicyPrefsPlistPath]; 25 | 26 | if (gIsSpringBoard) { 27 | NSDictionary *appSettings = [preferences objectForKey:kChoicyPrefsKeyAppSettings]; 28 | NSDictionary *oldAppSettings = [oldPreferences objectForKey:kChoicyPrefsKeyAppSettings]; 29 | 30 | NSMutableSet *allApps = [NSMutableSet setWithArray:[appSettings allKeys]]; 31 | [allApps unionSet:[NSMutableSet setWithArray:[oldAppSettings allKeys]]]; 32 | 33 | NSMutableSet *changedApps = [NSMutableSet new]; 34 | 35 | for (NSString *appKey in allApps) { 36 | if (![((NSDictionary *)[appSettings objectForKey:appKey]) isEqualToDictionary:((NSDictionary *)[oldAppSettings objectForKey:appKey])]) { 37 | [changedApps addObject:appKey]; 38 | } 39 | } 40 | 41 | for (NSString *applicationID in changedApps) { 42 | if (![applicationID isEqualToString:kSpringboardBundleID] && ![applicationID isEqualToString:kPreferencesBundleID]) { 43 | BKSTerminateApplicationForReasonAndReportWithDescription(applicationID, 5, false, @"Choicy - prefs changed, killed"); 44 | } 45 | } 46 | } 47 | } 48 | else { 49 | NSString *parentDir = [kChoicyPrefsPlistPath stringByDeletingLastPathComponent]; 50 | if (![[NSFileManager defaultManager] fileExistsAtPath:parentDir]) { 51 | [[NSFileManager defaultManager] createDirectoryAtPath:parentDir withIntermediateDirectories:YES attributes:nil error:nil]; 52 | } 53 | preferences = [NSDictionary dictionaryWithContentsOfFile:kChoicyPrefsPlistPath]; 54 | } 55 | } 56 | 57 | BOOL choicy_shouldDisableTweakInjectionForApplication(NSString *applicationID) 58 | { 59 | BOOL safeMode = NO; 60 | 61 | BOOL overrideExists; 62 | BOOL disableTweakInjectionOverrideValue = [[ChoicyOverrideManager sharedManager] disableTweakInjectionOverrideForApplication:applicationID overrideExists:&overrideExists]; 63 | if (overrideExists) { 64 | return disableTweakInjectionOverrideValue; 65 | } 66 | 67 | NSDictionary *settingsForApp = processPreferencesForApplication(preferences, applicationID); 68 | 69 | if (settingsForApp && [settingsForApp isKindOfClass:[NSDictionary class]]) { 70 | if (![applicationID isEqualToString:kPreferencesBundleID]) { 71 | safeMode = ((NSNumber *)[settingsForApp objectForKey:kChoicyProcessPrefsKeyTweakInjectionDisabled]).boolValue; 72 | } 73 | } 74 | 75 | return safeMode; 76 | } 77 | 78 | NSDictionary *choicy_applyEnvironmentChanges(NSDictionary *originalEnvironment, NSString *bundleIdentifier) 79 | { 80 | if (originalEnvironment[@"_MSSafeMode"] || originalEnvironment[@"_SafeMode"]) { 81 | // "Launch without tweaks" pressed on SpringBoard 82 | return originalEnvironment; 83 | } 84 | else if (originalEnvironment[@"_ChoicyInjectionEnabledFromSpringBoard"]) { 85 | // "Launch with tweaks" pressed on SpringBoard 86 | return originalEnvironment; 87 | } 88 | 89 | NSMutableDictionary *newEnvironment = originalEnvironment.mutableCopy ?: [NSMutableDictionary new]; 90 | if (choicy_shouldDisableTweakInjectionForApplication(bundleIdentifier)) { 91 | [newEnvironment setObject:@(1) forKey:@"_MSSafeMode"]; 92 | [newEnvironment setObject:@(1) forKey:@"_SafeMode"]; 93 | } 94 | else { 95 | ChoicyOverrideManager *overrideManager = [ChoicyOverrideManager sharedManager]; 96 | BOOL overrideExists = NO; 97 | 98 | BOOL customTweakConfigurationEnabledOverride = [overrideManager customTweakConfigurationEnabledOverwriteForApplication:bundleIdentifier overrideExists:&overrideExists]; 99 | if (overrideExists) { 100 | if (!customTweakConfigurationEnabledOverride) { 101 | // if custom tweak configuration has been overwritten with NO 102 | // set up an empty deny list 103 | [newEnvironment setObject:@"" forKey:@kEnvDeniedTweaksOverride]; 104 | } 105 | else { 106 | BOOL customTweakAllowDenyOverride = [overrideManager customTweakConfigurationAllowDenyModeOverrideForApplication:bundleIdentifier overrideExists:&overrideExists]; 107 | NSArray *allowDenyList = [overrideManager customTweakConfigurationAllowOrDenyListOverrideForApplication:bundleIdentifier overrideExists:&overrideExists]; 108 | 109 | if (overrideManager && allowDenyList) { 110 | NSString *allowDenyString = [allowDenyList componentsJoinedByString:@":"]; 111 | 112 | NSString *envName; 113 | if (customTweakAllowDenyOverride) { // DENY 114 | envName = @kEnvDeniedTweaksOverride; 115 | } 116 | else { //ALLOW 117 | envName = @kEnvAllowedTweaksOverride; 118 | } 119 | 120 | //NSLog(@"set %@ to %@", envName, allowDenyString); 121 | 122 | [newEnvironment setObject:allowDenyString forKey:envName]; 123 | } 124 | } 125 | } 126 | 127 | BOOL overwriteGlobalConfigurationOverride = [overrideManager overwriteGlobalConfigurationOverrideForApplication:bundleIdentifier overrideExists:&overrideExists]; 128 | //NSLog(@"overwriteGlobalConfigurationOverride=%i overrideExists=%i", overwriteGlobalConfigurationOverride, overrideExists); 129 | if (overrideExists) { 130 | NSString *envToSet; 131 | if (overwriteGlobalConfigurationOverride) { 132 | envToSet = @"1"; 133 | } 134 | else { 135 | envToSet = @"0"; 136 | } 137 | 138 | [newEnvironment setObject:envToSet forKey:@kEnvOverwriteGlobalConfigurationOverride]; 139 | } 140 | } 141 | return newEnvironment; 142 | } 143 | 144 | void choicy_applyEnvironmentChangesToLaunchContext(RBSLaunchContext *launchContext) 145 | { 146 | NSString *bundleIdentifier; 147 | if ([launchContext respondsToSelector:@selector(bundleIdentifier)]) { 148 | bundleIdentifier = launchContext.bundleIdentifier; 149 | } 150 | else { 151 | bundleIdentifier = launchContext.identity.embeddedApplicationIdentifier; 152 | } 153 | launchContext._additionalEnvironment = choicy_applyEnvironmentChanges(launchContext._additionalEnvironment, bundleIdentifier); 154 | } 155 | 156 | void choicy_applyEnvironmentChangesToExecutionContext(FBProcessExecutionContext *executionContext, NSString *bundleIdentifier) 157 | { 158 | executionContext.environment = choicy_applyEnvironmentChanges(executionContext.environment, bundleIdentifier); 159 | } 160 | 161 | %ctor 162 | { 163 | choicy_reloadPreferences(); 164 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)choicy_reloadPreferences, CFSTR("com.opa334.choicyprefs/ReloadPrefs"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately); 165 | 166 | if (preferences && [ChoicyPrefsMigrator preferencesNeedMigration:preferences]) { 167 | NSMutableDictionary *preferencesM = preferences.mutableCopy; 168 | [ChoicyPrefsMigrator migratePreferences:preferencesM]; 169 | [ChoicyPrefsMigrator updatePreferenceVersion:preferencesM]; 170 | [preferencesM writeToFile:kChoicyPrefsPlistPath atomically:NO]; 171 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.opa334.choicyprefs/ReloadPrefs"), NULL, NULL, YES); 172 | } 173 | 174 | NSString *executablePath = safe_getExecutablePath(); 175 | if ([executablePath.lastPathComponent isEqualToString:@"SpringBoard"]) { 176 | gIsSpringBoard = YES; 177 | choicy_initSpringBoard(); 178 | } 179 | else if ([executablePath.lastPathComponent isEqualToString:@"runningboardd"]) { 180 | choicy_initRunningBoardd(); 181 | } 182 | } -------------------------------------------------------------------------------- /ChoicyPrefs/CHPListController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPListController.h" 22 | #import "../Shared.h" 23 | #import "../ChoicyPrefsMigrator.h" 24 | #import "CHPPreferences.h" 25 | #import "CHPProcessConfigurationListController.h" 26 | 27 | @implementation CHPListController 28 | 29 | + (void)sendPostNotificationForSpecifier:(PSSpecifier *)specifier 30 | { 31 | NSString *postNotification = [specifier propertyForKey:@"PostNotification"]; 32 | if (postNotification) { 33 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge CFStringRef)postNotification, NULL, NULL, YES); 34 | } 35 | } 36 | 37 | + (void)sendChoicyPrefsPostNotification 38 | { 39 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.opa334.choicyprefs/ReloadPrefs"), NULL, NULL, YES); 40 | } 41 | 42 | + (NSString *)previewStringForProcessPreferences:(NSDictionary *)processPreferences 43 | { 44 | NSNumber *tweakInjectionDisabledNum = processPreferences[kChoicyProcessPrefsKeyTweakInjectionDisabled]; 45 | NSNumber *customTweakConfigurationEnabledNum = processPreferences[kChoicyProcessPrefsKeyCustomTweakConfigurationEnabled]; 46 | NSNumber *overwriteGlobalTweakConfigurationNum = processPreferences[kChoicyProcessPrefsKeyOverwriteGlobalTweakConfiguration]; 47 | 48 | if (tweakInjectionDisabledNum.boolValue) { 49 | return localize(@"TWEAKS_DISABLED"); 50 | } 51 | else if (customTweakConfigurationEnabledNum.boolValue) { 52 | return localize(@"CUSTOM"); 53 | } 54 | else if (overwriteGlobalTweakConfigurationNum.boolValue) { 55 | NSArray *globalDeniedTweaks = preferences[kChoicyPrefsKeyGlobalDeniedTweaks]; 56 | if (globalDeniedTweaks.count) { 57 | return localize(@"GLOBAL_OVERWRITE"); 58 | } 59 | } 60 | return @""; 61 | } 62 | 63 | + (NSString *)previewStringForSpecifier:(PSSpecifier *)specifier 64 | { 65 | NSString *appIdentifier = [specifier propertyForKey:@"applicationIdentifier"]; 66 | NSString *pluginIdentifier = [specifier propertyForKey:@"pluginIdentifier"]; 67 | NSString *executablePath = [specifier propertyForKey:@"executablePath"]; 68 | 69 | NSString *identifierToUse = appIdentifier ? appIdentifier : pluginIdentifier; 70 | 71 | if (identifierToUse) { 72 | NSDictionary *appSettings = [preferences objectForKey:kChoicyPrefsKeyAppSettings]; 73 | NSDictionary *settingsForApplication = [appSettings objectForKey:identifierToUse]; 74 | return [self previewStringForProcessPreferences:settingsForApplication]; 75 | } 76 | else { 77 | NSDictionary *daemonSettings = [preferences objectForKey:kChoicyPrefsKeyDaemonSettings]; 78 | NSDictionary *settingsForDaemon = [daemonSettings objectForKey:executablePath.lastPathComponent]; 79 | return [self previewStringForProcessPreferences:settingsForDaemon]; 80 | } 81 | } 82 | 83 | + (PSSpecifier *)createSpecifierForExecutable:(NSString *)executablePath named:(NSString *)name 84 | { 85 | PSSpecifier *specifier = [PSSpecifier preferenceSpecifierNamed:name 86 | target:self 87 | set:nil 88 | get:@selector(previewStringForSpecifier:) 89 | detail:[CHPProcessConfigurationListController class] 90 | cell:PSLinkListCell 91 | edit:nil]; 92 | 93 | if ([executablePath.stringByDeletingLastPathComponent.pathExtension isEqualToString:@"app"]) { 94 | NSString *appDirectory = executablePath.stringByDeletingLastPathComponent; 95 | NSDictionary *appInfo = [NSDictionary dictionaryWithContentsOfFile:[appDirectory stringByAppendingPathComponent:@"Info.plist"]]; 96 | NSString *appIdentifier = appInfo[@"CFBundleIdentifier"]; 97 | [specifier setProperty:appIdentifier forKey:@"applicationIdentifier"]; 98 | } 99 | else if ([executablePath.stringByDeletingLastPathComponent.pathExtension isEqualToString:@"appex"]) { 100 | NSString *pluginDirectory = executablePath.stringByDeletingLastPathComponent; 101 | NSDictionary *pluginInfo = [NSDictionary dictionaryWithContentsOfFile:[pluginDirectory stringByAppendingPathComponent:@"Info.plist"]]; 102 | NSString *pluginIdentifier = pluginInfo[@"CFBundleIdentifier"]; 103 | [specifier setProperty:pluginIdentifier forKey:@"pluginIdentifier"]; 104 | } 105 | else { 106 | [specifier setProperty:executablePath forKey:@"executablePath"]; 107 | } 108 | 109 | [specifier setProperty:@YES forKey:@"enabled"]; 110 | return specifier; 111 | } 112 | 113 | //Must be overwritten by subclass 114 | - (NSString *)topTitle 115 | { 116 | return nil; 117 | } 118 | 119 | //Must be overwritten by subclass 120 | - (NSString *)plistName 121 | { 122 | return nil; 123 | } 124 | 125 | - (void)applySearchControllerHideWhileScrolling:(BOOL)hideWhileScrolling 126 | { 127 | _searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; 128 | _searchController.searchResultsUpdater = self; 129 | if (@available(iOS 9.1, *)) _searchController.obscuresBackgroundDuringPresentation = NO; 130 | 131 | if (@available(iOS 11.0, *)) { 132 | if (@available(iOS 13.0, *)) { 133 | _searchController.hidesNavigationBarDuringPresentation = YES; 134 | } 135 | else { 136 | _searchController.hidesNavigationBarDuringPresentation = NO; 137 | } 138 | 139 | self.navigationItem.searchController = _searchController; 140 | self.navigationItem.hidesSearchBarWhenScrolling = hideWhileScrolling; 141 | } 142 | else { 143 | self.table.tableHeaderView = _searchController.searchBar; 144 | [self.table setContentOffset:CGPointMake(0,44) animated:NO]; 145 | } 146 | 147 | _searchKey = @""; 148 | } 149 | 150 | - (void)updateSearchResultsForSearchController:(UISearchController *)searchController 151 | { 152 | _searchKey = searchController.searchBar.text; 153 | [self reloadSpecifiers]; 154 | } 155 | 156 | - (NSMutableArray *)specifiers 157 | { 158 | if (!_specifiers) { 159 | NSString *plistName = [self plistName]; 160 | 161 | if (plistName) { 162 | _specifiers = [self loadSpecifiersFromPlistName:plistName target:self bundle:[NSBundle bundleForClass:[CHPListController class]]]; 163 | [self parseLocalizationsForSpecifiers:_specifiers]; 164 | } 165 | } 166 | 167 | NSString *title = [self topTitle]; 168 | if (title) { 169 | [(UINavigationItem *)self.navigationItem setTitle:title]; 170 | } 171 | 172 | return _specifiers; 173 | } 174 | 175 | - (void)parseLocalizationsForSpecifiers:(NSArray *)specifiers 176 | { 177 | //Localize specifiers 178 | NSMutableArray *mutableSpecifiers = (NSMutableArray *)specifiers; 179 | for (PSSpecifier *specifier in mutableSpecifiers) { 180 | HBLogDebug(@"title:%@",specifier.properties[@"label"]); 181 | NSString *localizedTitle = localize(specifier.properties[@"label"]); 182 | NSString *localizedFooter = localize(specifier.properties[@"footerText"]); 183 | specifier.name = localizedTitle; 184 | [specifier setProperty:localizedFooter forKey:@"footerText"]; 185 | } 186 | } 187 | 188 | - (void)setPreferenceValue:(id)value specifier:(PSSpecifier *)specifier 189 | { 190 | NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithContentsOfFile:kChoicyPrefsPlistPath]; 191 | if (!mutableDict) { 192 | mutableDict = [NSMutableDictionary new]; 193 | [ChoicyPrefsMigrator updatePreferenceVersion:mutableDict]; 194 | } 195 | [mutableDict setObject:value forKey:[[specifier properties] objectForKey:@"key"]]; 196 | [mutableDict writeToFile:kChoicyPrefsPlistPath atomically:YES]; 197 | 198 | [[self class] sendPostNotificationForSpecifier:specifier]; 199 | } 200 | 201 | - (id)readPreferenceValue:(PSSpecifier *)specifier 202 | { 203 | NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:kChoicyPrefsPlistPath]; 204 | 205 | id obj = [dict objectForKey:[[specifier properties] objectForKey:@"key"]]; 206 | 207 | if (!obj) { 208 | obj = [[specifier properties] objectForKey:@"default"]; 209 | } 210 | 211 | return obj; 212 | } 213 | 214 | @end -------------------------------------------------------------------------------- /ChoicySB/SpringBoard.x: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "../Shared.h" 22 | #import "../ChoicyPrefsMigrator.h" 23 | #import "SpringBoard.h" 24 | #import "ChoicyOverrideManager.h" 25 | #import 26 | #import 27 | #import "ChoicySB.h" 28 | 29 | NSBundle *CHBundle; 30 | NSString *toggleOneTimeApplicationID; 31 | 32 | BOOL choicy_shouldShow3DTouchOptionForDisableTweakInjectionState(BOOL disableTweakInjectionState) 33 | { 34 | BOOL shouldShow = NO; 35 | 36 | if (disableTweakInjectionState) { 37 | shouldShow = ((NSNumber *)[preferences objectForKey:@"launchWithTweaksOptionEnabled"]).boolValue; 38 | } 39 | else { 40 | NSNumber *shouldShowNumber = [preferences objectForKey:@"launchWithoutTweaksOptionEnabled"]; 41 | if (shouldShowNumber) { 42 | shouldShow = shouldShowNumber.boolValue; 43 | } 44 | else { 45 | shouldShow = YES; 46 | } 47 | } 48 | 49 | return shouldShow; 50 | } 51 | 52 | %hook FBProcessManager 53 | 54 | %new 55 | - (void)choicy_handleEnvironmentChangesForExecutionContext:(FBProcessExecutionContext *)executionContext forAppWithBundleIdentifier:(NSString *)bundleIdentifier 56 | { 57 | BOOL toggleOnce = [toggleOneTimeApplicationID isEqualToString:bundleIdentifier]; 58 | toggleOneTimeApplicationID = nil; 59 | if (toggleOnce) { 60 | NSMutableDictionary *environmentM = [executionContext.environment mutableCopy]; 61 | BOOL shouldDisableTweaks = !choicy_shouldDisableTweakInjectionForApplication(bundleIdentifier); 62 | if (shouldDisableTweaks) { 63 | [environmentM setObject:@(shouldDisableTweaks) forKey:@"_MSSafeMode"]; 64 | [environmentM setObject:@(shouldDisableTweaks) forKey:@"_SafeMode"]; 65 | } 66 | else { 67 | // If the the "Launch with Tweaks" option was pressed, we need to let the Choicy dylib know 68 | [environmentM setObject:@YES forKey:@"_ChoicyInjectionEnabledFromSpringBoard"]; 69 | } 70 | 71 | executionContext.environment = [environmentM copy]; 72 | } 73 | 74 | // runningboardd only exists on iOS 13 and up, so on lower versions we need to handle the logic in SpringBoard 75 | if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_13_0) { 76 | choicy_applyEnvironmentChangesToExecutionContext(executionContext, bundleIdentifier); 77 | } 78 | } 79 | 80 | // iOS >= 15 81 | - (id)_bootstrapProcessWithExecutionContext:(FBProcessExecutionContext *)executionContext synchronously:(BOOL)synchronously error:(id *)error 82 | { 83 | [self choicy_handleEnvironmentChangesForExecutionContext:executionContext forAppWithBundleIdentifier:executionContext.identity.embeddedApplicationIdentifier]; 84 | 85 | return %orig; 86 | } 87 | 88 | // iOS 13 - 14 89 | - (id)_createProcessWithExecutionContext:(FBProcessExecutionContext *)executionContext 90 | { 91 | [self choicy_handleEnvironmentChangesForExecutionContext:executionContext forAppWithBundleIdentifier:executionContext.identity.embeddedApplicationIdentifier]; 92 | 93 | return %orig; 94 | } 95 | 96 | // iOS <= 12 97 | - (id)createApplicationProcessForBundleID:(NSString *)bundleIdentifier withExecutionContext:(FBProcessExecutionContext *)executionContext 98 | { 99 | [self choicy_handleEnvironmentChangesForExecutionContext:executionContext forAppWithBundleIdentifier:bundleIdentifier]; 100 | 101 | return %orig; 102 | } 103 | 104 | %end 105 | 106 | %group Shortcut_iOS13Up 107 | 108 | %hook SBIconView 109 | 110 | - (NSArray *)applicationShortcutItems 111 | { 112 | NSArray *orig = %orig; 113 | 114 | NSString *applicationID; 115 | if ([self respondsToSelector:@selector(applicationBundleIdentifier)]) { 116 | applicationID = [self applicationBundleIdentifier]; 117 | } 118 | else if ([self respondsToSelector:@selector(applicationBundleIdentifierForShortcuts)]) { 119 | applicationID = [self applicationBundleIdentifierForShortcuts]; 120 | } 121 | 122 | if (!applicationID) { 123 | return orig; 124 | } 125 | 126 | BOOL tweakInjectionDisabled = choicy_shouldDisableTweakInjectionForApplication(applicationID); 127 | 128 | if (choicy_shouldShow3DTouchOptionForDisableTweakInjectionState(tweakInjectionDisabled)) { 129 | SBSApplicationShortcutItem *toggleSafeModeOnceItem = [[%c(SBSApplicationShortcutItem) alloc] init]; 130 | NSString *imageName; 131 | 132 | if (tweakInjectionDisabled) { 133 | toggleSafeModeOnceItem.localizedTitle = localize(@"LAUNCH_WITH_TWEAKS"); 134 | imageName = @"AppLaunchIcon"; 135 | } 136 | else { 137 | toggleSafeModeOnceItem.localizedTitle = localize(@"LAUNCH_WITHOUT_TWEAKS"); 138 | imageName = @"AppLaunchIcon_Crossed"; 139 | } 140 | 141 | UIImage *imageToSet = [[UIImage imageNamed:imageName inBundle:CHBundle compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 142 | toggleSafeModeOnceItem.icon = [[%c(SBSApplicationShortcutCustomImageIcon) alloc] initWithImageData:UIImagePNGRepresentation(imageToSet) dataType:0 isTemplate:1]; 143 | 144 | toggleSafeModeOnceItem.bundleIdentifierToLaunch = applicationID; 145 | toggleSafeModeOnceItem.type = @"com.opa334.choicy.toggleSafeModeOnce"; 146 | 147 | return [orig arrayByAddingObject:toggleSafeModeOnceItem]; 148 | } 149 | 150 | return orig; 151 | } 152 | 153 | + (void)activateShortcut:(SBSApplicationShortcutItem *)item withBundleIdentifier:(NSString *)bundleID forIconView:(id)iconView 154 | { 155 | if ([[item type] isEqualToString:@"com.opa334.choicy.toggleSafeModeOnce"]) { 156 | BKSTerminateApplicationForReasonAndReportWithDescription(bundleID, 5, false, @"Choicy - force touch, killed"); 157 | toggleOneTimeApplicationID = bundleID; 158 | } 159 | 160 | %orig; 161 | } 162 | 163 | %end 164 | 165 | %end 166 | 167 | %group Shortcut_iOS12Down 168 | 169 | %hook SBUIAppIconForceTouchControllerDataProvider 170 | 171 | - (NSArray *)applicationShortcutItems 172 | { 173 | NSArray *orig = %orig; 174 | 175 | NSString *applicationID = [self applicationBundleIdentifier]; 176 | 177 | if (!applicationID) { 178 | return orig; 179 | } 180 | 181 | BOOL disableTweakInjection = choicy_shouldDisableTweakInjectionForApplication(applicationID); 182 | 183 | if (choicy_shouldShow3DTouchOptionForDisableTweakInjectionState(disableTweakInjection)) { 184 | SBSApplicationShortcutItem *toggleSafeModeOnceItem = [[%c(SBSApplicationShortcutItem) alloc] init]; 185 | 186 | if (disableTweakInjection) { 187 | toggleSafeModeOnceItem.localizedTitle = localize(@"LAUNCH_WITH_TWEAKS"); 188 | } 189 | else { 190 | toggleSafeModeOnceItem.localizedTitle = localize(@"LAUNCH_WITHOUT_TWEAKS"); 191 | } 192 | 193 | toggleSafeModeOnceItem.bundleIdentifierToLaunch = applicationID; 194 | toggleSafeModeOnceItem.type = @"com.opa334.choicy.toggleSafeModeOnce"; 195 | 196 | if (!orig) { 197 | return @[toggleSafeModeOnceItem]; 198 | } 199 | else { 200 | return [orig arrayByAddingObject:toggleSafeModeOnceItem]; 201 | } 202 | } 203 | 204 | return orig; 205 | } 206 | 207 | %end 208 | 209 | %hook SBUIAppIconForceTouchController 210 | 211 | - (void)appIconForceTouchShortcutViewController:(id)arg1 activateApplicationShortcutItem:(SBSApplicationShortcutItem *)item 212 | { 213 | if ([item.type isEqualToString:@"com.opa334.choicy.toggleSafeModeOnce"]) { 214 | NSString *bundleID = item.bundleIdentifierToLaunch; 215 | 216 | BKSTerminateApplicationForReasonAndReportWithDescription(bundleID, 5, false, @"Choicy - force touch, killed"); 217 | toggleOneTimeApplicationID = bundleID; 218 | } 219 | 220 | %orig; 221 | } 222 | 223 | %end 224 | 225 | %hook SBUIAction 226 | 227 | - (id)initWithTitle:(id)title subtitle:(id)arg2 image:(id)image badgeView:(id)arg4 handler:(id)arg5 228 | { 229 | if ([title isEqualToString:localize(@"LAUNCH_WITHOUT_TWEAKS")]) { 230 | image = [[UIImage imageNamed:@"AppLaunchIcon_Crossed_Big" inBundle:CHBundle compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 231 | } 232 | else if ([title isEqualToString:localize(@"LAUNCH_WITH_TWEAKS")]) { 233 | image = [[UIImage imageNamed:@"AppLaunchIcon_Big" inBundle:CHBundle compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 234 | } 235 | 236 | return %orig; 237 | } 238 | 239 | %end 240 | 241 | %end 242 | 243 | void respring(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) 244 | { 245 | [[%c(FBSystemService) sharedInstance] exitAndRelaunch:YES]; 246 | } 247 | 248 | void choicy_initSpringBoard(void) 249 | { 250 | %init(); 251 | 252 | CHBundle = [NSBundle bundleWithPath:JBROOT_PATH_NSSTRING(@"/Library/Application Support/Choicy.bundle")]; 253 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, respring, CFSTR("com.opa334.choicy/respring"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately); 254 | 255 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_13_0) { 256 | %init(Shortcut_iOS13Up); 257 | } 258 | else { 259 | %init(Shortcut_iOS12Down); 260 | } 261 | } -------------------------------------------------------------------------------- /ChoicyPrefs/CHPMachoParser.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2021 Lars Fröder 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #import "CHPMachoParser.h" 22 | 23 | #import 24 | 25 | #import 26 | #import 27 | #import 28 | 29 | #import 30 | #import 31 | #import 32 | 33 | MachO *choicy_fat_find_preferred_slice(Fat *fat) 34 | { 35 | cpu_type_t cputype; 36 | cpu_subtype_t cpusubtype; 37 | if (host_get_cpu_information(&cputype, &cpusubtype) != 0) { return NULL; } 38 | 39 | MachO *candidateSlice = NULL; 40 | 41 | if (cputype == CPU_TYPE_ARM64) { 42 | if (!candidateSlice && cpusubtype == CPU_SUBTYPE_ARM64E) { 43 | // New arm64e ABI 44 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_14_0) { 45 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM64E | CPU_SUBTYPE_ARM64E_ABI_V2); 46 | if (!candidateSlice && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_15_0) { 47 | // Old ABI slice is also allowed but only before 15.0 48 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM64E); 49 | } 50 | } 51 | // Old arm64e ABI 52 | else { 53 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM64E); 54 | } 55 | } 56 | 57 | if (!candidateSlice) { 58 | if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_15_0) { 59 | // On iOS 15+ the kernels prefers ARM64_V8 to ARM64_ALL 60 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM64_V8); 61 | } 62 | if (!candidateSlice) { 63 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM64_ALL); 64 | } 65 | } 66 | } 67 | else if (cputype == CPU_TYPE_ARM) { 68 | candidateSlice = fat_find_slice(fat, cputype, cpusubtype); 69 | if (!candidateSlice && cpusubtype == CPU_SUBTYPE_ARM_V7S) { 70 | candidateSlice = fat_find_slice(fat, cputype, CPU_SUBTYPE_ARM_V7); 71 | } 72 | } 73 | 74 | return candidateSlice; 75 | } 76 | 77 | @implementation CHPMachoParser 78 | 79 | + (instancetype)sharedInstance 80 | { 81 | static CHPMachoParser *sharedInstance = nil; 82 | static dispatch_once_t onceToken; 83 | dispatch_once(&onceToken, ^ { 84 | //Initialise instance 85 | sharedInstance = [[CHPMachoParser alloc] init]; 86 | }); 87 | return sharedInstance; 88 | } 89 | 90 | + (BOOL)isMachoAtPath:(NSString *)path 91 | { 92 | return NO; 93 | } 94 | 95 | - (instancetype)init 96 | { 97 | self = [super init]; 98 | if (self) { 99 | _bundleIdentifierCache = [NSMutableDictionary new]; 100 | _dependencyPathCache = [NSMutableDictionary new]; 101 | 102 | task_dyld_info_data_t dyldInfo; 103 | uint32_t count = TASK_DYLD_INFO_COUNT; 104 | task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count); 105 | struct dyld_all_image_infos *allImageInfos = (void *)dyldInfo.all_image_info_addr; 106 | 107 | _sharedCache = dsc_init_from_path_premapped(litehook_locate_dsc(), allImageInfos->sharedCacheSlide); 108 | } 109 | return self; 110 | } 111 | 112 | - (NSString *)resolvedDependencyPathForDependencyPath:(NSString *)dependencyPath sourceImagePath:(NSString *)sourceImagePath sourceExecutablePath:(NSString *)sourceExecutablePath 113 | { 114 | @autoreleasepool { 115 | if (!dependencyPath) return nil; 116 | NSString *loaderPath = [sourceImagePath stringByDeletingLastPathComponent]; 117 | NSString *executablePath = [sourceExecutablePath stringByDeletingLastPathComponent]; 118 | 119 | NSString *resolvedPath = nil; 120 | 121 | NSString *(^resolveLoaderExecutablePaths)(NSString *) = ^NSString *(NSString *candidatePath) { 122 | if (!candidatePath) return nil; 123 | if ([[NSFileManager defaultManager] fileExistsAtPath:candidatePath]) return candidatePath; 124 | if (dsc_lookup_macho_by_path(_sharedCache, candidatePath.fileSystemRepresentation, NULL)) return candidatePath; 125 | if ([candidatePath hasPrefix:@"@loader_path"] && loaderPath) { 126 | NSString *loaderCandidatePath = [candidatePath stringByReplacingOccurrencesOfString:@"@loader_path" withString:loaderPath]; 127 | if ([[NSFileManager defaultManager] fileExistsAtPath:loaderCandidatePath]) return loaderCandidatePath; 128 | } 129 | if ([candidatePath hasPrefix:@"@executable_path"] && executablePath) { 130 | NSString *executableCandidatePath = [candidatePath stringByReplacingOccurrencesOfString:@"@executable_path" withString:executablePath]; 131 | if ([[NSFileManager defaultManager] fileExistsAtPath:executableCandidatePath]) return executableCandidatePath; 132 | } 133 | return nil; 134 | }; 135 | 136 | if ([dependencyPath hasPrefix:@"@rpath"]) { 137 | NSString *(^resolveRpaths)(NSString *) = ^NSString *(NSString *binaryPath) { 138 | if (!binaryPath) return nil; 139 | __block NSString *rpathResolvedPath = nil; 140 | Fat *fat = NULL; 141 | MachO *macho = dsc_lookup_macho_by_path(_sharedCache, binaryPath.fileSystemRepresentation, NULL); 142 | if (!macho) { 143 | fat = fat_init_from_path(binaryPath.fileSystemRepresentation); 144 | if (fat) { 145 | macho = choicy_fat_find_preferred_slice(fat); 146 | } 147 | } 148 | if (macho) { 149 | macho_enumerate_rpaths(macho, ^(const char *rpathC, bool *stop) { 150 | if (rpathC) { 151 | NSString *rpath = [NSString stringWithUTF8String:rpathC]; 152 | if (rpath) { 153 | rpathResolvedPath = resolveLoaderExecutablePaths([dependencyPath stringByReplacingOccurrencesOfString:@"@rpath" withString:rpath]); 154 | if (rpathResolvedPath) { 155 | *stop = true; 156 | } 157 | } 158 | } 159 | }); 160 | } 161 | if (fat) { 162 | fat_free(fat); 163 | } 164 | return rpathResolvedPath; 165 | }; 166 | 167 | resolvedPath = resolveRpaths(sourceImagePath); 168 | if (resolvedPath) return resolvedPath; 169 | 170 | // TODO: Check if this is even neccessary 171 | resolvedPath = resolveRpaths(sourceExecutablePath); 172 | if (resolvedPath) return resolvedPath; 173 | } 174 | else { 175 | resolvedPath = resolveLoaderExecutablePaths(dependencyPath); 176 | if (resolvedPath) return resolvedPath; 177 | } 178 | 179 | return nil; 180 | } 181 | } 182 | 183 | - (NSSet *)_dependencyPathsForMachoAtPath:(NSString *)path sourceImagePath:(NSString *)sourceImagePath sourceExecutablePath:(NSString *)sourceExecutablePath 184 | { 185 | NSString *standardizedPath = path.stringByStandardizingPath; 186 | 187 | if (_dependencyPathCache[standardizedPath]) { 188 | return _dependencyPathCache[standardizedPath]; 189 | } 190 | 191 | _dependencyPathCache[standardizedPath] = [NSMutableSet new]; 192 | 193 | Fat *fat = NULL; 194 | MachO *macho = dsc_lookup_macho_by_path(_sharedCache, standardizedPath.fileSystemRepresentation, NULL); 195 | if (!macho) { 196 | fat = fat_init_from_path(standardizedPath.fileSystemRepresentation); 197 | if (fat) { 198 | macho = choicy_fat_find_preferred_slice(fat); 199 | } 200 | } 201 | 202 | if (macho) { 203 | macho_enumerate_dependencies(macho, ^(const char *imagePathC, uint32_t cmd, struct dylib* dylib, bool *stop){ 204 | if (!imagePathC) return; 205 | NSString *imagePath = [NSString stringWithUTF8String:imagePathC].stringByStandardizingPath; 206 | imagePath = [self resolvedDependencyPathForDependencyPath:imagePath sourceImagePath:sourceImagePath sourceExecutablePath:sourceExecutablePath]; 207 | if (!imagePath) return; 208 | if (![_dependencyPathCache[standardizedPath] containsObject:imagePath]) { 209 | [_dependencyPathCache[standardizedPath] addObject:imagePath]; 210 | NSSet *nestedPaths = [self _dependencyPathsForMachoAtPath:imagePath sourceImagePath:path sourceExecutablePath:sourceExecutablePath]; 211 | [_dependencyPathCache[standardizedPath] unionSet:nestedPaths]; 212 | } 213 | }); 214 | } 215 | 216 | if (fat) { 217 | fat_free(fat); 218 | } 219 | 220 | return _dependencyPathCache[standardizedPath]; 221 | } 222 | 223 | - (NSSet *)dependencyPathsForMachoAtPath:(NSString *)path 224 | { 225 | return [self _dependencyPathsForMachoAtPath:path sourceImagePath:nil sourceExecutablePath:path]; 226 | } 227 | 228 | - (NSSet *)frameworkBundleIdentifiersForMachoAtPath:(NSString *)path 229 | { 230 | NSString *standardizedPath = path.stringByStandardizingPath; 231 | 232 | if (_bundleIdentifierCache[standardizedPath]) { 233 | return _bundleIdentifierCache[standardizedPath]; 234 | } 235 | 236 | NSMutableSet *bundleIdentifiers = [NSMutableSet set]; 237 | NSSet *dependencyPaths = [self dependencyPathsForMachoAtPath:standardizedPath]; 238 | 239 | void (^processDependencyPaths)(NSString *) = ^(NSString *dependencyPath){ 240 | NSString *parentPath = [dependencyPath stringByDeletingLastPathComponent]; 241 | if ([parentPath.pathExtension isEqualToString:@"framework"]) { 242 | NSString *infoPlistPath = [parentPath stringByAppendingPathComponent:@"Info.plist"]; 243 | NSDictionary *infoDictionary = [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; 244 | if (infoDictionary) { 245 | NSString *bundleIdentifier = infoDictionary[@"CFBundleIdentifier"]; 246 | if (bundleIdentifier) { 247 | [bundleIdentifiers addObject:bundleIdentifier]; 248 | } 249 | } 250 | } 251 | }; 252 | 253 | processDependencyPaths(standardizedPath); 254 | for (NSString *dependencyPath in dependencyPaths) { 255 | processDependencyPaths(dependencyPath); 256 | } 257 | 258 | if (bundleIdentifiers) { 259 | _bundleIdentifierCache[standardizedPath] = bundleIdentifiers; 260 | } 261 | 262 | return bundleIdentifiers; 263 | } 264 | 265 | @end --------------------------------------------------------------------------------