ソースを参照

try to commit tvOS changes again

Kevin Bradley 5 年 前
コミット
c2706b9dfc
共有65 個のファイルを変更した709 個の追加79 個の削除を含む
  1. 2 2
      Classes/Core/Controllers/FLEXFilteringTableViewController.m
  2. 18 2
      Classes/Core/Controllers/FLEXNavigationController.m
  3. 28 8
      Classes/Core/Controllers/FLEXTableViewController.m
  4. 2 0
      Classes/Core/FLEXTableViewSection.m
  5. 8 0
      Classes/Core/Views/FLEXTableView.m
  6. 9 1
      Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m
  7. 9 3
      Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m
  8. 4 2
      Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m
  9. 6 1
      Classes/Editing/FLEXFieldEditorViewController.m
  10. 2 1
      Classes/Editing/FLEXVariableEditorViewController.m
  11. 10 2
      Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m
  12. 17 10
      Classes/ExplorerInterface/FLEXExplorerViewController.m
  13. 3 0
      Classes/ExplorerInterface/FLEXWindowManagerController.m
  14. 10 0
      Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m
  15. 4 0
      Classes/GlobalStateExplorers/DatabaseBrowser/FLEXMultiColumnTableView.m
  16. 2 0
      Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m
  17. 4 0
      Classes/GlobalStateExplorers/FLEXAddressExplorerCoordinator.m
  18. 4 2
      Classes/GlobalStateExplorers/FLEXLiveObjectsController.m
  19. 2 0
      Classes/GlobalStateExplorers/FLEXWebViewController.m
  20. 14 10
      Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.m
  21. 3 2
      Classes/GlobalStateExplorers/Globals/FLEXGlobalsViewController.m
  22. 6 0
      Classes/GlobalStateExplorers/Keychain/FLEXKeychainViewController.m
  23. 4 0
      Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKBToolbarButton.m
  24. 4 0
      Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyPathSearchController.m
  25. 2 1
      Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyboardToolbar.m
  26. 2 0
      Classes/GlobalStateExplorers/RuntimeBrowser/FLEXObjcRuntimeViewController.m
  27. 2 0
      Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogCell.m
  28. 7 4
      Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogViewController.m
  29. 20 0
      Classes/Manager/FLEXManager.m
  30. 6 3
      Classes/Network/FLEXNetworkMITMViewController.m
  31. 19 5
      Classes/Network/FLEXNetworkSettingsController.m
  32. 8 2
      Classes/Network/FLEXNetworkTransactionDetailController.m
  33. 12 4
      Classes/ObjectExplorers/FLEXObjectExplorerFactory.m
  34. 16 0
      Classes/ObjectExplorers/FLEXObjectExplorerViewController.m
  35. 4 0
      Classes/ObjectExplorers/Sections/FLEXDefaultsContentSection.m
  36. 3 0
      Classes/ObjectExplorers/Sections/FLEXMetadataSection.m
  37. 5 1
      Classes/ObjectExplorers/Sections/Shortcuts/FLEXImageShortcuts.m
  38. 3 1
      Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.m
  39. 2 0
      Classes/Toolbar/FLEXExplorerToolbarItem.m
  40. 16 0
      Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.m
  41. 4 2
      Classes/Utility/Categories/UIFont+FLEX.m
  42. 6 0
      Classes/Utility/FLEXColor.h
  43. 16 0
      Classes/Utility/FLEXColor.m
  44. 1 1
      Classes/Utility/FLEXResources.m
  45. 3 0
      Classes/Utility/FLEXUtility.m
  46. 2 1
      Classes/Utility/Keyboard/FLEXKeyboardHelpViewController.m
  47. 1 0
      Classes/ViewHierarchy/FLEXHierarchyViewController.h
  48. 6 1
      Classes/ViewHierarchy/FLEXHierarchyViewController.m
  49. 6 2
      Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.h
  50. 10 2
      Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.m
  51. 7 2
      Classes/ViewHierarchy/SnapshotExplorer/FHSViewController.m
  52. 5 1
      Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewCell.m
  53. 26 0
      Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewController.m
  54. 22 0
      FLEX.xcodeproj/project.pbxproj
  55. 3 0
      README.md
  56. 59 0
      fakes.h
  57. 66 0
      fakes.m
  58. 1 0
      flexinjected/FLEXInjected.plist
  59. 12 0
      flexinjected/Makefile
  60. 78 0
      flexinjected/Tweak.x
  61. 1 0
      flexinjected/Tweak.x.jsont
  62. 3 0
      flexinjected/compile_commands.json
  63. 9 0
      flexinjected/layout/DEBIAN/control
  64. 60 0
      flexinjected/layout/Library/PreferenceLoader/Preferences/FLEXInjected.plist
  65. BIN
      flexinjected/layout/Library/PreferenceLoader/Preferences/flex.png

+ 2 - 2
Classes/Core/Controllers/FLEXFilteringTableViewController.m

@@ -188,7 +188,7 @@
 }
 
 #if FLEX_AT_LEAST_IOS13_SDK
-
+#if !TARGET_OS_TV
 - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
     FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section];
     NSString *title = [section menuTitleForRow:indexPath.row];
@@ -206,7 +206,7 @@
     
     return nil;
 }
-
+#endif
 #endif
 
 @end

+ 18 - 2
Classes/Core/Controllers/FLEXNavigationController.m

@@ -9,6 +9,7 @@
 #import "FLEXNavigationController.h"
 #import "FLEXExplorerViewController.h"
 #import "FLEXTabList.h"
+#import "FLEXColor.h"
 
 @interface UINavigationController (Private) <UIGestureRecognizerDelegate>
 - (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender;
@@ -45,8 +46,10 @@
     if (@available(iOS 13, *)) {
         switch (self.modalPresentationStyle) {
             case UIModalPresentationAutomatic:
+                #if !TARGET_OS_TV
             case UIModalPresentationPageSheet:
             case UIModalPresentationFormSheet:
+                #endif
                 break;
                 
             default:
@@ -82,6 +85,15 @@
             self.waitingToAddTab = NO;
         }
     }
+    //the timing is janky here but its better than nothing for now.
+#if TARGET_OS_TV
+    if ([[self view] darkMode]){
+        self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
+    } else {
+        self.view.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
+    }
+#endif
+    
 }
 
 - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
@@ -146,12 +158,14 @@
      
 - (void)handleNavigationBarTap:(UIGestureRecognizer *)sender {
     if (sender.state == UIGestureRecognizerStateRecognized) {
+            #if !TARGET_OS_TV
         if (self.toolbarHidden) {
             [self setToolbarHidden:NO animated:YES];
         }
+        #endif
     }
 }
-
+#if !TARGET_OS_TV
 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
     if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) {
         return YES;
@@ -159,8 +173,9 @@
     
     return NO;
 }
-
+#endif
 - (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
+    #if !TARGET_OS_TV
     if (sender.state == UIGestureRecognizerStateRecognized) {
         BOOL show = self.topViewController.toolbarItems.count;
         CGFloat yTranslation = [sender translationInView:self.view].y;
@@ -173,6 +188,7 @@
             [self setToolbarHidden:YES animated:YES];
         }
     }
+    #endif
 }
 
 @end

+ 28 - 8
Classes/Core/Controllers/FLEXTableViewController.m

@@ -52,7 +52,11 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
 - (id)init {
 #if FLEX_AT_LEAST_IOS13_SDK
     if (@available(iOS 13.0, *)) {
+        #if !TARGET_OS_TV
         self = [self initWithStyle:UITableViewStyleInsetGrouped];
+        #else
+        self = [self initWithStyle:UITableViewStyleGrouped];
+        #endif
     } else {
         self = [self initWithStyle:UITableViewStyleGrouped];
     }
@@ -99,7 +103,9 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
         self.searchController.searchBar.placeholder = @"Filter";
         self.searchController.searchResultsUpdater = (id)self;
         self.searchController.delegate = (id)self;
+        #if !TARGET_OS_TV
         self.searchController.dimsBackgroundDuringPresentation = NO;
+        #endif
         self.searchController.hidesNavigationBarDuringPresentation = NO;
         /// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target
         self.searchController.searchBar.delegate = self;
@@ -111,11 +117,14 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
             self.searchController.automaticallyShowsScopeBar = NO;
         }
         #endif
-        
+        #if !TARGET_OS_TV
         [self addSearchController:self.searchController];
+        #endif
     } else {
         // Search already shown and just set to NO, so remove it
+        #if !TARGET_OS_TV
         [self removeSearchController:self.searchController];
+        #endif
     }
 }
 
@@ -209,9 +218,11 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
 }
 
 - (void)disableToolbar {
+    #if !TARGET_OS_TV
     self.navigationController.toolbarHidden = YES;
     self.navigationController.hidesBarsOnSwipe = NO;
     self.toolbarItems = nil;
+    #endif
 }
 
 
@@ -241,9 +252,10 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
     
     // Toolbar
+    #if !TARGET_OS_TV
     self.navigationController.toolbarHidden = NO;
     self.navigationController.hidesBarsOnSwipe = YES;
-
+    #endif
     // On iOS 13, the root view controller shows it's search bar no matter what.
     // Turning this off avoids some weird flash the navigation bar does when we
     // toggle navigationItem.hidesSearchBarWhenScrolling on and off. The flash
@@ -262,7 +274,9 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     // When going back, make the search bar reappear instead of hiding
     if (@available(iOS 11.0, *)) {
         if ((self.pinSearchBar || self.showSearchBarInitially) && !self.didInitiallyRevealSearchBar) {
+            #if !TARGET_OS_TV
             self.navigationItem.hidesSearchBarWhenScrolling = NO;
+            #endif
         }
     }
 
@@ -280,7 +294,9 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
             // the search bar appear initially results in a bugged search bar that
             // becomes transparent and floats over the screen as you scroll
             [UIView animateWithDuration:0.2 animations:^{
+                #if !TARGET_OS_TV
                 self.navigationItem.hidesSearchBarWhenScrolling = YES;
+                #endif
                 [self.navigationController.view setNeedsLayout];
                 [self.navigationController.view layoutIfNeeded];
             }];
@@ -313,7 +329,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     if (!self.isViewLoaded) {
         return;
     }
