diff --git a/BrightIntosh.xcodeproj/project.pbxproj b/BrightIntosh.xcodeproj/project.pbxproj index 9b7a775..df64ce0 100644 --- a/BrightIntosh.xcodeproj/project.pbxproj +++ b/BrightIntosh.xcodeproj/project.pbxproj @@ -392,7 +392,7 @@ CODE_SIGN_ENTITLEMENTS = BrightIntosh/BrightIntosh_SE.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9K32WDUN2H; @@ -419,7 +419,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.2.0; + MARKETING_VERSION = 3.2.1; PRODUCT_BUNDLE_IDENTIFIER = de.brightintosh.app; PRODUCT_NAME = BrightIntosh; SDKROOT = auto; @@ -444,7 +444,7 @@ CODE_SIGN_ENTITLEMENTS = BrightIntosh/BrightIntosh_SE.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9K32WDUN2H; @@ -471,7 +471,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.2.0; + MARKETING_VERSION = 3.2.1; PRODUCT_BUNDLE_IDENTIFIER = de.brightintosh.app; PRODUCT_NAME = BrightIntosh; SDKROOT = auto; @@ -494,7 +494,7 @@ CODE_SIGN_ENTITLEMENTS = BrightIntosh/BrightIntosh.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9K32WDUN2H; @@ -521,7 +521,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.2.0; + MARKETING_VERSION = 3.2.1; PRODUCT_BUNDLE_IDENTIFIER = de.brightintosh.app; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; @@ -545,7 +545,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9K32WDUN2H; @@ -572,7 +572,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.2.0; + MARKETING_VERSION = 3.2.1; PRODUCT_BUNDLE_IDENTIFIER = de.brightintosh.app; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; diff --git a/BrightIntosh/AppDelegate.swift b/BrightIntosh/AppDelegate.swift index b35ac97..92de910 100644 --- a/BrightIntosh/AppDelegate.swift +++ b/BrightIntosh/AppDelegate.swift @@ -28,7 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { supportedDevice = isDeviceSupported() - + if UserDefaults.standard.object(forKey: "agreementAccepted") == nil || !UserDefaults.standard.bool(forKey: "agreementAccepted") { welcomeWindow() } diff --git a/BrightIntosh/BrightnessManager.swift b/BrightIntosh/BrightnessManager.swift index 014657c..71ce3e3 100644 --- a/BrightIntosh/BrightnessManager.swift +++ b/BrightIntosh/BrightnessManager.swift @@ -132,7 +132,7 @@ class BrightnessManager { func enableExtraBrightness() { // Put brightness value into device specific bounds, as earlier versions allowed storing higher brightness values. - let safeBrightness = max(1.0, min(getDeviceMaxBrightness() - 0.01, Settings.shared.brightness)) + let safeBrightness = max(1.0, min(getDeviceMaxBrightness(), Settings.shared.brightness)) if safeBrightness != Settings.shared.brightness { print("Fixing brightness") diff --git a/BrightIntosh/Localizable.xcstrings b/BrightIntosh/Localizable.xcstrings index 7064296..5b59815 100644 --- a/BrightIntosh/Localizable.xcstrings +++ b/BrightIntosh/Localizable.xcstrings @@ -1014,34 +1014,6 @@ } } }, - "Restore Purchases" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Käufe wiederherstellen" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurar compras" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurer les achats" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurar compras" - } - } - } - }, "Settings" : { "localizations" : { "de" : { diff --git a/BrightIntosh/UI/SettingsWindow.swift b/BrightIntosh/UI/SettingsWindow.swift index 039eb1a..d648840 100644 --- a/BrightIntosh/UI/SettingsWindow.swift +++ b/BrightIntosh/UI/SettingsWindow.swift @@ -5,10 +5,10 @@ // Created by Niklas Rousset on 17.09.23. // -import SwiftUI import KeyboardShortcuts -import StoreKit import OSLog +import StoreKit +import SwiftUI final class BasicSettingsViewModel: ObservableObject { /* @@ -29,13 +29,13 @@ final class BasicSettingsViewModel: ObservableObject { set { Settings.shared.batteryAutomation = newValue } get { return batteryAutomation } } - + private var timerAutomation = Settings.shared.timerAutomation var timerAutomationToggle: Bool { - set { Settings.shared.timerAutomation = newValue} - get { return timerAutomation} + set { Settings.shared.timerAutomation = newValue } + get { return timerAutomation } } - + init() { Settings.shared.addListener(setting: "brightintoshActive") { if Settings.shared.brightintoshActive && !checkBatteryAutomationContradiction() { @@ -69,15 +69,15 @@ final class BasicSettingsViewModel: ObservableObject { struct BasicSettings: View { @ObservedObject var viewModel = BasicSettingsViewModel() - + @State private var launchOnLogin = Settings.shared.launchAtLogin @State private var brightIntoshOnlyOnBuiltIn = Settings.shared.brightIntoshOnlyOnBuiltIn @State private var batteryLevelThreshold = Settings.shared.batteryAutomationThreshold @State private var timerAutomationTimeout = Settings.shared.timerAutomationTimeout - + @State private var entitledToUnrestrictedUse = false @Environment(\.isUnrestrictedUser) private var isUnrestrictedUser: Bool - + var body: some View { ScrollView { VStack(alignment: HorizontalAlignment.leading) { @@ -87,12 +87,18 @@ struct BasicSettings: View { Text("Brightness") } if isDeviceSupported() { - Toggle("Don't apply increased brightness to external XDR displays", isOn: $brightIntoshOnlyOnBuiltIn) - .onChange(of: brightIntoshOnlyOnBuiltIn) { value in - Settings.shared.brightIntoshOnlyOnBuiltIn = value - } + Toggle( + "Don't apply increased brightness to external XDR displays", + isOn: $brightIntoshOnlyOnBuiltIn + ) + .onChange(of: brightIntoshOnlyOnBuiltIn) { value in + Settings.shared.brightIntoshOnlyOnBuiltIn = value + } } else { - Label("Your device doesn't have a built-in XDR display. Increased brightness can only be enabled for external XDR displays.", systemImage: "exclamationmark.triangle.fill").foregroundColor(Color.yellow) + Label( + "Your device doesn't have a built-in XDR display. Increased brightness can only be enabled for external XDR displays.", + systemImage: "exclamationmark.triangle.fill" + ).foregroundColor(Color.yellow) } } Section(header: Text("Automations").bold()) { @@ -101,23 +107,29 @@ struct BasicSettings: View { Settings.shared.launchAtLogin = value } HStack { - Toggle("Disable when battery level drops under", isOn: $viewModel.batteryAutomationToggle) - TextField("Battery level threshold", value: $batteryLevelThreshold, format: .percent) - .onChange(of: batteryLevelThreshold) { value in - if !(0...100 ~= batteryLevelThreshold) { - batteryLevelThreshold = max(0, min(batteryLevelThreshold, 100)) - } else { - Settings.shared.batteryAutomationThreshold = value - } + Toggle( + "Disable when battery level drops under", + isOn: $viewModel.batteryAutomationToggle) + TextField( + "Battery level threshold", value: $batteryLevelThreshold, + format: .percent + ) + .onChange(of: batteryLevelThreshold) { value in + if !(0...100 ~= batteryLevelThreshold) { + batteryLevelThreshold = max(0, min(batteryLevelThreshold, 100)) + } else { + Settings.shared.batteryAutomationThreshold = value } - .textFieldStyle(.roundedBorder) - .frame(maxWidth: 60) - .multilineTextAlignment(.center) + } + .textFieldStyle(.roundedBorder) + .frame(maxWidth: 60) + .multilineTextAlignment(.center) } HStack { Toggle("Disable after", isOn: $viewModel.timerAutomationToggle) Picker(selection: $timerAutomationTimeout, label: EmptyView()) { - ForEach(Array(stride(from: 10, to: 51, by: 10)), id: \.self) { minutes in + ForEach(Array(stride(from: 10, to: 51, by: 10)), id: \.self) { + minutes in Text("\(minutes) min").tag(minutes) } ForEach(Array(stride(from: 1, to: 5, by: 0.5)), id: \.self) { hours in @@ -132,13 +144,16 @@ struct BasicSettings: View { } Section(header: Text("Shortcuts").bold()) { Form { - KeyboardShortcuts.Recorder("Toggle increased brightness:", name: .toggleBrightIntosh) - KeyboardShortcuts.Recorder("Increase brightness:", name: .increaseBrightness) - KeyboardShortcuts.Recorder("Decrease brightness:", name: .decreaseBrightness) + KeyboardShortcuts.Recorder( + "Toggle increased brightness:", name: .toggleBrightIntosh) + KeyboardShortcuts.Recorder( + "Increase brightness:", name: .increaseBrightness) + KeyboardShortcuts.Recorder( + "Decrease brightness:", name: .decreaseBrightness) } } Section(header: Text("Information").bold()) { - VStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) { + VStack(alignment: /*@START_MENU_TOKEN@*/ .center /*@END_MENU_TOKEN@*/) { Button(action: { Task { let report = await generateReport() @@ -182,58 +197,58 @@ struct Acknowledgments: View { } struct VersionView: View { -#if STORE - var title: String = "BrightIntosh SE v\(appVersion)" - @Environment(\.isUnrestrictedUser) private var isUnrestrictedUser: Bool -#else - var title: String = "BrightIntosh v\(appVersion)" - private let isUnrestrictedUser: Bool = true -#endif - - + #if STORE + var title: String = "BrightIntosh SE v\(appVersion)" + @Environment(\.isUnrestrictedUser) private var isUnrestrictedUser: Bool + #else + var title: String = "BrightIntosh v\(appVersion)" + private let isUnrestrictedUser: Bool = true + #endif + @State var clicks = 0 - + @State var ignoreAppTransaction = Settings.shared.ignoreAppTransaction - + var body: some View { - Label(title + (isUnrestrictedUser ? "" : " - Free Trial"), image: "LogoBordered").imageScale(.small) + Label(title + (isUnrestrictedUser ? "" : " - Free Trial"), image: "LogoBordered") + .imageScale(.small) .onTapGesture { clicks += 1 } -#if STORE - if clicks >= 5 { - VStack { - Text("Test Settings").font(.title2) - Toggle(isOn: $ignoreAppTransaction) { - Text("Test: Ignore App Transaction") - } - .onChange(of: ignoreAppTransaction) { _ in - Settings.shared.ignoreAppTransaction = ignoreAppTransaction - Task { - _ = await EntitlementHandler.shared.isUnrestrictedUser() + #if STORE + if clicks >= 5 { + VStack { + Text("Test Settings").font(.title2) + Toggle(isOn: $ignoreAppTransaction) { + Text("Test: Ignore App Transaction") + } + .onChange(of: ignoreAppTransaction) { _ in + Settings.shared.ignoreAppTransaction = ignoreAppTransaction + Task { + _ = await EntitlementHandler.shared.isUnrestrictedUser() + } + } + Button("Hide") { + clicks = 0 } - } - Button("Hide") { - clicks = 0 } } - } -#endif + #endif } } struct SettingsTabs: View { @Environment(\.isUnrestrictedUser) private var isUnrestrictedUser: Bool - + var body: some View { TabView { -#if STORE - if !isUnrestrictedUser { - BrightIntoshStoreView(showTrialExpiredWarning: true).tabItem { - Text("Store") + #if STORE + if !isUnrestrictedUser { + BrightIntoshStoreView(showTrialExpiredWarning: true).tabItem { + Text("Store") + } } - } -#endif + #endif BasicSettings().tabItem { Text("General") } @@ -247,7 +262,9 @@ struct SettingsTabs: View { struct SettingsView: View { var body: some View { VStack { - Text("Settings").font(.largeTitle) + if #unavailable(macOS 15) { + Text("Settings").font(.largeTitle) + } SettingsTabs() VersionView() } @@ -262,37 +279,34 @@ struct SettingsView_Previews: PreviewProvider { } } - final class SettingsWindowController: NSWindowController, NSWindowDelegate { init() { - + let settingsWindow = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 500, height: 580), - styleMask: [.titled, .closable], + contentRect: NSRect(x: 0, y: 0, width: 650, height: 580), + styleMask: [.titled, .closable, .unifiedTitleAndToolbar], backing: .buffered, defer: false ) - - let contentView = SettingsView().frame(width: 500, height: 580) - + + let contentView = SettingsView().frame(width: 650, height: 580) + settingsWindow.contentView = NSHostingView(rootView: contentView) - settingsWindow.titlebarAppearsTransparent = true - settingsWindow.titlebarSeparatorStyle = .none settingsWindow.center() - + super.init(window: settingsWindow) settingsWindow.delegate = self } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func showWindow(_ sender: Any?) { window?.level = .floating super.showWindow(sender) } - + func windowWillClose(_ notification: Notification) { NSApp.stopModal() } diff --git a/BrightIntosh/Utils.swift b/BrightIntosh/Utils.swift index 21ee963..109b5ab 100644 --- a/BrightIntosh/Utils.swift +++ b/BrightIntosh/Utils.swift @@ -6,14 +6,18 @@ // import Foundation -import StoreKit import IOKit +import StoreKit func getModelIdentifier() -> String? { - let service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice")) + let service = IOServiceGetMatchingService( + kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice")) var modelIdentifier: String? - if let modelData = IORegistryEntryCreateCFProperty(service, "model" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? Data { - modelIdentifier = String(data: modelData, encoding: .utf8)?.trimmingCharacters(in: .controlCharacters) + if let modelData = IORegistryEntryCreateCFProperty( + service, "model" as CFString, kCFAllocatorDefault, 0 + ).takeRetainedValue() as? Data { + modelIdentifier = String(data: modelData, encoding: .utf8)?.trimmingCharacters( + in: .controlCharacters) } IOObjectRelease(service) @@ -29,10 +33,11 @@ func isDeviceSupported() -> Bool { func getDeviceMaxBrightness() -> Float { if let device = getModelIdentifier(), - sdr600nitsDevices.contains(device) { - return 1.54 + sdr600nitsDevices.contains(device) + { + return 1.535 } - return 1.6 + return 1.59 } private func getAppTransaction() async -> VerificationResult? { @@ -48,11 +53,11 @@ private func getAppTransaction() async -> VerificationResult? { func generateReport() async -> String { var report = "BrightIntosh Report:\n" report += "OS-Version: \(ProcessInfo.processInfo.operatingSystemVersionString)\n" -#if STORE - report += "Version: BrightIntosh SE v\(appVersion)\n" -#else - report += "Version: BrightIntosh v\(appVersion)\n" -#endif + #if STORE + report += "Version: BrightIntosh SE v\(appVersion)\n" + #else + report += "Version: BrightIntosh v\(appVersion)\n" + #endif if let sharedAppTransaction = await getAppTransaction() { if case .verified(let appTransaction) = sharedAppTransaction { report += "Original Purchase Date: \(appTransaction.originalPurchaseDate)\n" @@ -61,21 +66,23 @@ func generateReport() async -> String { report += "Transaction Environment: \(appTransaction.environment.rawValue)\n" } if case .unverified(_, let verificationError) = sharedAppTransaction { - report += "Error: App Transaction: \(verificationError.errorDescription ?? "no error description") - \(verificationError.failureReason ?? "no failure reason")\n" + report += + "Error: App Transaction: \(verificationError.errorDescription ?? "no error description") - \(verificationError.failureReason ?? "no failure reason")\n" } } else { report += "Error: App Transaction could not be fetched \n" } - + let isUnrestricted = await EntitlementHandler.shared.isUnrestrictedUser() report += "Unrestricted user: \(isUnrestricted)\n" do { let trial = try await TrialData.getTrialData() - report += "Trial: Start Date: \(trial.purchaseDate) Current Date: \(trial.currentDate) Remaining: \(trial.getRemainingDays())\n" + report += + "Trial: Start Date: \(trial.purchaseDate) Current Date: \(trial.currentDate) Remaining: \(trial.getRemainingDays())\n" } catch { report += "Error: Trial Data could not be fetched\n" } report += "Screens: \(NSScreen.screens.map{$0.localizedName})" - + return report }