refactor oscprofilemanager to swift

This commit is contained in:
True Zhuanjia
2026-06-05 21:48:57 +08:00
parent e2eccd7968
commit 384724ff27
13 changed files with 592 additions and 17 deletions
+1
View File
@@ -4,6 +4,7 @@
.DS_Store
Build
DerivedData
build-home/
*.xccheckout
.github
README.md
+4 -6
View File
@@ -47,7 +47,7 @@
84351B0E2C29ACCB00A77989 /* LayoutOnScreenControls.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B072C29ACCA00A77989 /* LayoutOnScreenControls.m */; };
84351B0F2C29ACCB00A77989 /* OnScreenButtonState.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B092C29ACCA00A77989 /* OnScreenButtonState.m */; };
84351B102C29ACCB00A77989 /* OSCProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B0A2C29ACCA00A77989 /* OSCProfile.m */; };
84351B112C29ACCB00A77989 /* OSCProfilesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B0B2C29ACCB00A77989 /* OSCProfilesManager.m */; };
84351B112C29ACCB00A77989 /* OSCProfilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4E0A22FBBBBCC00112233 /* OSCProfilesManager.swift */; };
84351B2D2C29AD6100A77989 /* ToolBarContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 84351B292C29AD6000A77989 /* ToolBarContainerView.m */; };
84351B5D2C29ADBE00A77989 /* ProfileTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84351B5B2C29ADBE00A77989 /* ProfileTableViewCell.xib */; };
843667F02D9D7173004A7D7F /* widgetTemplates.bin in Resources */ = {isa = PBXBuildFile; fileRef = 843667EF2D9D7173004A7D7F /* widgetTemplates.bin */; };
@@ -216,10 +216,9 @@
84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnScreenWidgetView.swift; sourceTree = "<group>"; };
84351B062C29ACCA00A77989 /* OnScreenButtonState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OnScreenButtonState.h; sourceTree = "<group>"; };
84351B072C29ACCA00A77989 /* LayoutOnScreenControls.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LayoutOnScreenControls.m; sourceTree = "<group>"; };
84351B082C29ACCA00A77989 /* OSCProfilesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCProfilesManager.h; sourceTree = "<group>"; };
84351B092C29ACCA00A77989 /* OnScreenButtonState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnScreenButtonState.m; sourceTree = "<group>"; };
84351B0A2C29ACCA00A77989 /* OSCProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSCProfile.m; sourceTree = "<group>"; };
84351B0B2C29ACCB00A77989 /* OSCProfilesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSCProfilesManager.m; sourceTree = "<group>"; };
A1D4E0A22FBBBBCC00112233 /* OSCProfilesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSCProfilesManager.swift; sourceTree = "<group>"; };
84351B0C2C29ACCB00A77989 /* LayoutOnScreenControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayoutOnScreenControls.h; sourceTree = "<group>"; };
84351B0D2C29ACCB00A77989 /* OSCProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCProfile.h; sourceTree = "<group>"; };
84351B292C29AD6000A77989 /* ToolBarContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ToolBarContainerView.m; sourceTree = "<group>"; };
@@ -492,8 +491,7 @@
84351B092C29ACCA00A77989 /* OnScreenButtonState.m */,
84351B0D2C29ACCB00A77989 /* OSCProfile.h */,
84351B0A2C29ACCA00A77989 /* OSCProfile.m */,
84351B082C29ACCA00A77989 /* OSCProfilesManager.h */,
84351B0B2C29ACCB00A77989 /* OSCProfilesManager.m */,
A1D4E0A22FBBBBCC00112233 /* OSCProfilesManager.swift */,
);
path = CustomOSC;
sourceTree = "<group>";
@@ -1159,7 +1157,7 @@
FB53E1431BE5DC4400CD6ECE /* IdManager.m in Sources */,
189AD29A2DE7446000E24B88 /* HostCollectionViewController.m in Sources */,
9897B6A1221260EF00966419 /* VoidController.m in Sources */,
84351B112C29ACCB00A77989 /* OSCProfilesManager.m in Sources */,
84351B112C29ACCB00A77989 /* OSCProfilesManager.swift in Sources */,
FB89462919F646E200339C8A /* mkcert.c in Sources */,
FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */,
FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */,
@@ -11,7 +11,6 @@
#import "LayoutOnScreenControls.h"
#import "OnScreenButtonState.h"
#import "OSCProfilesManager.h"
#import "VoidLink-Swift.h"
@@ -171,6 +171,7 @@ static CGRect layoutViewBounds;
if(profile && targetProfiles.count > 1) [targetProfiles removeObject:profile];
*/
/*
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
bool importPencilProProfile = ([bundleId isEqualToString:@"com.voidlink.iOS"]
|| [bundleId isEqualToString:@"com.voidlinkextreme.iOS"]
@@ -179,6 +180,7 @@ static CGRect layoutViewBounds;
if(!importPencilProProfile && [GenericUtils isIPad]){
[profilesEncoded removeObjectAtIndex:1];
}
*/
if(targetProfiles.count > 0) [targetProfiles removeObjectAtIndex:0];
NSMutableArray* localEncodedPofiles = [self encodedProfilesFromArray:targetProfiles];
@@ -281,6 +283,18 @@ static CGRect layoutViewBounds;
return profilesDecoded;
}
- (NSMutableArray *) decodeProfilesFrom:(NSMutableArray* )profilesEncoded {
NSSet *classes = [NSSet setWithObjects:[NSString class], [NSMutableData class], [NSMutableArray class], [OSCProfile class], [OnScreenButtonState class], nil];
NSMutableArray *profilesDecoded = [[NSMutableArray alloc] init];
OSCProfile *profileDecoded;
for (NSData *profileEncoded in profilesEncoded) {
profileDecoded = [NSKeyedUnarchiver unarchivedObjectOfClasses: classes fromData:profileEncoded error: nil];
[profilesDecoded addObject: profileDecoded];
}
return profilesDecoded;
}
- (OSCProfile *) getSelectedProfile {
// NSLog(@"getAllProfiles test %f", CACurrentMediaTime());
NSMutableArray *profiles = [self getAllProfiles];
@@ -0,0 +1,543 @@
//
// OSCProfilesManager.m -> OSCProfilesManager.swift
// Moonlight
//
// Created by Long Le on 1/1/23.
// Copyright © 2023 Moonlight Game Streaming Project. All rights reserved.
//
// Modified by True砖家 since 2024.6.24
// Copyright © 2024 True砖家 @ Bilibili. All rights reserved.
//
import UIKit
@objcMembers
class OSCProfilesManager: NSObject {
private static let profilesDefaultsKey = "OSCProfiles"
private static let widgetProfileUpdatedKey = "widgetProfileUpdated-20260605"
private static var sharedInstance: OSCProfilesManager?
private static var onScreenWidgetViews: NSMutableSet?
private static var layoutViewBounds: CGRect = .zero
var currentProfiles: NSMutableArray = []
var widgetSizeTransition: WidgetSizeTransition = .keepWidgetSize
@objc(sharedManager:)
class func sharedManager(_ viewBounds: CGRect) -> OSCProfilesManager {
if viewBounds != .zero {
layoutViewBounds = viewBounds
}
if let sharedInstance {
return sharedInstance
}
let manager = OSCProfilesManager()
sharedInstance = manager
return manager
}
@objc(setOnScreenWidgetViewsSet:)
class func setOnScreenWidgetViewsSet(_ set: NSMutableSet) {
onScreenWidgetViews = set
}
@objc(setLayoutViewBounds:)
class func setLayoutViewBounds(_ bounds: CGRect) {
layoutViewBounds = bounds
}
private static let profileCodingClasses: [AnyClass] = [
NSString.self,
NSMutableData.self,
NSMutableArray.self,
OSCProfile.self,
OnScreenButtonState.self,
]
private static let buttonStateCodingClasses: [AnyClass] = [
NSString.self,
OnScreenButtonState.self,
NSSet.self,
NSNumber.self,
]
private func oscProfile(withName name: String) -> OSCProfile? {
let profiles = getAllProfiles()
for case let profile as OSCProfile in profiles where profile.name == name {
return profile
}
return nil
}
private func encodedProfiles(from profiles: NSMutableArray) -> NSMutableArray {
let profilesEncoded = NSMutableArray()
for case let profile as OSCProfile in profiles {
if let profileEncoded = try? NSKeyedArchiver.archivedData(withRootObject: profile, requiringSecureCoding: true) {
profilesEncoded.add(profileEncoded)
}
}
return profilesEncoded
}
private func persistProfiles(_ profiles: NSMutableArray) {
let profilesEncoded = encodedProfiles(from: profiles)
persistEncodedProfiles(profilesEncoded)
}
private func persistEncodedProfiles(_ profilesEncoded: NSMutableArray) {
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: profilesEncoded, requiringSecureCoding: true) else {
return
}
let defaults = UserDefaults.standard
defaults.set(data, forKey: Self.profilesDefaultsKey)
defaults.synchronize()
}
func deleteCurrentSelectedProfile() {
let profiles = getAllProfiles()
var selectedIndex = getIndexOfSelectedProfile()
if selectedIndex != 0 {
profiles.removeObject(at: selectedIndex)
} else {
NSLog("Default profile!")
}
selectedIndex -= 1
if selectedIndex < 0 {
selectedIndex = 0
}
if let newSelectedProfile = profiles[safe: selectedIndex] as? OSCProfile {
newSelectedProfile.isSelected = true
}
persistProfiles(profiles)
}
func replaceSelectedProfile(with newProfile: OSCProfile, overwriteDefault: Bool) {
let profiles = getAllProfiles()
var index = 0
for case let profile as OSCProfile in profiles where profile.isSelected {
index = profiles.index(of: profile)
}
if index > 0 || overwriteDefault {
profiles.replaceObject(at: index, with: newProfile)
}
persistProfiles(profiles)
}
@objc(replaceProfile:withProfile:)
func replaceProfile(_ oldProfile: OSCProfile, withProfile newProfile: OSCProfile) {
let profiles = getAllProfiles()
for case let profile as OSCProfile in profiles {
profile.isSelected = false
}
newProfile.isSelected = true
var index = 0
for i in 0 ..< profiles.count {
guard let profile = profiles[i] as? OSCProfile else { continue }
if profile.name == oldProfile.name {
index = i
}
}
profiles.removeObject(at: index)
profiles.insert(newProfile, at: index)
persistProfiles(profiles)
}
func replace(_ oldProfile: OSCProfile, with newProfile: OSCProfile) {
replaceProfile(oldProfile, withProfile: newProfile)
}
func getEncodedProfiles() -> NSMutableArray {
guard
let profilesArrayEncoded = UserDefaults.standard.object(forKey: Self.profilesDefaultsKey) as? Data,
let profilesEncoded = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.profileCodingClasses, from: profilesArrayEncoded) as? NSMutableArray
else {
return NSMutableArray()
}
return profilesEncoded
}
func importEncodedProfiles(_ profilesEncoded: NSMutableArray) {
let localProfiles = currentProfiles.mutableCopy() as? NSMutableArray ?? NSMutableArray()
if localProfiles.count > 0 {
localProfiles.removeObject(at: 0)
}
let profilesDecoded = decodeProfiles(from: profilesEncoded)
let localProfileNames = Set(
localProfiles.compactMap { ($0 as? OSCProfile)?.name }
)
for case let profile as OSCProfile in profilesDecoded {
if localProfileNames.contains(profile.name) {
profile.name = "\(profile.name)-\(LocalizationHelper.localizedString(forKey:"Restored"))"
}
}
let importedProfileNames = Set(
profilesDecoded.compactMap { ($0 as? OSCProfile)?.name }
)
var localProfilesToRemove: [OSCProfile] = []
for case let profile as OSCProfile in localProfiles {
if importedProfileNames.contains(profile.name) {localProfilesToRemove.append(profile)}
}
for case let profile as Any in localProfilesToRemove {
localProfiles.remove(profile)
}
for profile in localProfiles {
profilesDecoded.add(profile)
}
persistEncodedProfiles(encodedProfiles(from: profilesDecoded))
}
private func isIPhone() -> Bool {
UIDevice.current.userInterfaceIdiom == .phone
}
func importDefaultTemplates() {
guard let filePath = Bundle.main.path(forResource: isIPhone() ? "widgetTemplatesIPhone" : "widgetTemplates", ofType: "bin") else {
return
}
do {
let fileData = try Data(contentsOf: URL(fileURLWithPath: filePath), options: .mappedIfSafe)
if let profilesEncoded = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSMutableData.self, NSMutableArray.self], from: fileData) as? NSMutableArray {
importEncodedProfiles(profilesEncoded)
setProfileToSelected(0)
}
} catch {
return
}
}
func updateDefaultTemplates() {
guard let filePath = Bundle.main.path(forResource: isIPhone() ? "widgetTemplatesIPhone" : "widgetTemplates", ofType: "bin") else {
return
}
do {
let fileData = try Data(contentsOf: URL(fileURLWithPath: filePath), options: .mappedIfSafe)
guard let defaultProfilesEncoded = try NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.profileCodingClasses, from: fileData) as? NSMutableArray else {
return
}
let targetProfilesDecoded = getAllProfiles()
var newPencilProProfile: OSCProfile?
var pencilProProfileExisted = false
for case let profileEncoded as Data in defaultProfilesEncoded {
guard let defaultProfileDecoded = try NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.profileCodingClasses, from: profileEncoded) as? OSCProfile else {
continue
}
let targetProfile = findProfile(byName: defaultProfileDecoded.name, inProfileArray: targetProfilesDecoded)
if defaultProfileDecoded.name == "Pencil Pro" {
newPencilProProfile = defaultProfileDecoded.mutableCopy() as? OSCProfile
}
if let targetProfile {
if targetProfile.name == "Pencil Pro" {
pencilProProfileExisted = true
}
let targetIndex = targetProfilesDecoded.index(of: targetProfile)
targetProfilesDecoded.replaceObject(at: targetIndex, with: defaultProfileDecoded)
}
}
if let newPencilProProfile, !pencilProProfileExisted {
targetProfilesDecoded.insert(newPencilProProfile, at: 1)
}
persistProfiles(targetProfilesDecoded)
setProfileToSelected(0)
} catch {
return
}
}
func getAllProfiles() -> NSMutableArray {
guard
let profilesArrayEncoded = UserDefaults.standard.object(forKey: Self.profilesDefaultsKey) as? Data,
let profilesEncoded = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.profileCodingClasses, from: profilesArrayEncoded) as? NSMutableArray
else {
currentProfiles = NSMutableArray()
return currentProfiles
}
let profilesDecoded = decodeProfiles(from: profilesEncoded)
currentProfiles = profilesDecoded
return profilesDecoded
}
private func decodeProfiles(from profilesEncoded: NSMutableArray) -> NSMutableArray {
let profilesDecoded = NSMutableArray()
for case let profileEncoded as Data in profilesEncoded {
guard let profileDecoded = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.profileCodingClasses, from: profileEncoded) as? OSCProfile else {
continue
}
profilesDecoded.add(profileDecoded)
}
return profilesDecoded
}
func getSelectedProfile() -> OSCProfile {
var profiles = getAllProfiles()
let defaults = UserDefaults.standard
let needImportDefaultTemplates = defaults.object(forKey: Self.widgetProfileUpdatedKey) == nil
if profiles.count == 0 || needImportDefaultTemplates {
if profiles.count == 0 {
importDefaultTemplates()
} else {
updateDefaultTemplates()
}
defaults.set(true, forKey: Self.widgetProfileUpdatedKey)
defaults.synchronize()
profiles = getAllProfiles()
}
for case let profile as OSCProfile in profiles where profile.isSelected {
return profile
}
return (profiles.firstObject as? OSCProfile) ?? OSCProfile(name: "", buttonStates: NSMutableArray(), isSelected: false)
}
func getIndexOfSelectedProfile() -> Int {
let profiles = getAllProfiles()
for case let profile as OSCProfile in profiles where profile.isSelected {
return profiles.index(of: profile)
}
return 0
}
func getIndexOfLastProfile() -> UInt32 {
let profiles = getAllProfiles()
return profiles.count > 0 ? UInt32(profiles.count - 1) : 0
}
func setProfileToSelected(_ tableIndex: UInt32) {
let profiles = getAllProfiles()
for (idx, element) in profiles.enumerated() {
guard let profile = element as? OSCProfile else { continue }
profile.isSelected = idx == Int(tableIndex)
}
persistProfiles(profiles)
}
func updateSelectedProfile(_ oscButtonLayers: NSMutableSet) -> Bool {
let buttonStatesEncoded = convertOnScreenControllerAndWidgetsToButtonStates(oscButtonLayers)
if getIndexOfSelectedProfile() == 0 {
return false
}
let selectedProfile = getSelectedProfile()
selectedProfile.buttonStatesEncoded = buttonStatesEncoded
selectedProfile.unfoldedExclusiveFolderSequence = OnScreenWidgetView.unfoldedExclusiveFolderSequence
selectedProfile.postExclusiveUnfoldedSequences = Set(OnScreenWidgetView.postExclusiveUnfoldedSequences.map { NSNumber(value: $0) })
replaceSelectedProfile(with: selectedProfile, overwriteDefault: false)
return true
}
func duplicateSelectedProfile(withName name: String) {
if profileNameAlreadyExist(name) {
return
}
let profiles = getAllProfiles()
var newProfile: OSCProfile?
for case let profile as OSCProfile in profiles where profile.isSelected {
profile.isSelected = false
newProfile = profile.mutableCopy() as? OSCProfile
newProfile?.isSelected = true
newProfile?.name = name
}
guard let newProfile, let newProfileEncoded = try? NSKeyedArchiver.archivedData(withRootObject: newProfile, requiringSecureCoding: true) else {
return
}
let profilesEncoded = encodedProfiles(from: profiles)
profilesEncoded.add(newProfileEncoded)
persistEncodedProfiles(profilesEncoded)
}
func normalizeWidgetPosition(_ position: CGPoint) -> CGPoint {
var normalizedPosition = position
let originalPosition = position
if position.x > 1.0, position.y > 1.0 {
normalizedPosition.x = position.x / Self.layoutViewBounds.size.width
normalizedPosition.y = position.y / Self.layoutViewBounds.size.height
}
NSLog("layoutToolView bounds: %f, %f", Self.layoutViewBounds.size.width, Self.layoutViewBounds.size.height)
NSLog("position: %f, %f, denormalized position: %f, %f", normalizedPosition.x, normalizedPosition.y, originalPosition.x, originalPosition.y)
return normalizedPosition
}
private func denormalizeWidgetPosition(_ position: CGPoint) -> CGPoint {
var denormalizedPosition = position
if position.x < 1.0, position.y < 1.0 {
denormalizedPosition.x = position.x * Self.layoutViewBounds.size.width
denormalizedPosition.y = position.y * Self.layoutViewBounds.size.height
}
return denormalizedPosition
}
private func convertOnScreenControllerAndWidgetsToButtonStates(_ oscButtonLayers: NSMutableSet) -> NSMutableArray {
let buttonStatesEncoded = NSMutableArray()
for case let oscButtonLayer as CALayer in oscButtonLayers {
if oscButtonLayer.isHidden {
continue
}
let normalizedPosition = normalizeWidgetPosition(oscButtonLayer.position)
let layerName = oscButtonLayer.name ?? ""
let buttonState = OnScreenButtonState(buttonName: layerName, buttonType: 0, andPosition: normalizedPosition)
buttonState.isHidden = oscButtonLayer.isHidden
buttonState.oscLayerSizeFactor = OnScreenControls.getControllerLayerSizeFactor(oscButtonLayer)
buttonState.backgroundAlpha = CGFloat(oscButtonLayer.opacity)
if let style = OnScreenControls.layerVibrationStyleDic()?.object(forKey: layerName) as? NSNumber {
buttonState.vibrationStyle = style.uint8Value
} else {
buttonState.vibrationStyle = UInt8(UIImpactFeedbackGenerator.FeedbackStyle.light.rawValue)
}
if let buttonStateEncoded = try? NSKeyedArchiver.archivedData(withRootObject: buttonState, requiringSecureCoding: true) {
buttonStatesEncoded.add(buttonStateEncoded)
}
}
widgetSizeTransition = .keepWidgetSize
for widgetView in OnScreenWidgetView.mapping.values {
let normalizedPosition = normalizeWidgetPosition(widgetView.storedCenter)
let buttonState = OnScreenButtonState(buttonName: widgetView.cmdString, buttonType: 1, andPosition: normalizedPosition)
buttonState.alias = widgetView.widgetLabel
buttonState.sequence = widgetView.sequence
buttonState.sequenceSet = Set(widgetView.sequenceSet.map { NSNumber(value: $0) })
buttonState.parentSequence = widgetView.parentSequence
buttonState.autoDockTimer = Int16(widgetView.autoDockIdleDuration)
buttonState.dockedAlpha = widgetView.autoDockSettledAlpha
buttonState.folded = widgetView.folded
buttonState.revealMode = UInt8(widgetView.revealMode.rawValue)
buttonState.bulkMoveEnabled = widgetView.bulkMoveEnabled
buttonState.widthFactor = normalizeSizeWidthFactor(with: widgetView)
buttonState.heightFactor = normalizeSizeHeightFactor(with: widgetView)
buttonState.backgroundAlpha = widgetView.originalBackgroundAlpha
buttonState.labelAlpha = widgetView.originalLabelAlpha
buttonState.borderAlpha = widgetView.borderAlpha
buttonState.highlightAlpha = widgetView.highlightAlpha
buttonState.borderWidth = widgetView.borderWidth
buttonState.highlightSizeFactor = widgetView.highlightSizeFactor
buttonState.autoTapInterval = UInt16(widgetView.autoTapInterval)
buttonState.autoTapRepeats = widgetView.autoTapRepeats
buttonState.vibrationStyle = UInt8(widgetView.vibrationStyle)
buttonState.mouseButtonAction = UInt8(widgetView.mouseButtonAction.rawValue)
buttonState.animatesTransition = widgetView.animatesTransition
buttonState.sensitivityFactorY = widgetView.sensitivityFactorY
buttonState.slideThreshold = widgetView.slideThreshold
buttonState.yawFactor = widgetView.yawFactor
buttonState.pitchFactor = widgetView.pitchFactor
buttonState.rollFactor = widgetView.rollFactor
buttonState.decelerationRateX = widgetView.decelerationRateX
buttonState.decelerationRateY = widgetView.decelerationRateY
buttonState.widgetShape = widgetView.shape
buttonState.walkModeThreshold = widgetView.dWheelWalkModeThreshold
buttonState.minStickOffset = widgetView.minStickOffset
buttonState.buttonMode = UInt8(widgetView.buttonMode.rawValue)
buttonState.sprintKeyActionType = widgetView.sprintKeyActionType.rawValue
buttonState.sprintKeyThreshold = widgetView.sprintKeyThreshold
buttonState.walkKeyActionType = widgetView.walkKeyActionType.rawValue
buttonState.walkKeyThreshold = widgetView.walkKeyThreshold
buttonState.sensitivityFactorX = widgetView.sensitivityFactorX
buttonState.componentSizeFactor = normalizeComponentSizeFactor(with: widgetView)
buttonState.touchPointAnchored = widgetView.touchPointAnchored
buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset
if let buttonStateEncoded = try? NSKeyedArchiver.archivedData(withRootObject: buttonState, requiringSecureCoding: true) {
buttonStatesEncoded.add(buttonStateEncoded)
}
}
return buttonStatesEncoded
}
private func getReferenceLen() -> CGFloat {
let screenBounds = UIScreen.main.bounds
let screenWidthInPoints = screenBounds.width
let screenHeightInPoints = screenBounds.height
let longSideLen = max(screenWidthInPoints, screenHeightInPoints)
if widgetSizeTransition == .transitionWithOrientation {
return screenWidthInPoints
}
return longSideLen
}
private func normalizeSizeWidthFactor(with widget: OnScreenWidgetView) -> CGFloat {
widget.bounds.size.width / getReferenceLen() * 10000
}
private func normalizeSizeHeightFactor(with widget: OnScreenWidgetView) -> CGFloat {
widget.bounds.size.height / getReferenceLen() * 10000
}
private func normalizeComponentSizeFactor(with widget: OnScreenWidgetView) -> CGFloat {
if widget.isStickWheel || widget.isDisplacementBasedStickPad {
return widget.denormalizedComponentSizeFactor * widget.baselineDiameter / getReferenceLen() * 10000
}
return 1
}
func profileNameAlreadyExist(_ name: String) -> Bool {
let profiles = getAllProfiles()
for case let profile as OSCProfile in profiles where profile.name == name {
return true
}
return false
}
@objc(findProfileByName:inProfileArray:)
func findProfile(byName name: String, inProfileArray profiles: NSMutableArray) -> OSCProfile? {
for case let profile as OSCProfile in profiles where profile.name == name {
return profile
}
return nil
}
func unarchiveButtonStateEncoded(_ data: Data) -> OnScreenButtonState {
if let buttonState = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: Self.buttonStateCodingClasses, from: data) as? OnScreenButtonState {
return buttonState
}
return OnScreenButtonState(buttonName: "", buttonType: 0, andPosition: .zero)
}
}
private extension NSMutableArray {
subscript(safe index: Int) -> Any? {
guard index >= 0, index < count else { return nil }
return self[index]
}
}
+1 -1
View File
@@ -18,7 +18,7 @@
#import <CoreMotion/CoreMotion.h>
#endif
#import "OnScreenButtonState.h"
#import "OSCProfilesManager.h"
#import "VoidLink-Swift.h"
#import "DataManager.h"
#define UPDATE_BUTTON(x, y) (buttonFlags = \
-1
View File
@@ -17,7 +17,6 @@
#import "ControllerSupport.h"
#import "KeyboardSupport.h"
#import "VoidLink-Swift.h"
#import "OSCProfilesManager.h"
#import "NativeTouchPointer.h"
#import "NativeTouchHandler.h"
#import "PureNativeTouchHandler.h"
@@ -13199,6 +13199,29 @@
}
}
},
"Restored" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已恢复"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "已恢復"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "已恢復"
}
}
}
},
"Resume App" : {
"extractionState" : "manual",
"localizations" : {
+4 -1
View File
@@ -48,7 +48,7 @@ import StoreKit
case (.PencilProPack, "com.voidlink.tf.debug10.iOS"):
return "com.pencilpro.voidlink.debug.iOS"
case (.PencilProPack, "com.voidlink.tf.iOS"):
return "com.pencilpro.voidlink.tf.iOS"
return ""
default:
return ""
}
@@ -371,6 +371,9 @@ import StoreKit
@objc static public func inAppPurchaseAction(viewController: UIViewController, product: AddOnProduct){
let alert = UIAlertController(title: product.productName(),
message: LocalizationHelper.localizedString(forKey: "No purchase found", product.productName()),
preferredStyle: .alert)
@@ -12,7 +12,6 @@
#import "LayoutOnScreenControlsViewController.h"
#import "OnScreenButtonState.h"
#import "OnScreenControls.h"
#import "OSCProfilesManager.h"
#import "LocalizationHelper.h"
#import "VoidLink-Swift.h"
// #import "ThemeManager.h"
@@ -15,7 +15,6 @@
#import "VoidLink-Swift.h"
#import "Connection.h"
#import "Plot.h"
#import "OSCProfilesManager.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
@@ -24,8 +24,6 @@
#import "CustomTapGestureRecognizer.h"
#import "LocalizationHelper.h"
#import "VoidLink-Swift.h"
#import "OSCProfilesManager.h"
#import "VoidLink-Swift.h"
#import "NativeTouchPointer.h"
#include <sys/socket.h>
-1
View File
@@ -8,7 +8,6 @@
#import "DataManager.h"
#import "LocalizationHelper.h"
#import "OSCProfile.h"
#import "OSCProfilesManager.h"
#import "LayoutOnScreenControls.h"
#import "ToolBarContainerView.h"
#import "opus.h"