-    
+    #if !TARGET_OS_TV
     self.toolbarItems = @[
         self.leftmostToolbarItem,
         UIBarButtonItem.flex_flexibleSpace,
@@ -331,7 +347,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
         // This does not work for anything but fixed spaces for some reason
         // item.width = 60;
     }
-    
+    #endif
     // Disable tabs entirely when not presented by FLEXExplorerViewController
     UIViewController *presenter = self.navigationController.presentingViewController;
     if (![presenter isKindOfClass:[FLEXExplorerViewController class]]) {
@@ -449,15 +465,17 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
         self.tableView.tableHeaderView = nil;
     } else {
         if (self.showsSearchBar) {
+            #if !TARGET_OS_TV
             [self removeSearchController:self.searchController];
             [self addSearchController:self.searchController];
+            #endif
         } else {
             self.tableView.tableHeaderView = nil;
             _tableHeaderViewContainer = nil;
         }
     }
 }
-
+#if !TARGET_OS_TV
 - (void)addSearchController:(UISearchController *)controller {
     if (@available(iOS 11.0, *)) {
         self.navigationItem.searchController = controller;
@@ -498,7 +516,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
         }
     }
 }
-
+#endif
 - (UIView *)tableHeaderViewContainer {
     if (!_tableHeaderViewContainer) {
         _tableHeaderViewContainer = [UIView new];
@@ -548,7 +566,7 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
     }
 }
 
-
+#if !TARGET_OS_TV
 #pragma mark UISearchControllerDelegate
 
 - (void)willPresentSearchController:(UISearchController *)searchController {
@@ -572,16 +590,18 @@ CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
 - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
     [self updateSearchResultsForSearchController:self.searchController];
 }
-
+#endif
 
 #pragma mark Table View
 
 /// Not having a title in the first section looks weird with a rounded-corner table view style
 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
     if (@available(iOS 13, *)) {
+        #if !TARGET_OS_TV
         if (self.style == UITableViewStyleInsetGrouped) {
             return @" ";
         }
+        #endif
     }
 
     return nil; // For plain/gropued style

+ 2 - 0
Classes/Core/FLEXTableViewSection.m

