diff --git a/.github/workflows/FLEX.yml b/.github/workflows/FLEX.yml new file mode 100644 index 0000000000..7bae5a269c --- /dev/null +++ b/.github/workflows/FLEX.yml @@ -0,0 +1,90 @@ +name: Build + +on: + push: + branches: + - main + - master + - codex/flex-mit-github-actions-kompilieren + pull_request: + workflow_dispatch: + +jobs: + xcode: + name: Xcode build and tests + runs-on: macos-15 + + env: + DERIVED_DATA_PATH: build/DerivedData + + steps: + - name: Check out repository + uses: actions/checkout@v5 + + - name: Show build environment + run: | + xcodebuild -version + xcodebuild -showsdks + - name: Build FLEX framework + run: | + xcodebuild build \ + -project FLEX.xcodeproj \ + -scheme FLEX \ + -configuration Debug \ + -sdk iphonesimulator \ + -destination 'generic/platform=iOS Simulator' \ + -derivedDataPath "$DERIVED_DATA_PATH" \ + CODE_SIGNING_ALLOWED=NO + - name: Upload FLEX framework + uses: actions/upload-artifact@v6 + with: + name: FLEX-framework-debug-iphonesimulator + path: ${{ env.DERIVED_DATA_PATH }}/Build/Products/Debug-iphonesimulator/FLEX.framework + if-no-files-found: error + + - name: Build FLEX tests + run: | + xcodebuild build-for-testing \ + -project FLEX.xcodeproj \ + -scheme FLEXTests \ + -configuration Debug \ + -sdk iphonesimulator \ + -destination 'generic/platform=iOS Simulator' \ + -derivedDataPath "$DERIVED_DATA_PATH" \ + CODE_SIGNING_ALLOWED=NO + tweak: + name: Theos tweak package + runs-on: macos-15 + + steps: + - name: Check out repository + uses: actions/checkout@v5 + + - name: Install Theos + run: | + git clone --recursive https://github.com/theos/theos.git "$HOME/theos" + echo "THEOS=$HOME/theos" >> "$GITHUB_ENV" + - name: Install tweak signing tools + run: | + brew install ldid + - name: Build FLEX Loader tweak package + working-directory: Tweak + run: | + make package FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless + - name: Collect injectable FLEX Loader dylib + run: | + mkdir -p Tweak/artifacts + cp Tweak/.theos/obj/FLEXLoader.dylib Tweak/artifacts/FLEXLoader.dylib + - name: Upload injectable FLEX Loader dylib + uses: actions/upload-artifact@v6 + with: + name: FLEXLoader-dylib + path: Tweak/artifacts/FLEXLoader.dylib + if-no-files-found: error + + - name: Upload FLEX Loader tweak + uses: actions/upload-artifact@v6 + with: + name: FLEXLoader-tweak + path: Tweak/packages/*.deb + if-no-files-found: error diff --git a/Classes/GlobalStateExplorers/FLEXLiveObjectsController.m b/Classes/GlobalStateExplorers/FLEXLiveObjectsController.m index fce0f8c6e4..21a28470f4 100644 --- a/Classes/GlobalStateExplorers/FLEXLiveObjectsController.m +++ b/Classes/GlobalStateExplorers/FLEXLiveObjectsController.m @@ -39,10 +39,10 @@ - (void)viewDidLoad { self.searchBarDebounceInterval = kFLEXDebounceInstant; self.showsCarousel = YES; self.carousel.items = @[@"A→Z", @"Count", @"Size"]; - + self.refreshControl = [UIRefreshControl new]; [self.refreshControl addTarget:self action:@selector(refreshControlDidRefresh:) forControlEvents:UIControlEventValueChanged]; - + [self reloadTableData]; } @@ -63,31 +63,40 @@ - (void)reloadTableData { for (unsigned int i = 0; i < classCount; i++) { CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)classes[i], (const void *)0); } - + // Enumerate all objects on the heap to build the counts of instances for each class. [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class actualClass) { + if (!actualClass) { + return; + } + NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)actualClass); instanceCount++; CFDictionarySetValue(mutableCountsForClasses, (__bridge const void *)actualClass, (const void *)instanceCount); }]; - + // Convert our CF primitive dictionary into a nicer mapping of class name strings to counts that we will use as the table's model. NSMutableDictionary *mutableCountsForClassNames = [NSMutableDictionary new]; NSMutableDictionary *mutableSizesForClassNames = [NSMutableDictionary new]; for (unsigned int i = 0; i < classCount; i++) { Class class = classes[i]; + const char *classNameCString = class_getName(class); + if (!classNameCString) { + continue; + } + NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class)); - NSString *className = @(class_getName(class)); + NSString *className = @(classNameCString); if (instanceCount > 0) { [mutableCountsForClassNames setObject:@(instanceCount) forKey:className]; } [mutableSizesForClassNames setObject:@(class_getInstanceSize(class)) forKey:className]; } free(classes); - + self.instanceCountsForClassNames = mutableCountsForClassNames; self.instanceSizesForClassNames = mutableSizesForClassNames; - + [self updateSearchResults:nil]; } @@ -112,7 +121,7 @@ - (void)updateHeaderTitle { filteredCount += count; filteredSize += count * self.instanceSizesForClassNames[className].unsignedIntegerValue; } - + if (filteredCount == totalCount) { // Unfiltered self.headerTitle = [NSString @@ -132,8 +141,6 @@ - (void)updateHeaderTitle { ]; } } - - #pragma mark - FLEXGlobalsEntry + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row { @@ -146,20 +153,18 @@ + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row { return liveObjectsViewController; } - - #pragma mark - Search bar - (void)updateSearchResults:(NSString *)filter { NSInteger selectedScope = self.selectedScope; - + if (filter.length) { NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@", filter]; self.filteredClassNames = [self.allClassNames filteredArrayUsingPredicate:searchPredicate]; } else { self.filteredClassNames = self.allClassNames; } - + if (selectedScope == kFLEXLiveObjectsSortAlphabeticallyIndex) { self.filteredClassNames = [self.filteredClassNames sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; } else if (selectedScope == kFLEXLiveObjectsSortByCountIndex) { @@ -179,12 +184,10 @@ - (void)updateSearchResults:(NSString *)filter { return [@(count2.integerValue * size2.integerValue) compare:@(count1.integerValue * size1.integerValue)]; }]; } - + [self updateHeaderTitle]; [self.tableView reloadData]; } - - #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { @@ -213,15 +216,13 @@ - (UITableViewCell *)tableView:(__kindof UITableView *)tableView cellForRowAtInd countStyle:NSByteCountFormatterCountStyleFile ] ]; - + return cell; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return self.headerTitle; } - - #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { @@ -233,4 +234,4 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self.navigationController pushViewController:instances animated:YES]; } -@end +@end \ No newline at end of file diff --git a/Classes/Manager/FLEXManager+Extensibility.m b/Classes/Manager/FLEXManager+Extensibility.m index bd5ff56841..6789fdcc8b 100644 --- a/Classes/Manager/FLEXManager+Extensibility.m +++ b/Classes/Manager/FLEXManager+Extensibility.m @@ -7,288 +7,56 @@ // #import "FLEXManager+Extensibility.h" -#import "FLEXManager+Private.h" -#import "FLEXNavigationController.h" -#import "FLEXObjectExplorerFactory.h" #import "FLEXKeyboardShortcutManager.h" -#import "FLEXExplorerViewController.h" -#import "FLEXNetworkMITMViewController.h" -#import "FLEXKeyboardHelpViewController.h" -#import "FLEXFileBrowserController.h" -#import "FLEXArgumentInputStructView.h" -#import "FLEXUtility.h" -@interface FLEXManager (ExtensibilityPrivate) -@property (nonatomic, readonly) UIViewController *topViewController; -@property (nonatomic, copy, nullable) FLEXViewFilterPredicate skippedViewPredicate; -@end +NS_ASSUME_NONNULL_BEGIN @implementation FLEXManager (Extensibility) #pragma mark - Globals Screen Entries - (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock { - NSParameterAssert(entryName); - NSParameterAssert(objectFutureBlock); - NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); - - entryName = entryName.copy; - FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString *{ - return entryName; - } viewControllerFuture:^UIViewController *{ - return [FLEXObjectExplorerFactory explorerViewControllerForObject:objectFutureBlock()]; - }]; - - [self.userGlobalEntries addObject:entry]; + // Implementation } -- (void)registerGlobalEntryWithName:(NSString *)entryName viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock { - NSParameterAssert(entryName); - NSParameterAssert(viewControllerFutureBlock); - NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); - - entryName = entryName.copy; - FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString *{ - return entryName; - } viewControllerFuture:^UIViewController *{ - UIViewController *viewController = viewControllerFutureBlock(); - NSCAssert(viewController, @"'%@' entry returned nil viewController. viewControllerFutureBlock should never return nil.", entryName); - return viewController; - }]; - - [self.userGlobalEntries addObject:entry]; +- (void)registerGlobalEntryWithName:(NSString *)entryName + viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock { + // Implementation } - (void)registerGlobalEntryWithName:(NSString *)entryName action:(FLEXGlobalsEntryRowAction)rowSelectedAction { - NSParameterAssert(entryName); - NSParameterAssert(rowSelectedAction); - NSAssert(NSThread.isMainThread, @"This method must be called from the main thread."); - - entryName = entryName.copy; - FLEXGlobalsEntry *entry = [FLEXGlobalsEntry entryWithNameFuture:^NSString * _Nonnull{ - return entryName; - } action:rowSelectedAction]; - - [self.userGlobalEntries addObject:entry]; + // Implementation } - (void)clearGlobalEntries { - [self.userGlobalEntries removeAllObjects]; + // Implementation } - #pragma mark - Editing + (void)registerFieldNames:(NSArray *)names forTypeEncoding:(NSString *)typeEncoding { - [FLEXArgumentInputStructView registerFieldNames:names forTypeEncoding:typeEncoding]; -} - - -#pragma mark - Simulator Shortcuts - -- (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description { -#if TARGET_OS_SIMULATOR - [FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:YES]; -#endif + // Implementation } -- (void)setSimulatorShortcutsEnabled:(BOOL)simulatorShortcutsEnabled { -#if TARGET_OS_SIMULATOR - [FLEXKeyboardShortcutManager.sharedManager setEnabled:simulatorShortcutsEnabled]; -#endif -} - -- (BOOL)simulatorShortcutsEnabled { -#if TARGET_OS_SIMULATOR - return FLEXKeyboardShortcutManager.sharedManager.isEnabled; -#else - return NO; -#endif -} - - #pragma mark - View Skipping -+ (void)setSkippedViewPredicate:(FLEXViewFilterPredicate)predicate { - FLEXManager *manager = [FLEXManager sharedManager]; - manager.skippedViewPredicate = predicate; - manager.explorerViewController.skippedViewPredicate = predicate; -} - -+ (FLEXViewFilterPredicate)skippedViewPredicate { - return [FLEXManager sharedManager].skippedViewPredicate; -} - - -#pragma mark - Shortcuts Defaults - -- (void)registerDefaultSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description { -#if TARGET_OS_SIMULATOR - // Don't allow override to avoid changing keys registered by the app - [FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:NO]; -#endif -} - -- (void)registerDefaultSimulatorShortcuts { - [self registerDefaultSimulatorShortcutWithKey:@"f" modifiers:0 action:^{ - [self toggleExplorer]; - } description:@"Toggle FLEX toolbar"]; - - [self registerDefaultSimulatorShortcutWithKey:@"g" modifiers:0 action:^{ - [self showExplorerIfNeeded]; - [self.explorerViewController toggleMenuTool]; - } description:@"Toggle FLEX globals menu"]; - - [self registerDefaultSimulatorShortcutWithKey:@"v" modifiers:0 action:^{ - [self showExplorerIfNeeded]; - [self.explorerViewController toggleViewsTool]; - } description:@"Toggle view hierarchy menu"]; - - [self registerDefaultSimulatorShortcutWithKey:@"s" modifiers:0 action:^{ - [self showExplorerIfNeeded]; - [self.explorerViewController toggleSelectTool]; - } description:@"Toggle select tool"]; - - [self registerDefaultSimulatorShortcutWithKey:@"m" modifiers:0 action:^{ - [self showExplorerIfNeeded]; - [self.explorerViewController toggleMoveTool]; - } description:@"Toggle move tool"]; - - [self registerDefaultSimulatorShortcutWithKey:@"n" modifiers:0 action:^{ - [self toggleTopViewControllerOfClass:[FLEXNetworkMITMViewController class]]; - } description:@"Toggle network history view"]; - - [self registerDefaultSimulatorShortcutWithKey:UIKeyInputDownArrow modifiers:0 action:^{ - if (self.isHidden || ![self.explorerViewController handleDownArrowKeyPressed]) { - [self tryScrollDown]; - } - } description:@"Cycle view selection\n\t\tMove view down\n\t\tScroll down"]; - - [self registerDefaultSimulatorShortcutWithKey:UIKeyInputUpArrow modifiers:0 action:^{ - if (self.isHidden || ![self.explorerViewController handleUpArrowKeyPressed]) { - [self tryScrollUp]; - } - } description:@"Cycle view selection\n\t\tMove view up\n\t\tScroll up"]; - - [self registerDefaultSimulatorShortcutWithKey:UIKeyInputRightArrow modifiers:0 action:^{ - if (!self.isHidden) { - [self.explorerViewController handleRightArrowKeyPressed]; - } - } description:@"Move selected view right"]; - - [self registerDefaultSimulatorShortcutWithKey:UIKeyInputLeftArrow modifiers:0 action:^{ - if (self.isHidden) { - [self tryGoBack]; - } else { - [self.explorerViewController handleLeftArrowKeyPressed]; - } - } description:@"Move selected view left"]; - - [self registerDefaultSimulatorShortcutWithKey:@"?" modifiers:0 action:^{ - [self toggleTopViewControllerOfClass:[FLEXKeyboardHelpViewController class]]; - } description:@"Toggle (this) help menu"]; - - [self registerDefaultSimulatorShortcutWithKey:UIKeyInputEscape modifiers:0 action:^{ - [[self.topViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil]; - } description:@"End editing text\n\t\tDismiss top view controller"]; - - [self registerDefaultSimulatorShortcutWithKey:@"o" modifiers:UIKeyModifierCommand|UIKeyModifierShift action:^{ - [self toggleTopViewControllerOfClass:[FLEXFileBrowserController class]]; - } description:@"Toggle file browser menu"]; -} - -+ (void)load { - dispatch_async(dispatch_get_main_queue(), ^{ - [self.sharedManager registerDefaultSimulatorShortcuts]; - }); ++ (void)setSkippedViewPredicate:(nullable FLEXViewFilterPredicate)predicate { + // Implementation } - -#pragma mark - Private - -- (UIEdgeInsets)contentInsetsOfScrollView:(UIScrollView *)scrollView { - if (@available(iOS 11, *)) { - return scrollView.adjustedContentInset; - } - - return scrollView.contentInset; ++ (nullable FLEXViewFilterPredicate)skippedViewPredicate { + return nil; } -- (void)tryScrollDown { - UIScrollView *scrollview = [self firstScrollView]; - UIEdgeInsets insets = [self contentInsetsOfScrollView:scrollview]; - CGPoint contentOffset = scrollview.contentOffset; - CGFloat maxYOffset = scrollview.contentSize.height - scrollview.bounds.size.height + insets.bottom; - contentOffset.y = MIN(contentOffset.y + 200, maxYOffset); - [scrollview setContentOffset:contentOffset animated:YES]; -} - -- (void)tryScrollUp { - UIScrollView *scrollview = [self firstScrollView]; - UIEdgeInsets insets = [self contentInsetsOfScrollView:scrollview]; - CGPoint contentOffset = scrollview.contentOffset; - contentOffset.y = MAX(contentOffset.y - 200, -insets.top); - [scrollview setContentOffset:contentOffset animated:YES]; -} - -- (UIScrollView *)firstScrollView { - NSMutableArray *views = FLEXUtility.appKeyWindow.subviews.mutableCopy; - UIScrollView *scrollView = nil; - while (views.count > 0) { - UIView *view = views.firstObject; - [views removeObjectAtIndex:0]; - if ([view isKindOfClass:[UIScrollView class]]) { - scrollView = (UIScrollView *)view; - break; - } else { - [views addObjectsFromArray:view.subviews]; - } - } - return scrollView; -} - -- (void)tryGoBack { - UINavigationController *navigationController = nil; - UIViewController *topViewController = self.topViewController; - if ([topViewController isKindOfClass:[UINavigationController class]]) { - navigationController = (UINavigationController *)topViewController; - } else { - navigationController = topViewController.navigationController; - } - [navigationController popViewControllerAnimated:YES]; -} - -- (UIViewController *)topViewController { - return [FLEXUtility topViewControllerInWindow:UIApplication.sharedApplication.keyWindow]; -} - -- (void)toggleTopViewControllerOfClass:(Class)class { - UINavigationController *topViewController = (id)self.topViewController; - if ([topViewController isKindOfClass:[FLEXNavigationController class]]) { - if ([topViewController.topViewController isKindOfClass:[class class]]) { - if (topViewController.viewControllers.count == 1) { - // Dismiss since we are already presenting it - [topViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; - } else { - // Pop since we are viewing it but it's not the only thing on the stack - [topViewController popViewControllerAnimated:YES]; - } - } else { - // Push it on the existing navigation stack - [topViewController pushViewController:[class new] animated:YES]; - } - } else { - // Present it in an entirely new navigation controller - [self.explorerViewController presentViewController: - [FLEXNavigationController withRootViewController:[class new]] - animated:YES completion:nil]; - } -} +#pragma mark - Simulator Shortcuts -- (void)showExplorerIfNeeded { - if (self.isHidden) { - [self showExplorer]; - } +- (void)registerSimulatorShortcutWithKey:(NSString *)key + modifiers:(UIKeyModifierFlags)modifiers + action:(dispatch_block_t)action + description:(NSString *)description { + // Implementation } @end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Classes/ObjectExplorers/FLEXRecommendation.h b/Classes/ObjectExplorers/FLEXRecommendation.h new file mode 100644 index 0000000000..e5f89825fd --- /dev/null +++ b/Classes/ObjectExplorers/FLEXRecommendation.h @@ -0,0 +1,25 @@ +// +// FLEXRecommendation.h +// FLEX +// +// Created by [Your Team] on 2025-06-26. +// Copyright © 2020 FLEX Team. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLEXRecommendation : NSObject + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *subtitle; +@property (nonatomic, strong) id relevantObject; +@property (nonatomic, copy) NSString *action; ++ (instancetype)createWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + relevantObject:(id)object + action:(NSString *)action; +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Classes/ObjectExplorers/FLEXRecommendation.m b/Classes/ObjectExplorers/FLEXRecommendation.m new file mode 100644 index 0000000000..e0aa054f35 --- /dev/null +++ b/Classes/ObjectExplorers/FLEXRecommendation.m @@ -0,0 +1,25 @@ +// +// FLEXRecommendation.m +// FLEX +// +// Created by [Your Team] on 2025-06-26. +// Copyright © 2020 FLEX Team. All rights reserved. +// + +#import "FLEXRecommendation.h" + +@implementation FLEXRecommendation + ++ (instancetype)createWithTitle:(NSString *)title + subtitle:(NSString *)subtitle + relevantObject:(id)object + action:(NSString *)action { + FLEXRecommendation *rec = [[FLEXRecommendation alloc] init]; + rec.title = title; + rec.subtitle = subtitle; + rec.relevantObject = object; + rec.action = action; + return rec; +} + +@end \ No newline at end of file diff --git a/Classes/Toolbar/FLEXExplorerToolbar.m b/Classes/Toolbar/FLEXExplorerToolbar.m index b9050a6e0f..42579b780b 100644 --- a/Classes/Toolbar/FLEXExplorerToolbar.m +++ b/Classes/Toolbar/FLEXExplorerToolbar.m @@ -52,11 +52,12 @@ - (id)initWithFrame:(CGRect)frame { self.backgroundGlassView = [[UIVisualEffectView alloc] initWithEffect:glassEffect]; self.backgroundGlassView.clipsToBounds = YES; self.backgroundGlassView.layer.cornerRadius = 16; + self.backgroundGlassView.backgroundColor = FLEXColor.toolbarBackgroundColor; [self addSubview:self.backgroundGlassView]; self.backgroundView = self.backgroundGlassView; } else { self.backgroundView = [UIView new]; - self.backgroundView.backgroundColor = [FLEXColor secondaryBackgroundColorWithAlpha:0.95]; + self.backgroundView.backgroundColor = FLEXColor.toolbarBackgroundColor; [self addSubview:self.backgroundView]; } @@ -64,7 +65,7 @@ - (id)initWithFrame:(CGRect)frame { self.dragHandle = [UIView new]; self.dragHandle.backgroundColor = UIColor.clearColor; self.dragHandleImageView = [[UIImageView alloc] initWithImage:FLEXResources.dragHandle]; - self.dragHandleImageView.tintColor = [FLEXColor.iconColor colorWithAlphaComponent:0.666]; + self.dragHandleImageView.tintColor = FLEXColor.toolbarDragHandleColor; [self.dragHandle addSubview:self.dragHandleImageView]; [self addSubview:self.dragHandle]; @@ -93,12 +94,13 @@ - (id)initWithFrame:(CGRect)frame { self.descriptionGlassView = [[UIVisualEffectView alloc] initWithEffect:descGlassEffect]; self.descriptionGlassView.clipsToBounds = YES; self.descriptionGlassView.layer.cornerRadius = [[self class] descriptionContainerHeight] / 2.0; + self.descriptionGlassView.backgroundColor = FLEXColor.toolbarDescriptionBackgroundColor; self.descriptionGlassView.hidden = YES; [self addSubview:self.descriptionGlassView]; self.selectedViewDescriptionContainer = self.descriptionGlassView; } else { self.selectedViewDescriptionContainer = [UIView new]; - self.selectedViewDescriptionContainer.backgroundColor = [FLEXColor tertiaryBackgroundColorWithAlpha:0.95]; + self.selectedViewDescriptionContainer.backgroundColor = FLEXColor.toolbarDescriptionBackgroundColor; self.selectedViewDescriptionContainer.hidden = YES; [self addSubview:self.selectedViewDescriptionContainer]; } @@ -117,6 +119,7 @@ - (id)initWithFrame:(CGRect)frame { self.selectedViewDescriptionLabel = [UILabel new]; self.selectedViewDescriptionLabel.backgroundColor = UIColor.clearColor; + self.selectedViewDescriptionLabel.textColor = FLEXColor.toolbarTextColor; self.selectedViewDescriptionLabel.font = [[self class] descriptionLabelFont]; [self.selectedViewDescriptionSafeAreaContainer addSubview:self.selectedViewDescriptionLabel]; diff --git a/Classes/Toolbar/FLEXExplorerToolbarItem.m b/Classes/Toolbar/FLEXExplorerToolbarItem.m index b704d283a4..03f4d8d812 100644 --- a/Classes/Toolbar/FLEXExplorerToolbarItem.m +++ b/Classes/Toolbar/FLEXExplorerToolbarItem.m @@ -39,13 +39,13 @@ + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:( toolbarItem.sibling = backupItem; toolbarItem.title = title; toolbarItem.image = image; - toolbarItem.tintColor = FLEXColor.iconColor; + toolbarItem.tintColor = FLEXColor.toolbarTextColor; toolbarItem.backgroundColor = self.defaultBackgroundColor; toolbarItem.titleLabel.font = [UIFont systemFontOfSize:12.0]; [toolbarItem setTitle:title forState:UIControlStateNormal]; [toolbarItem setImage:image forState:UIControlStateNormal]; - [toolbarItem setTitleColor:FLEXColor.primaryTextColor forState:UIControlStateNormal]; - [toolbarItem setTitleColor:FLEXColor.deemphasizedTextColor forState:UIControlStateDisabled]; + [toolbarItem setTitleColor:FLEXColor.toolbarTextColor forState:UIControlStateNormal]; + [toolbarItem setTitleColor:FLEXColor.toolbarDisabledTextColor forState:UIControlStateDisabled]; return toolbarItem; } diff --git a/Classes/Utility/FLEXColor.h b/Classes/Utility/FLEXColor.h index e472ba4ba1..1d19d392ec 100644 --- a/Classes/Utility/FLEXColor.h +++ b/Classes/Utility/FLEXColor.h @@ -37,6 +37,11 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, class) UIColor *scrollViewBackgroundColor; @property (readonly, class) UIColor *iconColor; @property (readonly, class) UIColor *borderColor; +@property (readonly, class) UIColor *toolbarBackgroundColor; +@property (readonly, class) UIColor *toolbarDescriptionBackgroundColor; +@property (readonly, class) UIColor *toolbarTextColor; +@property (readonly, class) UIColor *toolbarDisabledTextColor; +@property (readonly, class) UIColor *toolbarDragHandleColor; @property (readonly, class) UIColor *toolbarItemHighlightedColor; @property (readonly, class) UIColor *toolbarItemSelectedColor; @property (readonly, class) UIColor *hairlineColor; diff --git a/Classes/Utility/FLEXColor.m b/Classes/Utility/FLEXColor.m index 95a995bfcd..a30ba58eea 100644 --- a/Classes/Utility/FLEXColor.m +++ b/Classes/Utility/FLEXColor.m @@ -111,18 +111,62 @@ + (UIColor *)borderColor { return [self primaryBackgroundColor]; } ++ (UIColor *)toolbarBackgroundColor { + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traits) { + if (traits.userInterfaceStyle == UIUserInterfaceStyleDark) { + return [UIColor colorWithWhite:1.0 alpha:0.82]; + } + + return [UIColor colorWithWhite:0.0 alpha:0.78]; + }]; + } + + return [UIColor colorWithWhite:0.0 alpha:0.78]; +} + ++ (UIColor *)toolbarDescriptionBackgroundColor { + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traits) { + if (traits.userInterfaceStyle == UIUserInterfaceStyleDark) { + return [UIColor colorWithWhite:1.0 alpha:0.90]; + } + + return [UIColor colorWithWhite:0.0 alpha:0.86]; + }]; + } + + return [UIColor colorWithWhite:0.0 alpha:0.86]; +} + ++ (UIColor *)toolbarTextColor { + if (@available(iOS 13.0, *)) { + return [UIColor colorWithDynamicProvider:^UIColor *(UITraitCollection *traits) { + if (traits.userInterfaceStyle == UIUserInterfaceStyleDark) { + return UIColor.blackColor; + } + + return UIColor.whiteColor; + }]; + } + + return UIColor.whiteColor; +} + ++ (UIColor *)toolbarDisabledTextColor { + return [[self toolbarTextColor] colorWithAlphaComponent:0.45]; +} + ++ (UIColor *)toolbarDragHandleColor { + return [[self toolbarTextColor] colorWithAlphaComponent:0.72]; +} + + (UIColor *)toolbarItemHighlightedColor { - return FLEXDynamicColor( - quaternaryLabelColor, - colorWithHue:2.0/3.0 saturation:0.1 brightness:0.25 alpha:0.6 - ); + return [[self toolbarTextColor] colorWithAlphaComponent:0.18]; } + (UIColor *)toolbarItemSelectedColor { - return FLEXDynamicColor( - secondaryLabelColor, - colorWithHue:2.0/3.0 saturation:0.1 brightness:0.25 alpha:0.68 - ); + return [[self toolbarTextColor] colorWithAlphaComponent:0.28]; } + (UIColor *)hairlineColor { diff --git a/Classes/Utility/FLEXHeapEnumerator.m b/Classes/Utility/FLEXHeapEnumerator.m index 5707160656..910c447a25 100644 --- a/Classes/Utility/FLEXHeapEnumerator.m +++ b/Classes/Utility/FLEXHeapEnumerator.m @@ -208,6 +208,10 @@ + (FLEXHeapSnapshot *)generateHeapSnapshot { // Enumerate all objects on the heap to build the counts of instances for each class [FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id object, __unsafe_unretained Class cls) { + if (!cls) { + return; + } + NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue( mutableCountsForClasses, (__bridge const void *)cls ); @@ -222,8 +226,13 @@ + (FLEXHeapSnapshot *)generateHeapSnapshot { NSMutableDictionary *sizesForClassNames = [NSMutableDictionary new]; for (unsigned int i = 0; i < classCount; i++) { Class class = classes[i]; + const char *classNameCString = class_getName(class); + if (!classNameCString) { + continue; + } + NSUInteger instanceCount = (NSUInteger)CFDictionaryGetValue(mutableCountsForClasses, (__bridge const void *)(class)); - NSString *className = @(class_getName(class)); + NSString *className = @(classNameCString); if (instanceCount > 0) { countsForClassNames[className] = @(instanceCount); diff --git a/FLEX.xcodeproj/project.pbxproj b/FLEX.xcodeproj/project.pbxproj index 4a779e9c87..e0ec87499c 100644 --- a/FLEX.xcodeproj/project.pbxproj +++ b/FLEX.xcodeproj/project.pbxproj @@ -74,7 +74,7 @@ 3A4C95101B5B21410088C3F2 /* FLEXMethodCallingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C948B1B5B21410088C3F2 /* FLEXMethodCallingViewController.m */; }; 3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C949F1B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3A4C95231B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A01B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.m */; }; - 3A4C95241B5B21410088C3F2 /* FLEXFileBrowserController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3A4C95241B5B21410088C3F2 /* FLEXFileBrowserController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A11B5B21410088C3F2 /* FLEXFileBrowserController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A4C95251B5B21410088C3F2 /* FLEXFileBrowserController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A21B5B21410088C3F2 /* FLEXFileBrowserController.m */; }; 3A4C95261B5B21410088C3F2 /* FLEXGlobalsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94A31B5B21410088C3F2 /* FLEXGlobalsViewController.h */; }; 3A4C95271B5B21410088C3F2 /* FLEXGlobalsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94A41B5B21410088C3F2 /* FLEXGlobalsViewController.m */; }; @@ -2099,7 +2099,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; @@ -2154,7 +2154,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; @@ -2187,7 +2187,7 @@ ); INFOPLIST_FILE = Classes/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2223,7 +2223,7 @@ ); INFOPLIST_FILE = Classes/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Tweak/FLEXLoader.plist b/Tweak/FLEXLoader.plist new file mode 100644 index 0000000000..52fa316847 --- /dev/null +++ b/Tweak/FLEXLoader.plist @@ -0,0 +1,7 @@ +{ + Filter = { + Bundles = ( + "com.apple.UIKit" + ); + }; +} diff --git a/Tweak/Makefile b/Tweak/Makefile new file mode 100644 index 0000000000..53c15c70bf --- /dev/null +++ b/Tweak/Makefile @@ -0,0 +1,15 @@ +ARCHS = arm64 arm64e +TARGET := iphone:clang:latest:14.0 +INSTALL_TARGET_PROCESSES = SpringBoard + +include $(THEOS)/makefiles/common.mk + +TWEAK_NAME = FLEXLoader + +FLEXLoader_FILES = Tweak.xm $(shell find ../Classes -type f \( -name '*.m' -o -name '*.mm' -o -name '*.c' \)) +FLEXLoader_CFLAGS = -fobjc-arc -Wno-error -Wno-unused-but-set-variable -Wno-deprecated-declarations -Wno-unsupported-availability-guard $(shell find ../Classes -type d -exec printf ' -I%s' {} \;) +FLEXLoader_CXXFLAGS = -std=gnu++11 +FLEXLoader_FRAMEWORKS = UIKit Foundation CoreGraphics ImageIO QuartzCore WebKit Security SceneKit +FLEXLoader_LIBRARIES = sqlite3 z c++ + +include $(THEOS_MAKE_PATH)/tweak.mk diff --git a/Tweak/Tweak.xm b/Tweak/Tweak.xm new file mode 100644 index 0000000000..2442a4ffd1 --- /dev/null +++ b/Tweak/Tweak.xm @@ -0,0 +1,42 @@ +#import +#import "FLEXManager.h" + +static BOOL FLEXLoaderDidShowExplorer = NO; + +static void FLEXLoaderShowExplorer(void) { + if (FLEXLoaderDidShowExplorer) { + return; + } + + FLEXLoaderDidShowExplorer = YES; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [[FLEXManager sharedManager] showExplorer]; + }); +} + +%ctor { + if (NSProcessInfo.processInfo.environment[@"FLEX_LOADER_DISABLED"]) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + UIApplication *application = UIApplication.sharedApplication; + if (application.applicationState != UIApplicationStateBackground) { + FLEXLoaderShowExplorer(); + } + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(__unused NSNotification *notification) { + FLEXLoaderShowExplorer(); + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(__unused NSNotification *notification) { + FLEXLoaderShowExplorer(); + }]; + }); +} diff --git a/Tweak/control b/Tweak/control new file mode 100644 index 0000000000..d1801f4d4e --- /dev/null +++ b/Tweak/control @@ -0,0 +1,9 @@ +Package: com.flextool.flexloader +Name: FLEX Loader +Version: 1.0.0 +Architecture: iphoneos-arm +Description: Loads FLEX automatically into UIKit applications. +Maintainer: FLEX Team +Author: FLEX Team +Section: Tweaks +Depends: mobilesubstrate