From 49a3ed0ddf9a0911dfa66b8f7609ad9ad488fc56 Mon Sep 17 00:00:00 2001 From: Takayama Fumihiko Date: Sat, 1 Jun 2024 08:44:52 +0900 Subject: [PATCH] Add OpenAtLogin.swift into MultitouchExtension --- .../MultitouchExtension/src/AppDelegate.swift | 21 +++---- .../MultitouchExtension/src/OpenAtLogin.swift | 58 +++++++++++++++++++ .../src/UserSettings.swift | 4 +- .../src/Views/Settings/SettingsMainView.swift | 21 ++++++- 4 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 src/apps/MultitouchExtension/src/OpenAtLogin.swift diff --git a/src/apps/MultitouchExtension/src/AppDelegate.swift b/src/apps/MultitouchExtension/src/AppDelegate.swift index b56ae2494..3900db632 100644 --- a/src/apps/MultitouchExtension/src/AppDelegate.swift +++ b/src/apps/MultitouchExtension/src/AppDelegate.swift @@ -16,23 +16,24 @@ public class AppDelegate: NSObject, NSApplicationDelegate { NSApplication.shared.disableRelaunchOnLogin() // - // Handle kHideIconInDock + // Register OpenAtLogin // - if !UserSettings.shared.hideIconInDock { - var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) - TransformProcessType( - &psn, ProcessApplicationTransformState(kProcessTransformToForegroundApplication)) + if !OpenAtLogin.shared.developmentBinary { + if !UserSettings.shared.initialOpenAtLoginRegistered { + OpenAtLogin.shared.update(register: true) + UserSettings.shared.initialOpenAtLoginRegistered = true + } } // - // Handle --start-at-login + // Handle kHideIconInDock // - if CommandLine.arguments.contains("--start-at-login") { - if !UserSettings.shared.openAtLogin { - NSApplication.shared.terminate(self) - } + if !UserSettings.shared.hideIconInDock { + var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) + TransformProcessType( + &psn, ProcessApplicationTransformState(kProcessTransformToForegroundApplication)) } // diff --git a/src/apps/MultitouchExtension/src/OpenAtLogin.swift b/src/apps/MultitouchExtension/src/OpenAtLogin.swift new file mode 100644 index 000000000..32697d892 --- /dev/null +++ b/src/apps/MultitouchExtension/src/OpenAtLogin.swift @@ -0,0 +1,58 @@ +import Foundation +import ServiceManagement + +final class OpenAtLogin: ObservableObject { + static let shared = OpenAtLogin() + + @Published var registered = false + + var error = "" + + init() { + registered = SMAppService.mainApp.status == .enabled + } + + var developmentBinary: Bool { + let bundlePath = Bundle.main.bundlePath + + // Xcode builds + // - /Build/Products/Debug/*.app + // - /Build/Products/Release/*.app + if bundlePath.contains("/Build/") { + return true + } + + // Command line builds + // - /build/Release/*.app + if bundlePath.contains("/build/") { + return true + } + + return false + } + + @MainActor + func update(register: Bool) { + error = "" + + do { + if register { + try SMAppService.mainApp.register() + } else { + // `unregister` throws `Operation not permitted` error in the following cases. + // + // 1. `unregister` is called. + // 2. macOS is restarted to clean up login items entries. + // 3. `unregister` is called again. + // + // So, we ignore the error of `unregister`. + + try? SMAppService.mainApp.unregister() + } + + registered = register + } catch { + self.error = error.localizedDescription + } + } +} diff --git a/src/apps/MultitouchExtension/src/UserSettings.swift b/src/apps/MultitouchExtension/src/UserSettings.swift index 24d32295d..052f189e4 100644 --- a/src/apps/MultitouchExtension/src/UserSettings.swift +++ b/src/apps/MultitouchExtension/src/UserSettings.swift @@ -3,8 +3,8 @@ import Foundation final class UserSettings: ObservableObject { static let shared = UserSettings() - @UserDefault("kStartAtLogin", defaultValue: false) - var openAtLogin: Bool { + @UserDefault("initialOpenAtLoginRegistered", defaultValue: false) + var initialOpenAtLoginRegistered: Bool { willSet { objectWillChange.send() } diff --git a/src/apps/MultitouchExtension/src/Views/Settings/SettingsMainView.swift b/src/apps/MultitouchExtension/src/Views/Settings/SettingsMainView.swift index aec513302..eb5500b8d 100644 --- a/src/apps/MultitouchExtension/src/Views/Settings/SettingsMainView.swift +++ b/src/apps/MultitouchExtension/src/Views/Settings/SettingsMainView.swift @@ -2,22 +2,37 @@ import SwiftUI struct SettingsMainView: View { @ObservedObject private var userSettings = UserSettings.shared + @ObservedObject private var openAtLogin = OpenAtLogin.shared var body: some View { VStack(alignment: .leading, spacing: 25.0) { GroupBox(label: Text("Basic")) { VStack(alignment: .leading, spacing: 25.0) { HStack { - Toggle(isOn: $userSettings.openAtLogin) { + Toggle(isOn: $openAtLogin.registered) { Text("Open at login") } .switchToggleStyle() - - Text("(Default: off)") + .disabled(openAtLogin.developmentBinary) + .onChange(of: openAtLogin.registered) { value in + OpenAtLogin.shared.update(register: value) + } Spacer() } + if openAtLogin.error.count > 0 { + VStack { + Label( + openAtLogin.error, + systemImage: "exclamationmark.circle.fill" + ) + .padding() + } + .foregroundColor(Color.errorForeground) + .background(Color.errorBackground) + } + VStack(alignment: .leading) { HStack { Toggle(isOn: $userSettings.hideIconInDock) {