@@ -101,7 +101,9 @@
                 image:copyIcon
                 identifier:nil
                 handler:^(__kindof UIAction *action) {
+                #if !TARGET_OS_TV
                     UIPasteboard.generalPasteboard.string = value;
+                #endif
                 }
             ];
             if (!value.length) {

+ 8 - 0
Classes/Core/Views/FLEXTableView.m

@@ -32,7 +32,11 @@ FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell = @"kFLEXCodeFontCell";
 + (instancetype)flexDefaultTableView {
 #if FLEX_AT_LEAST_IOS13_SDK
     if (@available(iOS 13.0, *)) {
+#if !TARGET_OS_TV
         return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
+#else
+        return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+#endif
     } else {
         return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
     }
@@ -46,7 +50,11 @@ FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell = @"kFLEXCodeFontCell";
 + (id)groupedTableView {
 #if FLEX_AT_LEAST_IOS13_SDK
     if (@available(iOS 13.0, *)) {
+#if !TARGET_OS_TV
         return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
+#else
+        return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+#endif
     } else {
         return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
     }

+ 9 - 1
Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m

@@ -9,12 +9,16 @@
 #import "FLEXArgumentInputColorView.h"
 #import "FLEXUtility.h"
 #import "FLEXRuntimeUtility.h"
-
+#import "fakes.h"
 @protocol FLEXColorComponentInputViewDelegate;
 
 @interface FLEXColorComponentInputView : UIView
 
+#if !TARGET_OS_TV
 @property (nonatomic) UISlider *slider;
+#else
+@property (nonatomic) UIFakeSlider *slider;
+#endif
 @property (nonatomic) UILabel *valueLabel;
 
 @property (nonatomic, weak) id <FLEXColorComponentInputViewDelegate> delegate;
@@ -33,7 +37,11 @@
 - (id)initWithFrame:(CGRect)frame {
     self = [super initWithFrame:frame];
     if (self) {
+#if !TARGET_OS_TV
         self.slider = [UISlider new];
+#else
+        self.slider = [UIFakeSlider new];
+#endif
         [self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
         [self addSubview:self.slider];
         

+ 9 - 3
Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m

@@ -7,11 +7,13 @@
 //
 
 #import "FLEXArgumentInputSwitchView.h"
-
+#import "fakes.h"
 @interface FLEXArgumentInputSwitchView ()
-
+#if !TARGET_OS_TV
 @property (nonatomic) UISwitch *inputSwitch;
-
+#else
+@property (nonatomic) UIFakeSwitch *inputSwitch;
+#endif
 @end
 
 @implementation FLEXArgumentInputSwitchView
@@ -19,7 +21,11 @@
 - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
     self = [super initWithArgumentTypeEncoding:typeEncoding];
     if (self) {
+#if !TARGET_OS_TV
         self.inputSwitch = [UISwitch new];
+#else
+        self.inputSwitch = [UIFakeSwitch new];
+#endif
         [self.inputSwitch addTarget:self action:@selector(switchValueDidChange:) forControlEvents:UIControlEventValueChanged];
         [self.inputSwitch sizeToFit];
         [self addSubview:self.inputSwitch];

+ 4 - 2
Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m

@@ -31,7 +31,9 @@
         self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
         self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo;
         self.inputTextView.delegate = self;
+        #if !TARGET_OS_TV
         self.inputTextView.inputAccessoryView = [self createToolBar];
+        #endif
         if (@available(iOS 11, *)) {
             [self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"];
         } else {
@@ -52,7 +54,7 @@
 }
 
 #pragma mark - Private
-
+#if !TARGET_OS_TV
 - (UIToolbar *)createToolBar {
     UIToolbar *toolBar = [UIToolbar new];
     [toolBar sizeToFit];
@@ -71,7 +73,7 @@
     toolBar.items = @[spaceItem, pasteItem, doneItem];
     return toolBar;
 }
-
+#endif
 - (void)setInputPlaceholderText:(NSString *)placeholder {
     self.placeholderLabel.text = placeholder;
     if (placeholder.length) {

+ 6 - 1
Classes/Editing/FLEXFieldEditorViewController.m

@@ -14,6 +14,7 @@
 #import "FLEXUtility.h"
 #import "FLEXColor.h"
 #import "UIBarButtonItem+FLEX.h"
+#import <TargetConditionals.h>
 
 @interface FLEXFieldEditorViewController () <FLEXArgumentInputViewDelegate>
 
@@ -63,9 +64,11 @@
         target:self
         action:@selector(getterButtonPressed:)
     ];
+    #if !TARGET_OS_TV
     self.toolbarItems = @[
         UIBarButtonItem.flex_flexibleSpace, self.getterButton, self.actionButton
     ];
+    #endif
 
     // Configure input view
     self.fieldEditorView.fieldDescription = self.fieldDescription;
@@ -78,10 +81,12 @@
     if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
         self.actionButton.enabled = NO;
         self.actionButton.title = @"Flip the switch to call the setter";
-        // Put getter button before setter button 
+        // Put getter button before setter button
+        #if !TARGET_OS_TV
         self.toolbarItems = @[
             UIBarButtonItem.flex_flexibleSpace, self.actionButton, self.getterButton
         ];
+        #endif
     }
 }
 

+ 2 - 1
Classes/Editing/FLEXVariableEditorViewController.m

@@ -100,9 +100,10 @@
         target:self
         action:@selector(actionButtonPressed:)
     ];
-    
+    #if !TARGET_OS_TV
     self.navigationController.toolbarHidden = NO;
     self.toolbarItems = @[UIBarButtonItem.flex_flexibleSpace, self.actionButton];
+    #endif
 }
 
 - (void)viewWillLayoutSubviews {

+ 10 - 2
Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m

@@ -32,8 +32,9 @@
 
 - (void)viewDidLoad {
     [super viewDidLoad];
-    
+    #if !TARGET_OS_TV
     self.navigationController.hidesBarsOnSwipe = NO;
+    #endif
     self.tableView.allowsMultipleSelectionDuringEditing = YES;
     
     [self reloadData];
@@ -56,6 +57,7 @@
 
 - (void)setupDefaultBarItems {
     self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
+    #if !TARGET_OS_TV
     self.toolbarItems = @[
         UIBarButtonItem.flex_flexibleSpace,
         FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
@@ -63,18 +65,20 @@
     
     // Disable editing if no bookmarks available
     self.toolbarItems.lastObject.enabled = self.bookmarks.count > 0;
+    #endif
 }
 
 - (void)setupEditingBarItems {
     self.navigationItem.rightBarButtonItem = nil;
+        #if !TARGET_OS_TV
     self.toolbarItems = @[
         [UIBarButtonItem flex_itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)],
         UIBarButtonItem.flex_flexibleSpace,
         // We use a non-system done item because we change its title dynamically
         [UIBarButtonItem flex_doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
     ];
-    
     self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
+    #endif
 }
 
 - (FLEXExplorerViewController *)corePresenter {
@@ -198,8 +202,10 @@
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     if (self.editing) {
         // Case: editing with multi-select
+        #if !TARGET_OS_TV
         self.toolbarItems.lastObject.title = @"Remove Selected";
         self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
+        #endif
     } else {
         // Case: selected a bookmark
         [self dismissAnimated:self.bookmarks[indexPath.row]];
@@ -210,8 +216,10 @@
     NSParameterAssert(self.editing);
     
     if (tableView.indexPathsForSelectedRows.count == 0) {
+        #if !TARGET_OS_TV
         self.toolbarItems.lastObject.title = @"Done";
         self.toolbarItems.lastObject.tintColor = self.view.tintColor;
+        #endif
     }
 }
 

+ 17 - 10
Classes/ExplorerInterface/FLEXExplorerViewController.m

@@ -69,10 +69,10 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 
 /// All views that we're KVOing. Used to help us clean up properly.
 @property (nonatomic) NSMutableSet<UIView *> *observedViews;
-
+#if !TARGET_OS_TV
 /// Used to preserve the target app's UIMenuController items.
 @property (nonatomic) NSArray<UIMenuItem *> *appMenuItems;
-
+#endif
 @end
 
 @implementation FLEXExplorerViewController
@@ -390,7 +390,11 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     };
     
     [actionsToItems enumerateKeysAndObjectsUsingBlock:^(NSString *sel, FLEXExplorerToolbarItem *item, BOOL *stop) {
-        [item addTarget:self action:NSSelectorFromString(sel) forControlEvents:UIControlEventTouchUpInside];
+        #if !TARGET_OS_TV
+            [item addTarget:self action:NSSelectorFromString(sel) forControlEvents:UIControlEventTouchUpInside];
+        #else
+            [item addTarget:self action:NSSelectorFromString(sel) forControlEvents:UIControlEventPrimaryActionTriggered];
+        #endif
     }];
 }
 
@@ -560,8 +564,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 - (void)handleToolbarShowTabsGesture:(UILongPressGestureRecognizer *)sender {
     if (sender.state == UIGestureRecognizerStateBegan) {
         // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+        #if !TARGET_OS_TV
         self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
-        
+        #endif
         // Don't use FLEXNavigationController because the tab viewer itself is not a tab
         [super presentViewController:[[UINavigationController alloc]
             initWithRootViewController:[FLEXTabsViewController new]
@@ -572,8 +577,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 - (void)handleToolbarWindowManagerGesture:(UILongPressGestureRecognizer *)sender {
     if (sender.state == UIGestureRecognizerStateBegan) {
         // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+        #if !TARGET_OS_TV
         self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
-        
+        #endif
         [super presentViewController:[FLEXNavigationController
             withRootViewController:[FLEXWindowManagerController new]
         ] animated:YES completion:nil];
@@ -583,8 +589,9 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 - (void)handleToolbarShowViewControllersGesture:(UILongPressGestureRecognizer *)sender {
     if (sender.state == UIGestureRecognizerStateBegan && self.viewsAtTapPoint.count) {
         // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+        #if !TARGET_OS_TV
         self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
-        
+        #endif
         UIViewController *list = [FLEXViewControllersViewController
             controllersForViews:self.viewsAtTapPoint
         ];
@@ -887,12 +894,12 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
     if (!@available(iOS 13, *)) {
         [self statusWindow].windowLevel = self.view.window.windowLevel + 1.0;
     }
-    
+    #if !TARGET_OS_TV
     // Back up and replace the UIMenuController items
     // Edit: no longer replacing the items, but still backing them
     // up in case we start replacing them again in the future
     self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
-    
+    #endif
     // Show the view controller
     [super presentViewController:toPresent animated:animated completion:completion];
 }
@@ -900,14 +907,14 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
 - (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion {    
     UIWindow *appWindow = self.window.previousKeyWindow;
     [appWindow makeKeyWindow];
+    #if !TARGET_OS_TV
     [appWindow.rootViewController setNeedsStatusBarAppearanceUpdate];
-    
     // Restore previous UIMenuController items
     // Back up and replace the UIMenuController items
     UIMenuController.sharedMenuController.menuItems = self.appMenuItems;
     [UIMenuController.sharedMenuController update];
     self.appMenuItems = nil;
-    
+    #endif
     // Restore the status bar window's normal window level.
     // We want it above FLEX while a modal is presented for
     // scroll to top, but below FLEX otherwise for exploration.

+ 3 - 0
Classes/ExplorerInterface/FLEXWindowManagerController.m

@@ -10,6 +10,7 @@
 #import "FLEXManager+Private.h"
 #import "FLEXUtility.h"
 #import "FLEXObjectExplorerFactory.h"
+#import <TargetConditionals.h>
 
 @interface FLEXWindowManagerController ()
 @property (nonatomic) UIWindow *keyWindow;
@@ -161,7 +162,9 @@
 
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
+    #if !TARGET_OS_TV
     cell.accessoryType = UITableViewCellAccessoryDetailButton;
+    #endif
     cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
     
     UIWindow *window = nil;

+ 10 - 0
Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m

@@ -39,7 +39,9 @@
     [super viewDidLoad];
     
     self.title = @"Open Tabs";
+    #if !TARGET_OS_TV
     self.navigationController.hidesBarsOnSwipe = NO;
+    #endif
     self.tableView.allowsMultipleSelectionDuringEditing = YES;
     
     [self reloadData:NO];
@@ -106,6 +108,7 @@
 
 - (void)setupDefaultBarItems {
     self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
+    #if !TARGET_OS_TV
     self.toolbarItems = @[
         UIBarButtonItem.flex_fixedSpace,
         UIBarButtonItem.flex_flexibleSpace,
@@ -116,10 +119,12 @@
     
     // Disable editing if no tabs available
     self.toolbarItems.lastObject.enabled = self.openTabs.count > 0;
+    #endif
 }
 
 - (void)setupEditingBarItems {
     self.navigationItem.rightBarButtonItem = nil;
+    #if !TARGET_OS_TV
     self.toolbarItems = @[
         [UIBarButtonItem flex_itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)],
         UIBarButtonItem.flex_flexibleSpace,
@@ -130,6 +135,7 @@
     ];
     
     self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
+    #endif
 }
 
 - (FLEXExplorerViewController *)corePresenter {
@@ -287,8 +293,10 @@
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     if (self.editing) {
         // Case: editing with multi-select
+        #if !TARGET_OS_TV
         self.toolbarItems.lastObject.title = @"Close Selected";
         self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
+        #endif
     } else {
         if (self.activeIndex == indexPath.row && self.corePresenter != self.presentingViewController) {
             // Case: selected the already active tab
@@ -307,8 +315,10 @@
     NSParameterAssert(self.editing);
     
     if (tableView.indexPathsForSelectedRows.count == 0) {
+        #if !TARGET_OS_TV
         self.toolbarItems.lastObject.title = @"Done";
         self.toolbarItems.lastObject.tintColor = self.view.tintColor;
+        #endif
     }
 }
 

+ 4 - 0
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXMultiColumnTableView.m

@@ -117,7 +117,9 @@ static const CGFloat kColumnMargin = 1;
     UITableView *tableView   = [UITableView new];
     tableView.delegate       = self;
     tableView.dataSource     = self;
+    #if !TARGET_OS_TV
     tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    #endif
     [tableView registerClass:[FLEXDBQueryRowCell class]
         forCellReuseIdentifier:kFLEXDBQueryRowCellReuse
     ];
@@ -133,7 +135,9 @@ static const CGFloat kColumnMargin = 1;
     UITableView *leftTableView   = [UITableView new];
     leftTableView.delegate       = self;
     leftTableView.dataSource     = self;
+    #if !TARGET_OS_TV
     leftTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    #endif
     self.leftTableView           = leftTableView;
     [self addSubview:leftTableView];
     

+ 2 - 0
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m

@@ -116,7 +116,9 @@
         NSString *message = [fields componentsJoinedByString:@"\n\n"];
         make.message(message);
         make.button(@"Copy").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             UIPasteboard.generalPasteboard.string = message;
+            #endif
         });
         make.button(@"Dismiss").cancelStyle();
     } showFrom:self];

+ 4 - 0
Classes/GlobalStateExplorers/FLEXAddressExplorerCoordinator.m

@@ -37,13 +37,17 @@
         [FLEXAlert makeAlert:^(FLEXAlert *make) {
             make.title(title).message(message);
             make.configuredTextField(^(UITextField *textField) {
+                #if !TARGET_OS_TV
                 NSString *copied = UIPasteboard.generalPasteboard.string;
+                #endif
                 textField.placeholder = @"0x00000070deadbeef";
                 // Go ahead and paste our clipboard if we have an address copied
+                #if !TARGET_OS_TV
                 if ([copied hasPrefix:@"0x"]) {
                     textField.text = copied;
                     [textField selectAll:nil];
                 }
+                #endif
             });
             make.button(@"Explore").handler(^(NSArray<NSString *> *strings) {
                 [host tryExploreAddress:strings.firstObject safely:YES];

+ 4 - 2
Classes/GlobalStateExplorers/FLEXLiveObjectsController.m

@@ -38,10 +38,10 @@ static const NSInteger kFLEXLiveObjectsSortBySizeIndex = 2;
     self.searchBarDebounceInterval = kFLEXDebounceInstant;
     self.showsCarousel = YES;
     self.carousel.items = @[@"A→Z", @"Count", @"Size"];
-    
+    #if !TARGET_OS_TV
     self.refreshControl = [UIRefreshControl new];
     [self.refreshControl addTarget:self action:@selector(refreshControlDidRefresh:) forControlEvents:UIControlEventValueChanged];
-    
+    #endif
     [self reloadTableData];
 }
 
@@ -101,7 +101,9 @@ static const NSInteger kFLEXLiveObjectsSortBySizeIndex = 2;
 
 - (void)refreshControlDidRefresh:(id)sender {
     [self reloadTableData];
+    #if !TARGET_OS_TV
     [self.refreshControl endRefreshing];
+    #endif
 }
 
 - (void)updateHeaderTitle {

+ 2 - 0
Classes/GlobalStateExplorers/FLEXWebViewController.m

@@ -73,7 +73,9 @@
 }
 
 - (void)copyButtonTapped:(id)sender {
+    #if !TARGET_OS_TV
     [UIPasteboard.generalPasteboard setString:self.originalText];
+    #endif
 }
 
 

+ 14 - 10
Classes/GlobalStateExplorers/FileBrowser/FLEXFileBrowserController.m

@@ -335,6 +335,7 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
 }
 
 - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
+    #if !TARGET_OS_TV
     UIMenuItem *rename = [[UIMenuItem alloc] initWithTitle:@"Rename" action:@selector(fileBrowserRename:)];
     UIMenuItem *delete = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(fileBrowserDelete:)];
     UIMenuItem *copyPath = [[UIMenuItem alloc] initWithTitle:@"Copy Path" action:@selector(fileBrowserCopyPath:)];
@@ -343,6 +344,9 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
     UIMenuController.sharedMenuController.menuItems = @[rename, delete, copyPath, share];
 
     return YES;
+    #else
+    return NO;
+    #endif
 }
 
 - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
@@ -359,10 +363,8 @@ typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
 }
 
 #if FLEX_AT_LEAST_IOS13_SDK
-
-- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView
-contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
-                                    point:(CGPoint)point __IOS_AVAILABLE(13.0) {
+#if !TARGET_OS_TV
+- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
     __weak __typeof__(self) weakSelf = self;
     return [UIContextMenuConfiguration configurationWithIdentifier:nil
                                                    previewProvider:nil
@@ -395,7 +397,7 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
         return [UIMenu menuWithTitle:@"Manage File" image:nil identifier:@"Manage File" options:UIMenuOptionsDisplayInline children:@[rename, delete, copyPath, share]];
     }];
 }
-
+#endif
 #endif
 
 - (void)openFileController:(NSString *)fullPath {
@@ -458,7 +460,9 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
 - (void)fileBrowserCopyPath:(UITableViewCell *)sender {
     NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
     NSString *fullPath = [self filePathAtIndexPath:indexPath];
+    #if !TARGET_OS_TV
     UIPasteboard.generalPasteboard.string = fullPath;
+    #endif
 }
 
 - (void)fileBrowserShare:(UITableViewCell *)sender {
@@ -542,20 +546,20 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
     id target = [self.nextResponder targetForAction:action withSender:sender];
     [UIApplication.sharedApplication sendAction:action to:target from:self forEvent:nil];
 }
-
-- (void)fileBrowserRename:(UIMenuController *)sender {
+//really UIMenuController but this is to silence warnings
+- (void)fileBrowserRename:(UIViewController *)sender {
     [self forwardAction:_cmd withSender:sender];
 }
 
-- (void)fileBrowserDelete:(UIMenuController *)sender {
+- (void)fileBrowserDelete:(UIViewController *)sender {
     [self forwardAction:_cmd withSender:sender];
 }
 
-- (void)fileBrowserCopyPath:(UIMenuController *)sender {
+- (void)fileBrowserCopyPath:(UIViewController *)sender {
     [self forwardAction:_cmd withSender:sender];
 }
 
-- (void)fileBrowserShare:(UIMenuController *)sender {
+- (void)fileBrowserShare:(UIViewController *)sender {
     [self forwardAction:_cmd withSender:sender];
 }
 

+ 3 - 2
Classes/GlobalStateExplorers/Globals/FLEXGlobalsViewController.m

@@ -165,8 +165,9 @@
     self.title = @"💪  FLEX";
     self.showsSearchBar = YES;
     self.searchBarDebounceInterval = kFLEXDebounceInstant;
-    self.navigationItem.backBarButtonItem = [UIBarButtonItem flex_backItemWithTitle:@"Back"];
-    
+    #if !TARGET_OS_TV
+    self.navigationItem.backBarButtonItem = [UIBarButtonItem backItemWithTitle:@"Back"];
+    #endif
     _manuallyDeselectOnAppear = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 10;
 }
 

+ 6 - 0
Classes/GlobalStateExplorers/Keychain/FLEXKeychainViewController.m

@@ -234,13 +234,19 @@
         make.message(@"\nPassword: ").message(query.password);
 
         make.button(@"Copy Service").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             [UIPasteboard.generalPasteboard flex_copy:query.service];
+            #endif
         });
         make.button(@"Copy Account").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             [UIPasteboard.generalPasteboard flex_copy:query.account];
+            #endif
         });
         make.button(@"Copy Password").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             [UIPasteboard.generalPasteboard flex_copy:query.password];
+            #endif
         });
         make.button(@"Dismiss").cancelStyle();
         

+ 4 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKBToolbarButton.m

@@ -33,7 +33,11 @@
 }
 
 + (instancetype)buttonWithTitle:(NSString *)title action:(FLEXKBToolbarAction)eventHandler {
+#if !TARGET_OS_TV
     return [self buttonWithTitle:title action:eventHandler forControlEvents:UIControlEventTouchUpInside];
+#else
+    return [self buttonWithTitle:title action:eventHandler forControlEvents:UIControlEventPrimaryActionTriggered];
+#endif
 }
 
 - (id)initWithTitle:(NSString *)title {

+ 4 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyPathSearchController.m

@@ -310,7 +310,11 @@
     ];
     
     if (self.bundlesOrClasses.count) {
+        #if !TARGET_OS_TV
         cell.accessoryType        = UITableViewCellAccessoryDetailButton;
+        #else
+        cell.accessoryType        = UITableViewCellAccessoryDisclosureIndicator;
+        #endif
         cell.textLabel.text       = self.bundlesOrClasses[indexPath.row];
         cell.detailTextLabel.text = nil;
         if (self.keyPath.classKey) {

+ 2 - 1
Classes/GlobalStateExplorers/RuntimeBrowser/FLEXKeyboardToolbar.m

@@ -108,8 +108,9 @@
         case UIKeyboardAppearanceDefault:
             #if FLEX_AT_LEAST_IOS13_SDK
             if (@available(iOS 13, *)) {
+                #if !TARGET_OS_TV
                 borderColor = UIColor.systemBackgroundColor;
-                
+                #endif
                 if (self.usingDarkMode) {
                     // style = UIBlurEffectStyleSystemThickMaterial;
                     backgroundColor = darkColor;

+ 2 - 0
Classes/GlobalStateExplorers/RuntimeBrowser/FLEXObjcRuntimeViewController.m

@@ -91,7 +91,9 @@
         make.message(path);
 
         make.button(@"Copy Path").handler(^(NSArray<NSString *> *strings) {
+#if !TARGET_OS_TV
             UIPasteboard.generalPasteboard.string = path;
+#endif
         });
         make.button(@"Dismiss").cancelStyle();
     } showFrom:self];

+ 2 - 0
Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogCell.m

@@ -26,8 +26,10 @@ NSString *const kFLEXSystemLogCellIdentifier = @"FLEXSystemLogCellIdentifier";
     
     self.logMessageLabel = [UILabel new];
     self.logMessageLabel.numberOfLines = 0;
+     #if !TARGET_OS_TV
     self.separatorInset = UIEdgeInsetsZero;
     self.selectionStyle = UITableViewCellSelectionStyleNone;
+    #endif
     [self.contentView addSubview:self.logMessageLabel];
 }
 

+ 7 - 4
Classes/GlobalStateExplorers/SystemLog/FLEXSystemLogViewController.m

@@ -111,8 +111,9 @@ static BOOL my_os_log_shim_enabled(void *addr) {
     } else {
         _logController = [FLEXASLLogController withUpdateHandler:logHandler];
     }
-
+#if !TARGET_OS_TV
     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+#endif
     self.title = @"Waiting for Logs...";
 
     // Toolbar buttons //
@@ -265,12 +266,14 @@ static BOOL my_os_log_shim_enabled(void *addr) {
 - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
     if (action == @selector(copy:)) {
         // We usually only want to copy the log message itself, not any metadata associated with it.
-        UIPasteboard.generalPasteboard.string = self.logMessages.filteredList[indexPath.row].messageText ?: @"";
+        #if !TARGET_OS_TV
+        UIPasteboard.generalPasteboard.string = self.logMessages.filteredList[indexPath.row].messageText;
+        #endif
     }
 }
 
 #if FLEX_AT_LEAST_IOS13_SDK
-
+#if !TARGET_OS_TV
 - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView
 contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
                                     point:(CGPoint)point __IOS_AVAILABLE(13.0) {
@@ -288,7 +291,7 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
         return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[copy]];
     }];
 }
-
+#endif
 #endif
 
 @end

+ 20 - 0
Classes/Manager/FLEXManager.m

@@ -57,10 +57,26 @@
     return _explorerWindow;
 }
 
+- (void)tripleTap:(UITapGestureRecognizer *)tapRecognizer {
+    NSLog(@"[FLEXInjected] triple tap!");
+    [self showExplorer];
+}
+
+
+- (void)_addTVOSGestureRecognizer:(UIViewController *)explorer {
+    UITapGestureRecognizer *tripleTaps = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tripleTap:)];
+    tripleTaps.allowedPressTypes = @[[NSNumber numberWithInteger:UIPressTypePlayPause]];
+    tripleTaps.numberOfTapsRequired = 3;
+    [explorer.view addGestureRecognizer:tripleTaps];
+}
+
 - (FLEXExplorerViewController *)explorerViewController {
     if (!_explorerViewController) {
         _explorerViewController = [FLEXExplorerViewController new];
         _explorerViewController.delegate = self;
+        #if TARGET_OS_TV
+                [self _addTVOSGestureRecognizer:_explorerViewController];
+        #endif
     }
 
     return _explorerViewController;
@@ -69,6 +85,10 @@
 - (void)showExplorer {
     UIWindow *flex = self.explorerWindow;
     flex.hidden = NO;
+    #if TARGET_OS_TV
+    FLEXWindow *exp = [self explorerWindow];
+    [exp makeKeyWindow];
+    #endif
 #if FLEX_AT_LEAST_IOS13_SDK
     if (@available(iOS 13.0, *)) {
         // Only look for a new scene if we don't have one

+ 6 - 3
Classes/Network/FLEXNetworkMITMViewController.m

@@ -64,9 +64,10 @@
         registerClass:[FLEXNetworkTransactionCell class]
         forCellReuseIdentifier:kFLEXNetworkTransactionCellIdentifier
     ];
+#if !TARGET_OS_TV
     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
     self.tableView.rowHeight = FLEXNetworkTransactionCell.preferredCellHeight;
-
+#endif
     [self registerForNotifications];
     [self updateTransactions];
 }
@@ -386,12 +387,14 @@
 - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
     if (action == @selector(copy:)) {
         NSURLRequest *request = [self transactionAtIndexPath:indexPath].request;
+        #if !TARGET_OS_TV
         UIPasteboard.generalPasteboard.string = request.URL.absoluteString ?: @"";
+        #endif
     }
 }
 
 #if FLEX_AT_LEAST_IOS13_SDK
-
+#if !TARGET_OS_TV
 - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
     NSURLRequest *request = [self transactionAtIndexPath:indexPath].request;
     return [UIContextMenuConfiguration
@@ -426,7 +429,7 @@
         }
     ];
 }
-
+#endif
 #endif
 
 - (FLEXNetworkTransaction *)transactionAtIndexPath:(NSIndexPath *)indexPath {

+ 19 - 5
Classes/Network/FLEXNetworkSettingsController.m

@@ -12,15 +12,23 @@
 #import "FLEXTableView.h"
 #import "FLEXColor.h"
 #import "NSUserDefaults+FLEX.h"
+#import "fakes.h"
+
 
 @interface FLEXNetworkSettingsController () <UIActionSheetDelegate>
 @property (nonatomic) float cacheLimitValue;
 @property (nonatomic, readonly) NSString *cacheLimitCellTitle;
-
+#if !TARGET_OS_TV
 @property (nonatomic, readonly) UISwitch *observerSwitch;
 @property (nonatomic, readonly) UISwitch *cacheMediaSwitch;
 @property (nonatomic, readonly) UISwitch *jsonViewerSwitch;
 @property (nonatomic, readonly) UISlider *cacheLimitSlider;
+#else
+@property (nonatomic, readonly) UIFakeSwitch *observerSwitch;
+@property (nonatomic, readonly) UIFakeSwitch *cacheMediaSwitch;
+@property (nonatomic, readonly) UIFakeSwitch *jsonViewerSwitch;
+@property (nonatomic, readonly) UIFakeSlider *cacheLimitSlider;
+#endif
 @property (nonatomic) UILabel *cacheLimitLabel;
 
 @property (nonatomic) NSMutableArray<NSString *> *hostBlacklist;
@@ -35,11 +43,17 @@
     self.hostBlacklist = FLEXNetworkRecorder.defaultRecorder.hostBlacklist.mutableCopy;
     
     NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
-    
+#if !TARGET_OS_TV
     _observerSwitch = [UISwitch new];
     _cacheMediaSwitch = [UISwitch new];
     _jsonViewerSwitch = [UISwitch new];
     _cacheLimitSlider = [UISlider new];
+#else
+    _observerSwitch = [UIFakeSwitch new];
+    _cacheMediaSwitch = [UIFakeSwitch new];
+    _jsonViewerSwitch = [UIFakeSwitch new];
+    _cacheLimitSlider = [UIFakeSlider new];
+#endif
     
     self.observerSwitch.on = FLEXNetworkObserver.enabled;
     [self.observerSwitch addTarget:self
@@ -64,7 +78,7 @@
         forControlEvents:UIControlEventValueChanged
     ];
     
-    UISlider *slider = self.cacheLimitSlider;
+    UIFakeSlider *slider = self.cacheLimitSlider;
     self.cacheLimitValue = FLEXNetworkRecorder.defaultRecorder.responseCacheByteLimit;
     const NSUInteger fiftyMega = 50 * 1024 * 1024;
     slider.minimumValue = 0;
@@ -99,7 +113,7 @@
     [NSUserDefaults.standardUserDefaults flex_toggleBoolForKey:kFLEXDefaultsRegisterJSONExplorerKey];
 }
 
-- (void)cacheLimitAdjusted:(UISlider *)sender {
+- (void)cacheLimitAdjusted:(UIFakeSlider *)sender {
     self.cacheLimitValue = sender.value;
 }
 
@@ -172,7 +186,7 @@
                     [cell.contentView addSubview:self.cacheLimitSlider];
                     
                     CGRect container = cell.contentView.frame;
-                    UISlider *slider = self.cacheLimitSlider;
+                    UIFakeSlider *slider = self.cacheLimitSlider;
                     [slider sizeToFit];
                     
                     CGFloat sliderWidth = 150.f;

+ 8 - 2
Classes/Network/FLEXNetworkTransactionDetailController.m

@@ -61,6 +61,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
             name:kFLEXNetworkRecorderTransactionUpdatedNotification
             object:nil
         ];
+        #if !TARGET_OS_TV
         self.toolbarItems = @[
             UIBarButtonItem.flex_flexibleSpace,
             [UIBarButtonItem
@@ -69,6 +70,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
                 action:@selector(copyButtonPressed:)
             ]
         ];
+        #endif
     }
     return self;
 }
