├── .gitignore
├── ampere.png
├── banner.png
├── amperesettings
├── Resources
│ ├── dark.png
│ ├── icon.png
│ ├── light.png
│ ├── charging.png
│ ├── critical.png
│ ├── icon@2x.png
│ ├── icon@3x.png
│ ├── lowpower.png
│ ├── Info.plist
│ └── Root.plist
├── Makefile
├── layout
│ └── Library
│ │ └── PreferenceLoader
│ │ └── Preferences
│ │ └── AmpereSettings.plist
├── AmpSwitch.h
├── AMPRootListController.h
├── AmpSwitch.m
└── AMPRootListController.m
├── entitlements.plist
├── control
├── .github
└── ISSUE_TEMPLATE
│ ├── feature-request.md
│ └── bug-report.md
├── README.md
├── Makefile
├── Ampere.plist
├── LICENSE
├── Ampere.h
└── Ampere.xm
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 | .theos/
3 | .DS_Store
--------------------------------------------------------------------------------
/ampere.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/ampere.png
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/banner.png
--------------------------------------------------------------------------------
/amperesettings/Resources/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/dark.png
--------------------------------------------------------------------------------
/amperesettings/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/icon.png
--------------------------------------------------------------------------------
/amperesettings/Resources/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/light.png
--------------------------------------------------------------------------------
/amperesettings/Resources/charging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/charging.png
--------------------------------------------------------------------------------
/amperesettings/Resources/critical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/critical.png
--------------------------------------------------------------------------------
/amperesettings/Resources/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/icon@2x.png
--------------------------------------------------------------------------------
/amperesettings/Resources/icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/icon@3x.png
--------------------------------------------------------------------------------
/amperesettings/Resources/lowpower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MTACS/Ampere/HEAD/amperesettings/Resources/lowpower.png
--------------------------------------------------------------------------------
/entitlements.plist:
--------------------------------------------------------------------------------
1 | platform-application
2 |
3 | com.apple.private.security.no-sandbox
4 |
5 | com.apple.private.security.storage.AppBundles
6 |
7 | com.apple.private.security.storage.AppDataContainers
8 |
--------------------------------------------------------------------------------
/control:
--------------------------------------------------------------------------------
1 | Package: com.mtac.ampere
2 | Name: Ampere
3 | Version: 1.4
4 | Architecture: iphoneos-arm
5 | Description: iOS 16 Style Battery Indicator
6 | Maintainer: MTAC
7 | Author: MTAC
8 | Section: Tweaks
9 | Depends: mobilesubstrate, firmware (>= 14.0), preferenceloader
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea to be added to this project
4 | title: "[FEATURE]"
5 | labels: ''
6 | assignees: MTACS
7 |
8 | ---
9 |
10 | # Please add a detailed description of the feature you would like added
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ampere
2 | Source for Ampere tweak for iOS 14, 15 & 16
3 |
4 | Available for rootful & rootless on [Havoc Repo](https://havoc.app/package/ampere). RootHide package available [here](https://mtac.app/repo/debs/com.mtac.ampere_1.4_iphoneos-arm64e.deb)
5 |
6 | 
7 |
8 | ## Building
9 |
10 | `git clone https://github.com/MTACS/Ampere.git`
11 |
12 | `cd Ampere`
13 |
14 | `make do`
15 |
16 | Make sure to set `PREFIX` and `THEOS_DEVICE_IP` to your setup and current device
17 |
--------------------------------------------------------------------------------
/amperesettings/Makefile:
--------------------------------------------------------------------------------
1 | TARGET := iphone:clang:latest:14.0
2 | SYSROOT = $(THEOS)/sdks/iPhoneOS14.2.sdk
3 |
4 | include $(THEOS)/makefiles/common.mk
5 |
6 | BUNDLE_NAME = AmpereSettings
7 |
8 | AmpereSettings_FILES = AMPRootListController.m AmpSwitch.m
9 | AmpereSettings_FRAMEWORKS = UIKit
10 | AmpereSettings_PRIVATE_FRAMEWORKS = Preferences
11 | AmpereSettings_INSTALL_PATH = /Library/PreferenceBundles
12 | AmpereSettings_CFLAGS = -fobjc-arc -Wdeprecated-declarations -Wno-deprecated-declarations
13 | ifeq ($(THEOS_PACKAGE_SCHEME),roothide)
14 | AmpereSettings_LDFLAGS += -lroothide
15 | endif
16 |
17 | include $(THEOS_MAKE_PATH)/bundle.mk
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TARGET := iphone:clang:latest:14.0
2 | INSTALL_TARGET_PROCESSES = SpringBoard Preferences
3 | ARCHS = arm64 arm64e
4 | SYSROOT = $(THEOS)/sdks/iPhoneOS14.2.sdk
5 | DEBUG = 1
6 | FINALPACKAGE = 0
7 |
8 | include $(THEOS)/makefiles/common.mk
9 |
10 | TWEAK_NAME = Ampere
11 |
12 | Ampere_CFLAGS = -fobjc-arc -Wdeprecated-declarations -Wno-deprecated-declarations
13 | Ampere_FILES = Ampere.xm
14 | Ampere_FRAMEWORKS = IOKit
15 | ifeq ($(THEOS_PACKAGE_SCHEME),roothide)
16 | Ampere_LDFLAGS += -lroothide
17 | endif
18 |
19 | include $(THEOS_MAKE_PATH)/tweak.mk
20 | SUBPROJECTS += amperesettings
21 | include $(THEOS_MAKE_PATH)/aggregate.mk
22 |
--------------------------------------------------------------------------------
/amperesettings/layout/Library/PreferenceLoader/Preferences/AmpereSettings.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | entry
6 |
7 | bundle
8 | AmpereSettings
9 | cell
10 | PSLinkCell
11 | detail
12 | AMPRootListController
13 | icon
14 | icon.png
15 | isController
16 |
17 | label
18 | Ampere
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Ampere.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Filter
6 |
7 | Bundles
8 |
9 | com.apple.CarPlayApp
10 | com.apple.springboard
11 | com.apple.DashBoard
12 |
13 | Executables
14 |
15 | DashBoard
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report unexpected behavior
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: MTACS
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Device Information (please complete the following information):**
27 | - Device: [e.g. iPhone 7+]
28 | - iOS Version: [e.g. iOS 15.6]
29 | - Ampere version [e.g. 1.0.3]
30 |
--------------------------------------------------------------------------------
/amperesettings/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | AmpereSettings
9 | CFBundleIdentifier
10 | com.mtac.ampere.settings
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1.0
21 | NSPrincipalClass
22 | AMPRootListController
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 D.F.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/amperesettings/AmpSwitch.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #define LIGHT_TINT [UIColor colorWithRed: 0.20 green: 0.60 blue: 0.86 alpha: 1.00]
5 | #define DARK_TINT [UIColor colorWithRed: 0.36 green: 0.68 blue: 0.89 alpha: 1.00]
6 |
7 | typedef enum {
8 | AmpSwitchStyleLight,
9 | AmpSwitchStyleDark,
10 | AmpSwitchStyleDefault
11 | } AmpSwitchStyle;
12 |
13 | typedef enum {
14 | AmpSwitchStateOn,
15 | AmpSwitchStateOff
16 | } AmpSwitchState;
17 |
18 | typedef enum {
19 | AmpSwitchSizeBig,
20 | AmpSwitchSizeNormal,
21 | AmpSwitchSizeSmall
22 | } AmpSwitchSize;
23 |
24 | @protocol AmpSwitchDelegate
25 | - (void)switchStateChanged:(AmpSwitchState)currentState;
26 | @end
27 |
28 | @interface AmpSwitch : UIControl
29 | @property (nonatomic, assign) id delegate;
30 | @property (nonatomic, assign) BOOL isOn;
31 | @property (nonatomic, assign) BOOL isEnabled;
32 | @property (nonatomic, strong) UIColor *thumbOnTintColor;
33 | @property (nonatomic, strong) UIColor *thumbOffTintColor;
34 | @property (nonatomic, strong) UIColor *trackOnTintColor;
35 | @property (nonatomic, strong) UIColor *trackOffTintColor;
36 | @property (nonatomic, strong) UIColor *thumbDisabledTintColor;
37 | @property (nonatomic, strong) UIColor *trackDisabledTintColor;
38 | @property (nonatomic, strong) UIButton *switchThumb;
39 | @property (nonatomic, strong) UIView *track;
40 | - (id)init;
41 | - (id)initWithSize:(AmpSwitchSize)size state:(AmpSwitchState)state;
42 | - (id)initWithSize:(AmpSwitchSize)size style:(AmpSwitchStyle)style state:(AmpSwitchState)state;
43 | - (BOOL)getSwitchState;
44 | - (void)setOn:(BOOL)on;
45 | - (void)setOn:(BOOL)on animated:(BOOL)animated;
46 | - (void)setThumbState:(BOOL)state animated:(BOOL)animated;
47 | @end
--------------------------------------------------------------------------------
/amperesettings/AMPRootListController.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 | #import "AmpSwitch.h"
6 | #import "spawn.h"
7 | #import
8 |
9 | #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
10 |
11 | static NSString *domain = @"com.mtac.amp";
12 |
13 | @interface NSUserDefaults (Ampere)
14 | - (id)objectForKey:(NSString *)key inDomain:(NSString *)domain;
15 | - (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain;
16 | @end
17 |
18 | @interface UIView (Ampere)
19 | - (id)_viewControllerForAncestor;
20 | @end
21 |
22 | @interface UIColor (Ampere)
23 | + (id)tableCellGroupedBackgroundColor;
24 | @end
25 |
26 | @interface BSPlatform : NSObject
27 | + (id)sharedInstance;
28 | - (NSInteger)homeButtonType;
29 | @end
30 |
31 | @interface UINavigationItem (Ampere)
32 | @property (assign, nonatomic) UINavigationBar *navigationBar;
33 | @end
34 |
35 | @interface PSTableCell (Ampere)
36 | - (void)setValue:(id)arg0;
37 | @end
38 |
39 | @interface FBSSystemService : NSObject
40 | + (id)sharedService;
41 | - (void)sendActions:(id)arg1 withResult:(id)arg2;
42 | @end
43 |
44 | @interface BSAction : NSObject
45 | @end
46 |
47 | @interface SBSRelaunchAction : BSAction
48 | + (id)actionWithReason:(id)arg1 options:(unsigned long long)arg2 targetURL:(id)arg3;
49 | @end
50 |
51 | @interface AmpColorCell : PSControlTableCell
52 | @property (nonatomic, retain) UIButton *control;
53 | - (NSDictionary *)dictionaryForColor:(UIColor *)color;
54 | - (void)selectColor;
55 | @end
56 |
57 | @interface AmpStepperCell : PSControlTableCell
58 | @property (nonatomic, retain) UIStepper *control;
59 | @end
60 |
61 | @interface AmpSwitchCell : PSSwitchTableCell
62 | - (void)selectColor;
63 | - (UIColor *)selectedColor;
64 | @end
65 |
66 | @interface AmpSelectorCell : PSControlTableCell
67 | @property (nonatomic, retain) UIButton *control;
68 | @end
69 |
70 | @interface AmpCodeCell : PSTableCell
71 | @end
72 |
73 | @interface AMPRootListController : PSListController {
74 | UITableView *_table;
75 | }
76 | @property (nonatomic, strong) UIView *headerView;
77 | @property (nonatomic, strong) UIImageView *batteryView;
78 | @property (nonatomic, retain) UILabel *percentageLabel;
79 | @property (nonatomic, retain) UILabel *sectionSegmentLabel;
80 | @property (nonatomic, retain) UISegmentedControl *segmentedControl;
81 | @property (nonatomic, strong) AmpSwitch *enableSwitch;
82 | - (void)setEnableSwitchState;
83 | @end
84 |
--------------------------------------------------------------------------------
/Ampere.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "spawn.h"
3 | #import
4 | #include
5 |
6 | #ifndef kCFCoreFoundationVersionNumber_iOS_16_0
7 | #define kCFCoreFoundationVersionNumber_iOS_16_0 1946.10
8 | #endif
9 | #define kSLSystemVersioniOS16 kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_16_0
10 |
11 | #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
12 | #define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
13 |
14 | #define WIDTH [UIScreen mainScreen].bounds.size.width
15 | #define HEIGHT [UIScreen mainScreen].bounds.size.height
16 |
17 | extern NSString *const kCAFilterDestOut;
18 |
19 | static NSString *domain = @"com.mtac.amp";
20 | static NSString *preferencesNotification = @"com.mtac.amp/preferences.changed";
21 | static NSString *statusBarNotification = @"com.mtac.amp/statusbar.changed";
22 | static BOOL enabled;
23 | static BOOL showBolt;
24 | static BOOL useGesture;
25 | static BOOL useStatsModule;
26 | static BOOL overrideColorStandard;
27 | static BOOL overrideColorCharging;
28 | static BOOL overrideColorLowPower;
29 | static BOOL overrideColorCritical;
30 | static NSInteger textStyle;
31 | static NSInteger fontSize;
32 | static NSInteger batterySizing;
33 |
34 | @interface CALayer (Ampere)
35 | @property (nonatomic, retain) NSString *compositingFilter;
36 | @property (nonatomic, assign) BOOL allowsGroupOpacity;
37 | @property (nonatomic, assign) BOOL allowsGroupBlending;
38 | @property (copy) NSString *cornerCurve;
39 | @end
40 |
41 | @interface _CDBatterySaver : NSObject
42 | + (id)batterySaver;
43 | - (NSInteger)getPowerMode;
44 | - (BOOL)setPowerMode:(NSInteger)arg0 error:(id)arg1;
45 | @end
46 |
47 | @interface _PMLowPowerMode : NSObject
48 | + (id)sharedInstance;
49 | - (NSInteger)getPowerMode;
50 | - (void)setPowerMode:(NSInteger)arg0 fromSource:(id)arg1;
51 | - (void)setPowerMode:(NSInteger)arg0 fromSource:(id)arg1 withCompletion:(id)arg2;
52 | @end
53 |
54 | @interface NSUserDefaults (Ampere)
55 | - (id)objectForKey:(NSString *)key inDomain:(NSString *)domain;
56 | - (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain;
57 | @end
58 |
59 | @interface _UIBatteryView : UIView
60 | @property (readonly, nonatomic, getter=isLowBattery) BOOL lowBattery;
61 | @property (retain, nonatomic) UIImageView *ampereImageView;
62 | @property (retain, nonatomic) UILabel *percentageLabel;
63 | @property (retain, nonatomic) CALayer *fillLayer;
64 | @property (retain, nonatomic) CALayer *pinLayer;
65 | @property (retain, nonatomic) CALayer *bodyLayer;
66 | @property (readonly, nonatomic) CAShapeLayer *bodyShapeLayer;
67 | @property (copy, nonatomic) UIColor *pinColor;
68 | @property (copy, nonatomic) UIColor *bodyColor;
69 | @property (nonatomic) CGFloat pinColorAlpha;
70 | @property (nonatomic) CGFloat bodyColorAlpha;
71 | @property (nonatomic) CGFloat chargePercent;
72 | @property (nonatomic) BOOL saverModeActive;
73 | @property (nonatomic) NSInteger chargingState;
74 | @property (nonatomic) NSInteger iconSize;
75 | - (CGRect)_bodyRectForTraitCollection:(id)arg0;
76 | - (struct CGRect )_updateBodyLayers;
77 | - (id)_batteryFillColor;
78 | - (id)_batteryTextColor;
79 | - (void)_updateBatteryFillColor;
80 | - (void)_updatePercentage;
81 | - (void)_updatePercentageFont;
82 | - (id)initWithSizeCategory:(NSInteger)arg0;
83 | - (UIColor *)ampereFillColor;
84 | @end
85 |
86 | @interface BCUIRowView : UIView
87 | @property (nonatomic, strong) _UIBatteryView *ampereBatteryView;
88 | @property (nonatomic) NSInteger percentCharge;
89 | @end
90 |
91 | @interface BCUIBatteryView : _UIBatteryView
92 | @end
93 |
94 | @interface _UIStaticBatteryView : _UIBatteryView
95 | - (void)setShowsPercentage:(BOOL)arg0;
96 | @end
97 |
98 | @interface UIStatusBarItem : NSObject
99 | @end
100 |
101 | @interface _UIStatusBarDisplayItem : NSObject
102 | @property (nonatomic, getter=isEnabled) BOOL enabled;
103 | @end
104 |
105 | @interface _UIStatusBarStringView : UIView
106 | - (void)setText:(id)arg0;
107 | @end
108 |
109 | @interface _UIStatusBarBatteryItem : UIStatusBarItem
110 | @property (retain, nonatomic) _UIStatusBarStringView *percentView;
111 | + (id)staticIconDisplayIdentifier;
112 | + (id)iconDisplayIdentifier;
113 | + (id)percentDisplayIdentifier;
114 | + (id)chargingDisplayIdentifier;
115 | - (void)toggleLowPower:(id)sender;
116 | @end
117 |
118 | @interface _UIStatusBarDataBatteryEntry : NSObject
119 | @property (nonatomic) NSInteger state;
120 | @end
121 |
122 | @interface MTMaterialView : UIView
123 | @end
124 |
125 | @protocol CCUIContentModuleContentViewController
126 | @end
127 |
128 | @protocol CCUIContentModuleBackgroundViewController
129 | @end
130 |
131 | @interface CCUIContentModuleContentContainerView : UIView {
132 | MTMaterialView *_moduleMaterialView;
133 | }
134 | @end
135 |
136 | @interface CCUIContentModuleContainerViewController : UIViewController
137 | @property (retain, nonatomic) UIViewController *contentViewController;
138 | @property (retain, nonatomic) UIViewController *backgroundViewController;
139 | @property (retain, nonatomic) CCUIContentModuleContentContainerView *contentContainerView;
140 | @property (copy, nonatomic) NSString *moduleIdentifier;
141 | @end
142 |
143 | @interface CCUIToggleModule : NSObject
144 | @end
145 |
146 | @interface CCUIButtonModuleView : UIControl
147 | @end
148 |
149 | @interface CCUIButtonModuleViewController : UIViewController
150 | @property (readonly, nonatomic) CCUIButtonModuleView *buttonView;
151 | @end
152 |
153 | @interface CCUIToggleViewController : CCUIButtonModuleViewController
154 | @property (weak, nonatomic) CCUIToggleModule *module;
155 | @property (readonly, nonatomic) CGFloat preferredExpandedContentHeight;
156 | @property (readonly, nonatomic) CGFloat preferredExpandedContentWidth;
157 | @property (readonly, nonatomic) BOOL providesOwnPlatter;
158 | @property (readonly, nonatomic) BOOL shouldPerformClickInteraction;
159 | @property (readonly, nonatomic) BOOL shouldPerformHoverInteraction;
160 | @end
161 |
162 | @interface CCUIContentModuleContext : NSObject
163 | @property (readonly, copy, nonatomic) NSString *moduleIdentifier;
164 | @end
165 |
166 | @interface CCUIMenuModuleViewController : CCUIButtonModuleViewController
167 | @property (weak, nonatomic) CCUIContentModuleContext *contentModuleContext;
168 | @end
169 |
170 | @interface CCUILowPowerModuleViewController : CCUIMenuModuleViewController
171 | @end
172 |
173 | @interface CCUILowPowerModule : NSObject
174 | @end
175 |
176 | @interface AMPStatsController : UIViewController
177 | @property (nonatomic, strong) UITableView *tableView;
178 | @end
--------------------------------------------------------------------------------
/amperesettings/Resources/Root.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | items
6 |
7 |
8 | cell
9 | PSGroupCell
10 | label
11 | Appearance
12 |
13 |
14 | cellClass
15 | AmpSelectorCell
16 | default
17 | 0
18 | defaults
19 | com.mtac.amp
20 | label
21 | Percent Color
22 | subtitle
23 | Swipe for more info
24 | key
25 | textStyle
26 | values
27 |
28 | 0
29 | 1
30 | 2
31 |
32 | titles
33 |
34 | Default
35 | Transparent
36 | Custom
37 |
38 | images
39 |
40 | applelogo
41 | shadow
42 | paintpalette.fill
43 |
44 | PostNotification
45 | com.mtac.amp/preferences.changed
46 | iconImageSystem
47 |
48 | name
49 | percent
50 |
51 |
52 |
53 | cellClass
54 | AmpColorCell
55 | defaults
56 | com.mtac.amp
57 | key
58 | textColor
59 | label
60 | Text Color
61 | subtitle
62 | Set percentage label color
63 | height
64 | 70
65 | id
66 | textColor
67 | PostNotification
68 | com.mtac.amp/preferences.changed
69 | iconImageSystem
70 |
71 | name
72 | paintbrush.pointed.fill
73 |
74 |
75 |
76 | cell
77 | PSGroupCell
78 | label
79 | Colors
80 |
81 |
82 | cell
83 | PSSwitchCell
84 | cellClass
85 | AmpSwitchCell
86 | default
87 |
88 | defaults
89 | com.mtac.amp
90 | label
91 | Fill Color
92 | subtitle
93 | Override normal color. Swipe cell to set color
94 | key
95 | overrideColorStandard
96 | id
97 | overrideColorStandard
98 | height
99 | 85
100 | PostNotification
101 | com.mtac.amp/preferences.changed
102 | iconImageSystem
103 |
104 | name
105 | battery.100
106 |
107 |
108 |
109 | cell
110 | PSSwitchCell
111 | cellClass
112 | AmpSwitchCell
113 | default
114 |
115 | defaults
116 | com.mtac.amp
117 | label
118 | Fill Color
119 | subtitle
120 | Override charging color. Swipe cell to set color
121 | key
122 | overrideColorCharging
123 | id
124 | overrideColorCharging
125 | height
126 | 85
127 | PostNotification
128 | com.mtac.amp/preferences.changed
129 | iconImageSystem
130 |
131 | name
132 | battery.100
133 |
134 |
135 |
136 | cell
137 | PSSwitchCell
138 | cellClass
139 | AmpSwitchCell
140 | default
141 |
142 | defaults
143 | com.mtac.amp
144 | label
145 | Fill Color
146 | subtitle
147 | Override low power color. Swipe cell to set color
148 | key
149 | overrideColorLowPower
150 | id
151 | overrideColorLowPower
152 | height
153 | 85
154 | PostNotification
155 | com.mtac.amp/preferences.changed
156 | iconImageSystem
157 |
158 | name
159 | battery.100
160 |
161 |
162 |
163 | cell
164 | PSSwitchCell
165 | cellClass
166 | AmpSwitchCell
167 | default
168 |
169 | defaults
170 | com.mtac.amp
171 | label
172 | Fill Color
173 | subtitle
174 | Override critical color. Swipe cell to set color
175 | key
176 | overrideColorCritical
177 | id
178 | overrideColorCritical
179 | height
180 | 85
181 | PostNotification
182 | com.mtac.amp/preferences.changed
183 | iconImageSystem
184 |
185 | name
186 | battery.100
187 |
188 |
189 |
190 | cell
191 | PSGroupCell
192 | label
193 | Font
194 |
195 |
196 | cellClass
197 | AmpStepperCell
198 | default
199 | 10
200 | defaults
201 | com.mtac.amp
202 | key
203 | fontSize
204 | id
205 | fontSize
206 | label
207 | Font Size:
208 | subtitle
209 | Swipe left to reset
210 | max
211 | 12
212 | min
213 | 4
214 | height
215 | 65
216 | PostNotification
217 | com.mtac.amp/preferences.changed
218 | iconImageSystem
219 |
220 | name
221 | textformat
222 |
223 |
224 |
225 | cell
226 | PSGroupCell
227 | label
228 | Bolt
229 |
230 |
231 | cell
232 | PSSwitchCell
233 | cellClass
234 | AmpSwitchCell
235 | default
236 |
237 | defaults
238 | com.mtac.amp
239 | label
240 | Show Charging Bolt
241 | subtitle
242 | Moves charging bolt next to battery percentage
243 | key
244 | showBolt
245 | id
246 | showBolt
247 | height
248 | 85
249 | PostNotification
250 | com.mtac.amp/preferences.changed
251 | iconImageSystem
252 |
253 | name
254 | bolt.fill
255 |
256 |
257 |
258 | cell
259 | PSGroupCell
260 | label
261 | Gestures
262 |
263 |
264 | cell
265 | PSSwitchCell
266 | cellClass
267 | AmpSwitchCell
268 | default
269 |
270 | defaults
271 | com.mtac.amp
272 | label
273 | Use Low Power Gesture
274 | subtitle
275 | Tap battery icon to toggle Low Power mode
276 | key
277 | useGesture
278 | height
279 | 85
280 | id
281 | lowPower
282 | PostNotification
283 | com.mtac.amp/preferences.changed
284 | iconImageSystem
285 |
286 | name
287 | hand.point.up.fill
288 |
289 |
290 |
291 | cell
292 | PSGroupCell
293 | label
294 | Control Center
295 |
296 |
297 | cell
298 | PSSwitchCell
299 | cellClass
300 | AmpSwitchCell
301 | default
302 |
303 | defaults
304 | com.mtac.amp
305 | label
306 | Enable Battery Info
307 | subtitle
308 | Force Touch on Low Power Mode module in Control Center to view
309 | key
310 | useStatsModule
311 | height
312 | 85
313 | id
314 | statsModule
315 | PostNotification
316 | com.mtac.amp/preferences.changed
317 | iconImageSystem
318 |
319 | name
320 | switch.2
321 |
322 |
323 |
324 | cell
325 | PSGroupCell
326 | label
327 | Source
328 |
329 |
330 | cellClass
331 | AmpCodeCell
332 | defaults
333 | com.mtac.amp
334 | key
335 | lowPowerColor
336 | label
337 | View Source Code
338 | subtitle
339 | https://github.com/MTACS/Ampere
340 | height
341 | 70
342 | iconImageSystem
343 |
344 | name
345 | link
346 |
347 |
348 |
349 | cell
350 | PSGroupCell
351 | label
352 |
353 |
354 |
355 | cell
356 | PSGroupCell
357 | label
358 |
359 |
360 |
361 | title
362 | Ampere
363 |
364 |
365 |
--------------------------------------------------------------------------------
/amperesettings/AmpSwitch.m:
--------------------------------------------------------------------------------
1 | #import "AmpSwitch.h"
2 |
3 | @interface AmpSwitch ()
4 | @property (nonatomic) CGFloat trackThickness;
5 | @property (nonatomic) CGFloat thumbSize;
6 | @end
7 |
8 | @implementation AmpSwitch {
9 | float thumbOnPosition;
10 | float thumbOffPosition;
11 | AmpSwitchStyle thumbStyle;
12 | }
13 | - (id)init {
14 | self = [self initWithSize:AmpSwitchSizeNormal style:AmpSwitchStyleDefault state:AmpSwitchStateOff];
15 | return self;
16 | }
17 | - (id)initWithSize:(AmpSwitchSize)size state:(AmpSwitchState)state {
18 | self.thumbOnTintColor = [UIColor colorWithRed:52./255. green:109./255. blue:241./255. alpha:1.0];
19 | self.thumbOffTintColor = [UIColor colorWithRed:249./255. green:249./255. blue:249./255. alpha:1.0];
20 | self.trackOnTintColor = [UIColor colorWithRed:143./255. green:179./255. blue:247./255. alpha:1.0];
21 | self.trackOffTintColor = [UIColor colorWithRed:193./255. green:193./255. blue:193./255. alpha:1.0];
22 | self.thumbDisabledTintColor = [UIColor colorWithRed:174./255. green:174./255. blue:174./255. alpha:1.0];
23 | self.trackDisabledTintColor = [UIColor colorWithRed:203./255. green:203./255. blue:203./255. alpha:1.0];
24 | self.isEnabled = YES;
25 |
26 | CGRect frame;
27 | CGRect trackFrame = CGRectZero;
28 | CGRect thumbFrame = CGRectZero;
29 | switch (size) {
30 | case AmpSwitchSizeBig:
31 | frame = CGRectMake(0, 0, 50, 40);
32 | self.trackThickness = 23.0;
33 | self.thumbSize = 31.0;
34 | break;
35 |
36 | case AmpSwitchSizeNormal:
37 | frame = CGRectMake(0, 0, 40, 30);
38 | self.trackThickness = 17.0;
39 | self.thumbSize = 24.0;
40 | break;
41 |
42 | case AmpSwitchSizeSmall:
43 | frame = CGRectMake(0, 0, 30, 25);
44 | self.trackThickness = 13.0;
45 | self.thumbSize = 18.0;
46 | break;
47 |
48 | default:
49 | frame = CGRectMake(0, 0, 40, 30);
50 | self.trackThickness = 13.0;
51 | self.thumbSize = 20.0;
52 | break;
53 | }
54 |
55 | trackFrame.size.height = self.trackThickness;
56 | trackFrame.size.width = frame.size.width;
57 | trackFrame.origin.x = 0.0;
58 | trackFrame.origin.y = (frame.size.height-trackFrame.size.height)/2;
59 | thumbFrame.size.height = self.thumbSize;
60 | thumbFrame.size.width = thumbFrame.size.height;
61 | thumbFrame.origin.x = 0.0;
62 | thumbFrame.origin.y = (frame.size.height-thumbFrame.size.height)/2;
63 |
64 | self = [super initWithFrame:frame];
65 |
66 | self.track = [[UIView alloc] initWithFrame:trackFrame];
67 | self.track.backgroundColor = [UIColor grayColor];
68 | self.track.layer.cornerRadius = MIN(self.track.frame.size.height, self.track.frame.size.width)/2;
69 | [self addSubview:self.track];
70 |
71 | self.switchThumb = [[UIButton alloc] initWithFrame:thumbFrame];
72 | self.switchThumb.backgroundColor = [UIColor whiteColor];
73 | self.switchThumb.layer.cornerRadius = self.switchThumb.frame.size.height/2;
74 | self.switchThumb.layer.shadowOpacity = 0.5;
75 | self.switchThumb.layer.shadowOffset = CGSizeMake(0.0, 1.0);
76 | self.switchThumb.layer.shadowColor = [UIColor blackColor].CGColor;
77 | self.switchThumb.layer.shadowRadius = 2.0f;
78 | [self.switchThumb addTarget:self action:@selector(onTouchDown:withEvent:) forControlEvents:UIControlEventTouchDown];
79 | [self.switchThumb addTarget:self action:@selector(onTouchUpOutsideOrCanceled:withEvent:) forControlEvents:UIControlEventTouchUpOutside];
80 | [self.switchThumb addTarget:self action:@selector(switchThumbTapped:) forControlEvents:UIControlEventTouchUpInside];
81 | [self.switchThumb addTarget:self action:@selector(onTouchDragInside:withEvent:) forControlEvents:UIControlEventTouchDragInside];
82 | [self.switchThumb addTarget:self action:@selector(onTouchUpOutsideOrCanceled:withEvent:) forControlEvents:UIControlEventTouchCancel];
83 |
84 | [self addSubview:self.switchThumb];
85 |
86 | thumbOnPosition = self.frame.size.width - self.switchThumb.frame.size.width;
87 | thumbOffPosition = self.switchThumb.frame.origin.x;
88 |
89 | switch (state) {
90 | case AmpSwitchStateOn:
91 | self.isOn = YES;
92 | self.switchThumb.backgroundColor = self.thumbOnTintColor;
93 | CGRect thumbFrame = self.switchThumb.frame;
94 | thumbFrame.origin.x = thumbOnPosition;
95 | self.switchThumb.frame = thumbFrame;
96 | break;
97 |
98 | case AmpSwitchStateOff:
99 | self.isOn = NO;
100 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
101 | break;
102 |
103 | default:
104 | self.isOn = NO;
105 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
106 | break;
107 | }
108 |
109 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(switchAreaTapped:)];
110 | [self addGestureRecognizer:singleTap];
111 | return self;
112 | }
113 | - (id)initWithSize:(AmpSwitchSize)size style:(AmpSwitchStyle)style state:(AmpSwitchState)state {
114 | self = [self initWithSize:size state:state];
115 | if (self) {
116 | self.thumbOnTintColor = LIGHT_TINT;
117 | self.thumbOffTintColor = [UIColor systemGray3Color];
118 | self.trackOnTintColor = DARK_TINT;
119 | self.trackOffTintColor = [UIColor systemGrayColor];
120 | self.thumbDisabledTintColor = [UIColor systemGray2Color];
121 | self.trackDisabledTintColor = [UIColor systemGray2Color];
122 | }
123 | return self;
124 | }
125 | - (void)willMoveToSuperview:(UIView *)newSuperview {
126 | [super willMoveToSuperview:newSuperview];
127 | if (self.isOn == YES) {
128 | self.switchThumb.backgroundColor = self.thumbOnTintColor;
129 | self.track.backgroundColor = self.trackOnTintColor;
130 | } else {
131 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
132 | self.track.backgroundColor = self.trackOffTintColor;
133 | [self setThumbState:NO animated:NO];
134 | }
135 | if (self.isEnabled == NO) {
136 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
137 | self.track.backgroundColor = self.trackDisabledTintColor;
138 | }
139 | }
140 | - (BOOL)getSwitchState {
141 | return self.isOn;
142 | }
143 | - (void)setOn:(BOOL)on {
144 | [self setOn:on animated:NO];
145 | }
146 | - (void)setOn:(BOOL)on animated:(BOOL)animated {
147 | [self setThumbState:on animated:animated];
148 | }
149 | - (void)setEnabled:(BOOL)enabled {
150 | [super setEnabled:enabled];
151 |
152 | [UIView animateWithDuration:0.1 animations:^{
153 | if (enabled == YES) {
154 | if (self.isOn == YES) {
155 | self.switchThumb.backgroundColor = self.thumbOnTintColor;
156 | self.track.backgroundColor = self.trackOnTintColor;
157 | } else {
158 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
159 | self.track.backgroundColor = self.trackOffTintColor;
160 | }
161 | self.isEnabled = YES;
162 | } else {
163 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
164 | self.track.backgroundColor = self.trackDisabledTintColor;
165 | self.isEnabled = NO;
166 | }
167 | }];
168 | }
169 | - (void)switchAreaTapped:(UITapGestureRecognizer *)recognizer {
170 | if ([self.delegate respondsToSelector:@selector(switchStateChanged:)]) {
171 | if (self.isOn == YES) {
172 | [self.delegate switchStateChanged:AmpSwitchStateOff];
173 | } else{
174 | [self.delegate switchStateChanged:AmpSwitchStateOn];
175 | }
176 | }
177 | [self changeThumbState];
178 | }
179 | - (void)changeThumbState {
180 | [self setThumbState:!self.isOn animated:YES];
181 | }
182 | - (void)setThumbState:(BOOL)state animated:(BOOL)animated {
183 | if (state) {
184 | if (animated) {
185 | [UIView animateWithDuration:0.15f delay:0.05f options:UIViewAnimationOptionCurveEaseInOut animations:^{
186 | CGRect thumbFrame = self.switchThumb.frame;
187 | thumbFrame.origin.x = thumbOnPosition + 3.0;
188 | self.switchThumb.frame = thumbFrame;
189 | if (self.isEnabled == YES) {
190 | self.switchThumb.backgroundColor = self.thumbOnTintColor;
191 | self.track.backgroundColor = self.trackOnTintColor;
192 | } else {
193 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
194 | self.track.backgroundColor = self.trackDisabledTintColor;
195 | }
196 | self.userInteractionEnabled = NO;
197 | } completion:^(BOOL finished) {
198 | if (self.isOn == NO) {
199 | self.isOn = YES;
200 | [self sendActionsForControlEvents:UIControlEventValueChanged];
201 | }
202 | self.isOn = YES;
203 | [UIView animateWithDuration:0.15f animations:^{
204 | CGRect thumbFrame = self.switchThumb.frame;
205 | thumbFrame.origin.x = thumbOnPosition;
206 | self.switchThumb.frame = thumbFrame;
207 | } completion:^(BOOL finished) {
208 | self.userInteractionEnabled = YES;
209 | }];
210 | }];
211 | } else {
212 | CGRect thumbFrame = self.switchThumb.frame;
213 | thumbFrame.origin.x = thumbOnPosition;
214 | self.switchThumb.frame = thumbFrame;
215 | if (self.isEnabled == YES) {
216 | self.switchThumb.backgroundColor = self.thumbOnTintColor;
217 | self.track.backgroundColor = self.trackOnTintColor;
218 | } else {
219 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
220 | self.track.backgroundColor = self.trackDisabledTintColor;
221 | }
222 |
223 | if (self.isOn == NO) {
224 | self.isOn = YES;
225 | [self sendActionsForControlEvents:UIControlEventValueChanged];
226 | }
227 | self.isOn = YES;
228 | }
229 | } else {
230 | if (animated) {
231 | [UIView animateWithDuration:0.15f delay:0.05f options:UIViewAnimationOptionCurveEaseInOut animations:^{
232 | CGRect thumbFrame = self.switchThumb.frame;
233 | thumbFrame.origin.x = thumbOffPosition - 3.0;
234 | self.switchThumb.frame = thumbFrame;
235 | if (self.isEnabled == YES) {
236 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
237 | self.track.backgroundColor = self.trackOffTintColor;
238 | } else {
239 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
240 | self.track.backgroundColor = self.trackDisabledTintColor;
241 | }
242 | self.userInteractionEnabled = NO;
243 | } completion:^(BOOL finished) {
244 | if (self.isOn == YES) {
245 | self.isOn = NO;
246 | [self sendActionsForControlEvents:UIControlEventValueChanged];
247 | }
248 | self.isOn = NO;
249 | [UIView animateWithDuration:0.15f animations:^{
250 | CGRect thumbFrame = self.switchThumb.frame;
251 | thumbFrame.origin.x = thumbOffPosition;
252 | self.switchThumb.frame = thumbFrame;
253 | } completion:^(BOOL finished) {
254 | self.userInteractionEnabled = YES;
255 | }];
256 | }];
257 | } else {
258 | CGRect thumbFrame = self.switchThumb.frame;
259 | thumbFrame.origin.x = thumbOffPosition;
260 | self.switchThumb.frame = thumbFrame;
261 | if (self.isEnabled == YES) {
262 | self.switchThumb.backgroundColor = self.thumbOffTintColor;
263 | self.track.backgroundColor = self.trackOffTintColor;
264 | } else {
265 | self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
266 | self.track.backgroundColor = self.trackDisabledTintColor;
267 | }
268 |
269 | if (self.isOn == YES) {
270 | self.isOn = NO;
271 | [self sendActionsForControlEvents:UIControlEventValueChanged];
272 | }
273 | self.isOn = NO;
274 | }
275 | }
276 | }
277 | - (void)onTouchDown:(UIButton *)button withEvent:(UIEvent *)event {
278 |
279 | }
280 | - (void)switchThumbTapped: (id)sender {
281 | if ([self.delegate respondsToSelector:@selector(switchStateChanged:)]) {
282 | if (self.isOn == YES) {
283 | [self.delegate switchStateChanged:AmpSwitchStateOff];
284 | } else{
285 | [self.delegate switchStateChanged:AmpSwitchStateOn];
286 | }
287 | }
288 | [self changeThumbState];
289 | }
290 | - (void)onTouchUpOutsideOrCanceled:(UIButton *)button withEvent:(UIEvent *)event {
291 | UITouch *touch = [[event touchesForView:button] anyObject];
292 | CGPoint previous = [touch previousLocationInView:button];
293 | CGPoint position = [touch locationInView:button];
294 | float dX = position.x - previous.x;
295 | float origin = button.frame.origin.x + dX;
296 |
297 | if (origin > (self.frame.size.width - self.switchThumb.frame.size.width) / 2) {
298 | [self setThumbState:YES animated:YES];
299 | } else {
300 | [self setThumbState:NO animated:YES];
301 | }
302 | }
303 | - (void)onTouchDragInside:(UIButton *)button withEvent:(UIEvent *)event {
304 | UITouch *touch = [[event touchesForView:button] anyObject];
305 | CGPoint previous = [touch previousLocationInView:button];
306 | CGPoint position = [touch locationInView:button];
307 | float dX = position.x - previous.x;
308 |
309 | CGRect thumbFrame = button.frame;
310 |
311 | thumbFrame.origin.x += dX;
312 | thumbFrame.origin.x = MIN(thumbFrame.origin.x, thumbOnPosition);
313 | thumbFrame.origin.x = MAX(thumbFrame.origin.x, thumbOffPosition);
314 |
315 | if (thumbFrame.origin.x != button.frame.origin.x) {
316 | button.frame = thumbFrame;
317 | }
318 | }
319 | @end
--------------------------------------------------------------------------------
/Ampere.xm:
--------------------------------------------------------------------------------
1 | #import "Ampere.h"
2 |
3 | NSString *batteryCharging() {
4 | UIDevice *device = [UIDevice currentDevice];
5 | device.batteryMonitoringEnabled = YES;
6 | switch ([device batteryState]) {
7 | case UIDeviceBatteryStateCharging:
8 | return @"Yes";
9 | case UIDeviceBatteryStateFull:
10 | case UIDeviceBatteryStateUnplugged:
11 | case UIDeviceBatteryStateUnknown:
12 | default:
13 | return @"No";
14 | }
15 | }
16 |
17 | NSString *celsiusTemperature() {
18 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
19 | if (powerSource) {
20 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
21 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
22 | float temperature = -1;
23 | CFNumberRef temperatureRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("Temperature"), kCFAllocatorDefault, 0);
24 | CFNumberGetValue(temperatureRef, kCFNumberFloatType, &temperature);
25 | CFRelease(temperatureRef);
26 | return [NSString stringWithFormat:@"%.1f°C", temperature / 100];
27 | }
28 | }
29 | return @"--°C";
30 | }
31 |
32 | NSString *fahrenheitTemperature() {
33 | NSString *celsiusMethod = celsiusTemperature();
34 | NSString *celsius = [celsiusMethod substringToIndex:celsiusMethod.length - 2];
35 | if ([celsius isEqualToString:@"--"])
36 | return [celsius stringByAppendingString:@"°F"];
37 | return [NSString stringWithFormat:@"%.1f°F", [celsius floatValue] * 1.8 + 32.0];
38 | }
39 |
40 | NSString *cycles() {
41 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
42 | if (powerSource) {
43 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
44 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
45 | int cycles = -1;
46 | CFNumberRef cyclesRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("CycleCount"), kCFAllocatorDefault, 0);
47 | CFNumberGetValue(cyclesRef, kCFNumberIntType, &cycles);
48 | CFRelease(cyclesRef);
49 | return [NSString stringWithFormat:@"%d", cycles];
50 | }
51 | }
52 | return @"-";
53 | }
54 |
55 | int maxCapacity() {
56 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
57 | if (powerSource) {
58 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
59 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
60 | int maxCapacity = -1;
61 | CFNumberRef maxCapRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("AppleRawMaxCapacity"), kCFAllocatorDefault, 0);
62 | CFNumberGetValue(maxCapRef, kCFNumberIntType, &maxCapacity);
63 | CFRelease(maxCapRef);
64 | return maxCapacity;
65 | }
66 | }
67 | return -1;
68 | }
69 |
70 | int designCapacity() {
71 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
72 | if (powerSource) {
73 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
74 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
75 | int designCapacity = -1;
76 | CFNumberRef designCapRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("DesignCapacity"), kCFAllocatorDefault, 0);
77 | CFNumberGetValue(designCapRef, kCFNumberIntType, &designCapacity);
78 | CFRelease(designCapRef);
79 | return designCapacity;
80 | }
81 | }
82 | return -1;
83 | }
84 |
85 | NSString *amperage() {
86 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
87 | if (powerSource) {
88 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
89 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
90 | int amperage = -1;
91 | CFNumberRef amperageRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("InstantAmperage"), kCFAllocatorDefault, 0);
92 | CFNumberGetValue(amperageRef, kCFNumberIntType, &erage);
93 | CFRelease(amperageRef);
94 | return [NSString stringWithFormat:@"%d mA", amperage];
95 | }
96 | }
97 | return @"- mA";
98 | }
99 |
100 | NSString *voltage() {
101 | io_service_t powerSource = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMPowerSource"));
102 | if (powerSource) {
103 | CFMutableDictionaryRef batteryDictionaryRef = NULL;
104 | if (IORegistryEntryCreateCFProperties(powerSource, &batteryDictionaryRef, 0, 0) == KERN_SUCCESS) {
105 | float voltage = -1;
106 | CFNumberRef voltageRef = (CFNumberRef)IORegistryEntryCreateCFProperty(powerSource, CFSTR("Voltage"), kCFAllocatorDefault, 0);
107 | CFNumberGetValue(voltageRef, kCFNumberFloatType, &voltage);
108 | CFRelease(voltageRef);
109 | return [NSString stringWithFormat:@"%.1f V", voltage / 1000];
110 | }
111 | }
112 | return @"- V";
113 | }
114 |
115 | @implementation AMPStatsController
116 | - (id)init {
117 | self = [super init];
118 | if (self) {
119 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadData) name:@"AMPReloadStatsController" object:nil];
120 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadData) name:@"SBUIACStatusChangedNotification" object:nil];
121 | }
122 | return self;
123 | }
124 | - (void)viewDidLoad {
125 | [super viewDidLoad];
126 |
127 | self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
128 | self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
129 | self.tableView.delegate = self;
130 | self.tableView.dataSource = self;
131 | self.tableView.separatorColor = [UIColor clearColor];
132 | self.tableView.backgroundColor = [UIColor clearColor];
133 | self.tableView.userInteractionEnabled = NO;
134 | [self.view addSubview:self.tableView];
135 |
136 | [NSLayoutConstraint activateConstraints:@[
137 | [self.tableView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
138 | [self.tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
139 | [self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
140 | [self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
141 | ]];
142 | }
143 | - (double)tableView:(id)arg1 heightForRowAtIndexPath:(id)arg2 {
144 | return 44.0;
145 | }
146 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
147 | return 1;
148 | }
149 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
150 | return 8;
151 | }
152 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
153 | static NSString *identifier = @"Cell";
154 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
155 | if (!cell) {
156 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
157 | }
158 | cell.backgroundColor = [UIColor clearColor];
159 | UIListContentConfiguration *content = [cell defaultContentConfiguration];
160 | [content setText:[self titleForRow:indexPath.row]];
161 | [content.textProperties setColor:[UIColor whiteColor]];
162 | [content setSecondaryText:[self detailForRow:indexPath.row]];
163 | [content.secondaryTextProperties setColor:[UIColor colorWithWhite:1.0 alpha:0.75]];
164 | [content.secondaryTextProperties setFont:[UIFont systemFontOfSize:18]];
165 | [content setPrefersSideBySideTextAndSecondaryText:YES];
166 | [cell setContentConfiguration:content];
167 |
168 | return cell;
169 | }
170 | - (NSString *)titleForRow:(NSInteger)row {
171 | NSString *title;
172 | switch (row) {
173 | case 0:
174 | title = @"Charging";
175 | break;
176 | case 1:
177 | title = @"Cycles";
178 | break;
179 | case 2:
180 | title = @"Temperature";
181 | break;
182 | case 3:
183 | title = @"Health";
184 | break;
185 | case 4:
186 | title = @"Max Capacity";
187 | break;
188 | case 5:
189 | title = @"Design Capacity";
190 | break;
191 | case 6:
192 | title = @"Amperage";
193 | break;
194 | case 7:
195 | title = @"Voltage";
196 | }
197 | return title;
198 | }
199 | - (NSString *)detailForRow:(NSInteger)row {
200 | NSString *detail;
201 | switch (row) {
202 | case 0:
203 | detail = batteryCharging();
204 | break;
205 | case 1:
206 | detail = cycles();
207 | break;
208 | case 2:
209 | detail = [NSString stringWithFormat:@"%@/%@", celsiusTemperature(), fahrenheitTemperature()];
210 | break;
211 | case 3:
212 | detail = [NSString stringWithFormat:@"%0.f%%", (CGFloat)maxCapacity() / (CGFloat)designCapacity() * 100];
213 | break;
214 | case 4:
215 | detail = [NSString stringWithFormat:@"%d mAh", maxCapacity()];
216 | break;
217 | case 5:
218 | detail = [NSString stringWithFormat:@"%d mAh", designCapacity()];
219 | break;
220 | case 6:
221 | detail = amperage();
222 | break;
223 | case 7:
224 | detail = voltage();
225 | break;
226 | }
227 | return detail;
228 | }
229 | - (void)reloadData {
230 | [self.tableView reloadData];
231 | }
232 | - (BOOL)_canShowWhileLocked {
233 | return YES;
234 | }
235 | @end
236 |
237 | %group Ampere
238 | %hook _UIStatusBarBatteryItem
239 | - (_UIBatteryView *)batteryView {
240 | _UIBatteryView *batteryView = %orig;
241 | if (useGesture) {
242 | [batteryView setUserInteractionEnabled:YES];
243 |
244 | UITapGestureRecognizer *toggleLowPower = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleLowPower:)];
245 | toggleLowPower.numberOfTapsRequired = 1;
246 | if (batteryView.gestureRecognizers.count == 0) {
247 | [batteryView addGestureRecognizer:toggleLowPower];
248 | }
249 | }
250 | return %orig;
251 | }
252 | %new
253 | - (void)toggleLowPower:(id)sender {
254 | if (objc_getClass("_PMLowPowerMode")) {
255 | _PMLowPowerMode *lowPowerMode = [objc_getClass("_PMLowPowerMode") sharedInstance];
256 | BOOL active = [lowPowerMode getPowerMode] == 1;
257 | [lowPowerMode setPowerMode:!active fromSource:@"SpringBoard"];
258 | } else {
259 | long long state = [[objc_getClass("_CDBatterySaver") batterySaver] getPowerMode];
260 | if (state == 0) {
261 | [[objc_getClass("_CDBatterySaver") batterySaver] setPowerMode:1 error:nil];
262 | }
263 | if (state == 1) {
264 | [[objc_getClass("_CDBatterySaver") batterySaver] setPowerMode:0 error:nil];
265 | }
266 | }
267 | }
268 | %end
269 |
270 | %hook _UIBatteryView
271 | - (id)initWithFrame:(CGRect)arg1 {
272 | self = %orig;
273 | if (self) {
274 | // Notifications for automatic prefs updates
275 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateBatteryFillColor) name:@"AmpereUpdate" object:nil];
276 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_updatePercentage) name:@"AmpereUpdate" object:nil];
277 | }
278 | return self;
279 | }
280 | - (id)_batteryTextColor {
281 | if (textStyle == 0) {
282 | if (self.saverModeActive) {
283 | return [UIColor blackColor];
284 | }
285 | } else if (textStyle == 2) { // Use custom percentage text color
286 | NSDictionary *textColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"textColorDict" inDomain:domain];
287 | UIColor *textColor = [UIColor colorWithRed:[textColorDict[@"red"] floatValue] green:[textColorDict[@"green"] floatValue] blue:[textColorDict[@"blue"] floatValue] alpha:1.0];
288 | return textColor;
289 | }
290 | return %orig;
291 | }
292 | %new
293 | - (UIColor *)ampereFillColor {
294 | NSDictionary *standardColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorStandardDict" inDomain:domain];
295 | NSDictionary *lowPowerColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorLowPowerDict" inDomain:domain];
296 | NSDictionary *chargingColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorChargingDict" inDomain:domain];
297 | NSDictionary *criticalColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorCriticalDict" inDomain:domain];
298 | UIColor *standardColor = overrideColorStandard ? [UIColor colorWithRed:[standardColorDict[@"red"] floatValue] green:[standardColorDict[@"green"] floatValue] blue:[standardColorDict[@"blue"] floatValue] alpha:1.0] : [UIColor labelColor];
299 | UIColor *lowPowerColor = overrideColorLowPower ? [UIColor colorWithRed:[lowPowerColorDict[@"red"] floatValue] green:[lowPowerColorDict[@"green"] floatValue] blue:[lowPowerColorDict[@"blue"] floatValue] alpha:1.0] : [UIColor systemYellowColor];
300 | UIColor *chargingColor = overrideColorCharging ? [UIColor colorWithRed:[chargingColorDict[@"red"] floatValue] green:[chargingColorDict[@"green"] floatValue] blue:[chargingColorDict[@"blue"] floatValue] alpha:1.0] : [UIColor systemGreenColor];
301 | UIColor *criticalColor = overrideColorCritical ? [UIColor colorWithRed:[criticalColorDict[@"red"] floatValue] green:[criticalColorDict[@"green"] floatValue] blue:[criticalColorDict[@"blue"] floatValue] alpha:1.0] : [UIColor systemRedColor];
302 |
303 | if (!self.saverModeActive) { // Normal use
304 | if (self.chargingState == 1) { // Charging
305 | return chargingColor;
306 | } else if (self.lowBattery) {
307 | return criticalColor;
308 | } else return standardColor; // Color of normal use (not charging & not in Low Power mode)
309 | } else { // Low Power, overrides custom charging color
310 | return lowPowerColor;
311 | }
312 | }
313 | - (UIColor *)_batteryFillColor { // Return default or custom fill colors based on charging state
314 | return [self ampereFillColor];
315 | }
316 | %end
317 |
318 | %hook _UIStatusBarDataBatteryEntry
319 | - (NSString *)detailString {
320 | return @""; // Return empty string to keep automatic sizing
321 | }
322 | %end
323 |
324 | %hook BCUIRowView
325 | %property (nonatomic, strong) _UIBatteryView *ampereBatteryView;
326 | - (void)_configureBatteryViewIfNecessary {
327 | %orig;
328 | BCUIBatteryView *batteryView = MSHookIvar(self, "_batteryView");
329 |
330 | if (!(kSLSystemVersioniOS16)) {
331 | if (!self.ampereBatteryView) self.ampereBatteryView = [[%c(_UIBatteryView) alloc] initWithSizeCategory:0];
332 | self.ampereBatteryView.translatesAutoresizingMaskIntoConstraints = NO;
333 |
334 | [self addSubview:self.ampereBatteryView];
335 | [NSLayoutConstraint activateConstraints:@[
336 | [self.ampereBatteryView.widthAnchor constraintEqualToAnchor:batteryView.widthAnchor constant:-1],
337 | [self.ampereBatteryView.heightAnchor constraintEqualToAnchor:batteryView.heightAnchor],
338 | [self.ampereBatteryView.centerXAnchor constraintEqualToAnchor:batteryView.centerXAnchor],
339 | [self.ampereBatteryView.centerYAnchor constraintEqualToAnchor:batteryView.centerYAnchor],
340 | ]];
341 | batteryView.hidden = YES;
342 |
343 | [self.ampereBatteryView setChargePercent:batteryView.chargePercent];
344 | [self.ampereBatteryView setChargingState:batteryView.chargingState];
345 | }
346 | }
347 | - (void)_configurePercentChargeLabelIfNecessary {
348 |
349 | }
350 | %end
351 | %end
352 |
353 | %group XVI
354 | %hook _UIStaticBatteryView
355 | - (void)setShowsPercentage:(BOOL)arg0 {
356 | %orig(YES);
357 | }
358 | - (void)_createFillLayer {
359 | %orig;
360 | [self setShowsPercentage:YES];
361 | }
362 | %end
363 |
364 | %hook _UIBatteryView
365 | - (BOOL)_batteryTextIsCutout {
366 | if (textStyle == 1 || [[NSBundle mainBundle].bundleIdentifier isEqualToString:@"com.apple.CarPlayApp"]) {
367 | return YES;
368 | }
369 | return %orig;
370 | }
371 | %end
372 | %end
373 |
374 | %group XV
375 | %hook _UIBatteryView
376 | - (void)setSaverModeActive:(BOOL)arg1 {
377 | %orig;
378 | [self _updatePercentage];
379 | }
380 | - (UIColor *)pinColor {
381 | return (self.chargePercent > 0.97) ? [self _batteryFillColor] : %orig; // Set pin color to fill color, but only when charge exceeds frame of regular battery body
382 | }
383 | + (id)_pinBezierPathForSize:(struct CGSize )arg0 complex:(BOOL)arg1 {
384 | UIBezierPath *path = %orig;
385 | if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
386 | [path applyTransform:CGAffineTransformMakeTranslation(1, 0)]; // Shift pin 1 px, done because setting line interspace width to fill body adds border
387 | }
388 | return path;
389 | }
390 | - (void)_updateFillLayer {
391 | %orig;
392 | [self.fillLayer setCornerRadius:([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? 4.0 : 3.5]; // Set fill corner radius whenever layer updates
393 | [self.fillLayer setCornerCurve:@"circular"];
394 | }
395 | - (void)_updatePercentage {
396 | %orig;
397 | self.percentageLabel.font = [UIFont systemFontOfSize:((self.chargingState == 1 || self.chargePercent == 1.0) && fontSize > 10) ? 10 : fontSize weight:((batterySizing == 1) ? UIFontWeightHeavy : UIFontWeightBold)]; // Set custom percentage font size
398 | if (showBolt && self.chargingState == 1 && self.chargePercent != 1.0) { // Show bolt next to percentage label text
399 | NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
400 | [attachment setBounds:CGRectMake(0, 0, roundf(self.percentageLabel.font.capHeight * 0.6), roundf(self.percentageLabel.font.capHeight))];
401 | [attachment setImage:[[UIImage systemImageNamed:@"bolt.fill"] imageWithTintColor:(self.saverModeActive) ? [UIColor blackColor] : [self _batteryTextColor]]];
402 |
403 | NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:self.percentageLabel.text];
404 | [atr appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
405 | self.percentageLabel.attributedText = atr;
406 | }
407 | self.percentageLabel.layer.allowsGroupBlending = YES;
408 | self.percentageLabel.layer.allowsGroupOpacity = YES;
409 | self.percentageLabel.layer.compositingFilter = (textStyle == 1) || (textStyle == 0 && (self.chargingState != 1) && !self.saverModeActive) ? kCAFilterDestOut : nil; // Enable cutout effect on text when in transparent mode or default (when not charging)
410 |
411 | [self.percentageLabel sizeToFit];
412 | if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { // Support RTL languages
413 | self.percentageLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
414 | }
415 | }
416 | - (CGFloat)_outsideCornerRadiusForTraitCollection:(id)arg0 {
417 | return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? 4.0 : 3.5; // Slightly adjust corner radius for expanded height
418 | }
419 | + (CGFloat)_lineWidthAndInterspaceForIconSize:(NSInteger)arg0 {
420 | return 0;
421 | }
422 | - (CGFloat)bodyColorAlpha {
423 | return 1.0; // Overrides default fill color alpha (normally 0.4)
424 | }
425 | - (CAShapeLayer *)bodyShapeLayer {
426 | CAShapeLayer *bodyLayer = %orig;
427 | bodyLayer.fillColor = [[UIColor labelColor] colorWithAlphaComponent:0.4].CGColor; // Fill exisiting battery view completely
428 | return bodyLayer;
429 | }
430 | - (CALayer *)fillLayer {
431 | CALayer *fill = %orig;
432 | fill.maskedCorners = (self.chargePercent > 0.82) ? (kCALayerMaxXMaxYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMinXMinYCorner) : (kCALayerMinXMaxYCorner | kCALayerMinXMinYCorner); // Rounded corners always on leading edge, flat on trailing until above 92% to match stock radius
433 | fill.bounds = CGRectMake(fill.bounds.origin.x - 1, fill.bounds.origin.y, fill.bounds.size.width, self.bounds.size.height);
434 | return fill;
435 | }
436 | - (CGRect)_bodyRectForTraitCollection:(id)arg0 {
437 | CGRect bodyRect = %orig;
438 | return CGRectMake(bodyRect.origin.x, bodyRect.origin.y, bodyRect.size.width - 1, bodyRect.size.height); // Resize view height to better replicate iOS 16
439 | }
440 | - (CGFloat)_lineWidthAndInterspaceForTraitCollection:(id)arg0 {
441 | return 0; // Disable space between fill layer and border of body layer
442 | }
443 | - (BOOL)_shouldShowBolt {
444 | return NO; // Disable interior bolt when charging
445 | }
446 | - (BOOL)_currentlyShowsPercentage {
447 | return YES; // Always display battery percentage label
448 | }
449 | %end
450 |
451 | %hook _UIStatusBarBatteryItem
452 | + (id)staticIconDisplayIdentifier {
453 | return [self iconDisplayIdentifier]; // Override static identifier, used in iPhone Control Center & everywhere on iPad
454 | }
455 | %end
456 | %end
457 |
458 | %group AmpereCarPlay
459 | %hook _UIBatteryView
460 | - (void)setSaverModeActive:(BOOL)arg1 {
461 | %orig;
462 | [self _updatePercentage];
463 | }
464 | - (void)_createBodyLayers {
465 | %orig;
466 | [self _updatePercentageFont];
467 | }
468 | - (BOOL)showsPercentage {
469 | return YES;
470 | }
471 | - (BOOL)_shouldShowBolt {
472 | return NO;
473 | }
474 | - (BOOL)showsInlineChargingIndicator {
475 | return NO;
476 | }
477 | - (CGFloat)_lineWidthAndInterspaceForTraitCollection:(id)arg0 {
478 | return 0;
479 | }
480 | - (void)_updatePercentage {
481 | %orig;
482 | [self.percentageLabel setFrame:CGRectMake(-1, self.bounds.origin.y, self.bounds.size.width -0.5, self.bounds.size.height)];
483 | [self.percentageLabel setTextAlignment:(NSTextAlignment)1];
484 | [self.percentageLabel setText:[NSString stringWithFormat:@"%.f", [self chargePercent] * 100]];
485 | self.percentageLabel.alpha = 1.0;
486 | // UIFontDescriptor *fontDescriptor = [UIFontDescriptor fontDescriptorWithName:@".SFUI-SemiCondensedBold" size:11];
487 | self.percentageLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightBold]; // Set custom percentage font size
488 | self.percentageLabel.textColor = [self _batteryTextColor];
489 | // self.percentageLabel.font = [UIFont fontWithDescriptor:fontDescriptor size:11];
490 | if (self.chargePercent != 1.0) { // Show bolt next to percentage label text
491 | NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
492 | [attachment setBounds:CGRectMake(0, 0, roundf(self.percentageLabel.font.capHeight * 0.6), roundf(self.percentageLabel.font.capHeight))];
493 | [attachment setImage:[[UIImage systemImageNamed:@"bolt.fill"] imageWithTintColor:(self.saverModeActive) ? [UIColor blackColor] : [self _batteryTextColor]]];
494 |
495 | NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.f", [self chargePercent] * 100]];
496 | [atr appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
497 | self.percentageLabel.attributedText = atr;
498 | }
499 | self.percentageLabel.layer.allowsGroupBlending = YES;
500 | self.percentageLabel.layer.allowsGroupOpacity = YES;
501 | self.percentageLabel.layer.compositingFilter = (textStyle == 1) || (textStyle == 0 && (self.chargingState != 1) && !self.saverModeActive) ? kCAFilterDestOut : nil; // Enable cutout effect on text when in transparent mode or default (when not charging)
502 |
503 | self.pinColor = (self.chargePercent > 0.97) ? [self _batteryFillColor] : [self bodyColor];
504 | }
505 | - (CALayer *)fillLayer {
506 | CALayer *fill = %orig;
507 | fill.maskedCorners = (self.chargePercent > 0.82) ? (kCALayerMaxXMaxYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMinXMinYCorner) : (kCALayerMinXMaxYCorner | kCALayerMinXMinYCorner); // Rounded corners always on leading edge, flat on trailing until above 92% to match stock radius
508 | fill.frame = CGRectMake(fill.frame.origin.x, fill.bounds.origin.y, fill.bounds.size.width - 0.5, self.bounds.size.height);
509 | return fill;
510 | }
511 | - (CAShapeLayer *)bodyShapeLayer {
512 | CAShapeLayer *bodyLayer = %orig;
513 | bodyLayer.fillColor = [[UIColor labelColor] colorWithAlphaComponent:0.4].CGColor; // Fill exisiting battery view completely
514 | return bodyLayer;
515 | }
516 | - (void)_updateFillLayer {
517 | %orig;
518 | [self.fillLayer setCornerRadius:3]; // Set fill corner radius whenever layer updates
519 | [self.fillLayer setCornerCurve:@"circular"];
520 | }
521 | - (UIColor *)pinColor {
522 | return (self.chargePercent > 0.97) ? [self _batteryFillColor] : [self bodyColor]; // Set pin color to fill color, but only when charge exceeds frame of regular battery body
523 | }
524 | - (CGRect)_bodyRectForTraitCollection:(id)arg0 {
525 | CGRect bodyRect = %orig;
526 | return CGRectMake(bodyRect.origin.x, bodyRect.origin.y, bodyRect.size.width - 1, bodyRect.size.height); // Resize view height to better replicate iOS 16
527 | }
528 | + (id)_pinBezierPathForSize:(struct CGSize )arg0 complex:(BOOL)arg1 {
529 | UIBezierPath *path = %orig;
530 | [path applyTransform:CGAffineTransformMakeTranslation(2, 0)]; // Shift pin 1 px, done because setting line interspace width to fill body adds borderr
531 | return path;
532 | }
533 | - (id)_batteryTextColor {
534 | if (textStyle == 0) {
535 | if (self.saverModeActive) {
536 | return [UIColor blackColor];
537 | }
538 | } else if (textStyle == 2) { // Use custom percentage text color
539 | NSDictionary *textColorDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"textColorDict" inDomain:domain];
540 | UIColor *textColor = [UIColor colorWithRed:[textColorDict[@"red"] floatValue] green:[textColorDict[@"green"] floatValue] blue:[textColorDict[@"blue"] floatValue] alpha:1.0];
541 | return textColor;
542 | }
543 | return %orig;
544 | }
545 | %end
546 | %end
547 |
548 | %group BatteryInfo
549 | %hook CCUIToggleViewController
550 | - (void)viewDidLoad {
551 | %orig;
552 | if ([self.module isKindOfClass:%c(CCUILowPowerModule)]) {
553 | AMPStatsController *statsController = [[AMPStatsController alloc] init];
554 | if (![self.view.subviews containsObject:statsController.view]) {
555 | statsController.view.translatesAutoresizingMaskIntoConstraints = NO;
556 | statsController.view.tag = 9999;
557 | statsController.view.hidden = YES;
558 | [self addChildViewController:statsController];
559 | [self.view addSubview:statsController.view];
560 |
561 | [NSLayoutConstraint activateConstraints:@[
562 | [statsController.view.topAnchor constraintEqualToAnchor:self.view.topAnchor],
563 | [statsController.view.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
564 | [statsController.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
565 | [statsController.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
566 | ]];
567 | }
568 | }
569 | }
570 | - (BOOL)shouldFinishTransitionToExpandedContentModule {
571 | if ([self.module isKindOfClass:%c(CCUILowPowerModule)]) {
572 | return YES;
573 | }
574 | return %orig;
575 | }
576 | - (CGFloat)preferredExpandedContentHeight {
577 | if ([self.module isKindOfClass:%c(CCUILowPowerModule)]) {
578 | return HEIGHT * 0.5;
579 | }
580 | return %orig;
581 | }
582 | - (CGFloat)preferredExpandedContentWidth {
583 | if ([self.module isKindOfClass:%c(CCUILowPowerModule)]) {
584 | return WIDTH * 0.6;
585 | }
586 | return %orig;
587 | }
588 | - (void)willTransitionToExpandedContentMode:(BOOL)arg0 {
589 | %orig;
590 | if ([self.module isKindOfClass:%c(CCUILowPowerModule)]) {
591 | UIView *statsView = (UIView *)[self.view viewWithTag:9999];
592 | statsView.hidden = !arg0;
593 |
594 | self.buttonView.hidden = arg0;
595 |
596 | if (arg0) {
597 | [[NSNotificationCenter defaultCenter] postNotificationName:@"AMPReloadStatsController" object:nil];
598 | }
599 | }
600 | }
601 | %end
602 |
603 | %hook CCUIContentModuleContainerViewController
604 | - (BOOL)clickPresentationInteractionShouldPresent:(id)arg0 {
605 | if ([self.moduleIdentifier isEqualToString:@"com.apple.control-center.LowPowerModule"] && SYSTEM_VERSION_GREATER_THAN(@"15.0")) {
606 | return YES;
607 | }
608 | return %orig;
609 | }
610 | - (void)viewDidLoad {
611 | %orig;
612 | if ([self.moduleIdentifier isEqualToString:@"com.apple.control-center.LowPowerModule"] && SYSTEM_VERSION_GREATER_THAN(@"15.0")) {
613 | AMPStatsController *statsController = [[AMPStatsController alloc] init];
614 | if (![self.view.subviews containsObject:statsController.view]) {
615 | statsController.view.translatesAutoresizingMaskIntoConstraints = NO;
616 | statsController.view.tag = 9999;
617 | statsController.view.hidden = YES;
618 | [self addChildViewController:statsController];
619 | [self.view addSubview:statsController.view];
620 |
621 | [NSLayoutConstraint activateConstraints:@[
622 | [statsController.view.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
623 | [statsController.view.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
624 | [statsController.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16],
625 | [statsController.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16],
626 | [statsController.view.heightAnchor constraintEqualToConstant:HEIGHT * 0.6],
627 | ]];
628 | }
629 | }
630 | }
631 | - (void)transitionToExpandedMode:(BOOL)arg0 {
632 | %orig;
633 | if ([self.moduleIdentifier isEqualToString:@"com.apple.control-center.LowPowerModule"] && SYSTEM_VERSION_GREATER_THAN(@"15.0")) {
634 | UIView *statsView = (UIView *)[self.view viewWithTag:9999];
635 | statsView.hidden = !arg0;
636 |
637 | self.contentViewController.view.hidden = arg0;
638 | CCUIContentModuleContentContainerView *containerView = self.contentContainerView;
639 | MTMaterialView *materialView = MSHookIvar(containerView, "_moduleMaterialView");
640 | materialView.hidden = arg0;
641 |
642 | if (arg0) {
643 | [[NSNotificationCenter defaultCenter] postNotificationName:@"AMPReloadStatsController" object:nil];
644 | }
645 | }
646 | }
647 | %end
648 | %end
649 |
650 | static void reloadStatusBar(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
651 | [[NSNotificationCenter defaultCenter] postNotificationName:@"AmpereUpdate" object:nil]; // Post local update notification
652 | }
653 |
654 | static void loadPreferences(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
655 | NSNumber *enabledValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"enabled" inDomain:domain];
656 | enabled = (enabledValue) ? [enabledValue boolValue] : NO;
657 | NSNumber *showBoltValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"showBolt" inDomain: domain];
658 | showBolt = (showBoltValue) ? [showBoltValue boolValue] : YES;
659 | NSNumber *overrideColorStandardValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorStandard" inDomain:domain];
660 | overrideColorStandard = (overrideColorStandardValue) ? [overrideColorStandardValue boolValue] : NO;
661 | NSNumber *overrideColorChargingValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorCharging" inDomain:domain];
662 | overrideColorCharging = (overrideColorChargingValue) ? [overrideColorChargingValue boolValue] : NO;
663 | NSNumber *overrideColorLowPowerValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorLowPower" inDomain:domain];
664 | overrideColorLowPower = (overrideColorLowPowerValue) ? [overrideColorLowPowerValue boolValue] : NO;
665 | NSNumber *overrideColorCriticalValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"overrideColorCritical" inDomain:domain];
666 | overrideColorCritical = (overrideColorCriticalValue) ? [overrideColorCriticalValue boolValue] : NO;
667 | NSNumber *useGestureValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"useGesture" inDomain:domain];
668 | useGesture = (useGestureValue) ? [useGestureValue boolValue] : NO;
669 | NSNumber *useStatsModuleValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"useStatsModule" inDomain:domain];
670 | useStatsModule = (useStatsModuleValue) ? [useStatsModuleValue boolValue] : YES;
671 |
672 | NSNumber *textStyleValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"textStyle" inDomain:domain];
673 | textStyle = (textStyleValue) ? [textStyleValue integerValue] : 0;
674 | NSNumber *fontSizeValue = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"fontSize" inDomain:domain];
675 | fontSize = (fontSizeValue) ? [fontSizeValue integerValue] : 8;
676 | }
677 |
678 | %ctor {
679 | loadPreferences(NULL, NULL, NULL, NULL, NULL); // Load prefs
680 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, loadPreferences, (CFStringRef)preferencesNotification, NULL, CFNotificationSuspensionBehaviorCoalesce); // Preferences changed
681 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, reloadStatusBar, (CFStringRef)statusBarNotification, NULL, CFNotificationSuspensionBehaviorCoalesce); // Update status bar
682 | if (enabled) {
683 | %init(Ampere);
684 | if (!(kSLSystemVersioniOS16)) {
685 | %init(XV);
686 | } else {
687 | %init(XVI);
688 | if ([[NSBundle mainBundle].bundleIdentifier isEqualToString:@"com.apple.CarPlayApp"]) {
689 | %init(AmpereCarPlay);
690 | }
691 | }
692 | if (useStatsModule) {
693 | %init(BatteryInfo);
694 | }
695 | }
696 | }
--------------------------------------------------------------------------------
/amperesettings/AMPRootListController.m:
--------------------------------------------------------------------------------
1 | #include "AMPRootListController.h"
2 |
3 | @import SafariServices;
4 |
5 | @implementation AMPRootListController
6 | - (NSArray *)specifiers {
7 | if (!_specifiers) {
8 | _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
9 | }
10 |
11 | for (PSSpecifier *specifier in _specifiers) {
12 | if (specifier.properties[@"iconImageSystem"] != nil) {
13 | NSDictionary *systemIconDict = specifier.properties[@"iconImageSystem"];
14 | UIImageSymbolConfiguration *configuration = [UIImageSymbolConfiguration configurationWithPointSize:18 weight:UIImageSymbolWeightSemibold scale:UIImageSymbolScaleMedium];
15 | UIImage *systemIcon = [[UIImage systemImageNamed:systemIconDict[@"name"] withConfiguration:configuration] imageWithTintColor:[UIColor systemGreenColor]];
16 | [specifier setProperty:systemIcon forKey:@"iconImage"];
17 | }
18 | }
19 | return _specifiers;
20 | }
21 | - (UITableViewStyle)tableViewStyle {
22 | return UITableViewStyleInsetGrouped;
23 | }
24 | - (instancetype)init {
25 | self = [super init];
26 | if (self) {
27 | self.enableSwitch = [[AmpSwitch alloc] initWithSize:AmpSwitchSizeBig style:AmpSwitchStyleDefault state:AmpSwitchStateOff];
28 | self.enableSwitch.delegate = self;
29 | self.enableSwitch.thumbOnTintColor = [UIColor systemGreenColor];
30 | self.enableSwitch.thumbOffTintColor = [UIColor labelColor];
31 | self.enableSwitch.trackOnTintColor = [UIColor secondaryLabelColor];
32 | self.enableSwitch.trackOffTintColor = [UIColor secondaryLabelColor];
33 | [self setupButtonMenu];
34 |
35 | self.navigationController.navigationBar.prefersLargeTitles = YES;
36 | self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;
37 | }
38 | return self;
39 | }
40 | - (void)setupButtonMenu {
41 | [self.enableSwitch addTarget:self action:@selector(toggleState:) forControlEvents:UIControlEventTouchUpInside];
42 |
43 | UIAction *respring = [UIAction actionWithTitle:@"Respring" image:[UIImage systemImageNamed:@"gearshape.fill"] identifier:nil handler:^(__kindof UIAction *_Nonnull action) {
44 | [self respring];
45 | }];
46 |
47 | UIAction *reset = [UIAction actionWithTitle:@"Reset Settings" image:[UIImage systemImageNamed:@"gearshape.fill"] identifier:nil handler:^(__kindof UIAction *_Nonnull action) {
48 | [self reset];
49 | }];
50 | reset.attributes = UIMenuElementAttributesDestructive;
51 |
52 | UIMenu *menuActions = [UIMenu menuWithTitle:@"" children:@[respring, reset]];
53 | UIBarButtonItem *optionsItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"gearshape.fill"] menu:menuActions];
54 | optionsItem.tintColor = [UIColor systemGreenColor];
55 |
56 | self.navigationItem.rightBarButtonItems = @[optionsItem];
57 | }
58 | - (void)viewWillAppear:(BOOL)animated {
59 | [super viewWillAppear:animated];
60 |
61 | [self setEnableSwitchState];
62 | [self reloadSpecifiers];
63 |
64 | self.view.tintColor = [UIColor systemGreenColor];
65 | [[UIApplication sharedApplication] keyWindow].tintColor = [UIColor systemGreenColor];
66 | [self.navigationController.navigationItem.navigationBar sizeToFit];
67 | _table.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
68 | }
69 | - (void)viewWillDisappear:(BOOL)animated {
70 | [[UIApplication sharedApplication] keyWindow].tintColor = nil;
71 | [super viewWillDisappear:animated];
72 | }
73 | - (void)viewDidLoad {
74 | [super viewDidLoad];
75 |
76 | self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;
77 |
78 | self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 280)];
79 | self.enableSwitch.translatesAutoresizingMaskIntoConstraints = NO;
80 |
81 | self.batteryView = [[UIImageView alloc] initWithFrame:CGRectZero];
82 | self.batteryView.contentMode = UIViewContentModeScaleAspectFit;
83 | self.batteryView.translatesAutoresizingMaskIntoConstraints = NO;
84 | self.batteryView.image = [[UIImage systemImageNamed:@"bolt.fill"] imageWithTintColor:[UIColor systemGreenColor]];
85 | self.batteryView.tintColor = [UIColor systemGreenColor];
86 | self.batteryView.layer.shadowOpacity = 1.0f;
87 | self.batteryView.layer.shadowOffset = CGSizeZero;
88 | self.batteryView.layer.shadowColor = [UIColor systemGreenColor].CGColor;
89 |
90 | CABasicAnimation *glowanimation = [CABasicAnimation animation];
91 | glowanimation.keyPath = @"shadowRadius";
92 | glowanimation.fromValue = @0;
93 | glowanimation.toValue = @10;
94 | glowanimation.autoreverses = YES;
95 | glowanimation.duration = 1.0;
96 | glowanimation.repeatCount = HUGE_VALF;
97 | [self.batteryView.layer addAnimation:glowanimation forKey:@"glow"];
98 |
99 | self.segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"Charging", @"Normal", @"Low Power", @"Critical"]];
100 | self.segmentedControl.translatesAutoresizingMaskIntoConstraints = NO;
101 | self.segmentedControl.frame = CGRectZero;
102 | self.segmentedControl.selectedSegmentIndex = [[[NSUserDefaults standardUserDefaults] objectForKey:@"preferenceSection" inDomain:domain] integerValue] ?: 0;
103 | self.segmentedControl.backgroundColor = [UIColor systemBackgroundColor];
104 | self.segmentedControl.selectedSegmentTintColor = [UIColor tableCellGroupedBackgroundColor];
105 | [self.segmentedControl addTarget:self action:@selector(segmentChanged:) forControlEvents: UIControlEventValueChanged];
106 | [self.segmentedControl setDividerImage:[UIImage new] forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
107 |
108 | [[UISegmentedControl appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor labelColor]} forState:UIControlStateSelected];
109 | [[UISegmentedControl appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor secondaryLabelColor]} forState:UIControlStateNormal];
110 |
111 | self.sectionSegmentLabel = [[UILabel alloc] initWithFrame:CGRectZero];
112 | self.sectionSegmentLabel.translatesAutoresizingMaskIntoConstraints = NO;
113 | self.sectionSegmentLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold];
114 | self.sectionSegmentLabel.textColor = [UIColor secondaryLabelColor];
115 | self.sectionSegmentLabel.textAlignment = NSTextAlignmentCenter;
116 | self.sectionSegmentLabel.text = @"ꜜ Select Battery Mode to Configure ꜜ";
117 |
118 | [self.headerView addSubview:self.enableSwitch];
119 | [self.headerView addSubview:self.batteryView];
120 | [self.headerView addSubview:self.sectionSegmentLabel];
121 | [self.headerView addSubview:self.segmentedControl];
122 |
123 | [NSLayoutConstraint activateConstraints:@[
124 | [self.batteryView.widthAnchor constraintEqualToConstant:50],
125 | [self.batteryView.heightAnchor constraintEqualToConstant:75],
126 | [self.batteryView.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
127 | [self.batteryView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor constant:8],
128 | [self.enableSwitch.topAnchor constraintEqualToAnchor:self.batteryView.bottomAnchor constant:8],
129 | [self.enableSwitch.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
130 | [self.enableSwitch.widthAnchor constraintEqualToConstant:50],
131 | [self.enableSwitch.heightAnchor constraintEqualToConstant:40],
132 | [self.segmentedControl.bottomAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:-30],
133 | [self.segmentedControl.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:16],
134 | [self.segmentedControl.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
135 | [self.segmentedControl.heightAnchor constraintEqualToConstant:40],
136 | [self.sectionSegmentLabel.bottomAnchor constraintEqualToAnchor:self.segmentedControl.topAnchor constant:-5],
137 | [self.sectionSegmentLabel.heightAnchor constraintEqualToConstant:40],
138 | [self.sectionSegmentLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
139 | [self.sectionSegmentLabel.widthAnchor constraintEqualToAnchor:self.segmentedControl.widthAnchor],
140 | ]];
141 | _table.tableHeaderView = self.headerView;
142 | }
143 | - (void)segmentChanged:(UISegmentedControl *)segmentControl {
144 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:segmentControl.selectedSegmentIndex] forKey:@"preferenceSection" inDomain:domain];
145 | [self reloadSpecifiers];
146 | }
147 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
148 | PSTableCell *tableCell = (PSTableCell *)cell;
149 | if (([tableCell.specifier.properties[@"id"] isEqualToString:@"fontSize"] || [tableCell.specifier.properties[@"id"] isEqualToString:@"showBolt"]) && SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"16.0") && [[objc_getClass("BSPlatform") sharedInstance] homeButtonType] == 2) {
150 | [tableCell setCellEnabled:NO];
151 | }
152 | }
153 | - (void)reloadSpecifiers {
154 | [super reloadSpecifiers];
155 | switch (self.segmentedControl.selectedSegmentIndex) {
156 | case 0:
157 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorStandard"]] animated:NO];
158 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorLowPower"]] animated:NO];
159 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCritical"]] animated:NO];
160 | break;
161 | case 1:
162 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCharging"]] animated:NO];
163 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorLowPower"]] animated:NO];
164 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCritical"]] animated:NO];
165 | break;
166 | case 2:
167 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorStandard"]] animated:NO];
168 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCharging"]] animated:NO];
169 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCritical"]] animated:NO];
170 | break;
171 | case 3:
172 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorStandard"]] animated:NO];
173 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorLowPower"]] animated:NO];
174 | [self removeContiguousSpecifiers:@[[self specifierForID:@"overrideColorCharging"]] animated:NO];
175 | break;
176 | }
177 | }
178 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
179 | tableView.tableHeaderView = self.headerView;
180 | return [super tableView:tableView cellForRowAtIndexPath:indexPath];
181 | }
182 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
183 | UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
184 | NSString *title = [self tableView:tableView titleForHeaderInSection:section];
185 | if (title != nil) {
186 | titleLabel.textColor = [UIColor secondaryLabelColor];
187 | titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightSemibold];
188 | titleLabel.text = [NSString stringWithFormat:@" %@", title];
189 | }
190 | return titleLabel;
191 | }
192 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
193 | if ([self tableView:tableView titleForHeaderInSection:section] != nil) {
194 | return 40;
195 | }
196 | return 10;
197 | }
198 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
199 | if (section == [self numberOfGroups] - 1) {
200 | UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(([UIScreen mainScreen].bounds.size.width / 2) - 100, 0, 200, 100)];
201 | titleLabel.numberOfLines = 2;
202 | titleLabel.textColor = [UIColor secondaryLabelColor];
203 | titleLabel.textAlignment = NSTextAlignmentCenter;
204 |
205 | NSString *primary = @"Ampere";
206 | NSString *secondary = @"v1.4 © MTAC";
207 |
208 | NSMutableAttributedString *final = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n%@", primary, secondary]];
209 | [final addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18 weight:UIFontWeightSemibold] range:[final.string rangeOfString:primary]];
210 | [final addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12 weight:UIFontWeightRegular] range:[final.string rangeOfString:secondary]];
211 |
212 | titleLabel.attributedText = final;
213 | return titleLabel;
214 | }
215 | return nil;
216 | }
217 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
218 | if (section == [self numberOfGroups] - 1) {
219 | return 50;
220 | }
221 | return 0;
222 | }
223 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
224 | if (indexPath.section == 6) {
225 | if (indexPath.row == 0) {
226 | [[NSBundle bundleWithPath:@"/System/Library/Frameworks/SafariServices.framework"] load];
227 | if ([SFSafariViewController class] != nil) {
228 | SFSafariViewController *safariView = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:@"https://github.com/MTACS/Ampere"]];
229 | if ([safariView respondsToSelector:@selector(setPreferredControlTintColor:)]) {
230 | safariView.preferredControlTintColor = [UIColor systemGreenColor];
231 | }
232 | [self.navigationController presentViewController:safariView animated:YES completion:nil];
233 | }
234 | }
235 | }
236 | }
237 | - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
238 | UISwipeActionsConfiguration *swipeActions;
239 | PSTableCell *cell = (PSTableCell *)[tableView cellForRowAtIndexPath:indexPath];
240 | NSMutableArray *actions = [NSMutableArray new];
241 | if ([cell isKindOfClass:NSClassFromString(@"AmpSwitchCell")] && indexPath.section == 1) {
242 | UIContextualAction *colorPickerAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:nil handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
243 | [(AmpSwitchCell *)cell selectColor];
244 | completionHandler(YES);
245 | }];
246 | colorPickerAction.backgroundColor = [(AmpSwitchCell *)cell selectedColor]; // [UIColor tableCellGroupedBackgroundColor];
247 | colorPickerAction.image = [UIImage systemImageNamed:@"paintbrush.pointed.fill"];
248 |
249 | if ([[[NSUserDefaults standardUserDefaults] objectForKey:cell.specifier.properties[@"key"] inDomain:domain] boolValue]) {
250 | [actions addObject:colorPickerAction];
251 | }
252 | } else {
253 | if (indexPath.section == 0) {
254 | if (indexPath.row == 0) {
255 | UIContextualAction *info = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:nil handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
256 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Transparent Text" message:@"\nEnabling transparent text allows the background of the current app to show through the battery's percentage text. This is done to replicate iOS 16's styling, and works best on Homescreen & Lockscreen" preferredStyle:UIAlertControllerStyleAlert];
257 | [alertController addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleDefault handler:nil]];
258 | [self presentViewController:alertController animated:YES completion:nil];
259 | completionHandler(YES);
260 | }];
261 |
262 | info.backgroundColor = [UIColor tableCellGroupedBackgroundColor];
263 | info.image = [UIImage systemImageNamed:@"info.circle.fill"];
264 |
265 | [actions addObject:info];
266 | }
267 | } else if (indexPath.section == 2) {
268 | if (indexPath.row == 0) {
269 | UIContextualAction *reset = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:nil handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
270 | [[NSUserDefaults standardUserDefaults] setObject:@10 forKey:@"fontSize" inDomain:domain];
271 | [[NSUserDefaults standardUserDefaults] synchronize];
272 | [self reloadSpecifier:cell.specifier];
273 | completionHandler(YES);
274 | }];
275 |
276 | reset.backgroundColor = [UIColor tableCellGroupedBackgroundColor];
277 | reset.image = [UIImage systemImageNamed:@"arrow.counterclockwise.circle"];
278 |
279 | [actions addObject:reset];
280 | }
281 | }
282 | }
283 | swipeActions = [UISwipeActionsConfiguration configurationWithActions:actions];
284 | swipeActions.performsFirstActionWithFullSwipe = YES;
285 | return swipeActions;
286 | }
287 | - (void)setEnableSwitchState {
288 | if (![[[NSUserDefaults standardUserDefaults] objectForKey:@"enabled" inDomain:domain] boolValue]) {
289 | [[self enableSwitch] setOn:NO animated:NO];
290 | } else {
291 | [[self enableSwitch] setOn:YES animated:NO];
292 | }
293 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/statusbar.changed", nil, nil, true);
294 | }
295 | - (void)switchStateChanged:(AmpSwitchState)switchState {
296 | AudioServicesPlaySystemSound(1519);
297 | if (!self.enableSwitch.isOn) {
298 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"enabled" inDomain:domain];
299 | [self.enableSwitch setOn:YES animated:YES];
300 | } else {
301 | [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:@"enabled" inDomain:domain];
302 | [self.enableSwitch setOn:NO animated:YES];
303 | }
304 | [[NSUserDefaults standardUserDefaults] synchronize];
305 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
306 | [self respring];
307 | });
308 | }
309 | - (void)setPreferenceValue:(id)value specifier:(PSSpecifier *)specifier {
310 | [super setPreferenceValue:value specifier:specifier];
311 | // [self reloadSpecifiers];
312 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/statusbar.changed", nil, nil, true);
313 | }
314 | - (void)respring {
315 | UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
316 | indicator.translatesAutoresizingMaskIntoConstraints = NO;
317 |
318 | UIAlertController *applyAlert = [UIAlertController alertControllerWithTitle:@"\n\n" message:@"\n" preferredStyle:UIAlertControllerStyleAlert];
319 | [applyAlert.view addSubview:indicator];
320 |
321 | [NSLayoutConstraint activateConstraints:@[
322 | [indicator.centerXAnchor constraintEqualToAnchor:applyAlert.view.centerXAnchor],
323 | [indicator.centerYAnchor constraintEqualToAnchor:applyAlert.view.centerYAnchor],
324 | ]];
325 |
326 | [indicator startAnimating];
327 | [self presentViewController:applyAlert animated:true completion:nil];
328 |
329 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.75 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
330 | SBSRelaunchAction *respringAction = [NSClassFromString(@"SBSRelaunchAction") actionWithReason:@"RestartRenderServer" options:4 targetURL:[NSURL URLWithString:@"prefs:root=Bridge"]];
331 | FBSSystemService *frontBoardService = [NSClassFromString(@"FBSSystemService") sharedService];
332 | NSSet *actions = [NSSet setWithObject:respringAction];
333 | [frontBoardService sendActions:actions withResult:nil];
334 | });
335 | }
336 | - (void)reset {
337 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Reset Settings" message:@"\n All settings will be restored to default and device will respring. Continue?" preferredStyle:UIAlertControllerStyleAlert];
338 | [alertController addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleDefault handler:nil]];
339 | [alertController addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
340 | [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:domain];
341 | [self reloadSpecifiers];
342 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
343 | [self respring];
344 | });
345 | }]];
346 | [self presentViewController:alertController animated:YES completion:nil];
347 | }
348 | @end
349 |
350 | @implementation AmpColorCell
351 | @dynamic control;
352 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier {
353 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier specifier:specifier];
354 | if (self) {
355 | self.accessoryView = self.control;
356 | self.detailTextLabel.text = [specifier.properties objectForKey:@"subtitle"];
357 | self.detailTextLabel.numberOfLines = 2;
358 | [self setCellEnabled:[[[NSUserDefaults standardUserDefaults] objectForKey:@"textStyle" inDomain:domain] integerValue] == 2];
359 | }
360 | return self;
361 | }
362 | - (void)setCellEnabled:(BOOL)cellEnabled {
363 | [super setCellEnabled:cellEnabled];
364 | self.control.backgroundColor = cellEnabled ? [self selectedColor] : [UIColor secondaryLabelColor];
365 | // self.control.hidden = !cellEnabled;
366 | }
367 | - (BOOL)cellEnabled {
368 | return [[[NSUserDefaults standardUserDefaults] objectForKey:@"textStyle" inDomain:domain] integerValue] == 2;
369 | }
370 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier {
371 | [super refreshCellContentsWithSpecifier:specifier];
372 | self.control.backgroundColor = [self cellEnabled] ? [self selectedColor] : [UIColor secondaryLabelColor];
373 | }
374 | - (UIButton *)newControl {
375 | UIButton *colorButton = [UIButton buttonWithType:UIButtonTypeCustom];
376 | colorButton.frame = CGRectMake(0, 0, 30, 30);
377 | colorButton.backgroundColor = [self selectedColor];
378 | colorButton.layer.masksToBounds = NO;
379 | colorButton.layer.cornerRadius = colorButton.frame.size.width / 2;
380 | [colorButton addTarget:self action:@selector(selectColor) forControlEvents:UIControlEventTouchUpInside];
381 | return colorButton;
382 | }
383 | - (void)selectColor {
384 | UIColorPickerViewController *colorPickerController = [[UIColorPickerViewController alloc] init];
385 | colorPickerController.delegate = self;
386 | colorPickerController.supportsAlpha = NO;
387 | colorPickerController.modalPresentationStyle = UIModalPresentationPageSheet;
388 | colorPickerController.modalInPresentation = YES;
389 | colorPickerController.selectedColor = [self selectedColor];
390 | [[self _viewControllerForAncestor] presentViewController:colorPickerController animated:YES completion:nil];
391 | }
392 | - (UIColor *)selectedColor {
393 | NSDictionary *colorDict = [[NSUserDefaults standardUserDefaults] objectForKey:[self.specifier.properties[@"key"] stringByAppendingString:@"Dict"] inDomain:domain];
394 | return colorDict ? [UIColor colorWithRed:[colorDict[@"red"] floatValue] green:[colorDict[@"green"] floatValue] blue:[colorDict[@"blue"] floatValue] alpha:1.0] : [UIColor secondaryLabelColor];
395 | }
396 | - (void)colorPickerViewControllerDidSelectColor:(UIColorPickerViewController *)viewController {
397 | [[NSUserDefaults standardUserDefaults] setObject:[self dictionaryForColor:viewController.selectedColor] forKey:[self.specifier.properties[@"key"] stringByAppendingString:@"Dict"] inDomain:domain];
398 | [[NSUserDefaults standardUserDefaults] synchronize];
399 | self.control.backgroundColor = [self selectedColor];
400 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/statusbar.changed", nil, nil, true);
401 | }
402 | - (NSDictionary *)dictionaryForColor:(UIColor *)color {
403 | const CGFloat *components = CGColorGetComponents(color.CGColor);
404 | NSMutableDictionary *colorDict = [NSMutableDictionary new];
405 | [colorDict setObject:[NSNumber numberWithFloat:components[0]] forKey:@"red"];
406 | [colorDict setObject:[NSNumber numberWithFloat:components[1]] forKey:@"green"];
407 | [colorDict setObject:[NSNumber numberWithFloat:components[2]] forKey:@"blue"];
408 | return colorDict;
409 | }
410 | @end
411 |
412 | @implementation AmpStepperCell
413 | @dynamic control;
414 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier {
415 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier specifier:specifier];
416 | if (self) {
417 | self.accessoryView = self.control;
418 | self.detailTextLabel.text = specifier.properties[@"subtitle"] ?: @"";
419 | self.detailTextLabel.numberOfLines = 2;
420 | }
421 | return self;
422 | }
423 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier {
424 | [super refreshCellContentsWithSpecifier:specifier];
425 | self.control.minimumValue = [specifier.properties[@"min"] doubleValue];
426 | self.control.maximumValue = [specifier.properties[@"max"] doubleValue];
427 | [self _updateLabel];
428 | }
429 | - (void)setCellEnabled:(BOOL)cellEnabled {
430 | [super setCellEnabled:cellEnabled];
431 | self.control.enabled = cellEnabled;
432 | }
433 | - (UIStepper *)newControl {
434 | UIStepper *stepper = [[UIStepper alloc] initWithFrame:CGRectZero];
435 | stepper.continuous = NO;
436 | return stepper;
437 | }
438 | - (NSNumber *)controlValue {
439 | return @(self.control.value);
440 | }
441 | - (void)setValue:(NSNumber *)value {
442 | [super setValue:value];
443 | self.control.value = value.doubleValue;
444 | }
445 | - (void)controlChanged:(UIStepper *)stepper {
446 | [super controlChanged:stepper];
447 | [self _updateLabel];
448 | }
449 | - (void)_updateLabel {
450 | if (!self.control) {
451 | return;
452 | }
453 | NSString *labelText = [NSString stringWithFormat:@"%@ %d", self.specifier.name, (int)self.control.value];
454 | if (labelText != nil) {
455 | self.textLabel.text = labelText;
456 | [self setNeedsLayout];
457 | }
458 | }
459 | - (void)prepareForReuse {
460 | [super prepareForReuse];
461 | self.control.value = 0;
462 | self.control.minimumValue = 0;
463 | self.control.maximumValue = 100;
464 | }
465 | @end
466 |
467 | @implementation AmpSwitchCell
468 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(id)identifier specifier:(PSSpecifier *)specifier {
469 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier];
470 | if (self) {
471 | self.accessoryView = self.control;
472 | self.detailTextLabel.text = specifier.properties[@"subtitle"] ?: @"";
473 | self.detailTextLabel.numberOfLines = [specifier.properties[@"subtitleLines"] intValue] ?: 2;
474 | }
475 | return self;
476 | }
477 | - (id)newControl {
478 | AmpSwitch *switchControl = [[AmpSwitch alloc] initWithSize:AmpSwitchSizeNormal state:AmpSwitchStateOff];
479 | switchControl.delegate = self;
480 | switchControl.thumbOnTintColor = [UIColor systemGreenColor];
481 | switchControl.thumbOffTintColor = [UIColor labelColor];
482 | switchControl.trackOnTintColor = [UIColor secondaryLabelColor];
483 | switchControl.trackOffTintColor = [UIColor secondaryLabelColor];
484 | return switchControl;
485 | }
486 | - (void)switchStateChanged:(AmpSwitchState)currentState {
487 | AudioServicesPlaySystemSound(1519);
488 | }
489 | - (void)selectColor {
490 | UIColorPickerViewController *colorPickerController = [[UIColorPickerViewController alloc] init];
491 | colorPickerController.delegate = self;
492 | colorPickerController.supportsAlpha = NO;
493 | colorPickerController.modalPresentationStyle = UIModalPresentationPageSheet;
494 | colorPickerController.modalInPresentation = YES;
495 | colorPickerController.selectedColor = [self selectedColor];
496 | [[self _viewControllerForAncestor] presentViewController:colorPickerController animated:YES completion:nil];
497 | }
498 | - (UIColor *)selectedColor {
499 | NSDictionary *colorDict = [[NSUserDefaults standardUserDefaults] objectForKey:[self.specifier.properties[@"key"] stringByAppendingString:@"Dict"] inDomain:domain];
500 | return colorDict ? [UIColor colorWithRed:[colorDict[@"red"] floatValue] green:[colorDict[@"green"] floatValue] blue:[colorDict[@"blue"] floatValue] alpha:1.0] : [UIColor secondaryLabelColor];
501 | }
502 | - (void)colorPickerViewControllerDidSelectColor:(UIColorPickerViewController *)viewController {
503 | [[NSUserDefaults standardUserDefaults] setObject:[self dictionaryForColor:viewController.selectedColor] forKey:[self.specifier.properties[@"key"] stringByAppendingString:@"Dict"] inDomain:domain];
504 | [[NSUserDefaults standardUserDefaults] synchronize];
505 |
506 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/statusbar.changed", nil, nil, true);
507 | }
508 | - (NSDictionary *)dictionaryForColor:(UIColor *)color {
509 | const CGFloat *components = CGColorGetComponents(color.CGColor);
510 | NSMutableDictionary *colorDict = [NSMutableDictionary new];
511 | [colorDict setObject:[NSNumber numberWithFloat:components[0]] forKey:@"red"];
512 | [colorDict setObject:[NSNumber numberWithFloat:components[1]] forKey:@"green"];
513 | [colorDict setObject:[NSNumber numberWithFloat:components[2]] forKey:@"blue"];
514 | return colorDict;
515 | }
516 | @end
517 |
518 | @implementation AmpSelectorCell
519 | @dynamic control;
520 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(id)identifier specifier:(PSSpecifier *)specifier {
521 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier];
522 | if (self) {
523 | self.accessoryView = self.control;
524 | self.detailTextLabel.text = specifier.properties[@"subtitle"] ?: @"";
525 | self.detailTextLabel.numberOfLines = 2;
526 |
527 | [self _updateControl];
528 | }
529 | return self;
530 | }
531 | - (id)newControl {
532 | UIButton *selectorButton = [UIButton buttonWithType:UIButtonTypeCustom];
533 | selectorButton.frame = CGRectMake(0, 0, 120, 32);
534 | selectorButton.menu = [self menu];
535 | selectorButton.showsMenuAsPrimaryAction = YES;
536 | selectorButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
537 | [selectorButton setTitleColor:[UIColor secondaryLabelColor] forState:UIControlStateNormal];
538 |
539 | [self _updateControl];
540 | return selectorButton;
541 | }
542 | - (UIMenu *)menu {
543 | NSMutableArray *menuItems = [NSMutableArray new];
544 | NSArray *values = self.specifier.properties[@"values"];
545 | NSArray *titles = self.specifier.properties[@"titles"];
546 | NSArray *images = self.specifier.properties[@"images"];
547 | // NSString *key = [self.specifier.properties objectForKey:@"key"];
548 | for (NSInteger i = 0; i < values.count; i++) {
549 | UIAction *action = [UIAction actionWithTitle:titles[i] image:[UIImage systemImageNamed:images[i]] identifier:nil handler:^(__kindof UIAction *_Nonnull action) {
550 | NSInteger valueIndex = [[values objectAtIndex:i] integerValue];
551 | [self setValue:[NSNumber numberWithInteger:valueIndex]];
552 | [self _updateControl];
553 | }];
554 | if ([[self value] integerValue] == i) {
555 | action.state = UIMenuElementStateOn;
556 | } else {
557 | action.state = UIMenuElementStateOff;
558 | }
559 | [menuItems addObject:action];
560 | }
561 |
562 | UIMenu *menuActions = [UIMenu menuWithTitle:@"" children:menuItems];
563 | return menuActions;
564 | }
565 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier {
566 | [super refreshCellContentsWithSpecifier:specifier];
567 | [self _updateControl];
568 | }
569 | - (void)_updateControl {
570 | NSArray *titles = self.specifier.properties[@"titles"];
571 | NSInteger value = [[self value] integerValue];
572 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/preferences.changed", nil, nil, true);
573 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)@"com.mtac.amp/statusbar.changed", nil, nil, true);
574 |
575 | [(UIButton *)self.control setMenu:[self menu]];
576 | [(UIButton *)self.control setTitle:[titles[value] stringByAppendingString:@" ›"] forState:UIControlStateNormal];
577 | }
578 | - (void)setValue:(id)arg0 {
579 | [super setValue:arg0];
580 | NSString *key = [self.specifier.properties objectForKey:@"key"];
581 | [[NSUserDefaults standardUserDefaults] setObject:arg0 forKey:key inDomain:domain];
582 | }
583 | @end
584 |
585 | @implementation AmpCodeCell
586 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(id)identifier specifier:(PSSpecifier *)specifier {
587 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier];
588 | if (self) {
589 | self.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage systemImageNamed:@"network"] imageWithTintColor:[UIColor secondaryLabelColor]]];
590 | self.detailTextLabel.text = specifier.properties[@"subtitle"] ?: @"";
591 | self.detailTextLabel.numberOfLines = 1;
592 | }
593 | return self;
594 | }
595 | @end
--------------------------------------------------------------------------------