├── .gitattributes
├── .github
└── workflows
│ ├── debug.yml
│ └── release.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── ent.plist
├── hud-prefix.pch
├── ipabuild.sh
├── layout
├── Applications
│ └── Helium.app
│ │ ├── Assets.car
│ │ ├── Info.plist
│ │ ├── credits
│ │ ├── bomberfish.png
│ │ └── fuuko.png
│ │ ├── en.lproj
│ │ └── Localizable.strings
│ │ ├── fonts
│ │ └── .gitkeep
│ │ ├── icon.png
│ │ └── zh-Hans.lproj
│ │ └── Localizable.strings
├── DEBIAN
│ └── control
└── Library
│ └── LaunchDaemons
│ └── com.leemin.helium.plist
└── src
├── app
├── MainApplication.h
├── MainApplication.mm
├── MainApplicationDelegate.h
└── MainApplicationDelegate.mm
├── assets
├── bezels
│ ├── BigNotch.png
│ ├── DynamicIsland.png
│ └── SmallNotch.png
└── credits
│ ├── bomberfish.png
│ ├── fuuko.png
│ ├── leminlimez.png
│ └── lessica.png
├── bridging
├── Helium-Bridging-Header.h
├── SwiftObjCPPBridger.h
└── SwiftObjCPPBridger.m
├── controllers
├── DeviceScaleManager.swift
└── WidgetManager.swift
├── extensions
├── Alert++.swift
├── Color++.swift
├── DarwinNotificationCenter.swift
├── EZTimer.h
├── EZTimer.mm
├── FlipEffect.swift
├── FontUtils.h
├── FontUtils.mm
├── FoundationExtensions.swift
├── Haptic++.swift
├── LunarDate.h
├── LunarDate.mm
├── UsefulFunctions.h
├── UsefulFunctions.mm
├── UserDefaultsExtensions.swift
├── ViewExtensions.swift
├── WeatherUtils.h
└── WeatherUtils.mm
├── helpers
├── private_headers
│ ├── AXEventHandInfoRepresentation.h
│ ├── AXEventPathInfoRepresentation.h
│ ├── AXEventRepresentation.h
│ ├── BackboardServices.h
│ ├── CAFilter.h
│ ├── FBSOrientationObserver.h
│ ├── FBSOrientationUpdate.h
│ ├── IOHIDEvent+KIF.h
│ ├── IOHIDEvent+KIF.m
│ ├── IOKit+SPI.h
│ ├── LSApplicationProxy.h
│ ├── LSApplicationWorkspace.h
│ ├── NSUserDefaults+Private.h
│ ├── SBSAccessibilityWindowHostingController.h
│ ├── SpringBoardServices.h
│ ├── UIApplication+Private.h
│ ├── UIApplicationRotationFollowingController.h
│ ├── UIApplicationRotationFollowingControllerNoTouches.h
│ ├── UIApplicationRotationFollowingWindow.h
│ ├── UIAutoRotatingWindow.h
│ ├── UIEvent+Private.h
│ ├── UIEventDispatcher.h
│ ├── UIEventFetcher.h
│ ├── UITouch+Private.h
│ ├── UITouch-KIFAdditions.h
│ ├── UITouch-KIFAdditions.m
│ ├── UIWindow+Private.h
│ ├── kern_memorystatus.h
│ └── libproc.h
└── ts
│ ├── TSEventFetcher.h
│ ├── TSEventFetcher.mm
│ └── pac_helper.h
├── hud
├── AnyBackdropView.h
├── AnyBackdropView.mm
├── HUDApp.mm
├── HUDHelper.h
├── HUDHelper.mm
├── HUDMainApplication.h
├── HUDMainApplication.mm
├── HUDMainApplicationDelegate.h
├── HUDMainApplicationDelegate.mm
├── HUDMainWindow.h
├── HUDMainWindow.mm
├── HUDRootViewController.h
└── HUDRootViewController.mm
├── views
├── UIApplicationViews.swift
├── navigation
│ ├── HomePageView.swift
│ ├── SettingsView.swift
│ └── WidgetCustomizationView.swift
├── widget
│ ├── WidgetConfigureView.swift
│ ├── WidgetPreferencesView.swift
│ └── WidgetPreviewsView.swift
└── widgetset
│ ├── EditWidgetSetView.swift
│ └── SelectPresetView.swift
└── widgets
├── DeviceScaleManager.h
├── DeviceScaleManager.m
├── WidgetManager.h
└── WidgetManager.mm
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/debug.yml:
--------------------------------------------------------------------------------
1 | name: Build Helium Debug
2 |
3 | on:
4 | push:
5 | branches:
6 | - "**"
7 | tags:
8 | - "test"
9 | pull_request:
10 |
11 | env:
12 | THEOS: ''
13 | XCODE_VERSION: '15.1'
14 | logT: ''
15 |
16 | jobs:
17 | build:
18 | runs-on: macos-13
19 |
20 | steps:
21 | - name: Setup Xcode version
22 | uses: maxim-lobanov/setup-xcode@v1
23 | with:
24 | xcode-version: ${{ env.XCODE_VERSION }}
25 |
26 | - name: Install Homebrew dependencies
27 | run: |
28 | HOMEBREW_NO_AUTO_UPDATE=1 brew install dpkg ldid make libplist openssl@3
29 | echo "/usr/local/opt/make/libexec/gnubin" >> $GITHUB_PATH
30 |
31 | - name: Checkout XXTouchNG/theos
32 | uses: actions/checkout@v4
33 | with:
34 | repository: XXTouchNG/theos
35 | ref: 78ee784d8d3238982c9abdc58cd39919263648b1
36 | path: theos
37 | submodules: recursive
38 |
39 | - name: Add THEOS environment variables
40 | run: |
41 | rm -rf $GITHUB_WORKSPACE/theos/sdks
42 | echo "THEOS=$GITHUB_WORKSPACE/theos" >> $GITHUB_ENV
43 |
44 | - name: Restore additional SDKs
45 | id: cached-sdks-restore
46 | uses: actions/cache/restore@v4
47 | with:
48 | path: ${{ env.THEOS }}/sdks
49 | key: ${{ runner.os }}-sdks-${{ env.XCODE_VERSION }}
50 |
51 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }}
52 | name: Checkout theos/sdks
53 | uses: actions/checkout@v4
54 | with:
55 | repository: theos/sdks
56 | ref: master
57 | path: ${{ env.THEOS }}/sdks
58 |
59 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }}
60 | name: Save SDKs
61 | id: cached-sdks-save
62 | uses: actions/cache/save@v4
63 | with:
64 | path: ${{ env.THEOS }}/sdks
65 | key: ${{ steps.cached-sdks-restore.outputs.cache-primary-key }}
66 |
67 | - name: Checkout
68 | uses: actions/checkout@v4
69 | with:
70 | path: Helium
71 | submodules: recursive
72 |
73 | - name: Setup build environment
74 | run: |
75 | echo "Available SDKs: $(find $THEOS/sdks -name "*.sdk" -maxdepth 1 -print)"
76 | sT=$(TZ=UTC-8 date +'%S')
77 | echo "msT=$(date -j -f "%Y-%m-%d %H:%M:%S" "$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" +%s)" >> $GITHUB_ENV
78 | echo "logT=$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" >> $GITHUB_ENV
79 | echo $msT
80 | echo $logT
81 |
82 | - name: Make IPA
83 | run: |
84 | cd $GITHUB_WORKSPACE/Helium
85 | ./ipabuild.sh --debug
86 |
87 | - name: Collect symbols
88 | run: |
89 | cd $GITHUB_WORKSPACE/Helium
90 | find .theos/obj -name "*.dSYM" -print | zip -r build/Helium_symbols.zip -@
91 |
92 | - name: Upload artifacts
93 | uses: actions/upload-artifact@v4
94 | with:
95 | name: Helium.tipa
96 | path: ${{ github.workspace }}/Helium/build/Helium.tipa
97 |
98 | - name: Upload symbols
99 | uses: actions/upload-artifact@v4
100 | with:
101 | name: Helium_symbols
102 | path: ${{ github.workspace }}/Helium/build/Helium_symbols.zip
103 |
104 | - name: Upload testing release
105 | uses: softprops/action-gh-release@v1
106 | if: startsWith(github.ref, 'refs/tags/test')
107 | with:
108 | name: "for testing"
109 | token: ${{ secrets.GITHUB_TOKEN }}
110 | body: build time ${{ env.logT }}
111 | generate_release_notes: true
112 | prerelease: true
113 | files: |
114 | ${{ github.workspace }}/Helium/build/Helium.tipa
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build Helium Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*.*.**"
7 |
8 | env:
9 | THEOS: ''
10 | XCODE_VERSION: '15.1'
11 | logT: ''
12 |
13 | jobs:
14 | build:
15 | runs-on: macos-13
16 |
17 | steps:
18 | - name: Setup Xcode version
19 | uses: maxim-lobanov/setup-xcode@v1
20 | with:
21 | xcode-version: ${{ env.XCODE_VERSION }}
22 |
23 | - name: Install Homebrew dependencies
24 | run: |
25 | HOMEBREW_NO_AUTO_UPDATE=1 brew install dpkg ldid make libplist openssl@3
26 | echo "/usr/local/opt/make/libexec/gnubin" >> $GITHUB_PATH
27 |
28 | - name: Checkout XXTouchNG/theos
29 | uses: actions/checkout@v4
30 | with:
31 | repository: XXTouchNG/theos
32 | ref: 78ee784d8d3238982c9abdc58cd39919263648b1
33 | path: theos
34 | submodules: recursive
35 |
36 | - name: Add THEOS environment variables
37 | run: |
38 | rm -rf $GITHUB_WORKSPACE/theos/sdks
39 | echo "THEOS=$GITHUB_WORKSPACE/theos" >> $GITHUB_ENV
40 |
41 | - name: Restore additional SDKs
42 | id: cached-sdks-restore
43 | uses: actions/cache/restore@v4
44 | with:
45 | path: ${{ env.THEOS }}/sdks
46 | key: ${{ runner.os }}-sdks-${{ env.XCODE_VERSION }}
47 |
48 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }}
49 | name: Checkout theos/sdks
50 | uses: actions/checkout@v4
51 | with:
52 | repository: theos/sdks
53 | ref: master
54 | path: ${{ env.THEOS }}/sdks
55 |
56 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }}
57 | name: Save SDKs
58 | id: cached-sdks-save
59 | uses: actions/cache/save@v4
60 | with:
61 | path: ${{ env.THEOS }}/sdks
62 | key: ${{ steps.cached-sdks-restore.outputs.cache-primary-key }}
63 |
64 | - name: Checkout
65 | uses: actions/checkout@v4
66 | with:
67 | path: Helium
68 | submodules: recursive
69 |
70 | - name: Setup build environment
71 | run: |
72 | echo "Available SDKs: $(find $THEOS/sdks -name "*.sdk" -maxdepth 1 -print)"
73 | sT=$(TZ=UTC-8 date +'%S')
74 | echo "msT=$(date -j -f "%Y-%m-%d %H:%M:%S" "$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" +%s)" >> $GITHUB_ENV
75 | echo "logT=$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" >> $GITHUB_ENV
76 | echo $msT
77 | echo $logT
78 |
79 | - name: Make IPA
80 | run: |
81 | cd $GITHUB_WORKSPACE/Helium
82 | ./ipabuild.sh
83 |
84 | - name: Collect symbols
85 | run: |
86 | cd $GITHUB_WORKSPACE/Helium
87 | find .theos/obj -name "*.dSYM" -print | zip -r build/Helium_symbols.zip -@
88 |
89 | - name: Upload artifacts
90 | uses: actions/upload-artifact@v4
91 | with:
92 | name: Helium.tipa
93 | path: ${{ github.workspace }}/Helium/build/Helium.tipa
94 |
95 | - name: Upload symbols
96 | uses: actions/upload-artifact@v4
97 | with:
98 | name: Helium_symbols
99 | path: ${{ github.workspace }}/Helium/build/Helium_symbols.zip
100 |
101 | - name: Upload release
102 | uses: softprops/action-gh-release@v1
103 | if: startsWith(github.ref, 'refs/tags/v')
104 | with:
105 | token: ${{ secrets.GITHUB_TOKEN }}
106 | body: build time ${{ env.logT }}
107 | generate_release_notes: true
108 | files: |
109 | ${{ github.workspace }}/Helium/build/Helium.tipa
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .theos
2 | .DS_Store
3 | build
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ARCHS := arm64 arm64e
2 | TARGET := iphone:clang:16.5:14.0
3 | INSTALL_TARGET_PROCESSES := Helium
4 |
5 | TARGET_CC := $(shell xcrun --sdk iphoneos --find clang)
6 | TARGET_CXX := $(shell xcrun --sdk iphoneos --find clang++)
7 | TARGET_LD := $(shell xcrun --sdk iphoneos --find clang++)
8 |
9 | include $(THEOS)/makefiles/common.mk
10 | APPLICATION_NAME = Helium
11 |
12 | SRC_DIR := src
13 | # Directories
14 | APP_DIR := $(SRC_DIR)/app
15 | BRIDGING_DIR := $(SRC_DIR)/bridging
16 | CONTROLLERS_DIR := $(SRC_DIR)/controllers
17 | EXTENSIONS_DIR := $(SRC_DIR)/extensions
18 |
19 | HELPERS_DIR := $(SRC_DIR)/helpers
20 | PRIV_DIR := $(HELPERS_DIR)/private_headers
21 | TS_DIR := $(HELPERS_DIR)/ts
22 |
23 | HUD_DIR := $(SRC_DIR)/hud
24 |
25 | VIEWS_DIR := $(SRC_DIR)/views
26 | NAVVIEWS_DIR := $(VIEWS_DIR)/navigation
27 | WIDGETVIEWS_DIR := $(VIEWS_DIR)/widget
28 | WIDGETSETVIEWS_DIR := $(VIEWS_DIR)/widgetset
29 |
30 | WIDGETS_DIR := $(SRC_DIR)/widgets
31 |
32 | $(APPLICATION_NAME)_USE_MODULES := 0
33 | # Add Files From Directories
34 | $(APPLICATION_NAME)_FILES += $(wildcard $(APP_DIR)/*.mm)
35 | $(APPLICATION_NAME)_FILES += $(wildcard $(BRIDGING_DIR)/*.m)
36 | $(APPLICATION_NAME)_FILES += $(wildcard $(CONTROLLERS_DIR)/*.swift)
37 | $(APPLICATION_NAME)_FILES += $(wildcard $(EXTENSIONS_DIR)/*.swift)
38 | $(APPLICATION_NAME)_FILES += $(wildcard $(EXTENSIONS_DIR)/*.mm)
39 | $(APPLICATION_NAME)_FILES += $(wildcard $(PRIV_DIR)/*.m)
40 | $(APPLICATION_NAME)_FILES += $(wildcard $(TS_DIR)/*.mm)
41 | $(APPLICATION_NAME)_FILES += $(wildcard $(HUD_DIR)/*.mm)
42 | $(APPLICATION_NAME)_FILES += $(wildcard $(VIEWS_DIR)/*.swift)
43 | $(APPLICATION_NAME)_FILES += $(wildcard $(NAVVIEWS_DIR)/*.swift)
44 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETVIEWS_DIR)/*.swift)
45 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETSETVIEWS_DIR)/*.swift)
46 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETS_DIR)/*.mm)
47 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETS_DIR)/*.m)
48 |
49 | $(APPLICATION_NAME)_CFLAGS += -fobjc-arc -Iinclude
50 | $(APPLICATION_NAME)_CFLAGS += -include hud-prefix.pch -Wno-deprecated-declarations
51 | $(APPLICATION_NAME)_SWIFTFLAGS += -import-objc-header src/bridging/Helium-Bridging-Header.h
52 |
53 | $(APPLICATION_NAME)_FRAMEWORKS += CoreGraphics QuartzCore UIKit Foundation
54 | $(APPLICATION_NAME)_PRIVATE_FRAMEWORKS += BackBoardServices GraphicsServices IOKit SpringBoardServices
55 |
56 | ifeq ($(TARGET_CODESIGN),ldid)
57 | $(APPLICATION_NAME)_CODESIGN_FLAGS += -Sent.plist
58 | else
59 | $(APPLICATION_NAME)_CODESIGN_FLAGS += --entitlements ent.plist $(TARGET_CODESIGN_FLAGS)
60 | endif
61 |
62 | include $(THEOS_MAKE_PATH)/application.mk
63 |
64 | after-stage::
65 | $(ECHO_NOTHING)mkdir -p packages $(THEOS_STAGING_DIR)/Payload$(ECHO_END)
66 | $(ECHO_NOTHING)cp -rp $(THEOS_STAGING_DIR)/Applications/Helium.app $(THEOS_STAGING_DIR)/Payload$(ECHO_END)
67 | $(ECHO_NOTHING)cd $(THEOS_STAGING_DIR); zip -qr Helium.tipa Payload; cd -;$(ECHO_END)
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Helium
2 | Status Bar Widgets for TrollStore iPhones on iOS 14+. Works on Jailbroken devices as well.
3 |
4 | More widgets to come in future updates!
5 |
6 | **Note:** on iOS 16+, you must enable developer mode for this to work properly.
7 |
8 | ## Building
9 | [Theos](https://theos.dev) is required to compile the app. The SDK used is iOS 15.0, but you can use any SDK you want.
10 | To change the SDK, go to the `Makefile` and modify the `TARGET` to your SDK version:
11 | ```
12 | TARGET := iphone:clang:[SDK Version]:[Minimum Version]
13 | ```
14 | Run `./ipabuild.sh` to build the ipa. The resulting tipa should be in a folder called 'build'.
15 |
16 | ## Tested Devices
17 | - iPhone 13 Pro (iOS 15.3.1, Jailed & Jailbroken)
18 | - iPhone X (iOS 16.1.1, Jailed & Jailbroken)
19 | - iPhone X (iOS 16.6.1, Jailed)
20 | - iPad 7th Generation (iOS 14.8.1, Jailed & Jailbroken)
21 | - iPad 7th Generation (iOS 16.7.2, Jailbroken)
22 |
23 | ## Credits
24 | - [TrollSpeed](https://github.com/Lessica/TrollSpeed) for the AssistiveTouch logic allowing this to work.
25 | - [Cowabunga](https://github.com/leminlimez/Cowabunga) for part of the code.
26 | - [AsakuraFuuko](https://github.com/AsakuraFuuko) for forking and updating.
--------------------------------------------------------------------------------
/ent.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.AXMediaUtilitiesService-access
6 |
7 | com.apple.DragUI.accessibility
8 |
9 | com.apple.QuartzCore.displayable-context
10 |
11 | com.apple.QuartzCore.secure-mode
12 |
13 | com.apple.accessibility.api
14 |
15 | com.apple.accessibility.physicalinteraction.client
16 |
17 | com.apple.accessibility.zoom.client
18 |
19 | com.apple.assistivetouch.daemon
20 |
21 | com.apple.backboard.client
22 |
23 | com.apple.backboardd.global-pointer-event-routing
24 |
25 | com.apple.backboardd.launchapplications
26 |
27 | com.apple.bluetooth.system
28 |
29 | com.apple.carousel.backlightaccess
30 |
31 | com.apple.coreaudio.allow-opus-codec
32 |
33 | com.apple.coreaudio.register-internal-aus
34 |
35 | com.apple.developer.ubiquity-kvstore-identifier
36 | com.leemin.helium
37 | com.apple.diagnosticd.diagnostic
38 |
39 | com.apple.frontboard.debugapplications
40 |
41 | com.apple.hid.manager.user-access-device
42 |
43 | com.apple.hid.manager.user-access-keyboard
44 |
45 | com.apple.idle-timer-services
46 |
47 | com.apple.managedconfiguration.profiled-access
48 |
49 | com.apple.pointerui.persistentlyHidePointer
50 |
51 | com.apple.private.CoreAuthentication.SPI
52 |
53 | com.apple.private.LocalAuthentication.Storage
54 |
55 | com.apple.private.acccessibility.motionTrackingClient
56 |
57 | com.apple.private.appleaccount.app-hidden-from-icloud-settings
58 |
59 | com.apple.private.applecredentialmanager.allow
60 |
61 | com.apple.private.assets.accessible-asset-types
62 |
63 | com.apple.MobileAsset.VoiceServices.CustomVoice
64 | com.apple.MobileAsset.VoiceServicesVocalizerVoice
65 | com.apple.MobileAsset.VoiceServices.GryphonVoice
66 | com.apple.MobileAsset.MacinTalkVoiceAssets
67 | com.apple.MobileAsset.VoiceServices.CombinedVocalizerVoices
68 |
69 | com.apple.private.avfoundation.capture.nonstandard-client.allow
70 |
71 | com.apple.private.coreaudio.viewInterruptorName.allow
72 |
73 | com.apple.private.hid.client.event-dispatch
74 |
75 | com.apple.private.hid.client.event-filter
76 |
77 | com.apple.private.hid.client.event-monitor
78 |
79 | com.apple.private.hid.client.service-protected
80 |
81 | com.apple.private.hid.manager.client
82 |
83 | com.apple.private.ids.messaging
84 |
85 | com.apple.private.alloy.accessibility.switchcontrol
86 |
87 | com.apple.private.ids.messaging.urgent-priority
88 |
89 | com.apple.private.alloy.accessibility.switchcontrol
90 |
91 | com.apple.private.kernel.jetsam
92 |
93 | com.apple.private.memorystatus
94 |
95 | com.apple.private.persona-mgmt
96 |
97 | com.apple.private.security.no-sandbox
98 |
99 | com.apple.private.sysdiagnose
100 |
101 | com.apple.private.ubiquity-additional-kvstore-identifiers
102 |
103 | com.apple.AssistiveTouch
104 | com.apple.Accessibility.SwitchControl
105 | com.apple.Accessibility
106 |
107 | com.apple.security.exception.mach-lookup.global-name
108 |
109 | com.apple.accessibility.motiontrackingd
110 | com.apple.accessibility.AXSpringBoardServer
111 | com.apple.accessibility.AXPineBoardServer
112 | com.apple.accessibility.AXCarouselServer
113 | AXPerformanceTestReportingServer
114 | com.apple.telephonyutilities.callservicesdaemon.callstatecontroller
115 | com.apple.AXMediaUtilitiesService
116 | com.apple.audio.hapticd
117 | AccessibilityDebuggerServices
118 | com.apple.sysdiagnose.service.xpc
119 | com.apple.TextInput
120 | com.apple.SBUserNotification
121 | com.apple.siri.activation.service
122 | com.apple.commandandcontrol
123 | com.apple.sharing.accessibility
124 | com.apple.TextInput.accessibility
125 | com.apple.appleneuralengine
126 | com.apple.fontservicesd
127 | com.apple.PointerUI.pointeruid.service
128 |
129 | com.apple.security.exception.mach-lookup.local-name
130 |
131 | com.apple.AXMediaUtilitiesService
132 |
133 | com.apple.security.exception.mach-register.global-name
134 |
135 | com.leemin.helium.gsEvents
136 |
137 | com.apple.security.exception.mach-register.local-name
138 |
139 | com.leemin.helium
140 |
141 | com.apple.security.exception.shared-preference.read-only
142 |
143 | com.apple.purplebuddy
144 | com.apple.uikitservices.userInterfaceStyleMode
145 |
146 | com.apple.security.exception.shared-preference.read-write
147 |
148 | com.apple.assistant.logging
149 |
150 | com.apple.security.iokit-user-client-class
151 |
152 | IOSurfaceRootUserClient
153 | AGXDeviceUserClient
154 | IOPMPowerSourceClient
155 |
156 | com.apple.security.system-groups
157 |
158 | systemgroup.com.apple.powerlog
159 |
160 | com.apple.springboard-ui.client
161 |
162 | com.apple.springboard.CFUserNotification
163 |
164 | com.apple.springboard.accessibility-window-hosting
165 |
166 | com.apple.springboard.activateassistant
167 |
168 | com.apple.springboard.appbackgroundstyle
169 |
170 | com.apple.springboard.launchapplications
171 |
172 | com.apple.system-task-ports
173 |
174 | com.apple.system.diagnostics.iokit-properties
175 |
176 | file-read-data
177 |
178 | get-task-allow
179 |
180 | platform-application
181 |
182 | run-unsigned-code
183 |
184 | task_for_pid-allow
185 |
186 | user-preference-read
187 |
188 | user-preference-write
189 |
190 |
191 |
--------------------------------------------------------------------------------
/hud-prefix.pch:
--------------------------------------------------------------------------------
1 | #if __has_include()
2 | #import
3 | #endif
4 |
5 | #ifdef __OBJC__
6 | #if __has_include()
7 | #import
8 | #endif
9 |
10 | #import
11 | #import "src/helpers/private_headers/NSUserDefaults+Private.h"
12 | #import
13 | #import
14 | #endif
15 |
16 | #define USER_DEFAULTS_PATH @"/var/mobile/Library/Preferences/com.leemin.helium.plist"
17 |
18 | // HUD -> APP: Notify APP that the HUD's view is appeared.
19 | #define NOTIFY_LAUNCHED_HUD "com.leemin.notification.hud.launched"
20 |
21 | // APP -> HUD: Notify HUD to dismiss itself.
22 | #define NOTIFY_DISMISSAL_HUD "com.leemin.notification.hud.dismissal"
23 |
24 | // APP -> HUD: Notify HUD that the user defaults has been changed by APP.
25 | #define NOTIFY_RELOAD_HUD "com.leemin.notification.hud.reload"
--------------------------------------------------------------------------------
/ipabuild.sh:
--------------------------------------------------------------------------------
1 | if [[ $* == *--scriptdebug* ]]; then
2 | set -x
3 | fi
4 | set -e
5 |
6 | WORKING_LOCATION="$(pwd)"
7 | APP_BUILD_FILES="$WORKING_LOCATION/layout/Applications/Helium.app"
8 | DEBUG_LOCATION="$WORKING_LOCATION/.theos/obj/debug"
9 | RELEASE_LOCATION="$WORKING_LOCATION/.theos/obj"
10 | if [[ $* == *--debug* ]]; then
11 | BUILD_LOCATION="$DEBUG_LOCATION/Helium.app"
12 | else
13 | BUILD_LOCATION="$RELEASE_LOCATION/Helium.app"
14 | fi
15 |
16 | if [[ $* == *--clean* ]]; then
17 | echo "[*] Cleaning..."
18 | rm -rf build
19 | make clean
20 | fi
21 |
22 | if [ ! -d "build" ]; then
23 | mkdir build
24 | fi
25 | #remove existing archive if there
26 | if [ -d "build/Helium.tipa" ]; then
27 | rm -rf "build/Helium.tipa"
28 | fi
29 |
30 | if ! type "gmake" >/dev/null; then
31 | echo "[!] gmake not found, using macOS bundled make instead"
32 | make clean
33 | if [[ $* == *--debug* ]]; then
34 | make
35 | else
36 | make FINALPACKAGE=1
37 | fi
38 | else
39 | gmake clean
40 | if [[ $* == *--debug* ]]; then
41 | gmake -j"$(sysctl -n machdep.cpu.thread_count)"
42 | else
43 | gmake -j"$(sysctl -n machdep.cpu.thread_count)" FINALPACKAGE=1
44 | fi
45 | fi
46 |
47 | if [ -d $BUILD_LOCATION ]; then
48 | # Add the necessary files
49 | echo "Adding application files"
50 | cp -r "$APP_BUILD_FILES/icon.png" "$BUILD_LOCATION/icon.png"
51 | cp -r "$APP_BUILD_FILES/Info.plist" "$BUILD_LOCATION/Info.plist"
52 | cp -r "$APP_BUILD_FILES/Assets.car" "$BUILD_LOCATION/Assets.car"
53 | cp -r "$APP_BUILD_FILES/en.lproj" "$BUILD_LOCATION/"
54 | cp -r "$APP_BUILD_FILES/zh-Hans.lproj" "$BUILD_LOCATION/"
55 | cp -r "$APP_BUILD_FILES/fonts" "$BUILD_LOCATION/"
56 | cp -r "$APP_BUILD_FILES/credits" "$BUILD_LOCATION/"
57 |
58 | # Create payload
59 | echo "Creating payload"
60 | cd build
61 | mkdir Payload
62 | cp -r $BUILD_LOCATION Payload/Helium.app
63 |
64 | # Archive
65 | echo "Archiving"
66 | if [[ $* != *--debug* ]]; then
67 | strip Payload/Helium.app/Helium
68 | fi
69 | zip -vr Helium.tipa Payload
70 | rm -rf Helium.app
71 | rm -rf Payload
72 | fi
73 |
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/Assets.car:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/Assets.car
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.leemin.helium
7 | CFBundleInfoDictionaryVersion
8 | 6.0
9 | CFBundleName
10 | Helium
11 | CFBundleDisplayName
12 | Helium
13 | CFBundleVersion
14 | 3.2.5
15 | CFBundleExecutable
16 | Helium
17 | NSPrincipalClass
18 | HUDMainApplication
19 | CFBundlePackageType
20 | APPL
21 | CFBundleIconFile
22 | icon.png
23 | CFBundleSignature
24 | ????
25 | UIApplicationShowsViewsWhileLocked
26 |
27 | UIApplicationSystemWindowsSecureKey
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationLandscapeLeft
32 | UIInterfaceOrientationLandscapeRight
33 | UIInterfaceOrientationPortrait
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationLandscapeLeft
38 | UIInterfaceOrientationLandscapeRight
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 |
42 | CFBundleURLTypes
43 |
44 |
45 | CFBundleTypeRole
46 | Editor
47 | CFBundleURLName
48 | com.leemin.helium
49 | CFBundleURLSchemes
50 |
51 | helium
52 |
53 |
54 |
55 | LSApplicationCategoryType
56 | public.app-category.developer-tools
57 | CFBundleLocalizations
58 |
59 | zh
60 | en
61 |
62 | CFBundleDevelopmentRegion
63 | en
64 | UIFileSharingEnabled
65 |
66 | NSHumanReadableCopyright
67 | Copyright © 2009 Apple Inc. All Rights Reserved.
68 | CFBundleShortVersionString
69 | 3.2.6
70 |
71 |
72 |
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/credits/bomberfish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/credits/bomberfish.png
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/credits/fuuko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/credits/fuuko.png
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Alert++.swift */
2 | "Error" = "Error";
3 | "OK" = "OK";
4 | "Cancel" = "Cancel";
5 | "Text" = "Text";
6 |
7 | /* ViewExtensions.swift */
8 | "Enter Value" = "Enter Value";
9 | "Enter a value." = "Enter a value.";
10 |
11 | /* HomePageView.swift */
12 | "You can quit the app now.\nThe HUD will persist on your screen." = "You can quit the app now.\nThe HUD will persist on your screen.";
13 | "Stopped." = "Stopped.";
14 | "Disable HUD" = "Disable HUD";
15 | "Enable HUD" = "Enable HUD";
16 | "Helium" = "Helium";
17 | "Closing HUD" = "Closing HUD";
18 | "Opening HUD" = "Opening HUD";
19 | "Status: Running" = "Status: Running";
20 | "Status: Stopped" = "Status: Stopped";
21 |
22 | /* SettingsView.swift */
23 | "Save" = "Save";
24 | "Version " = "Version ";
25 | "UNKNOWN" = "UNKNOWN";
26 | "Release" = "Release";
27 | "Date Locale" = "Date Locale";
28 | "Weather Api key" = "QWeather Api key";
29 | "Hide Save Confirmation Popup" = "Hide Save Confirmation Popup";
30 | "Display Debug Border" = "Display Debug Border";
31 | "Use Adaptive Colors" = "Use Adaptive Colors";
32 | "Helium Data" = "Helium Data";
33 | "Successfully deleted user data!" = "Successfully deleted user data!";
34 | "Please restart the app to continue." = "Please restart the app to continue.";
35 | "Failed to delete user data!" = "Failed to delete user data!";
36 | "Reset Data" = "Reset Data";
37 | "Preferences" = "Preferences";
38 | "Side Widget Size" = "Side Widget Size";
39 | "Side Size" = "Side Size";
40 | "Center Widget Size" = "Center Widget Size";
41 | "Center Size" = "Center Size";
42 | "Debug Preferences" = "Debug Preferences";
43 | "Main Developer" = "Main Developer";
44 | "Modder" = "Modder";
45 | "TrollSpeed & Assistive Touch Logic" = "TrollSpeed & Assistive Touch Logic";
46 | "UI improvements" = "UI improvements";
47 | "Credits" = "Credits";
48 | "Settings saved successfully" = "Settings saved successfully";
49 |
50 | /* WidgetCustomizationView.swift */
51 | "Delete " = "Delete ";
52 | "Are you sure you want to delete the widget set " = "Are you sure you want to delete the widget set ";
53 | "Enter Name" = "Enter Name";
54 | "Choose a name for the widget set." = "Choose a name for the widget set.";
55 | "Confirm" = "Confirm";
56 | "Name" = "Name";
57 | "Choose Side" = "Choose Side";
58 | "Choose a side for the widget set to anchor to. This can be changed later." = "Choose a side for the widget set to anchor to. This can be changed later.";
59 | "Left" = "Left";
60 | "Center" = "Center";
61 | "Right" = "Right";
62 | "Untitled" = "Untitled";
63 | "Customize" = "Customize";
64 |
65 | /* WidgetPreferencesView.swift */
66 | "Date Format" = "Date Format";
67 | "E MMM dd" = "E MMM dd";
68 | "Network Type" = "Network Type";
69 | "Download" = "Download";
70 | "Upload" = "Upload";
71 | "Speed Icon" = "Speed Icon";
72 | "Minimum Unit" = "Minimum Unit";
73 | "Hide Speed When 0" = "Hide Speed When 0";
74 | "Temperature Unit" = "Temperature Unit";
75 | "Celcius" = "Celcius";
76 | "Fahrenheit" = "Fahrenheit";
77 | "Battery Option" = "Battery Option";
78 | "Watts" = "Watts";
79 | "Charging Current" = "Charging Current";
80 | "Amperage" = "Amperage";
81 | "Charge Cycles" = "Charge Cycles";
82 | "Current Capacity" = "Current Capacity";
83 | "Time Format" = "Time Format";
84 | "Label Text" = "Label Text";
85 | "Example" = "Example";
86 | "Location" = "LocationID or Latitude,Longitude";
87 | "Input" = "Input";
88 | "Get" = "Get";
89 | "Input Location Name" = "Input Location Name";
90 | "Get Location ID" = "Get Location ID";
91 | "Show Percent (%) Symbol" = "Show Percent (%) Symbol";
92 | "Fill Symbol" = "Fill Symbol";
93 | "No Configurable Aspects" = "No Configurable Aspects";
94 | "Save Changes" = "Save Changes";
95 | "Would you like to save changes to the widget?" = "Would you like to save changes to the widget?";
96 | "Weather Format Now" = "Now Weather Format Explanation:\n{i} Weather Icon\t\t\t{n} Weather Text\n{t} Current Temperature\t{h} Humidity\n{w} Wind Direction\t\t{wp} Wind Power\n{bt} Body Temperature\t{pc} Precipitation\n{ps} Pressure\t\t\t\t{v} Visibility\n{c} Cloud Cover";
97 | "Weather Format Today" = "Today Weather Format Explanation:\n{di} Daytime Weather Icon\n{dn} Daytime Weather Text\n{dt} Daytime Temperature\n{dw} Daytime Wind Direction\n{dwp} Daytime Wind Power\n{ni} Nighttime Weather Icon\n{nn} Nighttime Weather Text\n{nt} Nighttime Temperature\n{nw} Nighttime Wind Direction\n{nwp} Nighttime Wind Power\n{tpc} Today Precipitation\n{tuv} Today UV Index\n{th} Today Humidity\n{tps} Today Pressure\n{tv} Today Visibility\n{tc} Today Cloud Cover";
98 | "Format" = "Format";
99 |
100 | /* WidgetPreviewsView.swift */
101 | "ERROR" = "ERROR";
102 | "Unknown" = "Unknown";
103 | "Weather Preview" = "🌤Sun 15°~20° (20°)💧54%";
104 |
105 | /* EditWidgetSetView.swift */
106 | "Enable" = "Enable Widget Set";
107 | "Orientation Mode" = "Orientation Mode";
108 | "Portrait & Landscape" = "Portrait & Landscape";
109 | "Portrait" = "Portrait";
110 | "Landscape" = "Landscape";
111 | "Widget Set Title" = "Widget Set Title";
112 | "Title" = "Title";
113 | "Update Interval (seconds)" = "Update Interval (seconds)";
114 | "Seconds" = "Seconds";
115 | "Widget Set Details" = "Widget Set Details";
116 | "Horizontal Anchor Side" = "Horizontal Anchor Side";
117 | "Vertical Anchor Side" = "Vertical Anchor Side";
118 | "Top" = "Top";
119 | "Bottom" = "Bottom";
120 | "Portrait Offset X" = "Portrait Offset X";
121 | "Portrait Offset Y" = "Portrait Offset Y";
122 | "Landscape Offset X" = "Offset X";
123 | "Landscape Offset Y" = "Offset Y";
124 | "Positioning" = "Positioning";
125 | "Auto Resize" = "Auto Resize";
126 | "Width" = "Width";
127 | "Height" = "Height";
128 | "Size Constraints" = "Size Constraints";
129 | "Background Blur" = "Background Blur";
130 | "Blur Style" = "Blur Style";
131 | "Light" = "Light";
132 | "Dark" = "Dark";
133 | "Blur Corner Radius" = "Blur Corner Radius";
134 | "Blur Alpha" = "Blur Alpha";
135 | "Blur" = "Blur";
136 | "Custom Text Color" = "Custom Text Color";
137 | "Text Color" = "Text Color";
138 | "Set Text Color" = "Set Text Color";
139 | "Adaptive Color" = "Adaptive Color";
140 | "Text Font" = "Text Font";
141 | "Font Preview" = "Preview Abc123";
142 | "Bold Text" = "Bold Text";
143 | "Italic Text" = "Italic Text";
144 | "Text Alignment" = "Text Alignment";
145 | "Font Size" = "Font Size";
146 | "Text Alpha" = "Text Alpha";
147 | "Text Properties" = "Text Properties";
148 | "Add Widget" = "Add Widget";
149 | "Delete Widget" = "Delete Widget";
150 | "Are you sure you want to delete this widget?" = "Are you sure you want to delete this widget?";
151 | "Widgets" = "Widgets";
152 | "Edit Widget" = "Edit Widget";
153 | "Would you like to save the changes to your current widget set?" = "Would you like to save the changes to your current widget set?";
154 |
155 | /* UIApplicationViews.swift */
156 | "Home" = "Home";
157 | "Settings" = "Settings";
158 | "Info" = "Info";
159 | "Make sure you enable developer mode before using! This will not work otherwise." = "Make sure you enable developer mode before using! This will not work otherwise.";
160 | "Not Supported" = "Not Supported";
161 | "This app must be installed with TrollStore." = "This app must be installed with TrollStore.";
162 |
163 | /* WidgetManager.swift */
164 | "Untitled" = "Untitled";
165 | "Date" = "Date";
166 | "Mon Oct 16" = "Mon Oct 16";
167 | "Network" = "Network";
168 | "Device Temperature" = "Device Temperature";
169 | "Battery Details" = "Battery Details";
170 | "Time" = "Time";
171 | "Text Label" = "Text Label";
172 | "Example" = "Example";
173 | "Battery Capacity" = "Battery Capacity";
174 | "Charging Symbol" = "Charging Symbol";
175 | "Weather" = "Weather";
176 |
177 | /* WeatherUtils.mm */
178 | "error" = "error";
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/fonts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/fonts/.gitkeep
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/icon.png
--------------------------------------------------------------------------------
/layout/Applications/Helium.app/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Alert++.swift */
2 | "Error" = "出错了";
3 | "OK" = "确定";
4 | "Cancel" = "取消";
5 | "Text" = "文字段";
6 |
7 | /* ViewExtensions.swift */
8 | "Enter Value" = "请输入值";
9 | "Enter a value." = "请输入一个值";
10 |
11 | /* HomePageView.swift */
12 | "You can quit the app now.\nThe HUD will persist on your screen." = "您现在可以退出应用。\nHUD 将持续显示在屏幕上。";
13 | "Stopped." = "已停止";
14 | "Disable HUD" = "关闭 HUD";
15 | "Enable HUD" = "启用 HUD";
16 | "Helium" = "氦气";
17 | "Closing HUD" = "正在关闭 HUD";
18 | "Opening HUD" = "正在打开 HUD";
19 | "Status: Running" = "状态: 运行中";
20 | "Status: Stopped" = "状态: 已停止";
21 |
22 | /* SettingsView.swift */
23 | "Save" = "保存";
24 | "Version " = "版本 ";
25 | "UNKNOWN" = "未知";
26 | "Release" = "发布";
27 | "Date Locale" = "日期地区";
28 | "Weather Api key" = "和风天气ApiKey";
29 | "Show when Rotating" = "旋转屏幕时显示";
30 | "Hide Save Confirmation Popup" = "不显示保存确认弹窗";
31 | "Display Debug Border" = "显示调试边框";
32 | "Use Adaptive Colors" = "使用自适应颜色";
33 | "Helium Data" = "氦气数据";
34 | "Successfully deleted user data!" = "已成功删除用户数据!";
35 | "Please restart the app to continue." = "请重启应用以继续";
36 | "Failed to delete user data!" = "删除用户数据失败!";
37 | "Reset Data" = "重置数据";
38 | "Preferences" = "偏好设置";
39 | "Side Widget Size" = "侧边小部件大小";
40 | "Side Size" = "侧边尺寸";
41 | "Center Widget Size" = "中心小部件大小";
42 | "Center Size" = "中心尺寸";
43 | "Debug Preferences" = "调试设置";
44 | "Main Developer" = "主开发者";
45 | "Modder" = "修改者";
46 | "TrollSpeed & Assistive Touch Logic" = "TrollSpeed & Assistive Touch 逻辑";
47 | "UI improvements" = "UI优化";
48 | "Credits" = "致谢";
49 | "Settings saved successfully" = "设置保存成功";
50 |
51 | /* WidgetCustomizationView.swift */
52 | "Delete " = "删除 ";
53 | "Are you sure you want to delete the widget set " = "确认要删除小部件集 ";
54 | "Enter Name" = "输入名称";
55 | "Choose a name for the widget set." = "为小部件集选择一个名称";
56 | "Confirm" = "确认";
57 | "Name" = "名称";
58 | "Choose Side" = "选择显示位置";
59 | "Choose a side for the widget set to anchor to. This can be changed later." = "选择小部件集的显示位置。稍后可以更改";
60 | "Left" = "左边";
61 | "Center" = "中间";
62 | "Right" = "右边";
63 | "Untitled" = "未命名";
64 | "Customize" = "自定义";
65 |
66 | /* WidgetPreferencesView.swift */
67 | "Date Format" = "日期格式";
68 | "E MMM dd" = "MM月dd E";
69 | "Network Type" = "网络类型";
70 | "Download" = "下载";
71 | "Upload" = "上传";
72 | "Speed Icon" = "网速图标";
73 | "Minimum Unit" = "最小单位";
74 | "Hide Speed When 0" = "网速为0时隐藏";
75 | "Temperature Unit" = "温度单位";
76 | "Celcius" = "摄氏度";
77 | "Fahrenheit" = "华氏度";
78 | "Battery Option" = "电池选项";
79 | "Watts" = "瓦特";
80 | "Charging Current" = "充电电流";
81 | "Amperage" = "使用电流";
82 | "Charge Cycles" = "充电周期";
83 | "Current Capacity" = "当前容量";
84 | "Time Format" = "时间格式";
85 | "Label Text" = "标签文本";
86 | "Example" = "示例";
87 | "Location" = "地区ID或者经纬度";
88 | "Input" = "输入";
89 | "Get" = "获取";
90 | "Input Location Name" = "输入地区名称";
91 | "Get Location ID" = "获取地区ID";
92 | "Show Percent (%) Symbol" = "显示电量%";
93 | "Fill Symbol" = "填充符号";
94 | "No Configurable Aspects" = "无可配置项";
95 | "Save Changes" = "保存更改";
96 | "Would you like to save changes to the widget?" = "是否保存对小部件的更改?";
97 | "Weather Format Now" = "实时天气格式说明:\n{i} 天气图标\t{n} 天气文字\n{t} 当前温度\t{h} 湿度\n{w} 风向\t\t{wp} 风力\n{bt} 体感温度\t{pc} 降水量\n{ps} 大气压力\t{v} 能见度\n{c} 云量";
98 | "Weather Format Today" = "今日天气格式说明:\n{di} 白天天气图标\t{dn} 白天天气文本\n{dt} 白天气温\t\t{dw} 白天风向\n{dwp} 白天风力\t{nwp} 夜间风力\n{ni} 夜间天气图标\t{nn} 夜间天气文本\n{nt} 夜间气温\t\t{nw} 夜间风向\n{tpc} 今日降水量\t{tuv} 今日紫外线指数\n{th} 今日湿度\t\t{tps} 今日气压\n{tv} 今日能见度\t{tc} 今日云量";
99 | "Format" = "格式";
100 |
101 | /* WidgetPreviewsView.swift */
102 | "ERROR" = "错误";
103 | "Unknown" = "未知";
104 | "Weather Preview" = "🌤晴 15°~20° (20°)💧54%";
105 |
106 | /* EditWidgetSetView.swift */
107 | "Enable" = "启用小部件集";
108 | "Orientation Mode" = "显示模式";
109 | "Portrait & Landscape" = "竖屏和横屏";
110 | "Portrait" = "竖屏";
111 | "Landscape" = "横屏";
112 | "Widget Set Title" = "小部件集标题";
113 | "Title" = "标题";
114 | "Update Interval (seconds)" = "更新间隔 (秒)";
115 | "Seconds" = "秒";
116 | "Widget Set Details" = "小部件集详情";
117 | "Horizontal Anchor Side" = "水平显示位置";
118 | "Vertical Anchor Side" = "垂直显示位置";
119 | "Top" = "顶部";
120 | "Bottom" = "底部";
121 | "Offset X" = "X偏移";
122 | "Offset Y" = "Y偏移";
123 | "Portrait Offset X" = "竖屏X偏移";
124 | "Portrait Offset Y" = "竖屏Y偏移";
125 | "Landscape Offset X" = "横屏X偏移";
126 | "Landscape Offset Y" = "横屏Y偏移";
127 | "Positioning" = "定位";
128 | "Auto Resize" = "自动调整大小";
129 | "Width" = "宽度";
130 | "Height" = "高度";
131 | "Size Constraints" = "尺寸限制";
132 | "Background Blur" = "背景模糊";
133 | "Blur Style" = "模糊样式";
134 | "Light" = "明亮";
135 | "Dark" = "黑暗";
136 | "Blur Corner Radius" = "模糊角半径";
137 | "Blur Alpha" = "模糊透明度";
138 | "Blur" = "模糊";
139 | "Custom Text Color" = "自定义文本颜色";
140 | "Text Color" = "文本颜色";
141 | "Set Text Color" = "设置文本颜色";
142 | "Adaptive Color" = "自适应颜色";
143 | "Text Font" = "文本字体";
144 | "Font Preview" = "预览Abc123";
145 | "Bold Text" = "粗体文本";
146 | "Italic Text" = "斜体文本";
147 | "Text Alignment" = "文本对齐";
148 | "Font Size" = "字体大小";
149 | "Text Alpha" = "文字透明度";
150 | "Text Properties" = "文本属性";
151 | "Add Widget" = "添加小部件";
152 | "Delete Widget" = "删除小部件";
153 | "Are you sure you want to delete this widget?" = "确认删除此小部件?";
154 | "Widgets" = "小部件";
155 | "Edit Widget" = "编辑小部件";
156 | "Save Changes" = "保存更改";
157 | "Would you like to save the changes to your current widget set?" = "是否保存当前小部件集的更改?";
158 |
159 | /* UIApplicationViews.swift */
160 | "Home" = "首页";
161 | "Settings" = "设置";
162 | "Info" = "信息";
163 | "Make sure you enable developer mode before using! This will not work otherwise." = "使用前,请确保已启用开发者模式!否则无法正常工作";
164 | "Not Supported" = "不支持";
165 | "This app must be installed with TrollStore." = "此应用必须通过 TrollStore 安装";
166 |
167 | /* WidgetManager.swift */
168 | "Untitled" = "未命名";
169 | "Date" = "日期";
170 | "Mon Oct 16" = "10月16 周一";
171 | "Network" = "网速";
172 | "Device Temperature" = "设备温度";
173 | "Battery Details" = "电池详情";
174 | "Time" = "时间";
175 | "Text Label" = "文本标签";
176 | "Example" = "示例";
177 | "Battery Capacity" = "电池容量";
178 | "Charging Symbol" = "充电符号";
179 | "Weather" = "天气";
180 |
181 | /* WeatherUtils.mm */
182 | "error" = "出错啦";
--------------------------------------------------------------------------------
/layout/DEBIAN/control:
--------------------------------------------------------------------------------
1 | Package: com.leemin.helium
2 | Name: Helium
3 | Version: 1.0
4 | Section: Tweaks
5 | Depends: firmware (>= 13.0), mobilesubstrate (>= 0.9.7000), com.rpetrich.rocketbootstrap (>= 1.0.9)
6 | Architecture: iphoneos-arm
7 | Author: lemin
8 | Maintainer: lemin
9 | Description: I am on helium
10 |
11 |
--------------------------------------------------------------------------------
/layout/Library/LaunchDaemons/com.leemin.helium.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EnablePressuredExit
6 |
7 | EnableTransactions
8 |
9 | GroupName
10 | wheel
11 | HighPriorityIO
12 |
13 | KeepAlive
14 |
15 | Label
16 | com.leemin.helium
17 | POSIXSpawnType
18 | App
19 | ProcessType
20 | Interactive
21 | ProgramArguments
22 |
23 | /Applications/Helium.app/Helium
24 | -hud
25 |
26 | RunAtLoad
27 |
28 | ThrottleInterval
29 | 5
30 | UserName
31 | root
32 | _AdditionalProperties
33 |
34 | RunningBoard
35 |
36 | Managed
37 |
38 | Reported
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/app/MainApplication.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface MainApplication : UIApplication
6 | @end
7 |
8 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/app/MainApplication.mm:
--------------------------------------------------------------------------------
1 | //
2 | // MainApplication.mm
3 | //
4 | //
5 | // Created by lemin on 10/4/23.
6 | //
7 |
8 | #import "MainApplication.h"
9 |
10 | @implementation MainApplication
11 |
12 | - (instancetype)init
13 | {
14 | self = [super init];
15 | if (self)
16 | {
17 | #if DEBUG
18 | /* Force HIDTransformer to print logs */
19 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogTouch" inDomain:@"com.apple.UIKit"];
20 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesture" inDomain:@"com.apple.UIKit"];
21 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogEventDispatch" inDomain:@"com.apple.UIKit"];
22 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureEnvironment" inDomain:@"com.apple.UIKit"];
23 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureExclusion" inDomain:@"com.apple.UIKit"];
24 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogSystemGestureUpdate" inDomain:@"com.apple.UIKit"];
25 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesturePerformance" inDomain:@"com.apple.UIKit"];
26 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogHIDTransformer" inDomain:@"com.apple.UIKit"];
27 | [[NSUserDefaults standardUserDefaults] synchronize];
28 | #endif
29 | }
30 | return self;
31 | }
32 |
33 | @end
--------------------------------------------------------------------------------
/src/app/MainApplicationDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface MainApplicationDelegate : UIResponder
6 | @property (nonatomic, strong) UIWindow *window;
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/app/MainApplicationDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "MainApplicationDelegate.h"
2 | #import "MainApplication.h"
3 | #import "Helium-Swift.h"
4 | #import "../extensions/FontUtils.h"
5 |
6 | @implementation MainApplicationDelegate
7 |
8 | - (instancetype)init {
9 | if (self = [super init]) {
10 | os_log_debug(OS_LOG_DEFAULT, "- [MainApplicationDelegate init]");
11 | }
12 | return self;
13 | }
14 |
15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
16 | os_log_debug(OS_LOG_DEFAULT, "- [MainApplicationDelegate application:%{public}@ didFinishLaunchingWithOptions:%{public}@]", application, launchOptions);
17 |
18 | // load fonts from app
19 | [FontUtils loadFontsFromFolder:[NSString stringWithFormat:@"%@%@", [[NSBundle mainBundle] resourcePath], @"/fonts"]];
20 | // load fonts from documents
21 | [FontUtils loadFontsFromFolder:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]];
22 | [FontUtils loadAllFonts];
23 |
24 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
25 | [self.window setRootViewController:[[[ContentInterface alloc] init] createUI]];
26 | [self.window makeKeyAndVisible];
27 |
28 | return YES;
29 | }
30 |
31 | @end
--------------------------------------------------------------------------------
/src/assets/bezels/BigNotch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/BigNotch.png
--------------------------------------------------------------------------------
/src/assets/bezels/DynamicIsland.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/DynamicIsland.png
--------------------------------------------------------------------------------
/src/assets/bezels/SmallNotch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/SmallNotch.png
--------------------------------------------------------------------------------
/src/assets/credits/bomberfish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/bomberfish.png
--------------------------------------------------------------------------------
/src/assets/credits/fuuko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/fuuko.png
--------------------------------------------------------------------------------
/src/assets/credits/leminlimez.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/leminlimez.png
--------------------------------------------------------------------------------
/src/assets/credits/lessica.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/lessica.png
--------------------------------------------------------------------------------
/src/bridging/Helium-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // XXTAssistiveTouch-Bridging-Header.h
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | #import "SwiftObjCPPBridger.h"
9 | #import "../extensions/LunarDate.h"
10 | #import "../extensions/FontUtils.h"
11 | #import "../extensions/WeatherUtils.h"
--------------------------------------------------------------------------------
/src/bridging/SwiftObjCPPBridger.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftObjCPPBridger.h
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | #import
9 |
10 | #pragma mark - HUD Functions
11 |
12 | BOOL IsHUDEnabledBridger();
13 | void SetHUDEnabledBridger(BOOL isEnabled);
14 | void waitForNotificationBridger(void (^onFinish)(), BOOL isEnabled);
--------------------------------------------------------------------------------
/src/bridging/SwiftObjCPPBridger.m:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftObjCPPBridger.m
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | #import "SwiftObjCPPBridger.h"
9 |
10 | #pragma mark - HUD Functions
11 |
12 | extern BOOL IsHUDEnabled(void);
13 | extern void SetHUDEnabled(BOOL isEnabled);
14 | extern void waitForNotification(void (^onFinish)(), BOOL isEnabled);
15 |
16 | BOOL IsHUDEnabledBridger()
17 | {
18 | return (int)IsHUDEnabled();
19 | }
20 |
21 | void SetHUDEnabledBridger(BOOL isEnabled)
22 | {
23 | SetHUDEnabled(isEnabled);
24 | }
25 |
26 | void waitForNotificationBridger(void (^onFinish)(), BOOL isEnabled)
27 | {
28 | waitForNotification(onFinish, isEnabled);
29 | }
--------------------------------------------------------------------------------
/src/controllers/DeviceScaleManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeviceScaleManager.swift
3 | //
4 | //
5 | // Created by lemin on 12/15/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct PresetStruct: Identifiable {
12 | var id = UUID()
13 |
14 | var width: Double
15 | var offsetX: Double
16 | var offsetY: Double
17 | }
18 |
19 | enum Preset: String, CaseIterable {
20 | case above = "Above Status Bar"
21 | case below = "Below Status Bar"
22 | }
23 |
24 | class DeviceScaleManager {
25 | static let shared = DeviceScaleManager()
26 |
27 | private func getDeviceName() -> String {
28 | if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
29 | return simulatorModelIdentifier
30 | }
31 |
32 | var sysinfo = utsname()
33 | uname(&sysinfo) // ignore return value
34 | let deviceModel = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)?.trimmingCharacters(in: .controlCharacters)
35 |
36 | return deviceModel ?? ""
37 | }
38 |
39 | /*
40 | Sizes:
41 | 0 = No Notch
42 | 1 = Small Notch
43 | 2 = Large Notch
44 | 3 = Dynamic Island
45 | */
46 | private func getDeviceSize() -> Int {
47 | let deviceModel: String = getDeviceName()
48 |
49 | // get the notch size
50 | if (deviceModel.starts(with: "iPhone14")) {
51 | // Small Notch
52 | return 1
53 | } else if (
54 | deviceModel.starts(with: "iPhone10,3")
55 | || deviceModel.starts(with: "iPhone10,6")
56 | || deviceModel.starts(with: "iPhone11")
57 | || deviceModel.starts(with: "iPhone12")
58 | || deviceModel.starts(with: "iPhone13")
59 | ) {
60 | // Big Notch
61 | return 2
62 | } else if (
63 | deviceModel.starts(with: "iPhone15")
64 | || deviceModel.starts(with: "iPhone16")
65 | ) {
66 | // Dynamic Island
67 | return 3
68 | }
69 | return 0
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/extensions/Alert++.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Alert++.swift
3 | // Cowabunga
4 | //
5 | // Created by sourcelocation on 30/01/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | // credit: sourcelocation & TrollTools
11 | var currentUIAlertController: UIAlertController?
12 |
13 |
14 | fileprivate let errorString = NSLocalizedString("Error", comment: "")
15 | fileprivate let okString = NSLocalizedString("OK", comment: "")
16 | fileprivate let cancelString = NSLocalizedString("Cancel", comment: "")
17 | fileprivate let placeholderString = NSLocalizedString("Text", comment: "")
18 |
19 | extension UIApplication {
20 |
21 | func dismissAlert(animated: Bool) {
22 | DispatchQueue.main.async {
23 | currentUIAlertController?.dismiss(animated: animated)
24 | }
25 | }
26 | func alert(title: String = errorString, body: String, animated: Bool = true, withButton: Bool = true) {
27 | DispatchQueue.main.async {
28 | var body = body
29 |
30 | if title == errorString {
31 | // append debug info
32 | let device = UIDevice.current
33 | let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
34 | let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
35 | let systemVersion = device.systemVersion
36 | body += "\n\(device.systemName) \(systemVersion), version \(appVersion) build \(appBuild) escaped=\(FileManager.default.isReadableFile(atPath: "/var/mobile"))"
37 | }
38 |
39 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
40 | if withButton { currentUIAlertController?.addAction(.init(title: okString, style: .cancel)) }
41 | self.present(alert: currentUIAlertController!)
42 | }
43 | }
44 | func inputAlert(title: String, body: String, confirmTitle: String = okString, placeholder: String = placeholderString, text: String = "", keyboardType: UIKeyboardType = .default, onOK: @escaping (String) -> (), noCancel: Bool) {
45 | DispatchQueue.main.async {
46 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
47 | currentUIAlertController?.addTextField { (textField) in
48 | textField.placeholder = placeholder
49 | textField.text = text
50 | textField.keyboardType = keyboardType
51 | }
52 | if !noCancel {
53 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel))
54 | }
55 | currentUIAlertController?.addAction(.init(title: confirmTitle, style: noCancel ? .cancel : .default, handler: { _ in
56 | onOK(currentUIAlertController?.textFields?[0].text ?? "")
57 | }))
58 | self.present(alert: currentUIAlertController!)
59 | }
60 | }
61 | func optionsAlert(title: String, body: String, options: [String], preferredStyle: UIAlertController.Style = .actionSheet, onSelection: @escaping (String) -> ()) {
62 | DispatchQueue.main.async {
63 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: preferredStyle)
64 | // add all the options
65 | for alertOption in options {
66 | currentUIAlertController?.addAction(.init(title: alertOption, style: .default, handler: { _ in
67 | onSelection(alertOption)
68 | }))
69 | }
70 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel))
71 | // present popover for iPads
72 | let view: UIView? = UIApplication.shared.windows.first?.rootViewController?.view
73 | currentUIAlertController?.popoverPresentationController?.sourceView = view // prevents crashing on iPads
74 | currentUIAlertController?.popoverPresentationController?.sourceRect = CGRect(x: view?.bounds.midX ?? 0, y: view?.bounds.maxY ?? 0, width: 0, height: 0) // show up at center bottom on iPads
75 | self.present(alert: currentUIAlertController!)
76 | }
77 | }
78 | func confirmAlert(title: String = errorString, body: String, confirmTitle: String = okString, onOK: @escaping () -> (), noCancel: Bool) {
79 | DispatchQueue.main.async {
80 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
81 | if !noCancel {
82 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel))
83 | }
84 | currentUIAlertController?.addAction(.init(title: confirmTitle, style: noCancel ? .cancel : .default, handler: { _ in
85 | onOK()
86 | }))
87 | self.present(alert: currentUIAlertController!)
88 | }
89 | }
90 | func change(title: String = errorString, body: String) {
91 | DispatchQueue.main.async {
92 | currentUIAlertController?.title = title
93 | currentUIAlertController?.message = body
94 | }
95 | }
96 |
97 | func present(alert: UIAlertController) {
98 | if var topController = self.windows[0].rootViewController {
99 | while let presentedViewController = topController.presentedViewController {
100 | topController = presentedViewController
101 | }
102 |
103 | topController.present(alert, animated: true)
104 | // topController should now be your topmost view controller
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/extensions/Color++.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color++.swift
3 | // Cowabunga
4 | //
5 | // Created by sourcelocation on 30/01/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | extension UIColor {
11 | var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
12 | var red: CGFloat = 0
13 | var green: CGFloat = 0
14 | var blue: CGFloat = 0
15 | var alpha: CGFloat = 0
16 | getRed(&red, green: &green, blue: &blue, alpha: &alpha)
17 |
18 | return (red, green, blue, alpha)
19 | }
20 |
21 | var data: Data? {
22 | do {
23 | let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
24 | return data
25 | } catch let error {
26 | print("error color key data not saved \(error.localizedDescription)")
27 | }
28 | return nil
29 | }
30 |
31 | static func getColorFromData(data colorData: Data?) -> UIColor? {
32 | guard let colorDataUnwrapped = colorData else { return nil; }
33 | do {
34 | return try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorDataUnwrapped)
35 | } catch let error {
36 | print("color error \(error.localizedDescription)")
37 | return nil
38 | }
39 | }
40 | }
41 |
42 | extension Color {
43 | init(uiColor14: UIColor) {
44 | self.init(red: Double(uiColor14.rgba.red),
45 | green: Double(uiColor14.rgba.green),
46 | blue: Double(uiColor14.rgba.blue),
47 | opacity: Double(uiColor14.rgba.alpha))
48 | }
49 |
50 | init?(hex: String) {
51 | let r, g, b, a: CGFloat
52 |
53 | if hex.hasPrefix("#") {
54 | let start = hex.index(hex.startIndex, offsetBy: 1)
55 | let hexColor = String(hex[start...])
56 |
57 | if hexColor.count == 8 {
58 | let scanner = Scanner(string: hexColor)
59 | var hexNumber: UInt64 = 0
60 |
61 | if scanner.scanHexInt64(&hexNumber) {
62 | r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
63 | g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
64 | b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
65 | a = CGFloat(hexNumber & 0x000000ff) / 255
66 |
67 | self.init(red: r, green: g, blue: b, opacity: a)
68 | return
69 | }
70 | } else if hexColor.count == 6 {
71 | let scanner = Scanner(string: hexColor)
72 | var hexNumber: UInt64 = 0
73 |
74 | if scanner.scanHexInt64(&hexNumber) {
75 | r = CGFloat((hexNumber & 0xff000000) >> 16) / 255
76 | g = CGFloat((hexNumber & 0x00ff0000) >> 8) / 255
77 | b = CGFloat(hexNumber & 0x0000ff00) / 255
78 |
79 | self.init(red: r, green: g, blue: b)
80 | return
81 | }
82 | }
83 | }
84 |
85 | return nil
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/extensions/DarwinNotificationCenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DarwinNotificationCenter.swift
3 | // DarwinNotificationCenter
4 | //
5 | // Copyright (c) 2019 - 2020 Jason Nam (https://jasonnam.com)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | public class DarwinNotificationCenter {
29 |
30 | public static let `default` = DarwinNotificationCenter()
31 |
32 | public private(set) var observations: [String: (String) -> Void] = [:]
33 |
34 | public func addObserver(forName name: String, using block: @escaping (String) -> Void) {
35 | observations[name] = block
36 |
37 | let callback: CFNotificationCallback = { _, _, name, _, _ in
38 | guard let name = name?.rawValue as String? else { return }
39 | DarwinNotificationCenter.default.observations[name]?(name)
40 | }
41 |
42 | CFNotificationCenterAddObserver(
43 | CFNotificationCenterGetDarwinNotifyCenter(),
44 | Unmanaged.passUnretained(self).toOpaque(),
45 | callback,
46 | name as CFString,
47 | nil,
48 | .deliverImmediately
49 | )
50 | }
51 |
52 | public func removeObserver(withName name: String) {
53 | observations.removeValue(forKey: name)
54 |
55 | CFNotificationCenterRemoveObserver(
56 | CFNotificationCenterGetDarwinNotifyCenter(),
57 | Unmanaged.passUnretained(self).toOpaque(),
58 | CFNotificationName(name as CFString),
59 | nil
60 | )
61 | }
62 |
63 | public func removeAllObservers() {
64 | CFNotificationCenterRemoveEveryObserver(
65 | CFNotificationCenterGetDarwinNotifyCenter(),
66 | Unmanaged.passUnretained(self).toOpaque()
67 | )
68 | }
69 |
70 | public func post(name: String) {
71 | CFNotificationCenterPostNotification(
72 | CFNotificationCenterGetDarwinNotifyCenter(),
73 | CFNotificationName(name as CFString),
74 | nil,
75 | nil,
76 | true
77 | )
78 | }
79 | }
--------------------------------------------------------------------------------
/src/extensions/EZTimer.h:
--------------------------------------------------------------------------------
1 | //
2 | // EZTimer.h
3 | // EZKit
4 | //
5 | // Created by macbook pro on 2018/3/20.
6 | // Copyright © 2018年 sheep. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | ///线程选择
12 | typedef NS_ENUM(NSUInteger, EZTimerQueueType) {
13 | EZTimerQueueTypeGlobal,
14 | EZTimerQueueTypeConcurrent,
15 | EZtimerQueueTypeSerial,
16 | };
17 |
18 | ///timer执行方式选择,默认立刻执行一次,此时block会立刻执行,next表示下一个interval执行。
19 | typedef NS_ENUM(NSUInteger, EZTimerResumeType) {
20 | EZTimerResumeTypeNow,
21 | EZTimerQueueTypeNext,
22 | };
23 |
24 | typedef void (^EZTimerBlock)(NSString *timerName);
25 |
26 | @interface EZTimer : NSObject
27 | /**
28 | * 单例模式
29 | */
30 | +(instancetype)shareInstance;
31 |
32 | /**
33 | * 简单方式创建并执行timer,其他未给出的参数均为默认参数
34 | * timerName timer的名称,创建好的timer以name为key存储于timers字典中
35 | * interval timer时间间隔
36 | * resumeType 是否立刻开始执行,默认立刻开始执行,此时block会立刻执行,next下一个interval执行
37 | * action timer回调
38 | */
39 | -(void)repeatTimer:(NSString*)timerName timerInterval:(double)interval resumeType:(EZTimerResumeType)resumeType action:(EZTimerBlock)action;
40 |
41 | /**
42 | * 简单方式创建并执行timer
43 | * timerName timer的名称,创建好的timer以name为key存储于timers字典中,必须
44 | * interval timer时间间隔 0 默认60s
45 | * leeway timer的精度,默认0.1s
46 | * resumeType 是否立刻开始执行,默认立刻开始执行,此时block会立刻执行,next下一个interval执行
47 | * queue 创建timer所在的线程,默认global
48 | * queueName 线程命名,当global时name不起作用。
49 | * repeats 是否重复运行
50 | * action timer回调
51 | */
52 | -(void)timer:(NSString*)timerName timerInterval:(double)interval leeway:(double)leeway resumeType:(EZTimerResumeType)resumeType queue:(EZTimerQueueType)queue queueName:(NSString *)queueName repeats:(BOOL)repeats action:(EZTimerBlock)action;
53 |
54 | /**
55 | * 注销此timer
56 | * timerName timer名称
57 | */
58 | -(void)cancel:(NSString *)timerName;
59 | /**
60 | * 暂停此timer
61 | * 暂停及恢复的操作不建议使用,这两个操作需配对使用,
62 | * 不然会出现崩溃,原因是source未提供检测状态的接口
63 | * timerName timer名称
64 | */
65 | -(void)pause:(NSString *)timerName;
66 | /**
67 | * 恢复此timer
68 | * 暂停及恢复的操作不建议使用,这两个操作需配对使用,
69 | * 不然会出现崩溃,原因是source未提供检测状态的接口
70 | * timerName timer名称
71 | */
72 | -(void)resume:(NSString *)timerName;
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/src/extensions/EZTimer.mm:
--------------------------------------------------------------------------------
1 | //
2 | // EZTimer.mm
3 | // EZKit
4 | //
5 | // Created by macbook pro on 2018/3/20.
6 | // Copyright © 2018年 sheep. All rights reserved.
7 | //
8 |
9 | #import "EZTimer.h"
10 |
11 | #define EZTimerQueueName(x) [NSString stringWithFormat:@"NSTimer_%@_queue",x]
12 |
13 | #define EZTimerDfaultLeeway 0.1
14 |
15 | #define EZTimerDfaultTimeInterval 60
16 |
17 | #define EZTIMERSTATUSKEY_RESUME @"EZTIMERSTATUSKEY_RESUME"
18 | #define EZTIMERSTATUSKEY_PAUSE @"EZTIMERSTATUSKEY_PAUSE"
19 |
20 | // #ifdef DEBUG
21 | // #define EZLog(...) NSLog(__VA_ARGS__)
22 | // #else
23 | #define EZLog(...)
24 | // #endif
25 |
26 | @interface EZTimer()
27 |
28 | @property(nonatomic,strong)NSMutableDictionary *timers;
29 |
30 | @property(nonatomic,strong)NSMutableDictionary *timersFlags;
31 |
32 | @end
33 |
34 | @implementation EZTimer
35 |
36 | +(instancetype)shareInstance{
37 | static EZTimer *instance = nil;
38 | static dispatch_once_t onceToken;
39 | dispatch_once(&onceToken, ^{
40 | instance = [[EZTimer alloc] init];
41 | });
42 |
43 | return instance;
44 | }
45 |
46 |
47 | -(void)repeatTimer:(NSString*)timerName timerInterval:(double)interval resumeType:(EZTimerResumeType)resumeType action:(EZTimerBlock)action{
48 | [self timer:timerName timerInterval:interval leeway:EZTimerDfaultLeeway resumeType:resumeType queue:EZTimerQueueTypeGlobal queueName:nil repeats:YES action:action];
49 | }
50 |
51 |
52 | -(void)timer:(NSString*)timerName timerInterval:(double)interval leeway:(double)leeway resumeType:(EZTimerResumeType)resumeType queue:(EZTimerQueueType)queue queueName:(NSString *)queueName repeats:(BOOL)repeats action:(EZTimerBlock)action{
53 |
54 | dispatch_queue_t que = nil;
55 | if (!timerName) { return; }
56 | if (!queueName) {
57 | queueName = EZTimerQueueName(timerName);
58 | }
59 | switch (queue) {
60 |
61 | case EZTimerQueueTypeConcurrent:{
62 | que = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_CONCURRENT);
63 | break;
64 | }
65 | case EZtimerQueueTypeSerial:{
66 | que = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
67 | break;
68 | }
69 | default:
70 | que = dispatch_get_global_queue(0, 0);
71 | break;
72 | }
73 | dispatch_source_t timer = [self.timers objectForKey:timerName];
74 | if (!timer) {
75 | timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, que);
76 | [self.timers setObject:timer forKey:timerName];
77 | //timer 状态标识
78 | NSMutableDictionary *dic =[NSMutableDictionary dictionaryWithObjectsAndKeys:@0,EZTIMERSTATUSKEY_RESUME,@0,EZTIMERSTATUSKEY_PAUSE, nil];
79 | [self.timersFlags setObject:dic forKey:timerName];
80 | }
81 |
82 | dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (interval==0?EZTimerDfaultTimeInterval:interval) * NSEC_PER_SEC, (leeway == 0 ? EZTimerDfaultLeeway:leeway) * NSEC_PER_SEC);
83 |
84 | __weak typeof(self) weakSelf = self;
85 | dispatch_source_set_event_handler(timer, ^{
86 | EZLog(@"tiemr action");
87 | action(timerName);
88 | if (!repeats) {
89 | //dispatch_source_cancel(timer);
90 | [weakSelf cancel:timerName];
91 | EZLog(@"tiemr action once");
92 | }
93 | });
94 | if (resumeType == EZTimerResumeTypeNow) {
95 | //dispatch_resume(timer);
96 | [self resume:timerName];
97 | }else{
98 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), que, ^{
99 | //dispatch_resume(timer);
100 | [weakSelf resume:timerName];
101 | });
102 | }
103 | }
104 |
105 |
106 | //当 timer 处于 suspend状态时不能被 释放.
107 | -(void)cancel:(NSString *)timerName{
108 |
109 | dispatch_source_t timer = [self.timers objectForKey:timerName];
110 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName);
111 | if (!timer) {
112 | EZLog(@"tiemr cancel retrun - because timer had been cancel");
113 | return;
114 | }
115 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName];
116 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_PAUSE] boolValue]) {
117 | EZLog(@"timer had paused,resume first then cancel it");
118 | dispatch_resume(timer);
119 | dispatch_source_cancel(timer);
120 | }
121 | [self.timers removeObjectForKey:timerName];
122 | [self.timersFlags removeObjectForKey:timerName];
123 |
124 | EZLog(@"tiemr cancel - cancel");
125 |
126 | }
127 |
128 | -(void)pause:(NSString *)timerName{
129 |
130 | dispatch_source_t timer = [self.timers objectForKey:timerName];
131 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName);
132 | if (!timer) {
133 | return;
134 | }
135 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName];
136 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_PAUSE] boolValue]) {
137 | EZLog(@"tiemr pause return- because timer had paused");
138 | return ;
139 | }
140 | dispatch_suspend(timer);
141 | timerDic[EZTIMERSTATUSKEY_PAUSE] = @1;
142 | timerDic[EZTIMERSTATUSKEY_RESUME] = @0;
143 | EZLog(@"tiemr pause - paused" );
144 |
145 | }
146 |
147 | -(void)resume:(NSString *)timerName{
148 | dispatch_source_t timer = [self.timers objectForKey:timerName];
149 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName);
150 | if (!timer) {
151 | return;
152 | }
153 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName];
154 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_RESUME] boolValue]) {
155 | EZLog(@"timer resuem return - because timer had resume");
156 | return;
157 | }
158 | dispatch_resume(timer);
159 | timerDic[EZTIMERSTATUSKEY_RESUME] = @1;
160 | timerDic[EZTIMERSTATUSKEY_PAUSE] = @0;
161 | EZLog(@"tiemr resume - resumed");
162 |
163 | }
164 |
165 | -(NSMutableDictionary *)timers{
166 | if (!_timers) {
167 | _timers = [NSMutableDictionary dictionary];
168 | }
169 | return _timers;
170 | }
171 |
172 | -(NSMutableDictionary *)timersFlags{
173 | if (!_timersFlags) {
174 | _timersFlags = [NSMutableDictionary dictionary];
175 | }
176 | return _timersFlags;
177 | }
178 |
179 | @end
180 |
--------------------------------------------------------------------------------
/src/extensions/FlipEffect.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlipEffect.swift
3 | // Cowabunga
4 | //
5 | // Created by lemin on 3/1/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct FlipEffect: GeometryEffect {
11 |
12 | var animatableData: Double {
13 | get { angle }
14 | set { angle = newValue }
15 | }
16 |
17 | @Binding var flipped: Bool
18 | var angle: Double
19 | let axis: (x: CGFloat, y: CGFloat)
20 |
21 | func effectValue(size: CGSize) -> ProjectionTransform {
22 |
23 | DispatchQueue.main.async {
24 | self.flipped = self.angle >= 90 && self.angle < 270
25 | }
26 |
27 | let tweakedAngle = flipped ? -180 + angle : angle
28 | let a = CGFloat(Angle(degrees: tweakedAngle).radians)
29 |
30 | var transform3d = CATransform3DIdentity;
31 | transform3d.m34 = -1/max(size.width, size.height)
32 |
33 | transform3d = CATransform3DRotate(transform3d, a, axis.x, axis.y, 0)
34 | transform3d = CATransform3DTranslate(transform3d, -size.width/2.0, -size.height/2.0, 0)
35 |
36 | let affineTransform = ProjectionTransform(CGAffineTransform(translationX: size.width/2.0, y: size.height / 2.0))
37 |
38 | return ProjectionTransform(transform3d).concatenating(affineTransform)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/extensions/FontUtils.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | @interface FontUtils : NSObject
7 |
8 | + (void) loadFontsFromFolder:(NSString *)fontFolder;
9 | + (void) loadAllFonts;
10 | + (NSArray *) allFontNames;
11 | + (UIFont*) loadFontWithName:(NSString*)fontName size:(float)size bold:(BOOL) bold italic:(BOOL) italic;
12 | @end
--------------------------------------------------------------------------------
/src/extensions/FontUtils.mm:
--------------------------------------------------------------------------------
1 | #import "FontUtils.h"
2 |
3 | static NSMutableArray *_allFontNames = [NSMutableArray array];
4 |
5 | @implementation FontUtils
6 |
7 | + (void)loadFontsFromFolder:(NSString *)fontFolder {
8 | NSFileManager *fileManager = [NSFileManager defaultManager];
9 | NSArray *contents = [fileManager contentsOfDirectoryAtPath:fontFolder error:NULL];
10 |
11 | for (NSString *filename in contents) {
12 | if ([filename.pathExtension isEqualToString:@"ttf"] || [filename.pathExtension isEqualToString:@"otf"]) {
13 | NSString *fontPath = [fontFolder stringByAppendingPathComponent:filename];
14 |
15 | NSData *fontData = [NSData dataWithContentsOfFile:fontPath];
16 | CFErrorRef error;
17 | CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData);
18 | CGFontRef font = CGFontCreateWithDataProvider(provider);
19 |
20 | if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
21 | CFStringRef errorDescription = CFErrorCopyDescription(error);
22 | NSLog(@"Failed to load font: %@", errorDescription);
23 | CFSafeRelease(errorDescription);
24 | }
25 | // CFStringRef fontNameRef = CGFontCopyPostScriptName(font);
26 | // NSString *fontName = (__bridge NSString*)fontNameRef;
27 | // CFRelease(fontNameRef);
28 | // [_allFontNames addObject:fontName];
29 | // NSLog(@"fontName: %@",fontName);
30 |
31 | CFSafeRelease(font);
32 | CFSafeRelease(provider);
33 | }
34 | }
35 | }
36 |
37 | + (void) loadAllFonts {
38 | NSArray *familyNames = [UIFont familyNames];
39 | // for (NSString *familyName in familyNames) {
40 | // NSArray *names = [UIFont fontNamesForFamilyName:familyName];
41 | // [_allFontNames addObjectsFromArray:names];
42 | // }
43 | [_allFontNames addObjectsFromArray:familyNames];
44 | // CFArrayRef allFonts = CTFontManagerCopyAvailableFontFamilyNames();
45 | // for (NSInteger i = 0; i < CFArrayGetCount(allFonts); i++) {
46 | // NSString *fontName = (__bridge NSString *)CFArrayGetValueAtIndex(allFonts, i);
47 | // [_allFontNames addObject:fontName];
48 | // }
49 | // CFRelease(allFonts);
50 | }
51 |
52 | + (NSArray *)allFontNames {
53 | NSMutableArray *copyArray = [[NSMutableArray alloc] init];
54 | copyArray = [_allFontNames sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)].mutableCopy;
55 | [copyArray insertObject:@"System Font" atIndex:0];
56 | return copyArray;
57 | }
58 |
59 | + (UIFont*)loadFontWithName:(NSString*)fontName size:(float)size bold:(BOOL) bold italic:(BOOL) italic{
60 | UIFont *font = [UIFont fontWithDescriptor:[UIFontDescriptor fontDescriptorWithName:fontName size:size] size:size];
61 | if ([fontName isEqualToString:@"System Font"]) {
62 | font = [UIFont systemFontOfSize: size];
63 | }
64 | UIFontDescriptorSymbolicTraits symbolicTraits = 0;
65 | if (bold) {
66 | symbolicTraits |= UIFontDescriptorTraitBold;
67 | }
68 | if (italic) {
69 | symbolicTraits |= UIFontDescriptorTraitItalic;
70 | }
71 | UIFont *specialFont = [UIFont fontWithDescriptor:[[font fontDescriptor] fontDescriptorWithSymbolicTraits:symbolicTraits] size:size];
72 | return specialFont;
73 | }
74 |
75 | void CFSafeRelease(CFTypeRef cf) {
76 | if (cf != NULL) {
77 | CFRelease(cf);
78 | }
79 | }
80 | @end
--------------------------------------------------------------------------------
/src/extensions/FoundationExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FoundationExtensions.swift
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Bundle {
11 | var releaseVersionNumber: String? {
12 | return infoDictionary?["CFBundleShortVersionString"] as? String
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/extensions/Haptic++.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Haptic++.swift
3 | // PsychicPaper
4 | //
5 | // Created by Hariz Shirazi on 2023-02-04.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | /// Wrapper around UIKit haptics
12 | class Haptic {
13 | /// Shared instance
14 | static let shared = Haptic()
15 | private init() {}
16 | /// Play haptic feedback
17 | func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) {
18 | UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred()
19 | }
20 |
21 | /// Provide haptic user feedback for an action
22 | func notify(_ feedbackType: UINotificationFeedbackGenerator.FeedbackType) {
23 | UINotificationFeedbackGenerator().notificationOccurred(feedbackType)
24 | }
25 |
26 | /// Play feedback for a selection
27 | func selection() {
28 | UISelectionFeedbackGenerator().selectionChanged()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/extensions/LunarDate.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LunarDate : NSObject
4 |
5 | + (NSCalendar *)chineseCalendar;
6 | + (NSString*)getChineseCalendarWithDate:(NSDate *)date format:(NSString *)format;
7 |
8 | @end
--------------------------------------------------------------------------------
/src/extensions/LunarDate.mm:
--------------------------------------------------------------------------------
1 | #import "LunarDate.h"
2 |
3 | #define ChineseMonths @[@"正月", @"二月", @"三月", @"四月", @"五月", @"六月", @"七月", @"八月",@"九月", @"十月", @"冬月", @"腊月"]
4 |
5 | #define ChineseDays @[@"初一", @"初二", @"初三", @"初四", @"初五", @"初六", @"初七", @"初八", @"初九", @"初十",@"十一", @"十二", @"十三", @"十四", @"十五", @"十六", @"十七", @"十八", @"十九", @"二十", @"廿一", @"廿二", @"廿三", @"廿四", @"廿五", @"廿六", @"廿七", @"廿八", @"廿九", @"三十"]
6 |
7 | #define ChineseYears @[@"甲子",@"乙丑",@"丙寅",@"丁卯",@"戊辰",@"己巳",@"庚午",@"辛未",@"壬申",@"癸酉",@"甲戌",@"乙亥",@"丙子",@"丁丑",@"戊寅",@"己卯",@"庚辰",@"辛巳",@"壬午",@"癸未",@"甲申",@"乙酉",@"丙戌",@"丁亥",@"戊子",@"己丑",@"庚寅",@"辛卯",@"壬辰",@"癸巳",@"甲午",@"乙未",@"丙申",@"丁酉",@"戊戌",@"己亥",@"庚子",@"辛丑",@"壬寅",@"癸卯",@"甲辰",@"乙巳",@"丙午",@"丁未",@"戊申",@"己酉",@"庚戌",@"辛亥",@"壬子",@"癸丑",@"甲寅",@"乙卯",@"丙辰",@"丁巳",@"戊午",@"己未",@"庚申",@"辛酉",@"壬戌",@"癸亥"]
8 |
9 | @implementation LunarDate
10 |
11 | + (NSCalendar *)chineseCalendar
12 | {
13 | static NSCalendar *chineseCalendar_sharedCalendar = nil;
14 | if (!chineseCalendar_sharedCalendar)
15 | chineseCalendar_sharedCalendar =[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierChinese];
16 | return chineseCalendar_sharedCalendar;
17 | }
18 |
19 | /**
20 | 获取农历年月日
21 |
22 | @param date 输入日期
23 | @return 返回干支年 农历月份 农历初几
24 | */
25 | + (NSString*)getChineseCalendarWithDate:(NSDate *)date format:(NSString *)format{
26 | NSCalendar *chineseCalendar = [[self class] chineseCalendar];
27 | unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
28 | NSDateComponents *localeComp = [chineseCalendar components:unitFlags fromDate:date];
29 |
30 | NSString *y_str = [ChineseYears objectAtIndex:localeComp.year-1];
31 | NSString *m_str = [ChineseMonths objectAtIndex:localeComp.month-1];
32 | NSString *d_str = [ChineseDays objectAtIndex:localeComp.day-1];
33 | format = [format stringByReplacingOccurrencesOfString:@"CNYY" withString:y_str];
34 | format = [format stringByReplacingOccurrencesOfString:@"CNMM" withString:m_str];
35 | format = [format stringByReplacingOccurrencesOfString:@"CNDD" withString:d_str];
36 | return format;
37 | }
38 |
39 | @end
--------------------------------------------------------------------------------
/src/extensions/UsefulFunctions.h:
--------------------------------------------------------------------------------
1 | //
2 | // UsefulFunctions.h
3 | //
4 | //
5 | // Created by lemin on 12/8/23.
6 | //
7 |
8 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key, BOOL defaultValue);
9 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key);
10 |
11 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key, NSInteger defaultValue);
12 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key);
13 |
14 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key, double defaultValue);
15 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key);
16 |
17 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key, NSString *defaultValue);
18 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key);
--------------------------------------------------------------------------------
/src/extensions/UsefulFunctions.mm:
--------------------------------------------------------------------------------
1 | //
2 | // UsefulFunctions.mm
3 | //
4 | //
5 | // Created by lemin on 12/8/23.
6 | //
7 |
8 | #import
9 | #import "UsefulFunctions.h"
10 |
11 | // MARK: Dictionary Value Functions
12 | // Bools
13 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key, BOOL defaultValue)
14 | {
15 | return [dict valueForKey:key] ? [[dict valueForKey:key] boolValue] : defaultValue;
16 | }
17 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key)
18 | {
19 | return getBoolFromDictKey(dict, key, NO);
20 | }
21 |
22 | // Ints
23 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key, NSInteger defaultValue)
24 | {
25 | return [dict valueForKey:key] ? [[dict valueForKey:key] integerValue] : defaultValue;
26 | }
27 |
28 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key)
29 | {
30 | return getIntFromDictKey(dict, key, 0);
31 | }
32 |
33 | // Doubles
34 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key, double defaultValue)
35 | {
36 | return [dict valueForKey:key] ? [[dict valueForKey:key] doubleValue] : defaultValue;
37 | }
38 |
39 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key)
40 | {
41 | return getDoubleFromDictKey(dict, key, 0.0);
42 | }
43 |
44 | // String
45 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key, NSString *defaultValue)
46 | {
47 | return [dict valueForKey:key] ? [dict valueForKey:key] : defaultValue;
48 | }
49 |
50 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key)
51 | {
52 | return getStringFromDictKey(dict, key, @"");
53 | }
--------------------------------------------------------------------------------
/src/extensions/UserDefaultsExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDefaultsExtensions.swift
3 | //
4 | //
5 | // Created by lemin on 12/8/23.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension UserDefaults {
12 | // MARK: Managing the Data
13 | func loadUserDefaults(forPath path: String) -> [String: Any] {
14 | do {
15 | let plistData: Data = try Data(contentsOf: URL(fileURLWithPath: path))
16 | if let dict: [String: Any] = try PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: Any] {
17 | return dict
18 | }
19 | } catch {
20 | // file doesn't exist
21 | }
22 | return [:]
23 | }
24 |
25 | func setValue(_ value: Any?, forKey key: String, forPath path: String) {
26 | var dict = loadUserDefaults(forPath: path)
27 | dict[key] = value
28 | do {
29 | let newData: Data = try PropertyListSerialization.data(fromPropertyList: dict, format: .xml, options: 0)
30 | try newData.write(to: URL(fileURLWithPath: path))
31 | } catch {
32 | print(error.localizedDescription)
33 | }
34 | }
35 |
36 | func removeObject(forKey defaultName: String, forPath path: String) {
37 | setValue(nil, forKey: defaultName, forPath: path)
38 | }
39 |
40 | func deleteUserDefaults(forPath path: String) throws {
41 | try FileManager.default.removeItem(atPath: path)
42 | }
43 |
44 |
45 | // MARK: Specific Type Functions
46 |
47 | // string
48 | func string(forKey defaultName: String, forPath path: String) -> String? {
49 | let dict = loadUserDefaults(forPath: path)
50 | return dict[defaultName] as? String
51 | }
52 |
53 | // double
54 | func double(forKey defaultName: String, forPath path: String) -> Double {
55 | let dict = loadUserDefaults(forPath: path)
56 | return dict[defaultName] as? Double ?? 0
57 | }
58 |
59 | // integer
60 | func integer(forKey defaultName: String, forPath path: String) -> Int {
61 | let dict = loadUserDefaults(forPath: path)
62 | return dict[defaultName] as? Int ?? 0
63 | }
64 |
65 | // bool
66 | func bool(forKey defaultName: String, forPath path: String) -> Bool {
67 | let dict = loadUserDefaults(forPath: path)
68 | return dict[defaultName] as? Bool ?? false
69 | }
70 |
71 | // array
72 | func array(forKey defaultName: String, forPath path: String) -> [Any]? {
73 | let dict = loadUserDefaults(forPath: path)
74 | return dict[defaultName] as? [Any]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/extensions/ViewExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewExtensions.swift
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // MARK: Tinted Button Style
12 | struct TintedButton: ButtonStyle {
13 | var color: Color
14 | var fullWidth: Bool = false
15 |
16 | func makeBody(configuration: Configuration) -> some View {
17 | ZStack {
18 | if fullWidth {
19 | configuration.label
20 | .padding(15)
21 | .frame(maxWidth: .infinity)
22 | .background(AnyView(color.opacity(0.2)))
23 | .cornerRadius(8)
24 | .foregroundColor(color)
25 | } else {
26 | configuration.label
27 | .padding(15)
28 | .background(AnyView(color.opacity(0.2)))
29 | .cornerRadius(8)
30 | .foregroundColor(color)
31 | }
32 | }
33 | }
34 |
35 | init(color: Color = .blue, fullWidth: Bool = false) {
36 | self.color = color
37 | self.fullWidth = fullWidth
38 | }
39 | }
40 |
41 | // MARK: Extended Slider
42 | struct BetterSlider: View {
43 | @Binding var value: Double
44 | var bounds: ClosedRange
45 | var step: Double.Stride? = nil
46 |
47 | var inputTitle: String = NSLocalizedString("Enter Value", comment:"")
48 | var inputBody: String = NSLocalizedString("Enter a value.", comment:"")
49 |
50 | var body: some View {
51 | HStack {
52 | if step == nil {
53 | Slider(value: $value, in: bounds)
54 | } else {
55 | Slider(value: $value, in: bounds, step: step!)
56 | }
57 | Spacer()
58 | Button(action: {
59 | UIApplication.shared.inputAlert(title: inputTitle, body: inputBody, onOK: { val in
60 | var finalVal: Double = Double(val) ?? value
61 | // make sure it is within bounds, then set it
62 | if (finalVal > bounds.upperBound) {
63 | finalVal = bounds.upperBound
64 | } else if (finalVal < bounds.lowerBound) {
65 | finalVal = bounds.lowerBound
66 | }
67 | // if step != nil {
68 | // // make sure it is within the step
69 | // finalVal = finalVal.truncatingRemainder(dividingBy: step!)
70 | // }
71 | value = finalVal
72 | }, noCancel: false)
73 | }) {
74 | Text(String(format: "%.2f", value))
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/extensions/WeatherUtils.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | @interface WeatherUtils : NSObject
7 | + (NSString *)getWeatherIcon:(NSString *)text;
8 | + (NSDictionary *)fetchNowWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale;
9 | + (NSDictionary *)fetchTodayWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale;
10 | + (NSData *)fetchLocationIDForName:(NSString *)name apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale;
11 | + (NSString *)formatNowResult:(NSDictionary *)data format:(NSString *)format;
12 | + (NSString *)formatTodayResult:(NSDictionary *)data format:(NSString *)format;
13 | + (NSString *)getDataFrom:(NSString *)url;
14 | + (NSString *)convertDateLocaleToLang:(NSString *)locale;
15 | @end
--------------------------------------------------------------------------------
/src/extensions/WeatherUtils.mm:
--------------------------------------------------------------------------------
1 | #import "WeatherUtils.h"
2 |
3 | @implementation WeatherUtils
4 |
5 | + (NSString *)getWeatherIcon:(NSString *)text {
6 | NSString *weatherIcon = @"🌤️";
7 | NSArray *weatherIconList = @[@"☀️", @"☁️", @"⛅️",
8 | @"☃️", @"⛈️", @"🏜️", @"🏜️", @"🌫️", @"🌫️", @"🌪️", @"🌧️"];
9 | NSArray *weatherType = @[@"晴|sunny", @"阴|overcast", @"云|cloudy", @"雪|snow", @"雷|thunder", @"沙|sand", @"尘|dust", @"雾|foggy", @"霾|haze", @"风|wind", @"雨|rain"];
10 |
11 | NSRegularExpression *regex;
12 | for (int i = 0; i < weatherType.count; i++) {
13 | NSString *pattern = [NSString stringWithFormat:@".*%@.*", weatherType[i]];
14 | regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
15 | if ([regex numberOfMatchesInString:text options:0 range:NSMakeRange(0, [text length])] > 0) {
16 | weatherIcon = weatherIconList[i];
17 | break;
18 | }
19 | }
20 |
21 | return weatherIcon;
22 | }
23 |
24 | + (NSDictionary *)fetchNowWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{
25 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://devapi.qweather.com/v7/weather/now?location=%@&key=%@&lang=%@", location, apiKey, [self convertDateLocaleToLang:dateLocale]]];
26 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding];
27 | NSError *erro = nil;
28 | if (data!=nil) {
29 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&erro ];
30 | return json;
31 | }
32 | return nil;
33 | }
34 |
35 | + (NSDictionary *)fetchTodayWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{
36 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://devapi.qweather.com/v7/weather/3d?location=%@&key=%@&lang=%@", location, apiKey, [self convertDateLocaleToLang:dateLocale]]];
37 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding];
38 | NSError *erro = nil;
39 | if (data!=nil) {
40 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&erro ];
41 | return json;
42 | }
43 | return nil;
44 | }
45 |
46 | + (NSData *)fetchLocationIDForName:(NSString *)name apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{
47 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://geoapi.qweather.com/v2/city/lookup?location=%@&key=%@&lang=%@", [self encodeURIComponent:name], apiKey, [self convertDateLocaleToLang:dateLocale]]];
48 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding];
49 | if (data!=nil) {
50 | // NSLog(@"weather location:%@", data);
51 | return data;
52 | }
53 | return nil;
54 | }
55 |
56 | + (NSString*)formatNowResult:(NSDictionary *)data format:(NSString *)format {
57 | // NSLog(@"weather now:%@", data);
58 | if(data && [data[@"code"] isEqualToString:@"200"]) {
59 | format = [format stringByReplacingOccurrencesOfString:@"{i}" withString:[WeatherUtils getWeatherIcon: data[@"now"][@"text"]]];
60 | format = [format stringByReplacingOccurrencesOfString:@"{n}" withString:data[@"now"][@"text"]];
61 | format = [format stringByReplacingOccurrencesOfString:@"{t}" withString:data[@"now"][@"temp"]];
62 | format = [format stringByReplacingOccurrencesOfString:@"{bt}" withString:data[@"now"][@"feelsLike"]];
63 | format = [format stringByReplacingOccurrencesOfString:@"{h}" withString:data[@"now"][@"humidity"]];
64 | format = [format stringByReplacingOccurrencesOfString:@"{w}" withString:data[@"now"][@"windDir"]];
65 | format = [format stringByReplacingOccurrencesOfString:@"{wp}" withString:data[@"now"][@"windScale"]];
66 | format = [format stringByReplacingOccurrencesOfString:@"{pc}" withString:data[@"now"][@"precip"]];
67 | format = [format stringByReplacingOccurrencesOfString:@"{ps}" withString:data[@"now"][@"pressure"]];
68 | format = [format stringByReplacingOccurrencesOfString:@"{v}" withString:data[@"now"][@"vis"]];
69 | format = [format stringByReplacingOccurrencesOfString:@"{c}" withString:data[@"now"][@"cloud"]];
70 | } else {
71 | format = NSLocalizedString(@"error", comment:@"");
72 | }
73 | return format;
74 | }
75 |
76 | + (NSString*)formatTodayResult:(NSDictionary *)data format:(NSString *)format {
77 | // NSLog(@"weather today:%@", data);
78 | if(data && [data[@"code"] isEqualToString:@"200"]) {
79 | format = [format stringByReplacingOccurrencesOfString:@"{di}" withString:[WeatherUtils getWeatherIcon: data[@"daily"][0][@"textDay"]]];
80 | format = [format stringByReplacingOccurrencesOfString:@"{dn}" withString:data[@"daily"][0][@"textDay"]];
81 | format = [format stringByReplacingOccurrencesOfString:@"{dt}" withString:data[@"daily"][0][@"tempMax"]];
82 | format = [format stringByReplacingOccurrencesOfString:@"{dw}" withString:data[@"daily"][0][@"windDirDay"]];
83 | format = [format stringByReplacingOccurrencesOfString:@"{dwp}" withString:data[@"daily"][0][@"windScaleDay"]];
84 | format = [format stringByReplacingOccurrencesOfString:@"{ni}" withString:[WeatherUtils getWeatherIcon: data[@"daily"][0][@"textNight"]]];
85 | format = [format stringByReplacingOccurrencesOfString:@"{nn}" withString:data[@"daily"][0][@"textNight"]];
86 | format = [format stringByReplacingOccurrencesOfString:@"{nt}" withString:data[@"daily"][0][@"tempMin"]];
87 | format = [format stringByReplacingOccurrencesOfString:@"{nw}" withString:data[@"daily"][0][@"windDirNight"]];
88 | format = [format stringByReplacingOccurrencesOfString:@"{nwp}" withString:data[@"daily"][0][@"windScaleNight"]];
89 | format = [format stringByReplacingOccurrencesOfString:@"{tpc}" withString:data[@"daily"][0][@"precip"]];
90 | format = [format stringByReplacingOccurrencesOfString:@"{tuv}" withString:data[@"daily"][0][@"uvIndex"]];
91 | format = [format stringByReplacingOccurrencesOfString:@"{th}" withString:data[@"daily"][0][@"humidity"]];
92 | format = [format stringByReplacingOccurrencesOfString:@"{tps}" withString:data[@"daily"][0][@"pressure"]];
93 | format = [format stringByReplacingOccurrencesOfString:@"{tv}" withString:data[@"daily"][0][@"vis"]];
94 | format = [format stringByReplacingOccurrencesOfString:@"{tc}" withString:data[@"daily"][0][@"cloud"]];
95 | } else {
96 | format = NSLocalizedString(@"error", comment:@"");
97 | }
98 | return format;
99 | }
100 |
101 | + (NSString *)getDataFrom:(NSString *)url{
102 | // NSLog(@"url:%@", url);
103 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
104 | [request setHTTPMethod:@"GET"];
105 | [request setURL:[NSURL URLWithString:url]];
106 |
107 | NSError *error = nil;
108 | NSHTTPURLResponse *responseCode = nil;
109 |
110 | NSData *oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseCode error:&error];
111 |
112 | if([responseCode statusCode] != 200){
113 | NSLog(@"Error getting %@, HTTP status code %li", url, [responseCode statusCode]);
114 | return nil;
115 | }
116 |
117 | return [[NSString alloc] initWithData:oResponseData encoding:NSUTF8StringEncoding];
118 | }
119 |
120 | + (NSString *)convertDateLocaleToLang:(NSString *)locale{
121 | NSArray *items = @[@"zh_CN", @"en_US"];
122 | int item = [items indexOfObject:locale];
123 | switch(item) {
124 | case 0: return @"zh";
125 | case 1: return @"en";
126 | default: return @"en";
127 | }
128 | }
129 |
130 | + (NSString *)encodeURIComponent:(NSString *)string
131 | {
132 | NSString *s = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
133 | return s;
134 | }
135 | @end
136 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/AXEventHandInfoRepresentation.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "AXEventPathInfoRepresentation.h"
3 |
4 | @interface AXEventHandInfoRepresentation : NSObject
5 | - (NSArray *)paths;
6 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/AXEventPathInfoRepresentation.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface AXEventPathInfoRepresentation : NSObject
4 | @property (assign, nonatomic) unsigned char pathIdentity;
5 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/AXEventRepresentation.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "AXEventHandInfoRepresentation.h"
3 | #import "IOKit+SPI.h"
4 |
5 | @interface AXEventRepresentation : NSObject
6 | @property (nonatomic, readonly) BOOL isTouchDown;
7 | @property (nonatomic, readonly) BOOL isMove;
8 | @property (nonatomic, readonly) BOOL isChordChange;
9 | @property (nonatomic, readonly) BOOL isLift;
10 | @property (nonatomic, readonly) BOOL isInRange;
11 | @property (nonatomic, readonly) BOOL isInRangeLift;
12 | @property (nonatomic, readonly) BOOL isCancel;
13 | + (instancetype)representationWithHIDEvent:(IOHIDEventRef)event hidStreamIdentifier:(NSString *)identifier;
14 | - (AXEventHandInfoRepresentation *)handInfo;
15 | - (CGPoint)location;
16 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/BackboardServices.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "IOKit+SPI.h"
3 |
4 | #if __cplusplus
5 | extern "C" {
6 | #endif
7 | void *BKSHIDEventRegisterEventCallback(void (*)(void *, void *, IOHIDServiceRef, IOHIDEventRef));
8 | void UIApplicationInstantiateSingleton(id aclass);
9 | void UIApplicationInitialize();
10 | void BKSDisplayServicesStart();
11 | void GSInitialize();
12 | void GSEventInitialize(Boolean registerPurple);
13 | void GSEventPopRunLoopMode(CFStringRef mode);
14 | void GSEventPushRunLoopMode(CFStringRef mode);
15 | void GSEventRegisterEventCallBack(void (*)(GSEventRef));
16 | #if __cplusplus
17 | }
18 | #endif
--------------------------------------------------------------------------------
/src/helpers/private_headers/CAFilter.h:
--------------------------------------------------------------------------------
1 | /* CoreAnimation - CAFilter.h
2 |
3 | Copyright (c) 2006-2007 Apple Inc.
4 | All rights reserved. */
5 |
6 | #ifndef CAFILTER_H
7 | #define CAFILTER_H
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 | CA_EXTERN_C_BEGIN
13 |
14 | @interface CAFilter : NSObject
15 |
16 | @property (class, readonly) NSArray *_Nonnull filterTypes;
17 |
18 | @property (assign) BOOL cachesInputImage;
19 | @property (assign, getter=isEnabled) BOOL enabled;
20 | @property (copy) NSString *name;
21 | @property (readonly, assign) NSString *type;
22 |
23 | @property (readonly, strong) NSArray *inputKeys;
24 | @property (readonly, strong) NSArray *outputKeys;
25 |
26 | + (nullable CAFilter *)filterWithType:(nonnull NSString *)type NS_SWIFT_UNAVAILABLE("Use init(type:) instead.");
27 | + (nullable CAFilter *)filterWithName:(nonnull NSString *)name NS_SWIFT_UNAVAILABLE("Use init(type:) instead.");
28 | - (nullable instancetype)initWithType:(nonnull NSString *)type;
29 | - (nullable instancetype)initWithName:(nonnull NSString *)name NS_SWIFT_UNAVAILABLE("Use init(type:) instead.");
30 |
31 | - (void)setDefaults;
32 |
33 | @end
34 |
35 | /** Filter types. **/
36 |
37 | CA_EXTERN NSString * const kCAFilterMultiplyColor;
38 | CA_EXTERN NSString * const kCAFilterColorAdd;
39 | CA_EXTERN NSString * const kCAFilterColorSubtract;
40 | CA_EXTERN NSString * const kCAFilterColorMonochrome;
41 | CA_EXTERN NSString * const kCAFilterColorMatrix;
42 | CA_EXTERN NSString * const kCAFilterColorHueRotate;
43 | CA_EXTERN NSString * const kCAFilterColorSaturate;
44 | CA_EXTERN NSString * const kCAFilterColorBrightness;
45 | CA_EXTERN NSString * const kCAFilterColorContrast;
46 | CA_EXTERN NSString * const kCAFilterColorInvert;
47 | CA_EXTERN NSString * const kCAFilterLuminanceToAlpha;
48 | CA_EXTERN NSString * const kCAFilterBias;
49 | CA_EXTERN NSString * const kCAFilterDistanceField;
50 | CA_EXTERN NSString * const kCAFilterGaussianBlur;
51 | CA_EXTERN NSString * const kCAFilterLanczosResize;
52 | CA_EXTERN NSString * const kCAFilterClear;
53 | CA_EXTERN NSString * const kCAFilterCopy;
54 | CA_EXTERN NSString * const kCAFilterSourceOver;
55 | CA_EXTERN NSString * const kCAFilterSourceIn;
56 | CA_EXTERN NSString * const kCAFilterSourceOut;
57 | CA_EXTERN NSString * const kCAFilterSourceAtop;
58 | CA_EXTERN NSString * const kCAFilterDest;
59 | CA_EXTERN NSString * const kCAFilterDestOver;
60 | CA_EXTERN NSString * const kCAFilterDestIn;
61 | CA_EXTERN NSString * const kCAFilterDestOut;
62 | CA_EXTERN NSString * const kCAFilterDestAtop;
63 | CA_EXTERN NSString * const kCAFilterXor;
64 | CA_EXTERN NSString * const kCAFilterPlusL;
65 | CA_EXTERN NSString * const kCAFilterSubtractS;
66 | CA_EXTERN NSString * const kCAFilterSubtractD;
67 | CA_EXTERN NSString * const kCAFilterMultiply;
68 | CA_EXTERN NSString * const kCAFilterMinimum;
69 | CA_EXTERN NSString * const kCAFilterMaximum;
70 | CA_EXTERN NSString * const kCAFilterPlusD;
71 | CA_EXTERN NSString * const kCAFilterNormalBlendMode;
72 | CA_EXTERN NSString * const kCAFilterMultiplyBlendMode;
73 | CA_EXTERN NSString * const kCAFilterScreenBlendMode;
74 | CA_EXTERN NSString * const kCAFilterOverlayBlendMode;
75 | CA_EXTERN NSString * const kCAFilterDarkenBlendMode;
76 | CA_EXTERN NSString * const kCAFilterLightenBlendMode;
77 | CA_EXTERN NSString * const kCAFilterColorDodgeBlendMode;
78 | CA_EXTERN NSString * const kCAFilterColorBurnBlendMode;
79 | CA_EXTERN NSString * const kCAFilterSoftLightBlendMode;
80 | CA_EXTERN NSString * const kCAFilterHardLightBlendMode;
81 | CA_EXTERN NSString * const kCAFilterDifferenceBlendMode;
82 | CA_EXTERN NSString * const kCAFilterExclusionBlendMode;
83 | CA_EXTERN NSString * const kCAFilterSubtractBlendMode;
84 | CA_EXTERN NSString * const kCAFilterDivideBlendMode;
85 | CA_EXTERN NSString * const kCAFilterLinearBurnBlendMode;
86 | CA_EXTERN NSString * const kCAFilterLinearDodgeBlendMode;
87 | CA_EXTERN NSString * const kCAFilterLinearLightBlendMode;
88 | CA_EXTERN NSString * const kCAFilterPinLightBlendMode;
89 | CA_EXTERN NSString * const kCAFilterPageCurl;
90 | CA_EXTERN NSString * const kCAFilterVibrantDark;
91 | CA_EXTERN NSString * const kCAFilterVibrantLight;
92 | CA_EXTERN NSString * const kCAFilterDarkenSourceOver;
93 | CA_EXTERN NSString * const kCAFilterLightenSourceOver;
94 |
95 | CA_EXTERN NSString * const kCAFilterHomeAffordanceBase;
96 |
97 | CA_EXTERN_C_END
98 |
99 | struct CAColorMatrix {
100 | float m11, m12, m13, m14, m15;
101 | float m21, m22, m23, m24, m25;
102 | float m31, m32, m33, m34, m35;
103 | float m41, m42, m43, m44, m45;
104 | };
105 | typedef struct CAColorMatrix CAColorMatrix;
106 |
107 | @interface NSValue (CADetails)
108 | + (NSValue *)valueWithCAColorMatrix:(CAColorMatrix)t;
109 | @end
110 |
111 | NS_ASSUME_NONNULL_END
112 |
113 | #endif // CAFILTER_H
--------------------------------------------------------------------------------
/src/helpers/private_headers/FBSOrientationObserver.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FBSOrientationObserver : NSObject
4 | - (long long)activeInterfaceOrientation;
5 | - (void)activeInterfaceOrientationWithCompletion:(id)arg1;
6 | - (void)invalidate;
7 | - (void)setHandler:(id)arg1;
8 | - (id)handler;
9 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/FBSOrientationUpdate.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FBSOrientationUpdate : NSObject
4 | - (unsigned long long)sequenceNumber;
5 | - (long long)rotationDirection;
6 | - (long long)orientation;
7 | - (double)duration;
8 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/IOHIDEvent+KIF.h:
--------------------------------------------------------------------------------
1 | //
2 | // IOHIDEvent+KIF.h
3 | // KIF
4 | //
5 | // Created by Thomas Bonnin on 7/6/15.
6 | //
7 |
8 | #import
9 | #import "IOKit+SPI.h"
10 |
11 | typedef struct __IOHIDEvent * IOHIDEventRef;
12 | IOHIDEventRef kif_IOHIDEventWithTouches(NSArray *touches) CF_RETURNS_RETAINED;
--------------------------------------------------------------------------------
/src/helpers/private_headers/IOHIDEvent+KIF.m:
--------------------------------------------------------------------------------
1 | //
2 | // IOHIDEvent+KIF.m
3 | // KIF
4 | //
5 | // Created by Thomas Bonnin on 7/6/15.
6 | //
7 |
8 | #import "IOHIDEvent+KIF.h"
9 | #import
10 | #import
11 |
12 | #define IOHIDEventFieldBase(type) (type << 16)
13 |
14 | #ifdef __LP64__
15 | typedef double IOHIDFloat;
16 | #else
17 | typedef float IOHIDFloat;
18 | #endif
19 |
20 | typedef UInt32 IOOptionBits;
21 | typedef uint32_t IOHIDDigitizerTransducerType;
22 | typedef uint32_t IOHIDEventField;
23 | typedef uint32_t IOHIDEventType;
24 |
25 | void IOHIDEventAppendEvent(IOHIDEventRef event, IOHIDEventRef childEvent);
26 | void IOHIDEventSetIntegerValue(IOHIDEventRef event, IOHIDEventField field,
27 | int value);
28 | void IOHIDEventSetSenderID(IOHIDEventRef event, uint64_t sender);
29 |
30 | // Derived from
31 | // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDFamily/IOHIDEventTypes.h
32 |
33 | enum {
34 | kIOHIDDigitizerTransducerTypeStylus = 0,
35 | kIOHIDDigitizerTransducerTypePuck,
36 | kIOHIDDigitizerTransducerTypeFinger,
37 | kIOHIDDigitizerTransducerTypeHand
38 | };
39 |
40 | enum {
41 | kIOHIDEventTypeNULL, // 0
42 | kIOHIDEventTypeVendorDefined,
43 | kIOHIDEventTypeButton,
44 | kIOHIDEventTypeKeyboard,
45 | kIOHIDEventTypeTranslation,
46 | kIOHIDEventTypeRotation, // 5
47 | kIOHIDEventTypeScroll,
48 | kIOHIDEventTypeScale,
49 | kIOHIDEventTypeZoom,
50 | kIOHIDEventTypeVelocity,
51 | kIOHIDEventTypeOrientation, // 10
52 | kIOHIDEventTypeDigitizer,
53 | kIOHIDEventTypeAmbientLightSensor,
54 | kIOHIDEventTypeAccelerometer,
55 | kIOHIDEventTypeProximity,
56 | kIOHIDEventTypeTemperature, // 15
57 | kIOHIDEventTypeNavigationSwipe,
58 | kIOHIDEventTypePointer,
59 | kIOHIDEventTypeProgress,
60 | kIOHIDEventTypeMultiAxisPointer,
61 | kIOHIDEventTypeGyro, // 20
62 | kIOHIDEventTypeCompass,
63 | kIOHIDEventTypeZoomToggle,
64 | kIOHIDEventTypeDockSwipe, // Just like kIOHIDEventTypeNavigationSwipe, but
65 | // intended for consumption by Dock
66 | kIOHIDEventTypeSymbolicHotKey,
67 | kIOHIDEventTypePower, // 25
68 | kIOHIDEventTypeLED,
69 | kIOHIDEventTypeFluidTouchGesture, // This will eventually superseed Navagation
70 | // and Dock swipes
71 | kIOHIDEventTypeBoundaryScroll,
72 | kIOHIDEventTypeBiometric,
73 | kIOHIDEventTypeUnicode, // 30
74 | kIOHIDEventTypeAtmosphericPressure,
75 | kIOHIDEventTypeUndefined,
76 | kIOHIDEventTypeCount, // This should always be last
77 |
78 | // DEPRECATED:
79 | kIOHIDEventTypeSwipe = kIOHIDEventTypeNavigationSwipe,
80 | kIOHIDEventTypeMouse = kIOHIDEventTypePointer
81 | };
82 |
83 | enum {
84 | kIOHIDDigitizerEventRange = 1 << 0,
85 | kIOHIDDigitizerEventTouch = 1 << 1,
86 | kIOHIDDigitizerEventPosition = 1 << 2,
87 | kIOHIDDigitizerEventStop = 1 << 3,
88 | kIOHIDDigitizerEventPeak = 1 << 4,
89 | kIOHIDDigitizerEventIdentity = 1 << 5,
90 | kIOHIDDigitizerEventAttribute = 1 << 6,
91 | kIOHIDDigitizerEventCancel = 1 << 7,
92 | kIOHIDDigitizerEventStart = 1 << 8,
93 | kIOHIDDigitizerEventResting = 1 << 9,
94 | kIOHIDDigitizerEventFromEdgeFlat = 1 << 10,
95 | kIOHIDDigitizerEventFromEdgeTip = 1 << 11,
96 | kIOHIDDigitizerEventFromCorner = 1 << 12,
97 | kIOHIDDigitizerEventSwipePending = 1 << 13,
98 | kIOHIDDigitizerEventSwipeUp = 1 << 24,
99 | kIOHIDDigitizerEventSwipeDown = 1 << 25,
100 | kIOHIDDigitizerEventSwipeLeft = 1 << 26,
101 | kIOHIDDigitizerEventSwipeRight = 1 << 27,
102 | kIOHIDDigitizerEventSwipeMask = 0xFF << 24,
103 | };
104 |
105 | enum {
106 | kIOHIDEventFieldDigitizerX = IOHIDEventFieldBase(kIOHIDEventTypeDigitizer),
107 | kIOHIDEventFieldDigitizerY,
108 | kIOHIDEventFieldDigitizerZ,
109 | kIOHIDEventFieldDigitizerButtonMask,
110 | kIOHIDEventFieldDigitizerType,
111 | kIOHIDEventFieldDigitizerIndex,
112 | kIOHIDEventFieldDigitizerIdentity,
113 | kIOHIDEventFieldDigitizerEventMask,
114 | kIOHIDEventFieldDigitizerRange,
115 | kIOHIDEventFieldDigitizerTouch,
116 | kIOHIDEventFieldDigitizerPressure,
117 | kIOHIDEventFieldDigitizerAuxiliaryPressure, // BarrelPressure
118 | kIOHIDEventFieldDigitizerTwist,
119 | kIOHIDEventFieldDigitizerTiltX,
120 | kIOHIDEventFieldDigitizerTiltY,
121 | kIOHIDEventFieldDigitizerAltitude,
122 | kIOHIDEventFieldDigitizerAzimuth,
123 | kIOHIDEventFieldDigitizerQuality,
124 | kIOHIDEventFieldDigitizerDensity,
125 | kIOHIDEventFieldDigitizerIrregularity,
126 | kIOHIDEventFieldDigitizerMajorRadius,
127 | kIOHIDEventFieldDigitizerMinorRadius,
128 | kIOHIDEventFieldDigitizerCollection,
129 | kIOHIDEventFieldDigitizerCollectionChord,
130 | kIOHIDEventFieldDigitizerChildEventMask,
131 | kIOHIDEventFieldDigitizerIsDisplayIntegrated,
132 | kIOHIDEventFieldDigitizerQualityRadiiAccuracy,
133 | };
134 |
135 | IOHIDEventRef IOHIDEventCreateDigitizerEvent(
136 | CFAllocatorRef allocator, AbsoluteTime timeStamp,
137 | IOHIDDigitizerTransducerType type, uint32_t index, uint32_t identity,
138 | uint32_t eventMask, uint32_t buttonMask, IOHIDFloat x, IOHIDFloat y,
139 | IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat barrelPressure,
140 | Boolean range, Boolean touch, IOOptionBits options);
141 |
142 | IOHIDEventRef IOHIDEventCreateDigitizerFingerEventWithQuality(
143 | CFAllocatorRef allocator, AbsoluteTime timeStamp, uint32_t index,
144 | uint32_t identity, uint32_t eventMask, IOHIDFloat x, IOHIDFloat y,
145 | IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat twist,
146 | IOHIDFloat minorRadius, IOHIDFloat majorRadius, IOHIDFloat quality,
147 | IOHIDFloat density, IOHIDFloat irregularity, Boolean range, Boolean touch,
148 | IOOptionBits options);
149 |
150 | IOHIDEventRef kif_IOHIDEventWithTouches(NSArray *touches)
151 | {
152 | uint64_t abTime = mach_absolute_time();
153 | AbsoluteTime timeStamp;
154 | timeStamp.hi = (UInt32)(abTime >> 32);
155 | timeStamp.lo = (UInt32)(abTime);
156 |
157 | IOHIDEventRef handEvent = IOHIDEventCreateDigitizerEvent(
158 | kCFAllocatorDefault, // allocator 内存分配器
159 | timeStamp, // timestamp 时间戳
160 | kIOHIDDigitizerTransducerTypeHand, // type
161 | 0, // index
162 | 0, // identity
163 | kIOHIDDigitizerEventTouch, // eventMask
164 | 0, // buttonMask
165 | 0, // x
166 | 0, // y
167 | 0, // z
168 | 0, // tipPressure
169 | 0, // barrelPressure
170 | 0, // range
171 | true, // touch
172 | 0); // options
173 |
174 | IOHIDEventSetIntegerValue(handEvent,
175 | kIOHIDEventFieldDigitizerIsDisplayIntegrated, true);
176 |
177 | for (UITouch *touch in touches)
178 | {
179 | uint32_t eventMask =
180 | (touch.phase == UITouchPhaseMoved)
181 | ? kIOHIDDigitizerEventPosition
182 | : (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch);
183 |
184 | uint32_t isTouching = (touch.phase == UITouchPhaseEnded) ? 0 : 1;
185 | CGPoint touchLocation = [touch locationInView:touch.window];
186 | IOHIDEventRef fingerEvent = IOHIDEventCreateDigitizerFingerEventWithQuality(
187 | kCFAllocatorDefault, // allocator
188 | timeStamp, // timestamp
189 | (UInt32)[touches indexOfObject:touch] + 1, // index
190 | 2, // identity
191 | eventMask, // eventMask
192 | (IOHIDFloat)touchLocation.x, // x
193 | (IOHIDFloat)touchLocation.y, // y
194 | 0.0, // z
195 | 0, // tipPressure
196 | 0, // twist
197 | 5.0, // minor radius
198 | 5.0, // major radius
199 | 1.0, // quality
200 | 1.0, // density
201 | 1.0, // irregularity
202 | (IOHIDFloat)isTouching, // range
203 | (IOHIDFloat)isTouching, // touch
204 | 0); // options
205 |
206 | IOHIDEventSetIntegerValue(fingerEvent,
207 | kIOHIDEventFieldDigitizerIsDisplayIntegrated, 1);
208 |
209 | IOHIDEventAppendEvent(handEvent, fingerEvent);
210 | CFRelease(fingerEvent);
211 | }
212 |
213 | return handEvent;
214 | }
--------------------------------------------------------------------------------
/src/helpers/private_headers/IOKit+SPI.h:
--------------------------------------------------------------------------------
1 | typedef struct __IOHIDEvent *IOHIDEventRef;
2 | typedef struct __IOHIDNotification *IOHIDNotificationRef;
3 | typedef struct __IOHIDService *IOHIDServiceRef;
4 | typedef struct __GSEvent *GSEventRef;
--------------------------------------------------------------------------------
/src/helpers/private_headers/LSApplicationProxy.h:
--------------------------------------------------------------------------------
1 | #ifndef LSApplicationProxy_h
2 | #define LSApplicationProxy_h
3 |
4 | #import
5 |
6 | @class LSPlugInKitProxy;
7 |
8 | @interface LSApplicationProxy : NSObject
9 | + (LSApplicationProxy *)applicationProxyForIdentifier:(NSString *)bundleIdentifier;
10 | - (BOOL)installed;
11 | - (BOOL)restricted;
12 | - (NSString *)applicationIdentifier;
13 | - (NSString *)localizedName;
14 | - (NSString *)shortVersionString;
15 | - (NSString *)applicationType;
16 | - (NSURL *)bundleURL;
17 | - (NSURL *)dataContainerURL;
18 | - (NSURL *)bundleContainerURL;
19 | - (NSDictionary *)groupContainerURLs;
20 | - (NSDictionary *)entitlements;
21 | - (NSArray *)plugInKitPlugins;
22 | @end
23 |
24 | #endif /* LSApplicationProxy_h */
25 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/LSApplicationWorkspace.h:
--------------------------------------------------------------------------------
1 | #ifndef LSApplicationWorkspace_h
2 | #define LSApplicationWorkspace_h
3 |
4 | #import
5 |
6 | @class LSApplicationProxy;
7 |
8 | @interface LSApplicationWorkspace : NSObject
9 | + (LSApplicationWorkspace *)defaultWorkspace;
10 | - (NSArray *)allApplications;
11 | - (void)enumerateApplicationsOfType:(NSInteger)type block:(void (^)(id))block;
12 | - (BOOL)openApplicationWithBundleID:(NSString *)bundleIdentifier;
13 | - (BOOL)installApplication:(NSURL *)ipaPath withOptions:(id)arg2 error:(NSError *__autoreleasing*)error;
14 | - (BOOL)uninstallApplication:(NSString *)bundleIdentifier withOptions:(id)arg2;
15 | - (BOOL)uninstallApplication:(NSString *)arg1 withOptions:(id)arg2 error:(NSError *__autoreleasing*)arg3 usingBlock:(/*^block*/id)arg4 ;
16 | - (BOOL)invalidateIconCache:(id)arg1;
17 | - (BOOL)openSensitiveURL:(NSURL *)url withOptions:(id)arg2 error:(NSError *__autoreleasing*)error;
18 | - (void)removeObserver:(id)arg1;
19 | - (void)addObserver:(id)arg1;
20 | @end
21 |
22 | #endif /* LSApplicationWorkspace_h */
23 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/NSUserDefaults+Private.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface NSUserDefaults (Private)
4 | - (instancetype)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container;
5 | - (id)objectForKey:(NSString *)key inDomain:(NSString *)domain;
6 | - (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain;
7 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/SBSAccessibilityWindowHostingController.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SBSAccessibilityWindowHostingController : NSObject
4 | - (void)registerWindowWithContextID:(unsigned)arg1 atLevel:(double)arg2;
5 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/SpringBoardServices.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | FOUNDATION_EXPORT mach_port_t SBSSpringBoardServerPort();
4 |
5 | FOUNDATION_EXPORT void SBFrontmostApplicationDisplayIdentifier(mach_port_t port, char *result);
6 | NSString *SBSCopyFrontmostApplicationDisplayIdentifier();
7 | FOUNDATION_EXPORT void SBGetScreenLockStatus(mach_port_t port, BOOL *lockStatus, BOOL *passcodeEnabled);
8 | FOUNDATION_EXPORT void SBSUndimScreen();
9 |
10 | FOUNDATION_EXPORT int SBSLaunchApplicationWithIdentifierAndURLAndLaunchOptions(NSString *bundleIdentifier, NSURL *url, NSDictionary *appOptions, NSDictionary *launchOptions, BOOL suspended);
11 | FOUNDATION_EXPORT int SBSLaunchApplicationWithIdentifierAndLaunchOptions(NSString *bundleIdentifier, NSDictionary *appOptions, NSDictionary *launchOptions, BOOL suspended);
12 | FOUNDATION_EXPORT bool SBSOpenSensitiveURLAndUnlock(CFURLRef url, char flags);
13 |
14 | FOUNDATION_EXPORT NSString *const SBSApplicationLaunchOptionUnlockDeviceKey;
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIApplication+Private.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication+Private.h
3 | // FakeTouch
4 | //
5 | // Created by Watanabe Toshinori on 2/6/19.
6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "IOKit+SPI.h"
11 |
12 | @interface UIApplication (Private)
13 | - (UIEvent *)_touchesEvent;
14 | - (void)_run;
15 | - (void)suspend;
16 | - (void)_accessibilityInit;
17 | - (void)terminateWithSuccess;
18 | - (void)__completeAndRunAsPlugin;
19 | - (id)_systemAnimationFenceExemptQueue;
20 | - (void)_enqueueHIDEvent:(IOHIDEventRef)event;
21 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIApplicationRotationFollowingController.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface UIApplicationRotationFollowingController : UIViewController {
4 | bool _sizesWindowToScene;
5 | }
6 |
7 | @property (assign,nonatomic) bool sizesWindowToScene;
8 |
9 | - (unsigned long long)supportedInterfaceOrientations;
10 | - (bool)shouldAutorotate;
11 | - (id)__autorotationSanityCheckObjectFromSource:(id)arg1 selector:(SEL)arg2;
12 | - (id)initWithNibName:(id)arg1 bundle:(id)arg2;
13 | - (long long)_preferredInterfaceOrientationGivenCurrentOrientation:(long long)arg1;
14 | - (void)window:(id)arg1 setupWithInterfaceOrientation:(long long)arg2;
15 | - (bool)sizesWindowToScene;
16 | - (bool)shouldAutorotateToInterfaceOrientation:(long long)arg1;
17 | - (void)setSizesWindowToScene:(bool)arg1;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIApplicationRotationFollowingControllerNoTouches.h:
--------------------------------------------------------------------------------
1 | #import "UIApplicationRotationFollowingController.h"
2 |
3 | @interface UIApplicationRotationFollowingControllerNoTouches : UIApplicationRotationFollowingController
4 | - (void)loadView;
5 | - (void)_prepareForRotationToOrientation:(UIInterfaceOrientation)arg1 duration:(NSTimeInterval)arg2;
6 | - (void)_rotateToOrientation:(UIInterfaceOrientation)arg1 duration:(NSTimeInterval)arg2;
7 | - (void)_finishRotationFromInterfaceOrientation:(UIInterfaceOrientation)arg1;
8 | @end
9 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIApplicationRotationFollowingWindow.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface UIApplicationRotationFollowingWindow : UIWindow
4 |
5 | + (BOOL)_isSystemWindow;
6 | - (id)init;
7 | - (void)dealloc;
8 | - (id)__autorotationSanityCheckObjectFromSource:(id)arg1 selector:(SEL)arg2;
9 | - (void)applicationWindowRotated:(id)arg1 ;
10 | - (void)_commonApplicationRotationFollowingWindowInit;
11 | - (id)_initWithFrame:(CGRect)arg1 attached:(BOOL)arg2;
12 | - (BOOL)_shouldControlAutorotation;
13 | - (BOOL)_shouldAutorotateToInterfaceOrientation:(long long)arg1;
14 | - (BOOL)isInterfaceAutorotationDisabled;
15 | - (void)_handleStatusBarOrientationChange:(id)arg1;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIAutoRotatingWindow.h:
--------------------------------------------------------------------------------
1 | #import "UIApplicationRotationFollowingWindow.h"
2 |
3 | @interface UIAutoRotatingWindow : UIApplicationRotationFollowingWindow {
4 | UIInterfaceOrientation _interfaceOrientation;
5 | BOOL _unknownOrientation;
6 | }
7 |
8 | + (instancetype)sharedPopoverHostingWindow;
9 | - (void)commonInit;
10 | - (UIView *)hitTest:(CGPoint)arg1 withEvent:(UIEvent *)arg2;
11 | - (void)_didRemoveSubview:(UIView *)arg1;
12 | - (instancetype)_initWithFrame:(CGRect)arg1 attached:(BOOL)arg2;
13 | - (void)updateForOrientation:(UIInterfaceOrientation)arg1;
14 | - (instancetype)_initWithFrame:(CGRect)arg1 debugName:(NSString *)arg2 windowScene:(UIWindowScene *)arg3;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIEvent+Private.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIEvent+Private.h
3 | // FakeTouch
4 | //
5 | // Created by Watanabe Toshinori on 2/6/19.
6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIEvent (Private)
12 | - (void)_addTouch:(UITouch *)touch forDelayedDelivery:(BOOL)delayed;
13 | - (void)_clearTouches;
14 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIEventDispatcher.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface UIEventDispatcher : NSObject
4 | - (void)_installEventRunLoopSources:(CFRunLoopRef)arg1;
5 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIEventFetcher.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "IOKit+SPI.h"
3 |
4 | @class UIEventDispatcher;
5 |
6 | @interface UIEventFetcher : NSObject
7 | - (void)_receiveHIDEvent:(IOHIDEventRef)arg1;
8 | - (void)setEventFetcherSink:(UIEventDispatcher *)arg1;
9 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UITouch+Private.h:
--------------------------------------------------------------------------------
1 | //
2 | // UITouch+Private.h
3 | // FakeTouch
4 | //
5 | // Created by Watanabe Toshinori on 2/6/19.
6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UITouch (Private)
12 |
13 | - (void)setWindow:(UIWindow *)window;
14 | - (void)setView:(UIView *)view;
15 | - (void)setIsTap:(BOOL)isTap;
16 | - (void)setTimestamp:(NSTimeInterval)timestamp;
17 | - (void)setPhase:(UITouchPhase)touchPhase;
18 | - (void)setGestureView:(UIView *)view;
19 | - (void)_setLocationInWindow:(CGPoint)location
20 | resetPrevious:(BOOL)resetPrevious;
21 | - (void)_setIsFirstTouchForView:(BOOL)firstTouchForView;
22 | - (void)_setIsTapToClick:(BOOL)isTapToClick;
23 |
24 | - (void)_setHidEvent:(IOHIDEventRef)event;
25 |
26 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UITouch-KIFAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // UITouch-KIFAdditions.h
3 | // KIF
4 | //
5 | // Created by Eric Firestone on 5/20/11.
6 | // Licensed to Square, Inc. under one or more contributor license agreements.
7 | // See the LICENSE file distributed with this work for the terms under
8 | // which Square, Inc. licenses this file to you.
9 | //
10 |
11 | #import "IOHIDEvent+KIF.h"
12 | #import "UITouch+Private.h"
13 | #import
14 |
15 | @interface UITouch (KIFAdditions)
16 |
17 | - (instancetype)initAtPoint:(CGPoint)point
18 | inWindow:(UIWindow *)window
19 | onView:(UIView *)view;
20 | - (instancetype)initTouch;
21 |
22 | - (void)setLocationInWindow:(CGPoint)location;
23 | - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase;
24 |
25 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UITouch-KIFAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // UITouch-KIFAdditions.m
3 | // KIF
4 | //
5 | // Created by Eric Firestone on 5/20/11.
6 | // Licensed to Square, Inc. under one or more contributor license agreements.
7 | // See the LICENSE file distributed with this work for the terms under
8 | // which Square, Inc. licenses this file to you.
9 | //
10 |
11 | #import "UITouch-KIFAdditions.h"
12 | #import
13 |
14 | @implementation UITouch (KIFAdditions)
15 |
16 | - (instancetype)initAtPoint:(CGPoint)point
17 | inWindow:(UIWindow *)window
18 | onView:(UIView *)view;
19 | {
20 | self = [super init];
21 | if (self == nil)
22 | return nil;
23 |
24 | // Create a fake tap touch
25 | [self setWindow:window]; // Wipes out some values. Needs to be first.
26 | [self _setLocationInWindow:point resetPrevious:YES];
27 |
28 | UIView *hitTestView = view;
29 |
30 | [self setView:hitTestView];
31 | [self setPhase:UITouchPhaseBegan];
32 | if (![[NSProcessInfo processInfo] isiOSAppOnMac] &&
33 | ![[NSProcessInfo processInfo] isMacCatalystApp])
34 | {
35 | [self _setIsTapToClick:NO];
36 | }
37 | else
38 | {
39 | [self _setIsFirstTouchForView:YES];
40 | [self setIsTap:NO];
41 | }
42 |
43 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]];
44 |
45 | if ([self respondsToSelector:@selector(setGestureView:)])
46 | [self setGestureView:hitTestView];
47 |
48 | [self kif_setHidEvent];
49 | return self;
50 | }
51 |
52 | - (instancetype)initTouch;
53 | {
54 | self = [super init];
55 | if (self == nil)
56 | return nil;
57 |
58 | NSArray *scenes =
59 | [[[UIApplication sharedApplication] connectedScenes] allObjects];
60 |
61 | NSArray *windows = [[scenes objectAtIndex:0] windows];
62 | UIWindow *window = [windows lastObject];
63 | CGPoint point = CGPointMake(0, 0);
64 |
65 | [self setWindow:window]; // Wipes out some values. Needs to be first.
66 | [self _setLocationInWindow:point resetPrevious:YES];
67 |
68 | UIView *hitTestView = [window hitTest:point withEvent:nil];
69 |
70 | [self setView:hitTestView];
71 | [self setPhase:UITouchPhaseEnded];
72 |
73 | if (![[NSProcessInfo processInfo] isiOSAppOnMac] &&
74 | ![[NSProcessInfo processInfo] isMacCatalystApp])
75 | {
76 | [self _setIsTapToClick:NO];
77 | }
78 | else
79 | {
80 | [self _setIsFirstTouchForView:YES];
81 | [self setIsTap:NO];
82 | }
83 |
84 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]];
85 |
86 | if ([self respondsToSelector:@selector(setGestureView:)])
87 | [self setGestureView:hitTestView];
88 |
89 | [self kif_setHidEvent];
90 | return self;
91 | }
92 |
93 | - (void)setLocationInWindow:(CGPoint)location
94 | {
95 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]];
96 | [self _setLocationInWindow:location resetPrevious:NO];
97 | }
98 |
99 | - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase
100 | {
101 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]];
102 | [self setPhase:phase];
103 | }
104 |
105 | - (void)kif_setHidEvent
106 | {
107 | IOHIDEventRef event = kif_IOHIDEventWithTouches(@[ self ]);
108 | [self _setHidEvent:event];
109 | CFRelease(event);
110 | }
111 |
112 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/UIWindow+Private.h:
--------------------------------------------------------------------------------
1 |
2 | @interface UIWindow (Private)
3 | - (unsigned int)_contextId;
4 | @end
--------------------------------------------------------------------------------
/src/helpers/private_headers/libproc.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved.
3 | *
4 | * @APPLE_LICENSE_HEADER_START@
5 | *
6 | * This file contains Original Code and/or Modifications of Original Code
7 | * as defined in and that are subject to the Apple Public Source License
8 | * Version 2.0 (the 'License'). You may not use this file except in
9 | * compliance with the License. Please obtain a copy of the License at
10 | * http://www.opensource.apple.com/apsl/ and read it before using this
11 | * file.
12 | *
13 | * The Original Code and all software distributed under the License are
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 | * Please see the License for the specific language governing rights and
19 | * limitations under the License.
20 | *
21 | * @APPLE_LICENSE_HEADER_END@
22 | */
23 | #ifndef _LIBPROC_H_
24 | #define _LIBPROC_H_
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include /* for audit_token_t */
35 |
36 | #include
37 |
38 | #include
39 | #include
40 |
41 | /*
42 | * This header file contains private interfaces to obtain process information.
43 | * These interfaces are subject to change in future releases.
44 | */
45 |
46 | /*!
47 | * @define PROC_LISTPIDSPATH_PATH_IS_VOLUME
48 | * @discussion This flag indicates that all processes that hold open
49 | * file references on the volume associated with the specified
50 | * path should be returned.
51 | */
52 | #define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1
53 |
54 |
55 | /*!
56 | * @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY
57 | * @discussion This flag indicates that file references that were opened
58 | * with the O_EVTONLY flag should be excluded from the matching
59 | * criteria.
60 | */
61 | #define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2
62 |
63 | __BEGIN_DECLS
64 |
65 |
66 | /*!
67 | * @function proc_listpidspath
68 | * @discussion A function which will search through the current
69 | * processes looking for open file references which match
70 | * a specified path or volume.
71 | * @param type types of processes to be searched (see proc_listpids)
72 | * @param typeinfo adjunct information for type
73 | * @param path file or volume path
74 | * @param pathflags flags to control which files should be considered
75 | * during the process search.
76 | * @param buffer a C array of int-sized values to be filled with
77 | * process identifiers that hold an open file reference
78 | * matching the specified path or volume. Pass NULL to
79 | * obtain the minimum buffer size needed to hold the
80 | * currently active processes.
81 | * @param buffersize the size (in bytes) of the provided buffer.
82 | * @result the number of bytes of data returned in the provided buffer;
83 | * -1 if an error was encountered;
84 | */
85 | int proc_listpidspath(uint32_t type,
86 | uint32_t typeinfo,
87 | const char *path,
88 | uint32_t pathflags,
89 | void *buffer,
90 | int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
91 |
92 | int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
93 | int proc_listallpids(void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1);
94 | int proc_listpgrppids(pid_t pgrpid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1);
95 | int proc_listchildpids(pid_t ppid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1);
96 | int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
97 | int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
98 | int proc_pidfileportinfo(int pid, uint32_t fileport, int flavor, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
99 | int proc_name(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
100 | int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
101 | int proc_kmsgbuf(void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
102 | int proc_pidpath(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
103 | int proc_pidpath_audittoken(audit_token_t *audittoken, void * buffer, uint32_t buffersize) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0));
104 | int proc_libversion(int *major, int * minor) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
105 |
106 | /*
107 | * Return resource usage information for the given pid, which can be a live process or a zombie.
108 | *
109 | * Returns 0 on success; or -1 on failure, with errno set to indicate the specific error.
110 | */
111 | int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
112 |
113 | /*
114 | * A process can use the following api to set its own process control
115 | * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values
116 | */
117 | #define PROC_SETPC_NONE 0
118 | #define PROC_SETPC_THROTTLEMEM 1
119 | #define PROC_SETPC_SUSPEND 2
120 | #define PROC_SETPC_TERMINATE 3
121 |
122 | int proc_setpcontrol(const int control) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
123 | int proc_setpcontrol(const int control);
124 |
125 | int proc_track_dirty(pid_t pid, uint32_t flags);
126 | int proc_set_dirty(pid_t pid, bool dirty);
127 | int proc_get_dirty(pid_t pid, uint32_t *flags);
128 | int proc_clear_dirty(pid_t pid, uint32_t flags);
129 |
130 | int proc_terminate(pid_t pid, int *sig);
131 |
132 | /*
133 | * NO_SMT means that on an SMT CPU, this thread must be scheduled alone,
134 | * with the paired CPU idle.
135 | *
136 | * Set NO_SMT on the current proc (all existing and future threads)
137 | * This attribute is inherited on fork and exec
138 | */
139 | int proc_set_no_smt(void) __API_AVAILABLE(macos(10.16));
140 |
141 | /* Set NO_SMT on the current thread */
142 | int proc_setthread_no_smt(void) __API_AVAILABLE(macos(10.16));
143 |
144 | /*
145 | * CPU Security Mitigation APIs
146 | *
147 | * Set CPU security mitigation on the current proc (all existing and future threads)
148 | * This attribute is inherited on fork and exec
149 | */
150 | int proc_set_csm(uint32_t flags) __API_AVAILABLE(macos(10.16));
151 |
152 | /* Set CPU security mitigation on the current thread */
153 | int proc_setthread_csm(uint32_t flags) __API_AVAILABLE(macos(10.16));
154 |
155 | /*
156 | * flags for CPU Security Mitigation APIs
157 | * PROC_CSM_ALL should be used in most cases,
158 | * the individual flags are provided only for performance evaluation etc
159 | */
160 | #define PROC_CSM_ALL 0x0001 /* Set all available mitigations */
161 | #define PROC_CSM_NOSMT 0x0002 /* Set NO_SMT - see above */
162 | #define PROC_CSM_TECS 0x0004 /* Execute VERW on every return to user mode */
163 |
164 | #ifdef PRIVATE
165 | #include
166 | /*
167 | * Enumerate potential userspace pointers embedded in kernel data structures.
168 | * Currently inspects kqueues only.
169 | *
170 | * NOTE: returned "pointers" are opaque user-supplied values and thus not
171 | * guaranteed to address valid objects or be pointers at all.
172 | *
173 | * Returns the number of pointers found (which may exceed buffersize), or -1 on
174 | * failure and errno set appropriately.
175 | */
176 | int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize);
177 |
178 | int proc_list_dynkqueueids(int pid, kqueue_id_t *buf, uint32_t bufsz);
179 | int proc_piddynkqueueinfo(int pid, int flavor, kqueue_id_t kq_id, void *buffer,
180 | int buffersize);
181 | #endif /* PRIVATE */
182 |
183 | int proc_udata_info(int pid, int flavor, void *buffer, int buffersize);
184 |
185 | __END_DECLS
186 |
187 | #endif /*_LIBPROC_H_ */
188 |
--------------------------------------------------------------------------------
/src/helpers/ts/TSEventFetcher.h:
--------------------------------------------------------------------------------
1 | //
2 | // TSEventFetcher.h
3 | // TrollSpeed
4 | //
5 | // Created by Lessica on 2024/1/24.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface TSEventFetcher : NSObject
13 | + (NSInteger)receiveAXEventID:(NSInteger)pointId
14 | atGlobalCoordinate:(CGPoint)point
15 | withTouchPhase:(UITouchPhase)phase
16 | inWindow:(UIWindow *)window
17 | onView:(UIView *)view;
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/helpers/ts/TSEventFetcher.mm:
--------------------------------------------------------------------------------
1 | //
2 | // TSEventFetcher.mm
3 | // TrollSpeed
4 | //
5 | // Created by Lessica on 2024/1/24.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 | #import "TSEventFetcher.h"
12 | #import "../private_headers/UIEvent+Private.h"
13 | #import "../private_headers/UITouch-KIFAdditions.h"
14 | #import "../private_headers/UIApplication+Private.h"
15 |
16 | static NSArray *_safeTouchAry = nil;
17 | static NSMutableArray *_touchAry = nil;
18 | static NSMutableArray *_livingTouchAry = nil;
19 | static CFRunLoopSourceRef _source = NULL;
20 |
21 | static UITouch *toRemove = nil;
22 | static UITouch *toStationarify = nil;
23 |
24 | static void __TSEventFetcherCallback(void *info)
25 | {
26 | static UIApplication *app = [UIApplication sharedApplication];
27 | UIEvent *event = [app _touchesEvent];
28 |
29 | // to retain objects from being released
30 | [event _clearTouches];
31 |
32 | NSArray *myAry = _safeTouchAry;
33 | for (UITouch *aTouch in myAry)
34 | {
35 | switch (aTouch.phase) {
36 | case UITouchPhaseEnded:
37 | case UITouchPhaseCancelled:
38 | toRemove = aTouch;
39 | break;
40 | case UITouchPhaseBegan:
41 | toStationarify = aTouch;
42 | break;
43 | default:
44 | break;
45 | }
46 |
47 | [event _addTouch:aTouch forDelayedDelivery:NO];
48 | }
49 |
50 | [app sendEvent:event];
51 | }
52 |
53 | @implementation TSEventFetcher
54 |
55 | + (void)load
56 | {
57 | _livingTouchAry = [[NSMutableArray alloc] init];
58 | _touchAry = [[NSMutableArray alloc] init];
59 |
60 | for (NSInteger i = 0; i < 100; i++)
61 | {
62 | UITouch *touch = [[UITouch alloc] initTouch];
63 | [touch setPhaseAndUpdateTimestamp:UITouchPhaseEnded];
64 | [_touchAry addObject:touch];
65 | }
66 |
67 | CFRunLoopSourceContext context;
68 | memset(&context, 0, sizeof(CFRunLoopSourceContext));
69 | context.perform = __TSEventFetcherCallback;
70 |
71 | // content of context is copied
72 | _source = CFRunLoopSourceCreate(kCFAllocatorDefault, -2, &context);
73 | CFRunLoopRef loop = CFRunLoopGetMain();
74 | CFRunLoopAddSource(loop, _source, kCFRunLoopCommonModes);
75 | }
76 |
77 | + (NSInteger)receiveAXEventID:(NSInteger)eventId
78 | atGlobalCoordinate:(CGPoint)coordinate
79 | withTouchPhase:(UITouchPhase)phase
80 | inWindow:(UIWindow *)window
81 | onView:(UIView *)view
82 | {
83 | BOOL deleted = NO;
84 | UITouch *touch = nil;
85 | BOOL needsCopy = NO;
86 |
87 | if (toRemove != nil)
88 | {
89 | touch = toRemove;
90 | toRemove = nil;
91 | [_livingTouchAry removeObjectIdenticalTo:touch];
92 | deleted = YES;
93 | needsCopy = YES;
94 | }
95 |
96 | if (toStationarify != nil)
97 | {
98 | // in case this is changed during the operations
99 | touch = toStationarify;
100 | toStationarify = nil;
101 | if (touch.phase == UITouchPhaseBegan)
102 | [touch setPhaseAndUpdateTimestamp:UITouchPhaseStationary];
103 | }
104 |
105 | eventId -= 1;
106 |
107 | // ideally should be phase began when this hit
108 | // but if by any means other phases come... well lets be forgiving
109 | touch = _touchAry[eventId];
110 | BOOL oldState = [_livingTouchAry containsObject:touch];
111 | BOOL newState = !oldState;
112 | if (newState)
113 | {
114 | if (phase == UITouchPhaseEnded || phase == UITouchPhaseCancelled)
115 | return deleted;
116 | touch = [[UITouch alloc] initAtPoint:coordinate inWindow:window onView:view];
117 | [_livingTouchAry addObject:touch];
118 | [_touchAry setObject:touch atIndexedSubscript:eventId];
119 | needsCopy = YES;
120 | }
121 | else
122 | {
123 | if (touch.phase == UITouchPhaseBegan && phase == UITouchPhaseMoved)
124 | return deleted;
125 | [touch setLocationInWindow:coordinate];
126 | }
127 |
128 | [touch setPhaseAndUpdateTimestamp:phase];
129 |
130 | if (needsCopy)
131 | {
132 | CFTypeRef delayRelease = CFBridgingRetain(_safeTouchAry);
133 | _safeTouchAry = [[NSArray alloc] initWithArray:_livingTouchAry copyItems:NO];
134 | CFBridgingRelease(delayRelease);
135 | }
136 |
137 | CFRunLoopSourceSignal(_source);
138 | return deleted;
139 | }
140 |
141 | @end
--------------------------------------------------------------------------------
/src/helpers/ts/pac_helper.h:
--------------------------------------------------------------------------------
1 | #ifndef PTRAUTH_HELPERS_H
2 | #define PTRAUTH_HELPERS_H
3 |
4 | // Helpers for PAC archs.
5 |
6 | // If the compiler understands __arm64e__, assume it's paired with an SDK that has
7 | // ptrauth.h. Otherwise, it'll probably error if we try to include it so don't.
8 | #if __arm64e__
9 | #include
10 | #endif
11 |
12 | #pragma clang diagnostic push
13 | #pragma clang diagnostic ignored "-Wunused-function"
14 |
15 | // Given a pointer to instructions, sign it so you can call it like a normal fptr.
16 | static void *make_sym_callable(void *ptr) {
17 | #if __arm64e__
18 | if (!ptr) return ptr;
19 | ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0);
20 | #endif
21 | return ptr;
22 | }
23 |
24 | // Given a function pointer, strip the PAC so you can read the instructions.
25 | static void *make_sym_readable(void *ptr) {
26 | #if __arm64e__
27 | if (!ptr) return ptr;
28 | ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer);
29 | #endif
30 | return ptr;
31 | }
32 |
33 | #pragma clang diagnostic pop
34 | #endif // PTRAUTH_HELPERS_H
--------------------------------------------------------------------------------
/src/hud/AnyBackdropView.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface AnyBackdropView : UIView
6 | @end
7 |
8 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/hud/AnyBackdropView.mm:
--------------------------------------------------------------------------------
1 | #import "AnyBackdropView.h"
2 | #import "../helpers/private_headers/CAFilter.h"
3 |
4 | @implementation AnyBackdropView
5 |
6 | + (Class)layerClass {
7 | return [NSClassFromString(@"CABackdropLayer") class];
8 | }
9 |
10 | - (instancetype)initWithCoder:(NSCoder *)coder
11 | {
12 | self = [super initWithCoder:coder];
13 | [self _updateFilters];
14 | return self;
15 | }
16 |
17 | - (instancetype)initWithFrame:(CGRect)frame
18 | {
19 | self = [super initWithFrame:frame];
20 | [self _updateFilters];
21 | return self;
22 | }
23 |
24 | - (void)_updateFilters {
25 | // code from Lessica/TrollSpeed
26 | CAFilter *blurFilter = [CAFilter filterWithName:kCAFilterGaussianBlur];
27 | [blurFilter setValue:@(50.0) forKey:@"inputRadius"]; // radius 50pt
28 | [blurFilter setValue:@YES forKey:@"inputNormalizeEdges"]; // do not use inputHardEdges
29 |
30 | CAFilter *brightnessFilter = [CAFilter filterWithName:kCAFilterColorBrightness];
31 | [brightnessFilter setValue:@(-0.285) forKey:@"inputAmount"]; // -28.5%
32 |
33 | CAFilter *contrastFilter = [CAFilter filterWithName:kCAFilterColorContrast];
34 | [contrastFilter setValue:@(1000.0) forKey:@"inputAmount"]; // 1000x
35 |
36 | CAFilter *saturateFilter = [CAFilter filterWithName:kCAFilterColorSaturate];
37 | [saturateFilter setValue:@(0.0) forKey:@"inputAmount"];
38 |
39 | CAFilter *colorInvertFilter = [CAFilter filterWithName:kCAFilterColorInvert];
40 |
41 | [self.layer setFilters:@[
42 | blurFilter, brightnessFilter, contrastFilter,
43 | saturateFilter, colorInvertFilter,
44 | ]];
45 | }
46 |
47 | @end
--------------------------------------------------------------------------------
/src/hud/HUDApp.mm:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | #import "HUDHelper.h"
6 |
7 | #import "../helpers/ts/TSEventFetcher.h"
8 | #import "../helpers/private_headers/AXEventRepresentation.h"
9 | #import "../helpers/private_headers/BackboardServices.h"
10 | #import "../helpers/private_headers/UIApplication+Private.h"
11 |
12 | static __used
13 | void _HUDEventCallback(void *target, void *refcon, IOHIDServiceRef service, IOHIDEventRef event)
14 | {
15 | static UIApplication *app = [UIApplication sharedApplication];
16 | #if DEBUG
17 | os_log_debug(OS_LOG_DEFAULT, "_HUDEventCallback => %{public}@", event);
18 | #endif
19 |
20 | // iOS 15.1+ has a new API for handling HID events.
21 | if (@available(iOS 15.1, *)) {}
22 | else {
23 | [app _enqueueHIDEvent:event];
24 | }
25 |
26 | BOOL shouldUseAXEvent = YES; // Always use AX events now...
27 |
28 | BOOL isExactly15 = NO;
29 | static NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
30 | if (version.majorVersion == 15 && version.minorVersion == 0 && version.patchVersion == 0) {
31 | isExactly15 = YES;
32 | }
33 |
34 | if (@available(iOS 15.0, *)) {
35 | shouldUseAXEvent = !isExactly15;
36 | } else {
37 | shouldUseAXEvent = NO;
38 | }
39 |
40 | if (shouldUseAXEvent)
41 | {
42 | static Class AXEventRepresentationCls = nil;
43 | static dispatch_once_t onceToken;
44 | dispatch_once(&onceToken, ^{
45 | [[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/AccessibilityUtilities.framework"] load];
46 | AXEventRepresentationCls = objc_getClass("AXEventRepresentation");
47 | });
48 |
49 | AXEventRepresentation *rep = [AXEventRepresentationCls representationWithHIDEvent:event hidStreamIdentifier:@"UIApplicationEvents"];
50 | #if DEBUG
51 | os_log_debug(OS_LOG_DEFAULT, "_HUDEventCallback => %{public}@", rep.handInfo);
52 | #endif
53 |
54 | /* I don't like this. It's too hacky, but it works. */
55 | {
56 | dispatch_async(dispatch_get_main_queue(), ^(void) {
57 | static UIWindow *keyWindow = nil;
58 | static dispatch_once_t onceToken;
59 | dispatch_once(&onceToken, ^{
60 | keyWindow = [[app windows] firstObject];
61 | });
62 |
63 | UIView *keyView = [keyWindow hitTest:[rep location] withEvent:nil];
64 |
65 | UITouchPhase phase = UITouchPhaseEnded;
66 | if ([rep isTouchDown])
67 | phase = UITouchPhaseBegan;
68 | else if ([rep isMove])
69 | phase = UITouchPhaseMoved;
70 | else if ([rep isCancel])
71 | phase = UITouchPhaseCancelled;
72 | else if ([rep isLift] || [rep isInRange] || [rep isInRangeLift])
73 | phase = UITouchPhaseEnded;
74 |
75 | NSInteger pointerId = [[[[rep handInfo] paths] firstObject] pathIdentity];
76 | if (pointerId > 0)
77 | [TSEventFetcher receiveAXEventID:MIN(MAX(pointerId, 1), 98) atGlobalCoordinate:[rep location] withTouchPhase:phase inWindow:keyWindow onView:keyView];
78 | });
79 | }
80 | }
81 | }
82 |
83 |
84 | #pragma mark -
85 |
86 | static NSString *_cachesDirectoryPath = nil;
87 | static NSString *_hudPIDFilePath = nil;
88 | static NSString *GetPIDFilePath(void)
89 | {
90 | static dispatch_once_t onceToken;
91 | dispatch_once(&onceToken, ^{
92 | _cachesDirectoryPath =
93 | [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
94 | _hudPIDFilePath = [_cachesDirectoryPath stringByAppendingPathComponent:@"hud.pid"];
95 | });
96 | return _hudPIDFilePath;
97 | }
98 |
99 | #pragma mark -
100 |
101 | int main(int argc, char *argv[])
102 | {
103 | @autoreleasepool
104 | {
105 | #if DEBUG
106 | os_log_debug(OS_LOG_DEFAULT, "launched argc %{public}d, argv[1] %{public}s", argc, argc > 1 ? argv[1] : "NULL");
107 | #endif
108 |
109 | if (argc <= 1)
110 | return UIApplicationMain(argc, argv, @"MainApplication", @"MainApplicationDelegate");
111 |
112 | if (strcmp(argv[1], "-hud") == 0)
113 | {
114 | pid_t pid = getpid();
115 | pid_t pgid = getgid();
116 | (void)pgid;
117 | #if DEBUG
118 | os_log_debug(OS_LOG_DEFAULT, "HUD pid %d, pgid %d", pid, pgid);
119 | #endif
120 | NSString *pidString = [NSString stringWithFormat:@"%d", pid];
121 | [pidString writeToFile:GetPIDFilePath()
122 | atomically:YES
123 | encoding:NSUTF8StringEncoding
124 | error:nil];
125 |
126 | [UIScreen initialize];
127 | CFRunLoopGetCurrent();
128 |
129 | GSInitialize();
130 | BKSDisplayServicesStart();
131 | UIApplicationInitialize();
132 |
133 | UIApplicationInstantiateSingleton(objc_getClass("HUDMainApplication"));
134 | static id appDelegate = [[objc_getClass("HUDMainApplicationDelegate") alloc] init];
135 | [UIApplication.sharedApplication setDelegate:appDelegate];
136 | [UIApplication.sharedApplication _accessibilityInit];
137 |
138 | [NSRunLoop currentRunLoop];
139 | BKSHIDEventRegisterEventCallback(_HUDEventCallback);
140 |
141 | if (@available(iOS 15.0, *)) {
142 | GSEventInitialize(0);
143 | GSEventPushRunLoopMode(kCFRunLoopDefaultMode);
144 | }
145 |
146 | [UIApplication.sharedApplication __completeAndRunAsPlugin];
147 |
148 | static int _springboardBootToken;
149 | notify_register_dispatch("SBSpringBoardDidLaunchNotification", &_springboardBootToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l), ^(int token) {
150 | notify_cancel(token);
151 |
152 | // Re-enable HUD after SpringBoard is launched.
153 | SetHUDEnabled(YES);
154 |
155 | // Exit the current instance of HUD.
156 | #ifdef NOTIFY_DISMISSAL_HUD
157 | notify_post(NOTIFY_DISMISSAL_HUD);
158 | #endif
159 | kill(pid, SIGKILL);
160 | });
161 |
162 | CFRunLoopRun();
163 | return EXIT_SUCCESS;
164 | }
165 | else if (strcmp(argv[1], "-exit") == 0)
166 | {
167 | NSString *pidString = [NSString stringWithContentsOfFile:GetPIDFilePath()
168 | encoding:NSUTF8StringEncoding
169 | error:nil];
170 |
171 | if (pidString)
172 | {
173 | pid_t pid = (pid_t)[pidString intValue];
174 | kill(pid, SIGKILL);
175 | unlink(GetPIDFilePath().UTF8String);
176 | }
177 |
178 | return EXIT_SUCCESS;
179 | }
180 | else if (strcmp(argv[1], "-check") == 0)
181 | {
182 | NSString *pidString = [NSString stringWithContentsOfFile:GetPIDFilePath()
183 | encoding:NSUTF8StringEncoding
184 | error:nil];
185 |
186 | if (pidString)
187 | {
188 | pid_t pid = (pid_t)[pidString intValue];
189 | int killed = kill(pid, 0);
190 | return (killed == 0 ? EXIT_FAILURE : EXIT_SUCCESS);
191 | }
192 | else return EXIT_SUCCESS; // No PID file, so HUD is not running
193 | }
194 | }
195 | }
--------------------------------------------------------------------------------
/src/hud/HUDHelper.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | OBJC_EXTERN BOOL IsHUDEnabled(void);
6 | OBJC_EXTERN void SetHUDEnabled(BOOL isEnabled);
7 | OBJC_EXTERN void waitForNotification(void (^onFinish)(), BOOL isEnabled);
8 |
9 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/hud/HUDHelper.mm:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | #import "HUDHelper.h"
6 |
7 | extern "C" char **environ;
8 |
9 | #define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1
10 | extern "C" int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t);
11 | extern "C" int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t);
12 | extern "C" int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t);
13 |
14 | BOOL IsHUDEnabled(void)
15 | {
16 | static char *executablePath = NULL;
17 | uint32_t executablePathSize = 0;
18 | _NSGetExecutablePath(NULL, &executablePathSize);
19 | executablePath = (char *)calloc(1, executablePathSize);
20 | _NSGetExecutablePath(executablePath, &executablePathSize);
21 |
22 | posix_spawnattr_t attr;
23 | posix_spawnattr_init(&attr);
24 |
25 | posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
26 | posix_spawnattr_set_persona_uid_np(&attr, 0);
27 | posix_spawnattr_set_persona_gid_np(&attr, 0);
28 |
29 | pid_t task_pid;
30 | const char *args[] = { executablePath, "-check", NULL };
31 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ);
32 | posix_spawnattr_destroy(&attr);
33 |
34 | #if DEBUG
35 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -check pid = %{public}d", executablePath, task_pid);
36 | #endif
37 |
38 | int status;
39 | do {
40 | if (waitpid(task_pid, &status, 0) != -1)
41 | {
42 | #if DEBUG
43 | os_log_debug(OS_LOG_DEFAULT, "child status %d", WEXITSTATUS(status));
44 | #endif
45 | }
46 | } while (!WIFEXITED(status) && !WIFSIGNALED(status));
47 |
48 | return WEXITSTATUS(status) != 0;
49 | }
50 |
51 | void SetHUDEnabled(BOOL isEnabled)
52 | {
53 | #ifdef NOTIFY_DISMISSAL_HUD
54 | notify_post(NOTIFY_DISMISSAL_HUD);
55 | #endif
56 |
57 | static char *executablePath = NULL;
58 | uint32_t executablePathSize = 0;
59 | _NSGetExecutablePath(NULL, &executablePathSize);
60 | executablePath = (char *)calloc(1, executablePathSize);
61 | _NSGetExecutablePath(executablePath, &executablePathSize);
62 |
63 | posix_spawnattr_t attr;
64 | posix_spawnattr_init(&attr);
65 |
66 | posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
67 | posix_spawnattr_set_persona_uid_np(&attr, 0);
68 | posix_spawnattr_set_persona_gid_np(&attr, 0);
69 |
70 | if (isEnabled)
71 | {
72 | posix_spawnattr_setpgroup(&attr, 0);
73 | posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP);
74 |
75 | pid_t task_pid;
76 | const char *args[] = { executablePath, "-hud", NULL };
77 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ);
78 | posix_spawnattr_destroy(&attr);
79 |
80 | #if DEBUG
81 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -hud pid = %{public}d", executablePath, task_pid);
82 | #endif
83 | }
84 | else
85 | {
86 | [NSThread sleepForTimeInterval:0.25];
87 |
88 | pid_t task_pid;
89 | const char *args[] = { executablePath, "-exit", NULL };
90 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ);
91 | posix_spawnattr_destroy(&attr);
92 |
93 | #if DEBUG
94 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -exit pid = %{public}d", executablePath, task_pid);
95 | #endif
96 |
97 | int status;
98 | do {
99 | if (waitpid(task_pid, &status, 0) != -1)
100 | {
101 | #if DEBUG
102 | os_log_debug(OS_LOG_DEFAULT, "child status %d", WEXITSTATUS(status));
103 | #endif
104 | }
105 | } while (!WIFEXITED(status) && !WIFSIGNALED(status));
106 | }
107 | }
108 |
109 | void waitForNotification(void (^onFinish)(), BOOL isEnabled) {
110 | if (isEnabled)
111 | {
112 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
113 |
114 | int token;
115 | notify_register_dispatch(NOTIFY_LAUNCHED_HUD, &token, dispatch_get_main_queue(), ^(int token) {
116 | notify_cancel(token);
117 | dispatch_semaphore_signal(semaphore);
118 | });
119 |
120 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
121 | int timedOut = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));
122 | dispatch_async(dispatch_get_main_queue(), ^{
123 | if (timedOut)
124 | os_log_error(OS_LOG_DEFAULT, "Timed out waiting for HUD to launch");
125 |
126 | onFinish();
127 | });
128 | });
129 | }
130 | else
131 | {
132 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
133 | onFinish();
134 | });
135 | }
136 | }
--------------------------------------------------------------------------------
/src/hud/HUDMainApplication.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface HUDMainApplication : UIApplication
6 | @end
7 |
8 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/hud/HUDMainApplication.mm:
--------------------------------------------------------------------------------
1 | //
2 | // HUDMainApplication.m
3 | //
4 | //
5 | // Created by lemin on 10/5/23.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | #import "HUDMainApplication.h"
15 |
16 | #import "../helpers/private_headers/UIEventDispatcher.h"
17 | #import "../helpers/private_headers/UIEventFetcher.h"
18 | #import "../helpers/private_headers/UIApplication+Private.h"
19 |
20 | #import "../helpers/ts/pac_helper.h"
21 |
22 | static void DumpThreads(void)
23 | {
24 | char name[256];
25 | mach_msg_type_number_t count;
26 | thread_act_array_t list;
27 | task_threads(mach_task_self(), &list, &count);
28 | for (int i = 0; i < count; ++i)
29 | {
30 | pthread_t pt = pthread_from_mach_thread_np(list[i]);
31 | if (pt)
32 | {
33 | name[0] = '\0';
34 | #if DEBUG
35 | __unused int rc = pthread_getname_np(pt, name, sizeof name);
36 | os_log_debug(OS_LOG_DEFAULT, "mach thread %u: getname returned %d: %{public}s", list[i], rc, name);
37 | #endif
38 | }
39 | else
40 | {
41 | #if DEBUG
42 | os_log_debug(OS_LOG_DEFAULT, "mach thread %u: no pthread found", list[i]);
43 | #endif
44 | }
45 | }
46 | }
47 |
48 | @implementation HUDMainApplication
49 |
50 | - (instancetype)init
51 | {
52 | if (self = [super init])
53 | {
54 | #if DEBUG
55 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplication init]");
56 | #endif
57 | notify_post(NOTIFY_LAUNCHED_HUD);
58 |
59 | #ifdef NOTIFY_DISMISSAL_HUD
60 | {
61 | int token;
62 | notify_register_dispatch(NOTIFY_DISMISSAL_HUD, &token, dispatch_get_main_queue(), ^(int token) {
63 | notify_cancel(token);
64 |
65 | // Fade out the HUD window
66 | [UIView animateWithDuration:0.25f animations:^{
67 | [[self.windows firstObject] setAlpha:0.0];
68 | } completion:^(BOOL finished) {
69 | // Terminate the HUD app
70 | [self terminateWithSuccess];
71 | }];
72 | });
73 | }
74 | #endif
75 | do {
76 | UIEventDispatcher *dispatcher = (UIEventDispatcher *)[self valueForKey:@"eventDispatcher"];
77 | if (!dispatcher)
78 | {
79 | #if DEBUG
80 | os_log_error(OS_LOG_DEFAULT, "failed to get ivar _eventDispatcher");
81 | #endif
82 | break;
83 | }
84 |
85 | #if DEBUG
86 | os_log_debug(OS_LOG_DEFAULT, "got ivar _eventDispatcher: %p", dispatcher);
87 | #endif
88 |
89 | if ([dispatcher respondsToSelector:@selector(_installEventRunLoopSources:)])
90 | {
91 | CFRunLoopRef mainRunLoop = CFRunLoopGetMain();
92 | [dispatcher _installEventRunLoopSources:mainRunLoop];
93 | }
94 | else
95 | {
96 | IMP runMethodIMP = class_getMethodImplementation([self class], @selector(_run));
97 | if (!runMethodIMP)
98 | {
99 | #if DEBUG
100 | os_log_error(OS_LOG_DEFAULT, "failed to get - [UIApplication _run] method");
101 | #endif
102 | break;
103 | }
104 |
105 | uint32_t *runMethodPtr = (uint32_t *)make_sym_readable((void *)runMethodIMP);
106 | #if DEBUG
107 | os_log_debug(OS_LOG_DEFAULT, "- [UIApplication _run]: %p", runMethodPtr);
108 | #endif
109 |
110 | void (*orig_UIEventDispatcher__installEventRunLoopSources_)(id _Nonnull, SEL _Nonnull, CFRunLoopRef) = NULL;
111 | for (int i = 0; i < 0x140; i++)
112 | {
113 | // mov x2, x0
114 | // mov x0, x?
115 | if (runMethodPtr[i] != 0xaa0003e2 || (runMethodPtr[i + 1] & 0xff000000) != 0xaa000000)
116 | continue;
117 |
118 | // bl -[UIEventDispatcher _installEventRunLoopSources:]
119 | uint32_t blInst = runMethodPtr[i + 2];
120 | uint32_t *blInstPtr = &runMethodPtr[i + 2];
121 | if ((blInst & 0xfc000000) != 0x94000000)
122 | {
123 | #if DEBUG
124 | os_log_error(OS_LOG_DEFAULT, "not a BL instruction: 0x%x, address %p", blInst, blInstPtr);
125 | #endif
126 | continue;
127 | }
128 |
129 | #if DEBUG
130 | os_log_debug(OS_LOG_DEFAULT, "found BL instruction: 0x%x, address %p", blInst, blInstPtr);
131 | #endif
132 |
133 | int32_t blOffset = blInst & 0x03ffffff;
134 | if (blOffset & 0x02000000)
135 | blOffset |= 0xfc000000;
136 | blOffset <<= 2;
137 |
138 | #if DEBUG
139 | os_log_debug(OS_LOG_DEFAULT, "BL offset: 0x%x", blOffset);
140 | #endif
141 |
142 | uint64_t blAddr = (uint64_t)blInstPtr + blOffset;
143 |
144 | #if DEBUG
145 | os_log_debug(OS_LOG_DEFAULT, "BL target address: %p", (void *)blAddr);
146 | #endif
147 |
148 | // cbz x0, loc_?????????
149 | uint32_t cbzInst = *((uint32_t *)make_sym_readable((void *)blAddr));
150 | if ((cbzInst & 0xff000000) != 0xb4000000)
151 | {
152 | #if DEBUG
153 | os_log_error(OS_LOG_DEFAULT, "not a CBZ instruction: 0x%x", cbzInst);
154 | #endif
155 | continue;
156 | }
157 |
158 | #if DEBUG
159 | os_log_debug(OS_LOG_DEFAULT, "found CBZ instruction: 0x%x, address %p", cbzInst, (void *)blAddr);
160 | #endif
161 |
162 | orig_UIEventDispatcher__installEventRunLoopSources_ = (void (*)(id _Nonnull __strong, SEL _Nonnull, CFRunLoopRef))make_sym_callable((void *)blAddr);
163 | }
164 |
165 | if (!orig_UIEventDispatcher__installEventRunLoopSources_)
166 | {
167 | #if DEBUG
168 | os_log_error(OS_LOG_DEFAULT, "failed to find -[UIEventDispatcher _installEventRunLoopSources:]");
169 | #endif
170 | break;
171 | }
172 |
173 | #if DEBUG
174 | os_log_debug(OS_LOG_DEFAULT, "- [UIEventDispatcher _installEventRunLoopSources:]: %p", orig_UIEventDispatcher__installEventRunLoopSources_);
175 | #endif
176 |
177 | CFRunLoopRef mainRunLoop = CFRunLoopGetMain();
178 | orig_UIEventDispatcher__installEventRunLoopSources_(dispatcher, @selector(_installEventRunLoopSources:), mainRunLoop);
179 | }
180 |
181 | #if DEBUG
182 | // Get image base with dyld, the image is /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore.
183 | uint64_t imageUIKitCore = 0;
184 | {
185 | uint32_t imageCount = _dyld_image_count();
186 | for (uint32_t i = 0; i < imageCount; i++)
187 | {
188 | const char *imageName = _dyld_get_image_name(i);
189 | if (imageName && !strcmp(imageName, "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore"))
190 | {
191 | imageUIKitCore = _dyld_get_image_vmaddr_slide(i);
192 | break;
193 | }
194 | }
195 | }
196 |
197 | os_log_debug(OS_LOG_DEFAULT, "UIKitCore: %p", (void *)imageUIKitCore);
198 | #endif
199 |
200 | UIEventFetcher *fetcher = [[objc_getClass("UIEventFetcher") alloc] init];
201 | [dispatcher setValue:fetcher forKey:@"eventFetcher"];
202 |
203 | if ([fetcher respondsToSelector:@selector(setEventFetcherSink:)]) {
204 | [fetcher setEventFetcherSink:dispatcher];
205 | }
206 | else
207 | {
208 | /* Tested on iOS 15.1.1 and below */
209 | [fetcher setValue:dispatcher forKey:@"eventFetcherSink"];
210 |
211 | /* Print NSThread names */
212 | DumpThreads();
213 |
214 | #if DEBUG
215 | /* Force HIDTransformer to print logs */
216 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogTouch" inDomain:@"com.apple.UIKit"];
217 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesture" inDomain:@"com.apple.UIKit"];
218 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogEventDispatch" inDomain:@"com.apple.UIKit"];
219 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureEnvironment" inDomain:@"com.apple.UIKit"];
220 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureExclusion" inDomain:@"com.apple.UIKit"];
221 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogSystemGestureUpdate" inDomain:@"com.apple.UIKit"];
222 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesturePerformance" inDomain:@"com.apple.UIKit"];
223 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogHIDTransformer" inDomain:@"com.apple.UIKit"];
224 | [[NSUserDefaults standardUserDefaults] synchronize];
225 | #endif
226 | }
227 |
228 | [self setValue:fetcher forKey:@"eventFetcher"];
229 | } while (NO);
230 | }
231 | return self;
232 | }
233 |
234 | @end
--------------------------------------------------------------------------------
/src/hud/HUDMainApplicationDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface HUDMainApplicationDelegate : UIResponder
6 | @property (nonatomic, strong) UIWindow *window;
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/hud/HUDMainApplicationDelegate.mm:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "HUDMainApplicationDelegate.h"
4 | #import "HUDMainWindow.h"
5 | #import "HUDRootViewController.h"
6 |
7 | #import "../helpers/private_headers/SBSAccessibilityWindowHostingController.h"
8 | #import "../helpers/private_headers/UIWindow+Private.h"
9 |
10 | @implementation HUDMainApplicationDelegate {
11 | HUDRootViewController *_rootViewController;
12 | SBSAccessibilityWindowHostingController *_windowHostingController;
13 | }
14 |
15 | - (instancetype)init
16 | {
17 | if (self = [super init])
18 | {
19 | #if DEBUG
20 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplicationDelegate init]");
21 | #endif
22 | }
23 | return self;
24 | }
25 |
26 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
27 | {
28 | #if DEBUG
29 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplicationDelegate application:%{public}@ didFinishLaunchingWithOptions:%{public}@]", application, launchOptions);
30 | #endif
31 |
32 | _rootViewController = [[HUDRootViewController alloc] init];
33 |
34 | self.window = [[HUDMainWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
35 | [self.window setRootViewController:_rootViewController];
36 |
37 | [self.window setWindowLevel:10000010.0];
38 | [self.window setHidden:NO];
39 | [self.window makeKeyAndVisible];
40 |
41 | _windowHostingController = [[objc_getClass("SBSAccessibilityWindowHostingController") alloc] init];
42 | unsigned int _contextId = [self.window _contextId];
43 | double windowLevel = [self.window windowLevel];
44 |
45 | #pragma clang diagnostic push
46 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
47 | // [_windowHostingController registerWindowWithContextID:_contextId atLevel:windowLevel];
48 | NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:Id"];
49 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
50 | [invocation setTarget:_windowHostingController];
51 | [invocation setSelector:NSSelectorFromString(@"registerWindowWithContextID:atLevel:")];
52 | [invocation setArgument:&_contextId atIndex:2];
53 | [invocation setArgument:&windowLevel atIndex:3];
54 | [invocation invoke];
55 | #pragma clang diagnostic pop
56 |
57 | return YES;
58 | }
59 |
60 | @end
--------------------------------------------------------------------------------
/src/hud/HUDMainWindow.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface HUDMainWindow : UIWindow
6 | @end
7 |
8 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/hud/HUDMainWindow.mm:
--------------------------------------------------------------------------------
1 | #import "HUDMainWindow.h"
2 | #import "HUDRootViewController.h"
3 |
4 | @implementation HUDMainWindow
5 |
6 | + (BOOL)_isSystemWindow { return YES; }
7 | - (BOOL)_isWindowServerHostingManaged { return NO; }
8 | - (BOOL)_ignoresHitTest { return YES; }
9 | - (BOOL)_isSecure { return YES; }
10 | - (BOOL)_shouldCreateContextAsSecure { return YES; }
11 |
12 | @end
--------------------------------------------------------------------------------
/src/hud/HUDRootViewController.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface HUDRootViewController: UIViewController
6 | - (void)resetLoopTimer;
7 | - (void)pauseLoopTimer;
8 | - (void)resumeLoopTimer;
9 | - (void)reloadUserDefaults;
10 | @end
11 |
12 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/src/views/UIApplicationViews.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | //
4 | //
5 | // Created by lemin on 10/11/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // MARK: Root View
12 | struct RootView: View {
13 | var body: some View {
14 | TabView {
15 | // Home Page
16 | HomePageView()
17 | .tabItem {
18 | Label(NSLocalizedString("Home", comment: ""), systemImage: "house")
19 | }
20 |
21 | // Widget Customization
22 | WidgetCustomizationView()
23 | .tabItem {
24 | Label(NSLocalizedString("Customize", comment: ""), systemImage: "paintbrush")
25 | }
26 |
27 | // Settings
28 | SettingsView()
29 | .tabItem {
30 | Label(NSLocalizedString("Settings", comment: ""), systemImage: "gear")
31 | }
32 | }
33 | .onAppear {
34 | let tabBarAppearance = UITabBarAppearance()
35 | tabBarAppearance.configureWithDefaultBackground()
36 | if #available(iOS 15.0, *) {
37 | UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
38 | } else {
39 | // Fallback on earlier versions
40 | }
41 |
42 | do {
43 | try FileManager.default.contentsOfDirectory(atPath: "/var/mobile")
44 | // warn to turn on developer mode if iOS 16+
45 | if #available(iOS 16.0, *), !UserDefaults.standard.bool(forKey: "hasWarnedOfDevMode", forPath: USER_DEFAULTS_PATH) {
46 | UIApplication.shared.confirmAlert(title: NSLocalizedString("Info", comment: ""), body: NSLocalizedString("Make sure you enable developer mode before using! This will not work otherwise.", comment: ""), onOK: {
47 | UserDefaults.standard.setValue(true, forKey: "hasWarnedOfDevMode", forPath: USER_DEFAULTS_PATH)
48 | }, noCancel: true)
49 | }
50 | return
51 | } catch {
52 | UIApplication.shared.alert(title: NSLocalizedString("Not Supported", comment: ""), body: NSLocalizedString("This app must be installed with TrollStore.", comment: ""))
53 | }
54 | }
55 | }
56 | }
57 |
58 | // MARK: Objc Bridging
59 | @objc
60 | open class ContentInterface: NSObject {
61 | @objc
62 | open func createUI() -> UIViewController {
63 | let contents = RootView()
64 | return HostingController(rootView: contents)
65 | }
66 | }
67 |
68 | struct RootView_Previews: PreviewProvider {
69 | static var previews: some View {
70 | RootView()
71 | }
72 | }
73 |
74 | class HostingController: UIHostingController where Content: View {
75 | @objc override var preferredStatusBarStyle: UIStatusBarStyle {
76 | return .lightContent
77 | }
78 | }
--------------------------------------------------------------------------------
/src/views/navigation/HomePageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomePageView.swift
3 | // Helium UI
4 | //
5 | // Created by lemin on 10/19/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // MARK: Home Page View
12 | struct HomePageView: View {
13 | @State private var isNowEnabled: Bool = false
14 | @State private var buttonDisabled: Bool = false
15 | @State private var inProgress = false
16 |
17 | var body: some View {
18 | NavigationView {
19 | VStack(spacing: 10) {
20 | Spacer()
21 |
22 | // Activate HUD Button
23 | Button(
24 | action: {
25 | #if targetEnvironment(simulator)
26 | isNowEnabled.toggle()
27 | #else
28 | toggleHUD(!isNowEnabled)
29 | #endif
30 | },
31 | label: {
32 | HStack(spacing: 10) {
33 | if inProgress {
34 | if #available(iOS 16.0, *) {
35 | ProgressView()
36 | .tint(isNowEnabled ? .red : .blue)
37 | } else {
38 | ProgressView()
39 | .foregroundColor(isNowEnabled ? .red : .blue)
40 | }
41 | }
42 | Text(
43 | isNowEnabled
44 | ? NSLocalizedString("Disable HUD", comment: "")
45 | : NSLocalizedString("Enable HUD", comment: ""))
46 | }
47 | }
48 | )
49 | .buttonStyle(TintedButton(color: isNowEnabled ? .red : .blue))
50 | .padding(5)
51 | .offset(y: isNowEnabled ? -10 : 0)
52 | Text(
53 | NSLocalizedString(
54 | "You can quit the app now.\nThe HUD will persist on your screen.", comment: "")
55 | )
56 | .multilineTextAlignment(.center)
57 | .offset(y: isNowEnabled ? 5 : 15)
58 | .foregroundColor(.blue)
59 | .opacity(isNowEnabled ? 1 : 0)
60 |
61 | Spacer()
62 | // HUD Info Text
63 | Text(
64 | isNowEnabled
65 | ? NSLocalizedString("Status: Running", comment: "")
66 | : NSLocalizedString("Status: Stopped", comment: "")
67 | )
68 | .font(.caption)
69 | .foregroundColor(isNowEnabled ? .blue : .red)
70 | .padding(.bottom, 10)
71 | .multilineTextAlignment(.center)
72 | }
73 | .disabled(buttonDisabled)
74 | .onAppear {
75 | #if !targetEnvironment(simulator)
76 | isNowEnabled = IsHUDEnabledBridger()
77 | #endif
78 | }
79 | .onOpenURL(perform: { url in
80 | let _ = FileManager.default
81 | // MARK: URL Schemes
82 | if url.absoluteString == "helium://toggle" {
83 | #if !targetEnvironment(simulator)
84 | toggleHUD(!isNowEnabled)
85 | #endif
86 | } else if url.absoluteString == "helium://on" {
87 | #if !targetEnvironment(simulator)
88 | toggleHUD(true)
89 | #endif
90 | } else if url.absoluteString == "helium://off" {
91 | #if !targetEnvironment(simulator)
92 | toggleHUD(false)
93 | #endif
94 | }
95 | })
96 | .navigationTitle(Text(NSLocalizedString("Helium", comment: "")))
97 | }
98 | .navigationViewStyle(StackNavigationViewStyle())
99 | .animation(.timingCurve(0.25, 0.1, 0.35, 1.75).speed(1.2), value: isNowEnabled)
100 | .animation(.timingCurve(0.25, 0.1, 0.35, 1.75).speed(1.2), value: inProgress)
101 | }
102 |
103 | func toggleHUD(_ isActive: Bool) {
104 | inProgress.toggle()
105 | Haptic.shared.play(.medium)
106 | if isNowEnabled == isActive { return }
107 | print(
108 | !isActive
109 | ? NSLocalizedString("Closing HUD", comment: "")
110 | : NSLocalizedString("Opening HUD", comment: ""))
111 | SetHUDEnabledBridger(isActive)
112 |
113 | buttonDisabled = true
114 | waitForNotificationBridger(
115 | {
116 | isNowEnabled = isActive
117 | buttonDisabled = false
118 | inProgress.toggle()
119 | }, !isNowEnabled)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/views/navigation/SettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsView.swift
3 | // Helium UI
4 | //
5 | // Created by lemin on 10/19/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | let buildNumber: Int = 0
12 | let DEBUG_MODE_ENABLED = false
13 | let USER_DEFAULTS_PATH = "/var/mobile/Library/Preferences/com.leemin.helium.plist"
14 |
15 | // MARK: Settings View
16 | // TODO: This
17 | struct SettingsView: View {
18 | // Debug Variables
19 | @State var sideWidgetSize: Int = 100
20 | @State var centerWidgetSize: Int = 100
21 |
22 | // Preference Variables
23 | @State var apiKey: String = ""
24 | @State var dateLocale: String = "en_US"
25 | @State var hideSaveConfirmation: Bool = false
26 | @State var debugBorder: Bool = false
27 |
28 | var body: some View {
29 | NavigationView {
30 | List {
31 | // App Version/Build Number
32 | Section {
33 |
34 | } header: {
35 | Label(NSLocalizedString("Version ", comment:"") + "\(Bundle.main.releaseVersionNumber ?? NSLocalizedString("UNKNOWN", comment:"")) (\(buildNumber != 0 ? "\(buildNumber)" : NSLocalizedString("Release", comment:"")))", systemImage: "info")
36 | }
37 |
38 | // Preferences List
39 | Section {
40 | HStack {
41 | Text(NSLocalizedString("Date Locale", comment:""))
42 | .bold()
43 | Spacer()
44 | Picker("", selection: $dateLocale) {
45 | Text("en_US").tag("en_US")
46 | Text("zh_CN").tag("zh_CN")
47 | }
48 | .pickerStyle(.menu)
49 | }
50 |
51 | HStack {
52 | Text(NSLocalizedString("Weather Api key", comment:""))
53 | .bold()
54 | Spacer()
55 | TextField("", text: $apiKey)
56 | .textFieldStyle(RoundedBorderTextFieldStyle())
57 | }
58 |
59 | HStack {
60 | Toggle(isOn: $hideSaveConfirmation) {
61 | Text(NSLocalizedString("Hide Save Confirmation Popup", comment:""))
62 | .bold()
63 | .minimumScaleFactor(0.5)
64 | }
65 | }
66 |
67 | HStack {
68 | Toggle(isOn: $debugBorder) {
69 | Text(NSLocalizedString("Display Debug Border", comment:""))
70 | .bold()
71 | .minimumScaleFactor(0.5)
72 | }
73 | }
74 |
75 | HStack {
76 | Text(NSLocalizedString("Helium Data", comment:""))
77 | .bold()
78 | Spacer()
79 | Button(action: {
80 | do {
81 | try UserDefaults.standard.deleteUserDefaults(forPath: USER_DEFAULTS_PATH)
82 | UIApplication.shared.alert(title: NSLocalizedString("Successfully deleted user data!", comment:""), body: NSLocalizedString("Please restart the app to continue.", comment:""))
83 | } catch {
84 | UIApplication.shared.alert(title: NSLocalizedString("Failed to delete user data!", comment:""), body: error.localizedDescription)
85 | }
86 | }) {
87 | Text(NSLocalizedString("Reset Data", comment:""))
88 | .foregroundColor(.red)
89 | }
90 | }
91 | } header: {
92 | Label(NSLocalizedString("Preferences", comment:""), systemImage: "gear")
93 | }
94 |
95 | // Debug Settings
96 | if #available(iOS 15, *), DEBUG_MODE_ENABLED {
97 | Section {
98 | HStack {
99 | Text(NSLocalizedString("Side Widget Size", comment:""))
100 | .bold()
101 | Spacer()
102 | TextField(NSLocalizedString("Side Size", comment:""), value: $sideWidgetSize, format: .number)
103 | .textFieldStyle(RoundedBorderTextFieldStyle())
104 | .keyboardType(.decimalPad)
105 | .submitLabel(.done)
106 | }
107 |
108 | HStack {
109 | Text(NSLocalizedString("Center Widget Size", comment:""))
110 | .bold()
111 | Spacer()
112 | TextField(NSLocalizedString("Center Size", comment:""), value: $centerWidgetSize, format: .number)
113 | .textFieldStyle(RoundedBorderTextFieldStyle())
114 | .keyboardType(.decimalPad)
115 | .submitLabel(.done)
116 | }
117 | } header: {
118 | Label(NSLocalizedString("Debug Preferences", comment:""), systemImage: "ladybug")
119 | }
120 | }
121 |
122 | // Credits List
123 | Section {
124 | LinkCell(imageName: "leminlimez", url: "https://github.com/leminlimez", title: "LeminLimez", contribution: NSLocalizedString("Main Developer", comment: "leminlimez's contribution"), circle: true)
125 | LinkCell(imageName: "lessica", url: "https://github.com/Lessica/TrollSpeed", title: "Lessica", contribution: NSLocalizedString("TrollSpeed & Assistive Touch Logic", comment: "lessica's contribution"), circle: true)
126 | LinkCell(imageName: "fuuko", url: "https://github.com/AsakuraFuuko", title: "Fuuko", contribution: NSLocalizedString("Modder", comment: "Fuuko's contribution"), imageInBundle: true, circle: true)
127 | LinkCell(imageName: "bomberfish", url: "https://github.com/BomberFish", title: "BomberFish", contribution: NSLocalizedString("UI improvements", comment: "BomberFish's contribution"), imageInBundle: true, circle: true)
128 | } header: {
129 | Label(NSLocalizedString("Credits", comment:""), systemImage: "wrench.and.screwdriver")
130 | }
131 | }
132 | .toolbar {
133 | HStack {
134 | Button(action: {
135 | saveChanges()
136 | }) {
137 | Text(NSLocalizedString("Save", comment:""))
138 | }
139 | }
140 | }
141 | .onAppear {
142 | loadSettings()
143 | }
144 | .navigationTitle(Text(NSLocalizedString("Settings", comment:"")))
145 | }
146 | .navigationViewStyle(StackNavigationViewStyle())
147 | }
148 |
149 | func loadSettings() {
150 | dateLocale = UserDefaults.standard.string(forKey: "dateLocale", forPath: USER_DEFAULTS_PATH) ?? "en_US"
151 | apiKey = UserDefaults.standard.string(forKey: "apiKey", forPath: USER_DEFAULTS_PATH) ?? ""
152 | hideSaveConfirmation = UserDefaults.standard.bool(forKey: "hideSaveConfirmation", forPath: USER_DEFAULTS_PATH)
153 | debugBorder = UserDefaults.standard.bool(forKey: "debugBorder", forPath: USER_DEFAULTS_PATH)
154 | }
155 |
156 | func saveChanges() {
157 | UserDefaults.standard.setValue(apiKey, forKey: "apiKey", forPath: USER_DEFAULTS_PATH)
158 | UserDefaults.standard.setValue(dateLocale, forKey: "dateLocale", forPath: USER_DEFAULTS_PATH)
159 | UserDefaults.standard.setValue(hideSaveConfirmation, forKey: "hideSaveConfirmation", forPath: USER_DEFAULTS_PATH)
160 | UserDefaults.standard.setValue(debugBorder, forKey: "debugBorder", forPath: USER_DEFAULTS_PATH)
161 | UIApplication.shared.alert(title: NSLocalizedString("Save Changes", comment:""), body: NSLocalizedString("Settings saved successfully", comment:""))
162 | DarwinNotificationCenter.default.post(name: NOTIFY_RELOAD_HUD)
163 | }
164 |
165 | // Link Cell code from Cowabunga
166 | struct LinkCell: View {
167 | var imageName: String
168 | var url: String
169 | var title: String
170 | var contribution: String
171 | var systemImage: Bool = false
172 | var imageInBundle: Bool = false
173 | var circle: Bool = false
174 |
175 | var body: some View {
176 | HStack(alignment: .center) {
177 | Group {
178 | if systemImage {
179 | Image(systemName: imageName)
180 | .resizable()
181 | .aspectRatio(contentMode: .fit)
182 | } else if imageInBundle {
183 | let url = Bundle.main.url(forResource: "credits/" + imageName, withExtension: "png")
184 | if url != nil {
185 | Image(uiImage: UIImage(contentsOfFile: url!.path)!)
186 | .resizable()
187 | .aspectRatio(contentMode: .fit)
188 | }
189 | } else {
190 | if imageName != "" {
191 | Image(imageName)
192 | .resizable()
193 | .aspectRatio(contentMode: .fit)
194 | }
195 | }
196 | }
197 | .cornerRadius(circle ? .infinity : 0)
198 | .frame(width: 24, height: 24)
199 |
200 | VStack {
201 | HStack {
202 | Button(action: {
203 | if url != "" {
204 | UIApplication.shared.open(URL(string: url)!)
205 | }
206 | }) {
207 | Text(title)
208 | .fontWeight(.bold)
209 | }
210 | .padding(.horizontal, 6)
211 | Spacer()
212 | }
213 | HStack {
214 | Text(contribution)
215 | .padding(.horizontal, 6)
216 | .font(.footnote)
217 | Spacer()
218 | }
219 | }
220 | }
221 | .foregroundColor(.blue)
222 | }
223 | }
224 | }
225 |
226 | struct SettingsView_Previews: PreviewProvider {
227 | static var previews: some View {
228 | SettingsView()
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/views/navigation/WidgetCustomizationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetCustomizationView.swift
3 | //
4 | //
5 | // Created by lemin on 10/13/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | // MARK: Widget Customization View
12 | struct WidgetCustomizationView: View {
13 | @StateObject var widgetManager: WidgetManager = .init()
14 |
15 | var body: some View {
16 | NavigationView {
17 | VStack {
18 | // List of Widget Sets
19 | List {
20 | ForEach($widgetManager.widgetSets) { widgetSet in
21 | NavigationLink(destination: EditWidgetSetView(widgetManager: widgetManager, widgetSet: widgetSet.wrappedValue)) {
22 | Text(widgetSet.title.wrappedValue)
23 | }
24 | }
25 | .onDelete { indexSet in
26 | indexSet.forEach { i in
27 | UIApplication.shared.confirmAlert(title: NSLocalizedString("Delete ", comment:"")+"\(widgetManager.widgetSets[i].title)", body: NSLocalizedString("Are you sure you want to delete the widget set ", comment:"") + "\"\(widgetManager.widgetSets[i].title)\"?", onOK: {
28 | widgetManager.removeWidgetSet(widgetSet: widgetManager.widgetSets[i])
29 | }, noCancel: false)
30 | }
31 | }
32 | }
33 | .toolbar {
34 | HStack {
35 | // create a new widget set
36 | Button(action: {
37 | UIApplication.shared.inputAlert(title: NSLocalizedString("Enter Name", comment:""), body: NSLocalizedString("Choose a name for the widget set.", comment:""), confirmTitle: NSLocalizedString("Confirm", comment:""), placeholder: NSLocalizedString("Name", comment:""), onOK: { title in
38 | // make selection for anchor
39 | UIApplication.shared.optionsAlert(title: NSLocalizedString("Choose Side", comment:""), body: NSLocalizedString("Choose a side for the widget set to anchor to. This can be changed later.", comment:""), options: [NSLocalizedString("Left", comment:""), NSLocalizedString("Center", comment:""), NSLocalizedString("Right", comment:"")]) { anchorSide in
40 | var anchor: Int
41 | switch (anchorSide) {
42 | case NSLocalizedString("Left", comment:""):
43 | anchor = 0
44 | break
45 | case NSLocalizedString("Center", comment:""):
46 | anchor = 1
47 | break
48 | case NSLocalizedString("Right", comment:""):
49 | anchor = 2
50 | break
51 | default:
52 | anchor = 0
53 | break
54 | }
55 | widgetManager.createWidgetSet(title: title == "" ? NSLocalizedString("Untitled", comment:"") : title, anchor: anchor)
56 | }
57 | }, noCancel: false)
58 | }) {
59 | Image(systemName: "plus")
60 | }
61 | }
62 | }
63 | }
64 | .navigationTitle(Text(NSLocalizedString("Customize", comment:"")))
65 | }
66 | .navigationViewStyle(StackNavigationViewStyle())
67 | }
68 | }
69 |
70 | // MARK: Widget Modify View
71 | //struct WidgetModifyView: View {
72 | // @StateObject var widgetManager: WidgetManager
73 | // @Binding var widgetIndex: Int
74 | //
75 | // var dismiss: () -> Void
76 | //
77 | // var body: some View {
78 | // return VStack {
79 | // if widgetIndex >= 0 && widgetIndex < widgetManager.widgetStructs.count {
80 | // // Widget Preferences
81 | // WidgetPreferencesView(widgetStruct: $widgetManager.widgetStructs[widgetIndex])
82 | // VStack {
83 | // Spacer()
84 | // // Save Button
85 | // Button("Save") {
86 | // widgetManager.saveWidgetStructs()
87 | // dismiss()
88 | // }
89 | // .buttonStyle(TintedButton(color: .blue, fullWidth: true))
90 | // .padding(.horizontal, 7)
91 | // // Delete Button
92 | // Button("Delete") {
93 | // widgetManager.removeWidget(id: widgetIndex)
94 | // dismiss()
95 | // }
96 | // .buttonStyle(TintedButton(color: .red, fullWidth: true))
97 | // .padding(.horizontal, 7)
98 | // }
99 | // .padding(10)
100 | // }
101 | // }
102 | // }
103 | //}
104 |
105 | // MARK: Widget Add View
106 | struct WidgetAddView: View {
107 | @StateObject var widgetManager: WidgetManager
108 | @State var widgetSet: WidgetSetStruct
109 | @Binding var isOpen: Bool
110 |
111 | var onChoice: (WidgetIDStruct) -> ()
112 |
113 | var body: some View {
114 | List {
115 | ForEach(WidgetModule.allCases, id: \.self) { id in
116 | Button(action: {
117 | let newWidget = widgetManager.addWidget(widgetSet: widgetSet, module: id)
118 | isOpen = false
119 | onChoice(newWidget)
120 | }) {
121 | WidgetChoiceView(widgetName: WidgetDetails.getWidgetName(id), exampleText: WidgetDetails.getWidgetExample(id))
122 | }
123 | }
124 | }
125 | }
126 | }
127 |
128 | // MARK: Widget Choice View
129 | struct WidgetChoiceView: View {
130 | var widgetName: String
131 | var exampleText: String
132 |
133 | var body: some View {
134 | HStack {
135 | ZStack {
136 | Image(uiImage: UIImage(named: "wallpaper")!)
137 | .resizable()
138 | .aspectRatio(contentMode: .fill)
139 | .scaleEffect(1.5)
140 | .frame(width: 125, height: 50)
141 | .cornerRadius(12)
142 | .clipped()
143 | ZStack {
144 | Text(exampleText)
145 | .padding(.vertical, 5)
146 | .foregroundColor(.white)
147 | }
148 | .frame(width: 125, height: 50)
149 | }
150 | .padding(.trailing, 5)
151 | Text(widgetName)
152 | .foregroundColor(.primary)
153 | .bold()
154 | }
155 | }
156 | }
157 |
158 | struct WidgetCustomizationView_Previews: PreviewProvider {
159 | static var previews: some View {
160 | WidgetCustomizationView()
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/views/widget/WidgetConfigureView.swift:
--------------------------------------------------------------------------------
1 | ////
2 | //// WidgetConfigureView.swift
3 | //// Helium UI
4 | ////
5 | //// Created by lemin on 11/2/23.
6 | ////
7 | //
8 | //import Foundation
9 | //import SwiftUI
10 | //
11 | //let cardScale: CGFloat = 0.9
12 | //
13 | //struct WidgetSettingsFlipView: View {
14 | // var screenGeom: GeometryProxy
15 | // @StateObject var widgetManager: WidgetManager
16 | //
17 | // @Binding var flippedWidget: WidgetStruct?
18 | //
19 | // @Binding var canPressButtons: Bool
20 | //
21 | // @Binding var flipped: Bool
22 | // @Binding var animate3d: Bool
23 | //
24 | // var body: some View {
25 | // return GeometryReader {cardGeometry in
26 | // VStack {
27 | // if animate3d && flippedWidget != nil {
28 | // BackWidgetOptionView(screenGeom: screenGeom, widgetManager: widgetManager, flippedWidget: $flippedWidget, widgetIndex: widgetManager.getWidgetID(widget: flippedWidget!), canPressButtons: $canPressButtons, flipFunction: flipCard)
29 | // .frame(
30 | // width: animate3d ? (min(screenGeom.size.width, screenGeom.size.height)) : 0,
31 | // height: animate3d ? (min(screenGeom.size.width, screenGeom.size.height)) : 0
32 | // )
33 | // } else {
34 | // Rectangle()
35 | // .opacity(0)
36 | // }
37 | // }
38 | // .scaleEffect(animate3d ? cardScale : 0)
39 | // .zIndex(animate3d ? 2 : 0)
40 | // .modifier(FlipEffect(flipped: $flipped, angle: animate3d ? 180 : 0, axis: (x: 0.0, y: 1)))
41 | // .aspectRatio(1.0, contentMode: .fit)
42 | // }
43 | // }
44 | //
45 | // func flipCard() {
46 | // withAnimation(Animation.easeInOut(duration: CardAnimationSpeed)) {
47 | // animate3d.toggle()
48 | // }
49 | // }
50 | //}
51 | //
52 | //// MARK: Back Widget View/Edit Widget View
53 | //struct BackWidgetOptionView: View {
54 | // var screenGeom: GeometryProxy
55 | // @StateObject var widgetManager: WidgetManager
56 | //
57 | // @Binding var flippedWidget: WidgetStruct?
58 | // @State var widgetIndex: Int
59 | //
60 | // @Binding var canPressButtons: Bool
61 | //
62 | // var flipFunction: () -> Void
63 | //
64 | // var body: some View {
65 | // return VStack(alignment: .center) {
66 | // Text("Configure Widget")
67 | // .bold()
68 | // .font(.title2)
69 | // .padding(.top, 25)
70 | //
71 | // WidgetModifyView(widgetManager: widgetManager, widgetIndex: $widgetIndex, dismiss: {
72 | // canPressButtons = false
73 | // withAnimation(Animation.easeInOut(duration: CardAnimationSpeed)) {
74 | // flippedWidget = nil
75 | // flipFunction()
76 | // }
77 | // DispatchQueue.main.asyncAfter(deadline: .now() + CardAnimationSpeed + 0.05) {
78 | // canPressButtons = true
79 | // }
80 | // })
81 | // .padding(5)
82 | //
83 | // Spacer()
84 | // }
85 | // .background(Color(uiColor14: .secondarySystemBackground))
86 | // .cornerRadius(16)
87 | // }
88 | //}
89 |
--------------------------------------------------------------------------------
/src/views/widget/WidgetPreviewsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetPreviewsView.swift
3 | // Helium UI
4 | //
5 | // Created by lemin on 10/16/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct WidgetPreviewsView: View {
12 | @Binding var widget: WidgetIDStruct
13 | @State var text: String = ""
14 | @State var image: Image?
15 | @State var previewColor: Color = .primary
16 |
17 | var body: some View {
18 | HStack {
19 | ZStack {
20 | Image(uiImage: UIImage(named: "wallpaper")!)
21 | .resizable()
22 | .aspectRatio(contentMode: .fill)
23 | .scaleEffect(1.5)
24 | .frame(width: 125, height: 50)
25 | .cornerRadius(12)
26 | .clipped()
27 | ZStack {
28 | if image == nil {
29 | Text(text)
30 | .padding(.vertical, 5)
31 | .foregroundColor(previewColor)
32 | .minimumScaleFactor(0.01)
33 | } else {
34 | Text(image!)
35 | .padding(.vertical, 5)
36 | .foregroundColor(previewColor)
37 | .minimumScaleFactor(0.01)
38 | }
39 | }
40 | .frame(width: 125, height: 50)
41 | }
42 | .padding(.trailing, 5)
43 | }
44 | .onAppear {
45 | updatePreview()
46 | }
47 | .onChange(of: widget.modified) { nv in
48 | if nv {
49 | updatePreview()
50 | }
51 | }
52 | }
53 |
54 | func updatePreview() {
55 | switch (widget.module) {
56 | case .dateWidget, .timeWidget:
57 | let dateFormat: String = widget.config["dateFormat"] as? String ?? (widget.module == .dateWidget ? NSLocalizedString("E MMM dd", comment:"") : "hh:mm")
58 | let dateFormatter = DateFormatter()
59 | let newDateFormat = LunarDate.getChineseCalendar(with: Date(), format: dateFormat)
60 | dateFormatter.dateFormat = newDateFormat
61 | let locale = UserDefaults.standard.string(forKey: "dateLocale", forPath: USER_DEFAULTS_PATH) ?? "en_US"
62 | dateFormatter.locale = Locale(identifier: locale)
63 | text = dateFormatter.string(from: Date())
64 | // SAFEGUARD
65 | if (text == "") {
66 | text = NSLocalizedString("ERROR", comment:"")
67 | }
68 | case .network:
69 | let isUp: Bool = widget.config["isUp"] as? Bool ?? false
70 | let speedIcon: Int = widget.config["speedIcon"] as? Int ?? 0
71 | text = "\(isUp ? (speedIcon == 0 ? "▲" : "↑") : (speedIcon == 0 ? "▼" : "↓")) 30 KB/s"
72 | case .temperature:
73 | text = widget.config["useFahrenheit"] as? Bool ?? false ? "78.84ºF" : "26.02ºC"
74 | case .battery:
75 | let batteryValue: Int = widget.config["batteryValueType"] as? Int ?? 0
76 | switch (batteryValue) {
77 | case 0:
78 | text = "0 W"
79 | case 1:
80 | text = "0 mA"
81 | case 2:
82 | text = "0 mA"
83 | case 3:
84 | text = "25"
85 | default:
86 | text = "???"
87 | }
88 | case .textWidget:
89 | text = widget.config["text"] as? String ?? NSLocalizedString("Unknown", comment:"")
90 | break;
91 | case .weather:
92 | text = NSLocalizedString("Weather Preview", comment:"")
93 | break;
94 | case .currentCapacity:
95 | text = "50\(widget.config["showPercentage"] as? Bool ?? true ? "%" : "")"
96 | case .chargeSymbol:
97 | image = widget.config["filled"] as? Bool ?? true ? Image(systemName: "bolt.fill") : Image(systemName: "bolt")
98 | }
99 | widget.modified = false
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/views/widgetset/SelectPresetView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectPresetView.swift
3 | //
4 | //
5 | // Created by lemin on 12/15/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | //struct SelectPresetView: View {
12 | // @StateObject var widgetManager: WidgetManager
13 | // @State var widgetSet: WidgetSetStruct
14 | // @Binding var isOpen: Bool
15 | //
16 | // var onChoice: (PresetStruct) -> ()
17 | //
18 | // var body: some View {
19 | // List {
20 | // ForEach(Preset.allCases, id: \.self) { id in
21 | // Button(action: {
22 | // let newWidget = widgetManager.addWidget(widgetSet: widgetSet, module: id)
23 | // isOpen = false
24 | // onChoice(newWidget)
25 | // }) {
26 | // Text(id.rawValue)
27 | // }
28 | // }
29 | // }
30 | // }
31 | //}
32 |
--------------------------------------------------------------------------------
/src/widgets/DeviceScaleManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // DeviceScaleManager.h
3 | //
4 | //
5 | // Created by lemin on 10/11/23.
6 | //
7 |
8 | double getCenterWidgetVerticalOffset(void);
9 | double getSideWidgetSize(void);
10 | double getCenterWidgetSize(void);
11 | double getCenterWidgetVerticalOffset(void);
12 |
--------------------------------------------------------------------------------
/src/widgets/DeviceScaleManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // DeviceScaleManager.m
3 | //
4 | //
5 | // Created by lemin on 10/11/23.
6 | //
7 |
8 | #import
9 | #import
10 | #import "DeviceScaleManager.h"
11 |
12 | // Small Notch/Dynamic Island Definitions
13 | #define SMALL_SIDE_WIDGET_SIZE 0.27435897 // Original Size (iPhone 13 Pro): 107
14 | #define SMALL_CENTER_WIDGET_SIZE 0.34615385 // Original Size (iPhone 13 Pro): 135
15 |
16 | // Large Notch Definitions
17 | #define LARGE_SIDE_WIDGET_SIZE 0.19466667 // Original Size (iPhone X): 73
18 | #define LARGE_CENTER_WIDGET_SIZE 0.50666667 // Original Size (iPhone X): 190
19 |
20 | NSString* getDeviceName(void)
21 | {
22 | struct utsname systemInfo;
23 | uname(&systemInfo);
24 |
25 | return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
26 | }
27 |
28 | /*
29 | Sizes:
30 | 0 = No Notch
31 | 1 = Small Notch
32 | 2 = Large Notch
33 | 3 = Dynamic Island
34 | */
35 | NSInteger getDeviceSize(void)
36 | {
37 | NSString *model = getDeviceName();
38 |
39 | // get the notch size
40 | if ([model rangeOfString: @"iPhone14"].location != NSNotFound) {
41 | // Small Notch
42 | return 1;
43 | } else if (
44 | [model rangeOfString: @"iPhone10,3"].location != NSNotFound // account for iPhone 8
45 | || [model rangeOfString: @"iPhone10,6"].location != NSNotFound // which is also iPhone10,_
46 | || [model rangeOfString: @"iPhone11"].location != NSNotFound
47 | || [model rangeOfString: @"iPhone12"].location != NSNotFound
48 | || [model rangeOfString: @"iPhone13"].location != NSNotFound
49 | ) {
50 | return 2;
51 | } else if (
52 | [model rangeOfString: @"iPhone15"].location != NSNotFound
53 | || [model rangeOfString: @"iPhone16"].location != NSNotFound
54 | ) {
55 | return 3;
56 | }
57 | return 0;
58 | }
59 |
60 | double getSideWidgetSize(void)
61 | {
62 |
63 | NSInteger deviceSize = getDeviceSize();
64 |
65 | if (deviceSize == 1 || deviceSize == 3) {
66 | // Small Notch/Dynamic Island
67 | return SMALL_SIDE_WIDGET_SIZE;
68 | } else if (deviceSize == 2) {
69 | // Large Notch
70 | return LARGE_SIDE_WIDGET_SIZE;
71 | }
72 |
73 | return LARGE_SIDE_WIDGET_SIZE;
74 | }
75 |
76 | double getCenterWidgetSize(void)
77 | {
78 |
79 | NSInteger deviceSize = getDeviceSize();
80 |
81 | if (deviceSize == 1 || deviceSize == 3) {
82 | // Small Notch/Dynamic Island
83 | return SMALL_CENTER_WIDGET_SIZE;
84 | } else if (deviceSize == 2) {
85 | // Large Notch
86 | return LARGE_CENTER_WIDGET_SIZE;
87 | }
88 |
89 | return LARGE_CENTER_WIDGET_SIZE;
90 | }
91 |
92 | double getCenterWidgetVerticalOffset(void)
93 | {
94 | NSInteger deviceSize = getDeviceSize();
95 |
96 | if (deviceSize == 3) {
97 | // Dynamic Island
98 | return 20;
99 | }
100 |
101 | return 0;
102 | }
103 |
--------------------------------------------------------------------------------
/src/widgets/WidgetManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetManager.h
3 | //
4 | //
5 | // Created by lemin on 10/6/23.
6 | //
7 |
8 | NSAttributedString* formattedAttributedString(NSArray *identifiers, double fontSize, UIColor *textColor, NSString *apiKey, NSString *dateLocale);
9 |
--------------------------------------------------------------------------------