@@ -129,7 +131,9 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
 }
 
 - (void)copyButtonPressed:(id)sender {
+    #if !TARGET_OS_TV
     [UIPasteboard.generalPasteboard setString:[FLEXNetworkCurlLogger curlCommandString:_transaction.request]];
+    #endif
 }
 
 #pragma mark - Table view data source
@@ -207,12 +211,14 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
 - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
     if (action == @selector(copy:)) {
         FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath];
+    #if !TARGET_OS_TV
         UIPasteboard.generalPasteboard.string = row.detailText;
+    #endif
     }
 }
 
 #if FLEX_AT_LEAST_IOS13_SDK
-
+#if !TARGET_OS_TV
 - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
     return [UIContextMenuConfiguration
         configurationWithIdentifier:nil
@@ -235,7 +241,7 @@ typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
         }
     ];
 }
-
+#endif
 #endif
 
 #pragma mark - View Configuration

+ 12 - 4
Classes/ObjectExplorers/FLEXObjectExplorerFactory.m

@@ -161,15 +161,23 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
         case FLEXGlobalsRowCurrentDevice:
             return [self explorerViewControllerForObject:UIDevice.currentDevice];
         case FLEXGlobalsRowPasteboard:
+#if !TARGET_OS_TV
             return [self explorerViewControllerForObject:UIPasteboard.generalPasteboard];
-            case FLEXGlobalsRowURLSession:
+#else
+            return nil; //FIXME: Probably not a safe fix
+#endif
+        case FLEXGlobalsRowURLSession:
             return [self explorerViewControllerForObject:NSURLSession.sharedSession];
         case FLEXGlobalsRowURLCache:
             return [self explorerViewControllerForObject:NSURLCache.sharedURLCache];
         case FLEXGlobalsRowNotificationCenter:
             return [self explorerViewControllerForObject:NSNotificationCenter.defaultCenter];
         case FLEXGlobalsRowMenuController:
+#if !TARGET_OS_TV
             return [self explorerViewControllerForObject:UIMenuController.sharedMenuController];
+#else
+            return nil; //FIXME: Probably not a safe fix
+#endif
         case FLEXGlobalsRowFileManager:
             return [self explorerViewControllerForObject:NSFileManager.defaultManager];
         case FLEXGlobalsRowTimeZone:
@@ -187,14 +195,14 @@ static NSMutableDictionary<id<NSCopying>, Class> *classesToRegisteredSections =
 
         case FLEXGlobalsRowKeyWindow:
             return [FLEXObjectExplorerFactory
-                explorerViewControllerForObject:FLEXUtility.appKeyWindow
-            ];
+                    explorerViewControllerForObject:FLEXUtility.appKeyWindow
+                    ];
         case FLEXGlobalsRowRootViewController: {
             id<UIApplicationDelegate> delegate = UIApplication.sharedApplication.delegate;
             if ([delegate respondsToSelector:@selector(window)]) {
                 return [self explorerViewControllerForObject:delegate.window.rootViewController];
             }
-
+            
             return nil;
         }
         default: return nil;

+ 16 - 0
Classes/ObjectExplorers/FLEXObjectExplorerViewController.m

@@ -134,7 +134,9 @@
 }
 
 - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
+    #if !TARGET_OS_TV
     [self.navigationController setToolbarHidden:NO animated:YES];
+    #endif
     return YES;
 }
 
@@ -223,10 +225,14 @@
             [FLEXBookmarkManager.bookmarks addObject:self.object];
         });
         make.button(@"Copy Description").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             UIPasteboard.generalPasteboard.string = self.explorer.objectDescription;
+            #endif
         });
         make.button(@"Copy Address").handler(^(NSArray<NSString *> *strings) {
+            #if !TARGET_OS_TV
             UIPasteboard.generalPasteboard.string = [FLEXUtility addressOfObject:self.object];
+            #endif
         });
         make.button(@"Cancel").cancelStyle();
     } showFrom:self source:sender];
@@ -264,6 +270,7 @@
 
 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
     // Prioritize important pan gestures over our swipe gesture
+    #if !TARGET_OS_TV
     if ([g2 isKindOfClass:[UIPanGestureRecognizer class]]) {
         if (g2 == self.navigationController.interactivePopGestureRecognizer ||
             g2 == self.navigationController.barHideOnSwipeGestureRecognizer ||
@@ -271,6 +278,13 @@
             return NO;
         }
     }
+    #else
+    if ([g2 isKindOfClass:[UIPanGestureRecognizer class]]) {
+           if (g2 == self.tableView.panGestureRecognizer) {
+               return NO;
+           }
+       }
+    #endif
     
     return YES;
 }
@@ -374,7 +388,9 @@
 
 - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
     if (action == @selector(copy:)) {
+#if !TARGET_OS_TV
         UIPasteboard.generalPasteboard.string = self.explorer.objectDescription;
+#endif
     }
 }
 

+ 4 - 0
Classes/ObjectExplorers/Sections/FLEXDefaultsContentSection.m

@@ -62,7 +62,11 @@
 }
 
 - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row {
+#if !TARGET_OS_TV
     return UITableViewCellAccessoryDetailDisclosureButton;
+#else
+    return UITableViewCellAccessoryDisclosureIndicator;
+#endif
 }
 
 #pragma mark - Private

+ 3 - 0
Classes/ObjectExplorers/Sections/FLEXMetadataSection.m

@@ -161,8 +161,11 @@
 
 - (BOOL)canSelectRow:(NSInteger)row {
     UITableViewCellAccessoryType accessory = [self accessoryTypeForRow:row];
+    #if !TARGET_OS_TV
     return accessory == UITableViewCellAccessoryDisclosureIndicator ||
         accessory == UITableViewCellAccessoryDetailDisclosureButton;
+    #endif
+    return accessory == UITableViewCellAccessoryDisclosureIndicator;
 }
 
 - (NSString *)reuseIdentifierForRow:(NSInteger)row {

+ 5 - 1
Classes/ObjectExplorers/Sections/Shortcuts/FLEXImageShortcuts.m

@@ -39,11 +39,15 @@
                     make.title(@"Saving Image…");
                 }];
                 [host presentViewController:alert animated:YES completion:nil];
-            
+            #if !TARGET_OS_TV
                 // Save the image
                 UIImageWriteToSavedPhotosAlbum(
                     image, alert, @selector(flex_image:disSaveWithError::), nil
                 );
+            #else
+            //FIXME: do something else for tvOS
+            NSLog(@"not saving image to photo album, tvOS!");
+            #endif
             }
             accessoryType:^UITableViewCellAccessoryType(id image) {
                 return UITableViewCellAccessoryDisclosureIndicator;

+ 3 - 1
Classes/ObjectExplorers/Sections/Shortcuts/FLEXShortcutsSection.m

@@ -185,8 +185,10 @@
 - (BOOL)canSelectRow:(NSInteger)row {
     UITableViewCellAccessoryType type = [self.shortcuts[row] accessoryTypeWith:self.object];
     BOOL hasDisclosure = NO;
-    hasDisclosure |= type == UITableViewCellAccessoryDisclosureIndicator;	
+    hasDisclosure |= type == UITableViewCellAccessoryDisclosureIndicator;
+    #if !TARGET_OS_TV
     hasDisclosure |= type == UITableViewCellAccessoryDetailDisclosureButton;
+    #endif
     return hasDisclosure;
 }
 

+ 2 - 0
Classes/Toolbar/FLEXExplorerToolbarItem.m

@@ -31,7 +31,9 @@
 }
 
 + (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image sibling:(FLEXExplorerToolbarItem *)backupItem {
+#if !TARGET_OS_TV
     NSParameterAssert(title); NSParameterAssert(image);
+#endif
     
     FLEXExplorerToolbarItem *toolbarItem = [self buttonWithType:UIButtonTypeSystem];
     toolbarItem.sibling = backupItem;

+ 16 - 0
Classes/Utility/Categories/FLEXRuntime+UIKitHelpers.m

@@ -110,7 +110,11 @@ FLEXObjectExplorerDefaultsImpl
     if ([self getPotentiallyUnboxedValue:targetForValueCheck]) {
         if (self.defaults.isEditable) {
             // Editable non-nil value, both
+            #if !TARGET_OS_TV
             return UITableViewCellAccessoryDetailDisclosureButton;
+            #else
+            return UITableViewCellAccessoryDisclosureIndicator; //FIXME: not a proper fix
+            #endif
         } else {
             // Uneditable non-nil value, chevron only
             return UITableViewCellAccessoryDisclosureIndicator;
@@ -118,7 +122,11 @@ FLEXObjectExplorerDefaultsImpl
     } else {
         if (self.defaults.isEditable) {
             // Editable nil value, just (i)
+            #if !TARGET_OS_TV
             return UITableViewCellAccessoryDetailButton;
+            #else
+            return UITableViewCellAccessoryDisclosureIndicator; //FIXME: not a proper fix
+            #endif
         } else {
             // Non-editable nil value, neither
             return UITableViewCellAccessoryNone;
@@ -244,7 +252,11 @@ FLEXObjectExplorerDefaultsImpl
     if ([self getPotentiallyUnboxedValue:object]) {
         if (self.defaults.isEditable) {
             // Editable non-nil value, both
+#if !TARGET_OS_TV
             return UITableViewCellAccessoryDetailDisclosureButton;
+#else
+            return UITableViewCellAccessoryDisclosureIndicator; //FIXME: not a proper fix
+#endif
         } else {
             // Uneditable non-nil value, chevron only
             return UITableViewCellAccessoryDisclosureIndicator;
@@ -252,7 +264,11 @@ FLEXObjectExplorerDefaultsImpl
     } else {
         if (self.defaults.isEditable) {
             // Editable nil value, just (i)
+#if !TARGET_OS_TV
             return UITableViewCellAccessoryDetailButton;
+#else
+            return UITableViewCellAccessoryDisclosureIndicator; //FIXME: not a proper fix
+#endif
         } else {
             // Non-editable nil value, neither
             return UITableViewCellAccessoryNone;

+ 4 - 2
Classes/Utility/Categories/UIFont+FLEX.m

@@ -7,9 +7,11 @@
 //
 
 #import "UIFont+FLEX.h"
-
+#if TARGET_OS_TV
+#define kFLEXDefaultCellFontSize 24.0
+#else
 #define kFLEXDefaultCellFontSize 12.0
-
+#endif
 @implementation UIFont (FLEX)
 
 + (UIFont *)flex_defaultTableCellFont {

+ 6 - 0
Classes/Utility/FLEXColor.h

@@ -9,6 +9,12 @@
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
 
+@interface UIView (darkMode)
+- (BOOL)darkMode;
+@end
+
+#define UIColorFromRGB(rgbValue, alp) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:alp]
+
 NS_ASSUME_NONNULL_BEGIN
 
 @interface FLEXColor : NSObject

+ 16 - 0
Classes/Utility/FLEXColor.m

@@ -9,6 +9,22 @@
 #import "FLEXColor.h"
 #import "FLEXUtility.h"
 
+@implementation UIView (darkMode)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+- (BOOL)darkMode {
+
+    if ([[self traitCollection] respondsToSelector:@selector(userInterfaceStyle)]){
+        return ([[self traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark);
+    } else {
+        return false;
+    }
+    return false;
+}
+#pragma clang diagnostic pop
+@end
+
 #if FLEX_AT_LEAST_IOS13_SDK
 #define FLEXDynamicColor(dynamic, static) ({ \
     UIColor *c; \

+ 1 - 1
Classes/Utility/FLEXResources.m

@@ -8843,7 +8843,7 @@ static const u_int8_t FLEXHierarchyIndentPattern3x[] = {
 }
 
 + (UIImage *)hierarchyIndentPattern {
-    return FLEXImageTemplate(FLEXHierarchyIndentPattern);
+    return FLEXRetinaOnlyImage(FLEXHierarchyIndentPattern);
 }
 
 

+ 3 - 0
Classes/Utility/FLEXUtility.m

@@ -175,6 +175,9 @@ BOOL FLEXConstructorsShouldRun() {
 }
 
 + (UIColor *)hierarchyIndentPatternColor {
+//#if TARGET_OS_TV
+  //  return [UIColor colorWithPatternImage:FLEXResources.hierarchyIndentPattern];
+//#endif
     static UIColor *patternColor = nil;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{

+ 2 - 1
Classes/Utility/Keyboard/FLEXKeyboardHelpViewController.m

@@ -29,8 +29,9 @@
     self.textView.backgroundColor = UIColor.blackColor;
     self.textView.textColor = UIColor.whiteColor;
     self.textView.font = [UIFont boldSystemFontOfSize:14.0];
+    #if !TARGET_OS_TV
     self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
-    
+    #endif
     self.title = @"Simulator Shortcuts";
     self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed:)];
 }

+ 1 - 0
Classes/ViewHierarchy/FLEXHierarchyViewController.h

@@ -7,6 +7,7 @@
 //
 
 #import "FLEXNavigationController.h"
+#import <TargetConditionals.h>
 
 @protocol FLEXHierarchyDelegate <NSObject>
 - (void)viewHierarchyDidDismiss:(UIView *)selectedView;

+ 6 - 1
Classes/ViewHierarchy/FLEXHierarchyViewController.m

@@ -14,6 +14,7 @@
 #import "FLEXResources.h"
 #import "UIBarButtonItem+FLEX.h"
 
+
 typedef NS_ENUM(NSUInteger, FLEXHierarchyViewMode) {
     FLEXHierarchyViewModeTree = 1,
     FLEXHierarchyViewMode3DSnapshot
@@ -127,16 +128,20 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyViewMode) {
         switch (mode) {
             case FLEXHierarchyViewModeTree:
                 [self popViewControllerAnimated:NO];
+#if !TARGET_OS_TV
                 self.toolbarHidden = YES;
+#endif
                 self.treeViewController.selectedView = self.selectedView;
                 break;
             case FLEXHierarchyViewMode3DSnapshot:
                 [self pushViewController:self.snapshotViewController animated:NO];
+#if !TARGET_OS_TV
                 self.toolbarHidden = NO;
+#endif
                 self.snapshotViewController.selectedView = self.selectedView;
                 break;
         }
-
+        
         // Change this last so that self.selectedView works right above
         _mode = mode;
     }

+ 6 - 2
Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.h

@@ -8,7 +8,7 @@
 
 #import "FHSViewSnapshot.h"
 #import "FHSRangeSlider.h"
-
+#import "fakes.h"
 NS_ASSUME_NONNULL_BEGIN
 
 @protocol FHSSnapshotViewDelegate <NSObject>
@@ -30,9 +30,13 @@ NS_ASSUME_NONNULL_BEGIN
 
 /// Views of these classes will have their headers hidden
 @property (nonatomic) NSArray<Class> *headerExclusions;
-
+#if !TARGET_OS_TV
 @property (nonatomic, readonly) UISlider *spacingSlider;
 @property (nonatomic, readonly) FHSRangeSlider *depthSlider;
+#else
+@property (nonatomic, readonly) UIFakeSlider *spacingSlider;
+@property (nonatomic, readonly) UIFakeSlider *depthSlider;
+#endif
 
 - (void)emphasizeViews:(NSArray<UIView *> *)emphasizedViews;
 

+ 10 - 2
Classes/ViewHierarchy/SnapshotExplorer/FHSSnapshotView.m

@@ -66,7 +66,11 @@
 }
 
 - (void)initSpacingSlider {
+    #if !TARGET_OS_TV
     _spacingSlider = [UISlider new];
+    #else
+    _spacingSlider = [UIFakeSlider new];
+    #endif
     self.spacingSlider.minimumValue = 0;
     self.spacingSlider.maximumValue = 100;
     self.spacingSlider.continuous = YES;
@@ -80,7 +84,11 @@
 }
 
 - (void)initDepthSlider {
+    #if !TARGET_OS_TV
     _depthSlider = [FHSRangeSlider new];
+    #else
+    _depthSlider = [UIFakeSlider new];
+    #endif
     [self.depthSlider
         addTarget:self
         action:@selector(depthSliderDidChange:)
@@ -273,7 +281,7 @@
     }
 }
 
-- (void)spacingSliderDidChange:(UISlider *)slider {
+- (void)spacingSliderDidChange:(UIFakeSlider *)slider {
     // TODO: hiding the header when flat logic
 
     for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) {
@@ -293,7 +301,7 @@
     }
 }
 
-- (void)depthSliderDidChange:(FHSRangeSlider *)slider {
+- (void)depthSliderDidChange:(UIFakeSlider *)slider {
     CGFloat min = slider.minValue, max = slider.maxValue;
     for (FHSSnapshotNodes *nodes in self.nodesMap.allValues) {
         CGFloat depth = nodes.depth;

+ 7 - 2
Classes/ViewHierarchy/SnapshotExplorer/FHSViewController.m

@@ -118,7 +118,9 @@ BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
     [super viewDidLoad];
 
     // Initialize back bar button item for 3D view to look like a button
+    #if !TARGET_OS_TV
     self.navigationItem.hidesBackButton = YES;
+    #endif
     self.navigationItem.leftBarButtonItem = [UIBarButtonItem
         flex_itemWithImage:FLEXResources.toggle2DIcon
         target:self.navigationController
@@ -155,7 +157,7 @@ BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
     NSParameterAssert(snapshotView);
 
     _snapshotView = snapshotView;
-
+#if !TARGET_OS_TV
     // Initialize our toolbar items
     self.toolbarItems = @[
         [UIBarButtonItem flex_itemWithCustomView:snapshotView.spacingSlider],
@@ -168,7 +170,7 @@ BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
         [UIBarButtonItem flex_itemWithCustomView:snapshotView.depthSlider]
     ];
     [self resizeToolbarItems:self.view.frame.size];
-
+#endif
     // If we have views-at-tap, dim the other views
     [snapshotView emphasizeViews:self.viewsAtTap];
     // Set the selected view, if any
@@ -230,7 +232,9 @@ BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
     } showFrom:self source:sender];
 }
 
+
 - (void)resizeToolbarItems:(CGSize)viewSize {
+    #if !TARGET_OS_TV
     CGFloat sliderHeights = self.snapshotView.spacingSlider.bounds.size.height;
     CGFloat sliderWidths = viewSize.width / 3.f;
     CGRect frame = CGRectMake(0, 0, sliderWidths, sliderHeights);
@@ -238,6 +242,7 @@ BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
     self.snapshotView.depthSlider.frame = frame;
 
     [self.navigationController.toolbar setNeedsLayout];
+    #endif
 }
 
 - (void)viewWillTransitionToSize:(CGSize)size

+ 5 - 1
Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewCell.m

@@ -43,9 +43,13 @@
         
         self.textLabel.font = UIFont.flex_defaultTableCellFont;
         self.detailTextLabel.font = UIFont.flex_defaultTableCellFont;
+        #if !TARGET_OS_TV
         self.accessoryType = UITableViewCellAccessoryDetailButton;
-        
+        #else
+        self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+        #endif
         // Use a pattern-based color to simplify application of the checker pattern
+        
         static UIColor *checkerPatternColor = nil;
         static dispatch_once_t once;
         dispatch_once(&once, ^{

+ 26 - 0
Classes/ViewHierarchy/TreeExplorer/FLEXHierarchyTableViewController.m

@@ -62,6 +62,27 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
     return self;
 }
 
+- (void)longPress:(UILongPressGestureRecognizer*)gesture {
+    if ( gesture.state == UIGestureRecognizerStateBegan) {
+        
+    }
+    else if ( gesture.state == UIGestureRecognizerStateEnded) {
+        NSLog(@"do something different for long press!");
+        UITableView *tv = [self tableView];
+        //naughty naughty
+        NSIndexPath *focus = [tv valueForKey:@"_focusedCellIndexPath"];
+        NSLog(@"[FLEX] focusedIndexPath: %@", focus);
+        [self tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:focus];
+        
+    }
+}
+- (void)addlongPressGestureRecognizer {
+    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
+    longPress.allowedPressTypes = @[[NSNumber numberWithInteger:UIPressTypePlayPause],[NSNumber numberWithInteger:UIPressTypeSelect]];
+    [self.tableView addGestureRecognizer:longPress];
+    
+}
+
 - (void)viewDidLoad {
     [super viewDidLoad];
 
@@ -70,7 +91,12 @@ typedef NS_ENUM(NSUInteger, FLEXHierarchyScope) {
     
     // A little more breathing room
     self.tableView.rowHeight = 50.0;
+    #if !TARGET_OS_TV
     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    #else
+    [self addlongPressGestureRecognizer];
+    self.tableView.rowHeight = 70.0;
+    #endif
     // Separator inset clashes with persistent cell selection
     [self.tableView setSeparatorInset:UIEdgeInsetsZero];
     

+ 22 - 0
FLEX.xcodeproj/project.pbxproj

@@ -17,6 +17,9 @@
 		224D49AB1C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 224D49A71C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m */; };
 		2EF6B04D1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EF6B04B1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m */; };
 		2EF6B04E1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EF6B04C1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h */; };
+		32D4E15B259273E60027F238 /* fakes.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D4E15A259273E60027F238 /* fakes.h */; };
+		32D4E15D2592749D0027F238 /* fakes.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D4E15C2592749D0027F238 /* fakes.m */; };
+		32D4E15E2592749D0027F238 /* fakes.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D4E15C2592749D0027F238 /* fakes.m */; };
 		3A4C94251B5B20570088C3F2 /* FLEX.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94241B5B20570088C3F2 /* FLEX.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3A4C94CD1B5B21410088C3F2 /* FLEXGlobalsEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A4C94441B5B21410088C3F2 /* FLEXGlobalsEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3A4C94CE1B5B21410088C3F2 /* FLEXGlobalsEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A4C94451B5B21410088C3F2 /* FLEXGlobalsEntry.m */; };
@@ -372,6 +375,8 @@
 		224D49A71C673AB5000EAB86 /* FLEXSQLiteDatabaseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXSQLiteDatabaseManager.m; sourceTree = "<group>"; };
 		2EF6B04B1D494BE50006BDA5 /* FLEXNetworkCurlLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXNetworkCurlLogger.m; sourceTree = "<group>"; };
 		2EF6B04C1D494BE50006BDA5 /* FLEXNetworkCurlLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXNetworkCurlLogger.h; sourceTree = "<group>"; };
+		32D4E15A259273E60027F238 /* fakes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fakes.h; sourceTree = "<group>"; };
+		32D4E15C2592749D0027F238 /* fakes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fakes.m; sourceTree = "<group>"; };
 		3A4C941F1B5B20570088C3F2 /* FLEX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FLEX.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		3A4C94231B5B20570088C3F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		3A4C94241B5B20570088C3F2 /* FLEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEX.h; sourceTree = "<group>"; };
@@ -745,6 +750,8 @@
 		3A4C94151B5B20570088C3F2 = {
 			isa = PBXGroup;
 			children = (
+				32D4E15A259273E60027F238 /* fakes.h */,
+				32D4E15C2592749D0027F238 /* fakes.m */,
 				3A4C94211B5B20570088C3F2 /* FLEX */,
 				1C27A8B71F0E5A0400F0D02D /* FLEXTests */,
 				3A4C95451B5B216C0088C3F2 /* Frameworks */,
@@ -1545,6 +1552,7 @@
 				3A4C953C1B5B21410088C3F2 /* FLEXNetworkTransaction.h in Headers */,
 				C383C3C223B6B429007A321B /* NSTimer+FLEX.h in Headers */,
 				3A4C952E1B5B21410088C3F2 /* FLEXWebViewController.h in Headers */,
+				32D4E15B259273E60027F238 /* fakes.h in Headers */,
 				C3A9424D23C78CFF006871A3 /* FHSViewSnapshot.h in Headers */,
 				C3EE76BF22DFC63600EC0AA0 /* FLEXScopeCarousel.h in Headers */,
 				C398627223AD7951007E6793 /* UIGestureRecognizer+Blocks.h in Headers */,
@@ -1696,6 +1704,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				32D4E15E2592749D0027F238 /* fakes.m in Sources */,
 				C33C825B23159EAF00DD2451 /* FLEXTests.m in Sources */,
 				1C27A8B91F0E5A0400F0D02D /* FLEXTestsMethodsList.m in Sources */,
 				C3854DF023F36C1700FCD1E2 /* FLEXTypeEncodingParserTests.m in Sources */,
@@ -1820,6 +1829,7 @@
 				C36B097123E1EDCD008F5D47 /* FLEXTableViewSection.m in Sources */,
 				C3531BA623E88A2100A184AD /* FLEXNavigationController.m in Sources */,
 				3A4C94EA1B5B21410088C3F2 /* FLEXHierarchyTableViewController.m in Sources */,
+				32D4E15D2592749D0027F238 /* fakes.m in Sources */,
 				3A4C95331B5B21410088C3F2 /* FLEXSystemLogCell.m in Sources */,
 				C3A9423523C3F98E006871A3 /* FHSViewController.m in Sources */,
 				C3DB9F652107FC9600B46809 /* FLEXObjectRef.m in Sources */,
@@ -2047,6 +2057,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
@@ -2055,9 +2066,14 @@
 				INSTALL_PATH = "@rpath";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				OTHER_LDFLAGS = (
+					"-undefined",
+					dynamic_lookup,
+				);
 				PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
@@ -2080,6 +2096,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
@@ -2088,9 +2105,14 @@
 				INSTALL_PATH = "@rpath";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				OTHER_LDFLAGS = (
+					"-undefined",
+					dynamic_lookup,
+				);
 				PRODUCT_BUNDLE_IDENTIFIER = "com.flipboard.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_VERSION = 5.0;
 				WARNING_CFLAGS = "-Wno-unsupported-availability-guard";

+ 3 - 0
README.md

@@ -1,4 +1,7 @@
 # FLEX
+
+[tvOS Demo](https://www.dropbox.com/s/v685gabrefo614f/better_flex.MP4?dl=1)
+
 [![CocoaPods](https://img.shields.io/cocoapods/v/FLEX.svg)](https://cocoapods.org/?q=FLEX)
  [![CocoaPods](https://img.shields.io/cocoapods/l/FLEX.svg)](https://github.com/Flipboard/FLEX/blob/master/LICENSE)
  [![CocoaPods](https://img.shields.io/cocoapods/p/FLEX.svg)]()

+ 59 - 0
fakes.h

@@ -0,0 +1,59 @@
+//
+//  fakes.h
+//  FLEX
+//
+//  Created by Kevin Bradley on 12/22/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIFakeSwitch : UIControl <NSCoding>
+@property(nullable, nonatomic, strong) UIColor *onTintColor;
+@property(nullable, nonatomic, strong) UIColor *thumbTintColor;
+@property(nullable, nonatomic, strong) UIImage *onImage;
+@property(nullable, nonatomic, strong) UIImage *offImage;
+@property(nonatomic,getter=isOn) BOOL on;
+- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;      // This class enforces a size appropriate for the control, and so the frame size is ignored.
+- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
+- (void)setOn:(BOOL)on animated:(BOOL)animated; // does not send action
+@end
+
+@interface UIFakeSlider: UIControl <NSCoding>
+@property(nonatomic) float value;
+@property(nonatomic) float minimumValue;
+@property(nonatomic) float maximumValue;
+@property(nonatomic) float minValue;
+@property(nonatomic) float maxValue;
+@property(nonatomic) float allowedMinValue;
+@property(nonatomic) float allowedMaxValue;
+@property(nullable, nonatomic,strong) UIImage *minimumValueImage;
+@property(nullable, nonatomic,strong) UIImage *maximumValueImage;
+
+@property(nonatomic,getter=isContinuous) BOOL continuous;
+
+@property(nullable, nonatomic,strong) UIColor *minimumTrackTintColor;
+@property(nullable, nonatomic,strong) UIColor *maximumTrackTintColor;
+@property(nullable, nonatomic,strong) UIColor *thumbTintColor;
+
+- (void)setValue:(float)value animated:(BOOL)animated;
+
+- (void)setThumbImage:(nullable UIImage *)image forState:(UIControlState)state;
+- (void)setMinimumTrackImage:(nullable UIImage *)image forState:(UIControlState)state;
+- (void)setMaximumTrackImage:(nullable UIImage *)image forState:(UIControlState)state;
+
+- (nullable UIImage *)thumbImageForState:(UIControlState)state;
+- (nullable UIImage *)minimumTrackImageForState:(UIControlState)state;
+- (nullable UIImage *)maximumTrackImageForState:(UIControlState)state;
+
+@property(nullable,nonatomic,readonly) UIImage *currentThumbImage;
+@property(nullable,nonatomic,readonly) UIImage *currentMinimumTrackImage;
+@property(nullable,nonatomic,readonly) UIImage *currentMaximumTrackImage;
+
+// lets a subclass lay out the track and thumb as needed
+- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds;
+- (CGRect)maximumValueImageRectForBounds:(CGRect)bounds;
+- (CGRect)trackRectForBounds:(CGRect)bounds;
+- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value;
+
+@end

+ 66 - 0
fakes.m

@@ -0,0 +1,66 @@
+//
+//  fakes.h
+//  FLEX
+//
+//  Created by Kevin Bradley on 12/22/20.
+//  Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "fakes.h"
+
+@implementation UIFakeSwitch
+
+- (void)setOn:(BOOL)on animated:(BOOL)animated {
+    
+}
+
+-(instancetype)initWithFrame:(CGRect)frame {
+    return [super initWithFrame:frame];
+}
+
+- (instancetype)initWithCoder:(id)coder {
+    return [super initWithCoder:coder];
+}
+
+@end
+
+@implementation UIFakeSlider
+
+- (void)setValue:(float)value animated:(BOOL)animated {
+    
+}
+
+- (void)setThumbImage:(nullable UIImage *)image forState:(UIControlState)state {
+    
+}
+- (void)setMinimumTrackImage:(nullable UIImage *)image forState:(UIControlState)state {
+    
+}
+- (void)setMaximumTrackImage:(nullable UIImage *)image forState:(UIControlState)state {
+    
+}
+
+- (nullable UIImage *)thumbImageForState:(UIControlState)state {
+    return nil;
+}
+- (nullable UIImage *)minimumTrackImageForState:(UIControlState)state {
+    return nil;
+}
+- (nullable UIImage *)maximumTrackImageForState:(UIControlState)state {
+    return nil;
+}
+
+- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds {
+    return CGRectZero;
+}
+- (CGRect)maximumValueImageRectForBounds:(CGRect)bounds {
+    return CGRectZero;
+}
+- (CGRect)trackRectForBounds:(CGRect)bounds {
+    return CGRectZero;
+}
+- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
+    return CGRectZero;
+}
+
+@end

+ 1 - 0
flexinjected/FLEXInjected.plist

@@ -0,0 +1 @@
+{ Filter = { Bundles = ( "com.apple.UIKit" ); }; }

+ 12 - 0
flexinjected/Makefile

@@ -0,0 +1,12 @@
+target = appletv:12.1
+
+INSTALL_TARGET_PROCESSES = PineBoard
+
+include $(THEOS)/makefiles/common.mk
+
+TWEAK_NAME = FLEXInjected
+
+FLEXInjected_FILES = Tweak.x
+FLEXInjected_CFLAGS = -fobjc-arc
+
+include $(THEOS_MAKE_PATH)/tweak.mk

+ 78 - 0
flexinjected/Tweak.x

@@ -0,0 +1,78 @@
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#include <TargetConditionals.h>
+@interface NSObject (topViewController)
+- (id)topViewController;
+@end
+@interface FLEXManager: NSObject
++(id)sharedManager;
+-(void)showExplorer;
+- (void)_addTVOSGestureRecognizer:(UIViewController *)explorer;
+@end
+
+
+@interface UIWindow (Additions)
+- (UIViewController *)visibleViewController;
+@end
+
+@interface NSObject (Additions)
+- (UIViewController *)topViewController;
+@end
+
+
+@implementation UIWindow (Additions)
+- (UIViewController *)visibleViewController {
+    UIViewController *rootViewController = self.rootViewController;
+    return [UIWindow getVisibleViewControllerFrom:rootViewController];
+}
++ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
+    if ([vc isKindOfClass:[UINavigationController class]]) {
+        return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
+    } else if ([vc isKindOfClass:[UITabBarController class]]) {
+        return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
+    } else {
+        if (vc.presentedViewController) {
+            return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];
+        } else {
+            return vc;
+        }
+    }
+}
+@end
+
+@implementation NSObject (Additions)
+- (UIViewController *)topViewController {
+    return [[[UIApplication sharedApplication] keyWindow] visibleViewController];
+}
+@end
+
+// The dylib constructor sets decryptedIPAPath, spawns a thread to do the app decryption, then exits.
+__attribute__ ((constructor)) static void FLEXInjected_main() {
+    
+    NSBundle *bundle = [NSBundle mainBundle];
+    NSString *bundleID = [bundle bundleIdentifier];
+    NSDictionary *ourDict = [[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.nito.flexinjected.plist"];
+    NSNumber *value = [ourDict objectForKey:bundleID];
+    NSLog(@"[FLEXInjected) bundle ID %@", bundleID);
+    if ([value boolValue] == YES) {
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        NSLog(@"[FLEXInjected] weouchea...");
+        NSString *p = @"/Library/Frameworks/FLEX.framework";
+        NSBundle *bundle = [NSBundle bundleWithPath:p];
+        [bundle load];
+        id flexManager = [%c(FLEXManager) sharedManager];
+        UIViewController *tvc = [[UIApplication sharedApplication] topViewController];
+        if([tvc respondsToSelector: @selector(tabBarController)]){
+            UITabBarController *tabBar = [tvc tabBarController];
+            if (tabBar) tvc = tabBar;
+        }
+        NSLog(@"[FLEXInjected] top view controller: %@ violated...", tvc);
+        [flexManager _addTVOSGestureRecognizer:tvc];
+        [flexManager showExplorer];
+    });
+      
+        NSLog(@"[FLEXInjected] All done, exiting constructor.");
+        
+    }
+}
+

ファイルの差分が大きいため隠しています
+ 1 - 0
flexinjected/Tweak.x.jsont


ファイルの差分が大きいため隠しています
+ 3 - 0
flexinjected/compile_commands.json


+ 9 - 0
flexinjected/layout/DEBIAN/control

@@ -0,0 +1,9 @@
+Package: com.nito.flexinjected
+Name: FLEXInjected
+Depends: mobilesubstrate, preferenceloader (>=1.5-15), applist (>=1.3-11), libflex (>=4.1.1a-1)
+Version: 1.0.0
+Architecture: appletvos-arm64
+Description: Inject FLEX into your favorite Applications utilizing applist
+Maintainer: Kevin Bradley
+Author: Kevin Bradley
+Section: Tweaks

+ 60 - 0
flexinjected/layout/Library/PreferenceLoader/Preferences/FLEXInjected.plist

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>entry</key>
+	<dict>
+		<key>label</key>
+		<string>FLEXInjected</string>
+		<key>icon</key>
+		<string>flex.png</string>
+		<key>cell</key>
+		<string>PSLinkCell</string>
+	</dict>
+	<key>items</key>
+	<array>
+		<dict>
+			<key>cell</key>
+			<string>PSGroupCell</string>
+			<key>label</key>
+			<string>FLEX</string>
+		</dict>
+		<dict>
+			<key>ALAllowsSelection</key>
+			<string>1</string>
+			<key>ALChangeNotification</key>
+			<string>com.nito.flexinjected.settingschanged</string>
+			<key>ALSettingsDefaultValue</key>
+			<integer>0</integer>
+			<key>ALSettingsKeyPrefix</key>
+			<string></string>
+			<key>ALSettingsPath</key>
+			<string>/var/mobile/Library/Preferences/com.nito.flexinjected.plist</string>
+			<key>ALSingleEnabledMode</key>
+			<false/>
+			<key>bundle</key>
+			<string>AppList</string>
+			<key>cell</key>
+			<string>PSLinkCell</string>
+			<key>isController</key>
+			<string>1</string>
+			<key>icon</key>
+			<string>flex.png</string>
+			<key>label</key>
+			<string>FLEX Injected Applications</string>
+		</dict>
+		<dict>
+			<key>cell</key>
+			<string>PSGroupCell</string>
+		</dict>
+		<dict>
+			<key>cell</key>
+			<string>PSGroupCell</string>
+		</dict>
+	</array>
+	<key>title</key>
+	<string>FLEXInjected</string>
+	<key>icon</key>
+	<string>flex.png</string>
+</dict>
+</plist>

BIN
flexinjected/layout/Library/PreferenceLoader/Preferences/flex.png