diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bcdfa12 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,18 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + } +} +{ + "name": "my-project-devcontainer", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", // Any generic, debian-based image. + "features": { + "ghcr.io/devcontainers/features/go:1": { + "version": "1.18" + }, + "ghcr.io/devcontainers/features/docker-in-docker:1": { + "version": "latest", + "moby": true + } + } +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c97ded9..022218e 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ["https://www.buymeacoffee.com/tsone", "https://paypal.me/takeshisone"] +custom: ["https://www.buymeacoffee.com/tsone"] diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index b1f5563..c7befb8 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -42,6 +42,14 @@ /* Begin PBXFileReference section */ 3D2CF85A255102B300157996 /* MediaRemote.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaRemote.framework; path = ../../../../System/Library/PrivateFrameworks/MediaRemote.framework; sourceTree = "<group>"; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; + 3D43D0112765FCCB00D15991 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/AboutBox.strings; sourceTree = "<group>"; }; + 3D43D0122765FCCB00D15991 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; }; + 3D43D0132765FCFD00D15991 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/AboutBox.strings; sourceTree = "<group>"; }; + 3D43D0142765FCFD00D15991 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; }; + 3D43D0152765FD1600D15991 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/AboutBox.strings; sourceTree = "<group>"; }; + 3D43D0162765FD1600D15991 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; }; + 3D43D0172765FD2800D15991 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/AboutBox.strings; sourceTree = "<group>"; }; + 3D43D0182765FD2800D15991 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; }; 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; 3D4EE4122279E56900AF9E93 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; @@ -69,6 +77,8 @@ 3DD4B668226C1E3700451B7B /* login.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = login.framework; path = ../../../../System/Library/PrivateFrameworks/login.framework; sourceTree = "<group>"; }; B1EFF91422E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutBox.strings"; sourceTree = "<group>"; }; B1EFF91622E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; + F749D28C277515C600C78C28 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AboutBox.strings; sourceTree = "<group>"; }; + F749D28D277515C600C78C28 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -242,6 +252,11 @@ Base, ja, "zh-Hans", + de, + da, + nb, + sv, + tr, ); mainGroup = 3DD4B647226C1C3200451B7B; productRefGroup = 3DD4B651226C1C3200451B7B /* Products */; @@ -329,6 +344,11 @@ 3D600A36226CC1A40068FB7B /* Base */, 3D600A38226CC1AA0068FB7B /* ja */, B1EFF91622E010F50010DB0A /* zh-Hans */, + 3D43D0122765FCCB00D15991 /* de */, + 3D43D0142765FCFD00D15991 /* da */, + 3D43D0162765FD1600D15991 /* nb */, + 3D43D0182765FD2800D15991 /* sv */, + F749D28D277515C600C78C28 /* tr */, ); name = Localizable.strings; sourceTree = "<group>"; @@ -339,6 +359,11 @@ 3D953798227853E20017D8B9 /* Base */, 3D95379B227853EC0017D8B9 /* ja */, B1EFF91422E010F50010DB0A /* zh-Hans */, + 3D43D0112765FCCB00D15991 /* de */, + 3D43D0132765FCFD00D15991 /* da */, + 3D43D0152765FD1600D15991 /* nb */, + 3D43D0172765FD2800D15991 /* sv */, + F749D28C277515C600C78C28 /* tr */, ); name = AboutBox.xib; sourceTree = "<group>"; diff --git a/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme b/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme new file mode 100644 index 0000000..0227bea --- /dev/null +++ b/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1300" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3DD4B64F226C1C3200451B7B" + BuildableName = "BLEUnlock.app" + BlueprintName = "BLEUnlock" + ReferencedContainer = "container:BLEUnlock.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3DD4B64F226C1C3200451B7B" + BuildableName = "BLEUnlock.app" + BlueprintName = "BLEUnlock" + ReferencedContainer = "container:BLEUnlock.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3DD4B64F226C1C3200451B7B" + BuildableName = "BLEUnlock.app" + BlueprintName = "BLEUnlock" + ReferencedContainer = "container:BLEUnlock.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index a1aa4e4..4ee9c91 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -31,13 +31,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var inScreensaver = false var lastRSSI: Int? = nil - override init() { - // Hide dock icon. - // This is required because we can't have LSUIElement set to true in Info.plist, - // otherwise CBCentralManager.scanForPeripherals won't work. - NSApp.setActivationPolicy(.accessory) - } - func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { ble.startScanning() @@ -209,12 +202,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } - func lockOrSaveScreen() -> Bool { + func lockOrSaveScreen() { if prefs.bool(forKey: "screensaver") { NSWorkspace.shared.launchApplication("ScreenSaverEngine") - return true // Really!? } else { - return lockScreen() + if SACLockScreenImmediate() != 0 { + print("Failed to lock screen") + } + if prefs.bool(forKey: "sleepDisplay") { + print("sleep display") + sleepDisplay() + } } } @@ -238,12 +236,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } else { if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { pauseNowPlaying() - if lockOrSaveScreen() { - notifyUser(reason) - runScript(reason) - } else { - print("Failed to lock") - } + lockOrSaveScreen() + notifyUser(reason) + runScript(reason) } manualLock = false } @@ -297,21 +292,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: false)?.post(tap: .cghidEventTap) } + guard !self.prefs.bool(forKey: "wakeWithoutUnlocking") else { return } + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in guard self.isScreenLocked() else { return } guard let password = self.fetchPassword(warn: true) else { return } print("Entering password") + self.unlockedAt = Date().timeIntervalSince1970 self.fakeKeyStrokes(password) self.playNowPlaying() self.runScript("unlocked") - self.unlockedAt = Date().timeIntervalSince1970 }) } @objc func onDisplayWake() { print("display wake") - unlockedAt = Date().timeIntervalSince1970 + //unlockedAt = Date().timeIntervalSince1970 displaySleep = false wakeTimer?.invalidate() wakeTimer = nil @@ -327,6 +324,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa print("system wake") Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in print("delayed system wake job") + NSApp.setActivationPolicy(.accessory) // Hide Dock icon again self.systemSleep = false self.tryUnlockScreen() }) @@ -335,13 +333,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onSystemSleep() { print("system sleep") systemSleep = true + // Set activation policy to regular, so the CBCentralManager can scan for peripherals + // when the Bluetooth will become on again. + // This enables Dock icon but the screen is off anyway. + NSApp.setActivationPolicy(.regular) } @objc func onUnlock() { - if Date().timeIntervalSince1970 >= unlockedAt + 10 { - runScript("intruded") - self.playNowPlaying() - } + Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { _ in + print("onUnlock") + if Date().timeIntervalSince1970 >= self.unlockedAt + 10 { + if self.ble.unlockRSSI != self.ble.UNLOCK_DISABLED { + self.runScript("intruded") + } + self.playNowPlaying() + } + }) manualLock = false Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { _ in checkUpdate() @@ -525,6 +532,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa prefs.set(value, forKey: "screensaver") menuItem.state = value ? .on : .off } + + @objc func toggleSleepDisplay(_ menuItem: NSMenuItem) { + let value = !prefs.bool(forKey: "sleepDisplay") + prefs.set(value, forKey: "sleepDisplay") + menuItem.state = value ? .on : .off + } @objc func togglePassiveMode(_ menuItem: NSMenuItem) { let passiveMode = !prefs.bool(forKey: "passiveMode") @@ -533,11 +546,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa ble.setPassiveMode(passiveMode) } + @objc func toggleWakeWithoutUnlocking(_ menuItem: NSMenuItem) { + let wakeWithoutUnlocking = !prefs.bool(forKey: "wakeWithoutUnlocking") + prefs.set(wakeWithoutUnlocking, forKey: "wakeWithoutUnlocking") + menuItem.state = wakeWithoutUnlocking ? .on : .off + } + @objc func lockNow() { guard !isScreenLocked() else { return } manualLock = true pauseNowPlaying() - _ = lockOrSaveScreen() + lockOrSaveScreen() } @objc func showAboutBox() { @@ -581,10 +600,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let lockDelayItem = mainMenu.addItem(withTitle: t("lock_delay"), action: nil, keyEquivalent: "") lockDelayItem.submenu = lockDelayMenu - lockDelayMenu.addItem(withTitle: "0 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 0 + lockDelayMenu.addItem(withTitle: "2 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 2 lockDelayMenu.addItem(withTitle: "5 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 5 - lockDelayMenu.addItem(withTitle: "10 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 10 - lockDelayMenu.addItem(withTitle: "20 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 20 + lockDelayMenu.addItem(withTitle: "15 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 15 lockDelayMenu.addItem(withTitle: "30 " + t("seconds"), action: #selector(setLockDelay), keyEquivalent: "").tag = 30 lockDelayMenu.addItem(withTitle: "1 " + t("minute"), action: #selector(setLockDelay), keyEquivalent: "").tag = 60 lockDelayMenu.addItem(withTitle: "2 " + t("minutes"), action: #selector(setLockDelay), keyEquivalent: "").tag = 120 @@ -605,6 +623,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .on } + item = mainMenu.addItem(withTitle: t("wake_without_unlocking"), action: #selector(toggleWakeWithoutUnlocking), keyEquivalent: "") + if prefs.bool(forKey: "wakeWithoutUnlocking") { + item.state = .on + } + item = mainMenu.addItem(withTitle: t("pause_now_playing"), action: #selector(togglePauseNowPlaying), keyEquivalent: "") if prefs.bool(forKey: "pauseItunes") { item.state = .on @@ -614,6 +637,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if prefs.bool(forKey: "screensaver") { item.state = .on } + + item = mainMenu.addItem(withTitle: t("sleep_display"), action: #selector(toggleSleepDisplay), keyEquivalent: "") + if prefs.bool(forKey: "sleepDisplay") { + item.state = .on + } mainMenu.addItem(withTitle: t("set_password"), action: #selector(askPassword), keyEquivalent: "") @@ -691,11 +719,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa dnc.addObserver(self, selector: #selector(onScreensaverStart), name: NSNotification.Name(rawValue: "com.apple.screensaver.didstart"), object: nil) dnc.addObserver(self, selector: #selector(onScreensaverStop), name: NSNotification.Name(rawValue: "com.apple.screensaver.didstop"), object: nil) - if ble.unlockRSSI != ble.UNLOCK_DISABLED && fetchPassword() == nil { + if ble.unlockRSSI != ble.UNLOCK_DISABLED && !prefs.bool(forKey: "wakeWithoutUnlocking") && fetchPassword() == nil { askPassword() } checkAccessibility() checkUpdate() + + // Hide dock icon. + // This is required because we can't have LSUIElement set to true in Info.plist, + // otherwise CBCentralManager.scanForPeripherals won't work. + NSApp.setActivationPolicy(.accessory) } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 4c4cbe8..3434e16 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> <deployment identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.3"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -17,7 +17,7 @@ <window title="About BLEUnlock" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="" animationBehavior="default" id="hgX-Z1-8qq"> <windowStyleMask key="styleMask" titled="YES" closable="YES"/> <rect key="contentRect" x="196" y="240" width="500" height="450"/> - <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/> + <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/> <view key="contentView" id="4gT-J6-CkT"> <rect key="frame" x="0.0" y="0.0" width="500" height="450"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/> @@ -36,16 +36,16 @@ </textField> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xhw-xw-Rxl"> <rect key="frame" x="134" y="254" width="233" height="16"/> - <textFieldCell key="cell" lineBreakMode="clipping" title="Copyright © 2019-2021 Takeshi Sone" id="GN4-8z-o8I"> + <textFieldCell key="cell" lineBreakMode="clipping" title="Copyright © 2019-2022 Takeshi Sone" id="GN4-8z-o8I"> <font key="font" metaFont="system"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> </textFieldCell> </textField> <scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zi4-VN-Z2O"> - <rect key="frame" x="20" y="49" width="460" height="197"/> + <rect key="frame" x="20" y="48" width="460" height="198"/> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="HAL-4b-8ea"> - <rect key="frame" x="0.0" y="0.0" width="460" height="197"/> + <rect key="frame" x="0.0" y="0.0" width="460" height="198"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="wTr-M6-frY"> @@ -53,7 +53,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="textColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> - <size key="minSize" width="460" height="197"/> + <size key="minSize" width="460" height="198"/> <size key="maxSize" width="500" height="10000000"/> <attributedString key="textStorage"> <fragment content="Permission is hereby granted"> @@ -404,12 +404,12 @@ Included icons are derived from SVGs downloaded from materialdesignicons.com. Th <autoresizingMask key="autoresizingMask"/> </scroller> <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="scb-1K-7dN"> - <rect key="frame" x="444" y="0.0" width="16" height="197"/> + <rect key="frame" x="444" y="0.0" width="16" height="198"/> <autoresizingMask key="autoresizingMask"/> </scroller> </scrollView> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8gJ-J5-6Ty"> - <rect key="frame" x="74" y="13" width="136" height="32"/> + <rect key="frame" x="73" y="13" width="130" height="32"/> <buttonCell key="cell" type="push" title="Visit Homepage" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="fPY-L3-Edh"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="system"/> @@ -419,7 +419,7 @@ Included icons are derived from SVGs downloaded from materialdesignicons.com. Th </connections> </button> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AHV-DJ-ucE"> - <rect key="frame" x="289" y="13" width="137" height="32"/> + <rect key="frame" x="297" y="13" width="130" height="32"/> <buttonCell key="cell" type="push" title="Check Releases" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WIg-Gy-UeI"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="system"/> diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 0d16039..4e75488 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -30,7 +30,9 @@ "seconds" = "seconds"; "set_password" = "Set Password…"; "set_rssi_threshold" = "Set Minimum RSSI…"; +"sleep_display" = "Turn Off Screen on Lock"; "timeout" = "No-Signal Timeout"; "unlock_rssi" = "Unlock RSSI"; "use_screensaver_to_lock" = "Use Screensaver to Lock"; "wake_on_proximity" = "Wake on Proximity"; +"wake_without_unlocking" = "Wake without Unlocking"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 89bdb4d..a871345 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>1.11-rc2</string> + <string>1.12.2</string> <key>CFBundleVersion</key> - <string>764</string> + <string>796</string> <key>LSApplicationCategoryType</key> <string>public.app-category.utilities</string> <key>LSMinimumSystemVersion</key> @@ -27,7 +27,7 @@ <key>NSBluetoothAlwaysUsageDescription</key> <string>BLEUnlock uses Bluetooth to detect devices.</string> <key>NSHumanReadableCopyright</key> - <string>Copyright © 2019-2021 Takeshi Sone. MIT Licensed.</string> + <string>Copyright © 2019-2022 Takeshi Sone. MIT Licensed.</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index f60522c..04d3823 100644 --- a/BLEUnlock/appleDeviceNames.swift +++ b/BLEUnlock/appleDeviceNames.swift @@ -150,4 +150,16 @@ let appleDeviceNames = [ "AudioAccessory1,1": "HomePod", "AudioAccessory1,2": "HomePod", "AudioAccessory5,1": "HomePod mini", + "iPad12,1": "iPad (9th generation)", + "iPad12,2": "iPad (9th generation)", + "iPad14,1": "iPad mini (6th generation)", + "iPad14,2": "iPad mini (6th generation)", + "Watch6,6": "Apple Watch Series 7", + "Watch6,7": "Apple Watch Series 7", + "Watch6,8": "Apple Watch Series 7", + "Watch6,9": "Apple Watch Series 7", + "iPhone14,2": "iPhone 13 Pro", + "iPhone14,3": "iPhone 13 Pro Max", + "iPhone14,4": "iPhone 13 mini", + "iPhone14,5": "iPhone 13", ] diff --git a/BLEUnlock/da.lproj/AboutBox.strings b/BLEUnlock/da.lproj/AboutBox.strings new file mode 100644 index 0000000..6a5c295 --- /dev/null +++ b/BLEUnlock/da.lproj/AboutBox.strings @@ -0,0 +1,11 @@ +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock version #{version}"; + +/* Class = "NSButtonCell"; title = "Check Releases"; ObjectID = "WIg-Gy-UeI"; */ +"WIg-Gy-UeI.title" = "Tjek opdateringer"; + +/* Class = "NSButtonCell"; title = "Visit Homepage"; ObjectID = "fPY-L3-Edh"; */ +"fPY-L3-Edh.title" = "Besøg hjemmesiden"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "Om BLEUnlock"; diff --git a/BLEUnlock/da.lproj/Localizable.strings b/BLEUnlock/da.lproj/Localizable.strings new file mode 100644 index 0000000..ccc259e --- /dev/null +++ b/BLEUnlock/da.lproj/Localizable.strings @@ -0,0 +1,38 @@ +"about" = "Om BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth er slået fra. Du skal slå den til for at få BLEUnlock til at virke."; +"cancel" = "Annuller"; +"closer" = "⬆Nærmere"; +"device" = "Enhed"; +"device_not_set" = "Enheden er ikke valgt"; +"disabled" = "Deaktivere"; +"enter_password" = "Indtast adgangskode for at låse låseskærmen op."; +"enter_rssi_threshold" = "Indtast mindste RSSI"; +"enter_rssi_threshold_info" = "Enheder, hvis RSSI er mindre end denne værdi, vil blive ignoreret under scanningen."; +"farther" = "⬇Længere væk"; +"launch_at_login" = "Start ved login"; +"lock_delay" = "Forsinkelse til låsning"; +"lock_now" = "Lås skærm nu"; +"lock_rssi" = "Lås RSSI"; +"minute" = "minut"; +"minutes" = "minutter"; +"not_detected" = "Signal ikke fundet"; +"notification_device_away" = "Enheden er væk"; +"notification_locked" = "Denne computer er blevet låst af BLEUnlock"; +"notification_lost_signal" = "Signalet er tabt"; +"notification_update_available" = "Opdatering er tilgængelig."; +"ok" = "OK"; +"passive_mode" = "Passiv tilstand"; +"password_info" = "Din adgangskode vil blive gemt sikkert i nøglering."; +"password_not_set" = "Der er ikke indstillet et kodeord."; +"pause_now_playing" = "Pause \"Afspilning nu\", mens den er låst"; +"quit" = "Afslut BLEUnlock"; +"scanning" = "Scanning…"; +"seconds" = "seconder"; +"set_password" = "Indstil adgangskode…"; +"set_rssi_threshold" = "Indstil mindste RSSI…"; +"sleep_display" = "Slå skærmen fra ved låsning"; +"timeout" = "Timeout for manglende signal"; +"unlock_rssi" = "Låse op RSSI"; +"use_screensaver_to_lock" = "Brug screensaver til at låse"; +"wake_on_proximity" = "Vågn op på nærhed"; +"wake_without_unlocking" = "Vågn op uden at låse op"; diff --git a/BLEUnlock/de.lproj/AboutBox.strings b/BLEUnlock/de.lproj/AboutBox.strings new file mode 100644 index 0000000..515dfef --- /dev/null +++ b/BLEUnlock/de.lproj/AboutBox.strings @@ -0,0 +1,4 @@ +"VLW-23-BX0.title" = "BLEUnlock Version #{version}"; +"hgX-Z1-8qq.title" = "Über BLEUnlock"; +"fPY-L3-Edh.title" = "Homepage"; +"WIg-Gy-UeI.title" = "Versionen"; diff --git a/BLEUnlock/de.lproj/Localizable.strings b/BLEUnlock/de.lproj/Localizable.strings new file mode 100644 index 0000000..c9d978e --- /dev/null +++ b/BLEUnlock/de.lproj/Localizable.strings @@ -0,0 +1,38 @@ +"about" = "Über BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth ist ausgeschalten. Muss eingeschalten sein, damit BLEUnlock funktioniert."; +"cancel" = "Abbruch"; +"closer" = "⬆näher"; +"device" = "Gerät"; +"device_not_set" = "Kein Gerät ausgewählt"; +"disabled" = "Ausgeschalten"; +"enter_password" = "Passwort eingeben, um den Sperrschirm zu entsperren."; +"enter_rssi_threshold" = "Minimum RSSI eingeben"; +"enter_rssi_threshold_info" = "Gerät mit geringerem RSSI als diesem Wert wird beim Scannen ignoriert."; +"farther" = "⬇weiter"; +"launch_at_login" = "Beim Login starten"; +"lock_delay" = "Sperrverzögerung"; +"lock_now" = "Jetzt sperren"; +"lock_rssi" = "RSSI Sperren"; +"minute" = "Minute"; +"minutes" = "Minuten"; +"not_detected" = "Kein Signal entdeckt"; +"notification_device_away" = "Gerät ist entfernt"; +"notification_locked" = "Dieses Gerät wurde durch BLEUnlock gesperrt"; +"notification_lost_signal" = "Signal ging verloren"; +"notification_update_available" = "Update verfügbar"; +"ok" = "OK"; +"passive_mode" = "Passiver Modus"; +"password_info" = "Das Passwort wurde sicher am Schlüsselbund gespeichert."; +"password_not_set" = "Kein Passwort gesetzt."; +"pause_now_playing" = "Pause \"Spielt jetzt\" während der Sperre"; +"quit" = "BLEUnlock beenden"; +"scanning" = "Scanning"; +"seconds" = "seconds"; +"set_password" = "Passwort setzen…"; +"set_rssi_threshold" = "Setze Minimum RSSI…"; +"sleep_display" = "Bildschirm beim Sperren ausschalten"; +"timeout" = "Kein-Signal Zeitablauf"; +"unlock_rssi" = "RSSI entsperren"; +"use_screensaver_to_lock" = "Den Bildschirmschoner verwenden zum Sperren"; +"wake_on_proximity" = "Aufwachen bei Annäherung"; +"wake_without_unlocking" = "Aufwachen ohne Entsperren"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 9a4f6cf..39910a7 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -30,7 +30,9 @@ "seconds" = "秒"; "set_password" = "パスワードを設定…"; "set_rssi_threshold" = "最小RSSIを設定…"; +"sleep_display" = "ロック時画面をスリープ"; "timeout" = "無信号タイムアウト"; "unlock_rssi" = "アンロック信号強度"; "use_screensaver_to_lock" = "スクリーンセーバーでロック"; "wake_on_proximity" = "画面スリープから復帰"; +"wake_without_unlocking" = "アンロックせずに画面復帰"; diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index 2f70ccb..add5e5b 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -1,5 +1,6 @@ #include "lowlevel.h" #include <IOKit/pwr_mgt/IOPMLib.h> +#include <IOKit/IOKitLib.h> void wakeDisplay(void) { @@ -7,9 +8,11 @@ void wakeDisplay(void) IOPMAssertionDeclareUserActivity(CFSTR("BLEUnlock"), kIOPMUserActiveLocal, &assertionID); } -bool lockScreen(void) +void sleepDisplay(void) { - // Go to lock screen by private API. Doesn't work in Sandbox. - extern int SACLockScreenImmediate(void); - return SACLockScreenImmediate() == 0; + io_registry_entry_t reg = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler"); + if (reg) { + IORegistryEntrySetCFProperty(reg, CFSTR("IORequestIdle"), kCFBooleanTrue); + IOObjectRelease(reg); + } } diff --git a/BLEUnlock/lowlevel.h b/BLEUnlock/lowlevel.h index eec0625..af1127e 100644 --- a/BLEUnlock/lowlevel.h +++ b/BLEUnlock/lowlevel.h @@ -2,7 +2,8 @@ #define lowlevel_h #include <stdbool.h> +void sleepDisplay(void); void wakeDisplay(void); -bool lockScreen(void); +int SACLockScreenImmediate(void); #endif /* lowlevel_h */ diff --git a/BLEUnlock/nb.lproj/AboutBox.strings b/BLEUnlock/nb.lproj/AboutBox.strings new file mode 100644 index 0000000..c9e4608 --- /dev/null +++ b/BLEUnlock/nb.lproj/AboutBox.strings @@ -0,0 +1,12 @@ + +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock version #{version}"; + +/* Class = "NSButtonCell"; title = "Check Releases"; ObjectID = "WIg-Gy-UeI"; */ +"WIg-Gy-UeI.title" = "Sjekk utgivelser"; + +/* Class = "NSButtonCell"; title = "Visit Homepage"; ObjectID = "fPY-L3-Edh"; */ +"fPY-L3-Edh.title" = "Besøk hjemmesiden"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "Om BLEUnlock"; diff --git a/BLEUnlock/nb.lproj/Localizable.strings b/BLEUnlock/nb.lproj/Localizable.strings new file mode 100644 index 0000000..37bc754 --- /dev/null +++ b/BLEUnlock/nb.lproj/Localizable.strings @@ -0,0 +1,38 @@ +"about" = "Om BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth er slått av. Slå den på for at BLEUnlock skal fungere."; +"cancel" = "Annullere"; +"closer" = "⬆Nærmere"; +"device" = "Enhet"; +"device_not_set" = "Enheten er ikke valgt"; +"disabled" = "Deaktivere"; +"enter_password" = "Skriv inn passord for å låse opp låseskjermen."; +"enter_rssi_threshold" = "Angi minimum RSSI"; +"enter_rssi_threshold_info" = "Enhet der RSSI er mindre enn denne verdien, ignoreres under skanning."; +"farther" = "⬇Lenger unna"; +"launch_at_login" = "Start ved innlogging"; +"lock_delay" = "Forsinkelse for låsing"; +"lock_now" = "Lås skjerm nå"; +"lock_rssi" = "Lås RSSI"; +"minute" = "minutt"; +"minutes" = "minutn"; +"not_detected" = "Finner ikke signal"; +"notification_device_away" = "Enheten er borte"; +"notification_locked" = "Denne datamaskinen er låst av BLEUnlock"; +"notification_lost_signal" = "Signalet går tapt"; +"notification_update_available" = "Oppdatering er tilgjengelig."; +"ok" = "OK"; +"passive_mode" = "Passiv Modus"; +"password_info" = "Passordet lagres sikkert i nøkkelringen."; +"password_not_set" = "Passord er ikke angitt."; +"pause_now_playing" = "Stans \"Avspilling nå\" mens den er låst"; +"quit" = "Slutte BLEUnlock"; +"scanning" = "kcanning…"; +"seconds" = "seconder"; +"set_password" = "Angi passord…"; +"set_rssi_threshold" = "Angi minimum RSSI…"; +"sleep_display" = "Slå av skjerm ved låsing"; +"timeout" = "Tidsavbrudd uten signal"; +"unlock_rssi" = "Låse opp RSSI"; +"use_screensaver_to_lock" = "Bruke skjermsparer til å låse"; +"wake_on_proximity" = "Vekk ved nærhet"; +"wake_without_unlocking" = "Våkne uten å låse opp"; diff --git a/BLEUnlock/sv.lproj/AboutBox.strings b/BLEUnlock/sv.lproj/AboutBox.strings new file mode 100644 index 0000000..96d18c1 --- /dev/null +++ b/BLEUnlock/sv.lproj/AboutBox.strings @@ -0,0 +1,13 @@ + +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock version #{version}"; + +/* Class = "NSButtonCell"; title = "Check Releases"; ObjectID = "WIg-Gy-UeI"; */ +"WIg-Gy-UeI.title" = "Observera updateringar"; + +/* Class = "NSButtonCell"; title = "Visit Homepage"; ObjectID = "fPY-L3-Edh"; */ +"fPY-L3-Edh.title" = "Besök hemsida"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "Om BLEUnlock"; + diff --git a/BLEUnlock/sv.lproj/Localizable.strings b/BLEUnlock/sv.lproj/Localizable.strings new file mode 100644 index 0000000..4ca0820 --- /dev/null +++ b/BLEUnlock/sv.lproj/Localizable.strings @@ -0,0 +1,38 @@ +"about" = "Om BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth är avstängt. För att BLEUnlock ska fungera måste du slå på det."; +"cancel" = "Avbryta"; +"closer" = "⬆Närmare"; +"device" = "Enhet"; +"device_not_set" = "Ingen enhet har valts"; +"disabled" = "Inaktivera"; +"enter_password" = "Ange lösenord för att låsa upp skärmen automatiskt."; +"enter_rssi_threshold" = "Ange minsta RSSI värde"; +"enter_rssi_threshold_info" = "Enheter vars RSSI-värde är lägre än detta kommer att ignoreras vid skanning efter en ny enhet."; +"farther" = "⬇Längre bort"; +"launch_at_login" = "Starta vid inloggning"; +"lock_delay" = "Fördröjning för låsning"; +"lock_now" = "Lås skärmen nu"; +"lock_rssi" = "Lås RSSI"; +"minute" = "minut"; +"minutes" = "minuter"; +"not_detected" = "Ingen enhet upptäcktes"; +"notification_device_away" = "Enheten är borta"; +"notification_locked" = "Den här datorn har låsts av BLEUnlock"; +"notification_lost_signal" = "Signalen är förlorad"; +"notification_update_available" = "En updatering finns tillgänglig."; +"ok" = "OK"; +"passive_mode" = "Passivt läge"; +"password_info" = "Ditt lösenord lagras säkert i nyckelringen."; +"password_not_set" = "Inget lösenordet har angetts."; +"pause_now_playing" = "Pausa \"Spela nu\" när låst"; +"quit" = "Avsluta BLEUnlock"; +"scanning" = "Skannar…"; +"seconds" = "sekunder"; +"set_password" = "Ange lösenord…"; +"set_rssi_threshold" = "Ange lägsta RSSI…"; +"sleep_display" = "Stäng av skärmen vid låsning"; +"timeout" = "Ingen-signal tidsgräns"; +"unlock_rssi" = "Upplåsnings RSSI"; +"use_screensaver_to_lock" = "Använd skärmsläckare för att låsa"; +"wake_on_proximity" = "Väck om i närheten"; +"wake_without_unlocking" = "Vakna utan att låsa upp"; diff --git a/BLEUnlock/tr.lproj/AboutBox.strings b/BLEUnlock/tr.lproj/AboutBox.strings new file mode 100644 index 0000000..d607635 --- /dev/null +++ b/BLEUnlock/tr.lproj/AboutBox.strings @@ -0,0 +1,12 @@ + +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock versiyon #{version}"; + +/* Class = "NSButtonCell"; title = "Check Releases"; ObjectID = "WIg-Gy-UeI"; */ +"WIg-Gy-UeI.title" = "Sürüm Kontrol et"; + +/* Class = "NSButtonCell"; title = "Visit Homepage"; ObjectID = "fPY-L3-Edh"; */ +"fPY-L3-Edh.title" = "Anasayfayı ziyaret et"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "BLEUnlock Hakkında"; diff --git a/BLEUnlock/tr.lproj/Localizable.strings b/BLEUnlock/tr.lproj/Localizable.strings new file mode 100644 index 0000000..1376de2 --- /dev/null +++ b/BLEUnlock/tr.lproj/Localizable.strings @@ -0,0 +1,38 @@ +"about" = "BLEUnlock Hakkında"; +"bluetooth_power_warn" = "Bluetooth Kapalı. BLEUnlock çalışması için Bluetooth u açın"; +"cancel" = "İptal"; +"closer" = "⬆Yakın"; +"device" = "Aygıt"; +"device_not_set" = "Aygıt seçilmedi"; +"disabled" = "Devredışı"; +"enter_password" = "Kilit ekranı açmak için şifrenizi girin"; +"enter_rssi_threshold" = "Minimum RSSI belirle"; +"enter_rssi_threshold_info" = "Eğer aygıt RSSI değeri bu değerin altında ise arama sırasında yok sayılacaktır."; +"farther" = "⬇Uzak"; +"launch_at_login" = "Başlangıçta çalıştır"; +"lock_delay" = "Kilitleme gecikmesi"; +"lock_now" = "Ekranı şimdi kilitle"; +"lock_rssi" = "Kilitleme RSSI değeri"; +"minute" = "dakika"; +"minutes" = "dakika"; +"not_detected" = "Sinyal tespit edilemedi"; +"notification_device_away" = "Aygıt uzakta"; +"notification_locked" = "Bu bilgisayar BLEUnlock tarafından kilitlenmiştir"; +"notification_lost_signal" = "Sinyal kayboldu"; +"notification_update_available" = "Güncelleme var"; +"ok" = "TAMAM"; +"passive_mode" = "Passif Mod"; +"password_info" = "Şifreniz güvenli bir şekilde anahtar zincirinde saklanacaktır."; +"password_not_set" = "Şifre belirlenmedi."; +"pause_now_playing" = "Kilitliyken \"Now Playing\" beklet"; +"quit" = "BLEUnlock'tan çıkış"; +"scanning" = "Taranıyor…"; +"seconds" = "saniye"; +"set_password" = "Şifre belirle…"; +"set_rssi_threshold" = "Minimum RSSI belirle"; +"sleep_display" = "Kilitli Ekranı Kapat"; +"timeout" = "Sinyal yok Zaman Aşımı"; +"unlock_rssi" = "Kilit açma RSSI"; +"use_screensaver_to_lock" = "Kilit ekranı için ekran kuruyucu kullan"; +"wake_on_proximity" = "Uyandırma yakınlığı"; +"wake_without_unlocking" = "Kilidi açmadan uyanın"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index ae958fa..c8a0303 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -30,7 +30,9 @@ "seconds" = "秒钟"; "set_password" = "设置密码…"; "set_rssi_threshold" = "设置最小RSSI…"; +"sleep_display" = "锁定时关闭屏幕"; "timeout" = "无信号超时"; "unlock_rssi" = "解锁 RSSI"; "use_screensaver_to_lock" = "用屏保来锁定它"; "wake_on_proximity" = "靠近唤醒"; +"wake_without_unlocking" = "唤醒时不需解锁"; diff --git a/README.ja.md b/README.ja.md index fb69671..314da5d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -75,6 +75,11 @@ BLEデバイスが遠ざかってから実際にロックをするまでの時 ロック中にBLEデバイスが近づいてきたとき、ディスプレイをスリープ画面から復帰させます。 +### アンロックせずに画面復帰 + +ディスプレイがスリープから復帰したとき(「画面スリープから復帰」による自動または手動に関わらず)、Macをアンロックしません。 +これはApple WatchやTouch IDなどのよりセキュアなアンロック機構を使用したい場合に、「画面スリープから復帰」と共に使用すると素早く画面にアクセスできます。 + ### ロック中 "再生中" を一時停止 ロック時に音楽や動画の再生を一時停止し、ロック解除時に再開します。対応しているのはApple Music, QuickTime Player, Spotifyなど、*再生中*ウィジェットやキーボードの⏯キーで制御できるアプリです。 @@ -83,6 +88,10 @@ BLEデバイスが遠ざかってから実際にロックをするまでの時 ロック時にスクリーンセーバーを起動します。このオプションが正しく動作するには、*セキュリティとプライバシー*システム環境パネルで*スリープとスクリーンセーバーの解除にパスワードを要求*を*すぐに*に設定する必要があります。 +### ロック時画面をスリープ + +ロック時にロック画面を表示せずディスプレイをスリープします。 + ### パスワードを設定... Macのログインパスワードを変更したときに、このオプションを使って変更してください。 @@ -120,6 +129,15 @@ Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取 メニューバーもしくはコントロールセンターにあるBluetoothアイコンをShift+Option+クリックし、表示される*Bluetoothモジュールのリセット*をしてみてください。 +macOS 12 Montereyでは、上記のオプションはなくなっています。 +代わりに、ターミナルで以下のコマンドを入力してBluetoothモジュールをリセットしてください。 + +``` +sudo pkill bluetoothd +``` + +このコマンドは、ログインパスワードを要求します。 + それでも問題が繰り返し起こる場合、*パッシブモード*をオンにしてください。 ## MACアドレスについて @@ -210,20 +228,20 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y ## FUNDING -1.9.0以降のバイナリリリースはAppleによって公証されていません。 -このため、**起動するには右クリックして開き、Keychain等のパーミッションを再認証する必要があります**。 - -現在私の会社ではMacやiOSのアプリを開発していないため、有料のApple Developerアカウントにアクセスできません。 +Apple Developer Programの年間費用は、寄付金で賄われています。 -このアプリを気に入っていただけたら、Apple Developer Programの費用を自分で払うことができるよう、[Buy Me a Coffee](https://www.buymeacoffee.com/tsone) もしくは [PayPal.Me](https://paypal.me/takeshisone) で寄付をいただけるとありがたいです。 +もしこのアプリを気に入っていただけたら、継続できるよう、[Buy Me a Coffee](https://www.buymeacoffee.com/tsone) もしくは [PayPal.Me](https://paypal.me/takeshisone) で寄付をしていただけるとありがたいです。 ## クレジット -- peiit: 中国語の翻訳 -- wenmin-wu: 最小RSSIと移動平均 -- stephengroat: CI -- joeyhoer: Homebrew Cask -- Skyearn: Big Surスタイルのアイコン +- [peiit](https://github.com/peiit): 中国語のローカリゼーション +- [wenmin-wu](https://github.com/wenmin-wu): 最小RSSIと移動平均 +- [stephengroat](https://github.com/stephengroat): CI +- [joeyhoer](https://github.com/joeyhoer): Homebrew Cask +- [Skyearn](https://github.com/Skyearn): Big Surスタイルのアイコン +- [cyberclaus](https://github.com/cyberclaus): ドイツ語, スウェーデン語, ノルウェー語 (Bokmål) およびデンマーク語のローカリゼーション +- [alonewolfx2](https://github.com/alonewolfx2): トルコ語のローカリゼーション +- [wernjie](https://github.com/wernjie): アンロックせずに画面復帰 アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 @@ -231,4 +249,4 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y MIT -Copyright © 2019-2021 Takeshi Sone. +Copyright © 2019-2022 Takeshi Sone. diff --git a/README.md b/README.md index 6d8341b..cedbed6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # BLEUnlock +## Please note that I don't distribute this app on the Mac App Store. You can find it here for free! +   [](https://www.buymeacoffee.com/tsone) @@ -30,7 +32,7 @@ This document is also available in [Japanese (日本語版はこちら)](README. ### Using Homebrew Cask ``` -$ brew install bleunlock +brew install bleunlock ``` ### Manual installation @@ -67,8 +69,10 @@ Lock RSSI | Bluetooth signal strength to lock. Smaller value indicates that the Delay to Lock | Duration of time before it locks the Mac when it detects that the BLE device is away. If the BLE device comes closer within that time, no lock will occur. No-Signal Timeout | Time between last signal reception and locking. If you experience frequent "Signal is lost" locking, increase this value. Wake on Proximity | Wakes up the display from sleep when the BLE device approaches while locking. +Wake without Unlocking | BLEUnlock will not unlock the Mac when the display wakes up from sleep, whether automatically via "Wake on Proximity" or manually. This allows for compatibility with the macOS built-in unlock with Apple Watch feature (which can operate immediately after BLEUnlock wakes the screen), or if you just prefer the lock screen to appear more quickly but don't want it to auto-unlock. Pause "Now Playing" while Locked | On lock/unlock, BLEUnlock pauses/unpauses playback of music or video (including Apple Music, QuickTime Player and Spotify) that is controlled by *Now Playing* widget or the ⏯ key on the keyboard. Use Screensaver to Lock | If this option is set, BLEUnlock launches screensaver instead of locking. For this option to work properly, you need to set *Require password **immediately** after sleep or screen saver begins* option in *Security & Privacy* preference pane. +Turn Off Screen on Lock | Turn off the display immediately when locking. Set Password... | If you changed your login password, use this. Passive Mode | By default it actively tries to connect to the BLE device and read the RSSI. Most of the time, the default is recommended and works stably. However, if you are using other Bluetooth things like keyboard, mouse, track pad or most notably Bluetooth Personal Hotspot, the default mode may interfere with each other. 2.4GHz WiFi may interfere as well. If you are experiencing instability of Bluetooth, turn on Passive Mode. Launch at Login | Launches BLEUnlock when you login. @@ -100,6 +104,15 @@ Or try *Passive Mode*. Firstly, Shift + Option + Click the Bluetooth icon in the menubar or Control Center, then click *Reset the Bluetooth module*. +In macOS 12 Monterey, this option is no longer available. +Instead, type the command below in Terminal to reset the Bluetooth module: + +``` +sudo pkill bluetoothd +``` + +This command will ask your login password. + If the problem persists, turn on *Passive Mode*. ## Notes on MAC address @@ -192,20 +205,22 @@ Giving permission to this app resolves the problem. ## Funding -Since version 1.9.0, binary releases are not notarized by Apple. -Because of this, **you have to right-click and Open to start, and you have to re-authorize Keychain and other permissions.** +The annual Apple Developer Program fee is funded by donations. -My company is not developing iOS or Mac app at the moment, so I don't have access to a paid Apple Developer account. - -If you like this app, I'd appreciate it if you could make a donation via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) so that I can pay for the Apple Developer Program myself! +If you like this app, I'd appreciate it if you could make a donation via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal Me](https://www.paypal.com/paypalme/my/profile) so I can keep up. ## Credits -- peiit: Chinese translation -- wenmin-wu: Minimum RSSI and moving average -- stephengroat: CI -- joeyhoer: Homebrew Cask -- Skyearn: Big Sur style icon +- [peiit](https://github.com/peiit): Chinese translation +- [wenmin-wu](https://github.com/wenmin-wu): Minimum RSSI and moving average +- [stephengroat](https://github.com/stephengroat): CI +- [joeyhoer](https://github.com/joeyhoer): Homebrew Cask +- [Skyearn](https://github.com/Skyearn): Big Sur style icon +- [cyberclaus](https://github.com/cyberclaus): German, Swedish, Norwegian (Bokmål) and Danish localizations +- [alonewolfx2](https://github.com/alonewolfx2): Turkish localization +- [wernjie](https://github.com/wernjie): Wake without Unlocking +- [tokfrans03](https://github.com/tokfrans03): Language fixes + Icons are based on SVGs downloaded from materialdesignicons.com. They are originally designed by Google LLC and licensed under Apache License version 2.0. @@ -214,4 +229,4 @@ They are originally designed by Google LLC and licensed under Apache License ver MIT -Copyright © 2019-2021 Takeshi Sone. +Copyright © 2019-2022 Takeshi Sone. diff --git a/apple-device-names b/apple-device-names index 12237d5..c2d1db9 100755 --- a/apple-device-names +++ b/apple-device-names @@ -1,26 +1,32 @@ #!/usr/bin/env python3 import re, json +from glob import glob from subprocess import Popen, PIPE -p = Popen([ - 'plutil', - '-convert', 'json', - '/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/MobileDevices.bundle/Contents/Info.plist', - '-o', '-' -], stdout=PIPE) - -data = json.load(p.stdout) mapping = {} -for item in data['UTExportedTypeDeclarations']: - if 'UTTypeTagSpecification' not in item: continue - for code in item['UTTypeTagSpecification']['com.apple.device-model-code']: - if re.match(r'^[a-zA-Z]+\d+,\d+$', code): - name = item['UTTypeDescription'] - name = re.sub(r'Model A?\d+(, A?\d+)*', '', name) - name = re.sub(r' *?\)', ')', name) - name = re.sub(r' \(\)', '', name) - mapping[code] = name +def parse_info(filename): + p = Popen([ + 'plutil', + '-convert', 'json', + f, + '-o', '-' + ], stdout=PIPE) + + data = json.load(p.stdout) + + for item in data['UTExportedTypeDeclarations']: + if 'UTTypeTagSpecification' not in item: continue + for code in item['UTTypeTagSpecification']['com.apple.device-model-code']: + if re.match(r'^[a-zA-Z]+\d+,\d+$', code): + name = item['UTTypeDescription'] + name = re.sub(r'Model A?\d+(, A?\d+)*', '', name) + name = re.sub(r' *?\)', ')', name) + name = re.sub(r' \(\)', '', name) + mapping[code] = name + +for f in glob('/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/MobileDevices*.bundle/Contents/Info.plist'): + parse_info(f) print('let appleDeviceNames = [') for code, name in mapping.items(): diff --git a/release b/release index d73a86b..5641551 100755 --- a/release +++ b/release @@ -2,7 +2,12 @@ set -o pipefail -USERNAME=takeshisone@gmail.com +if [ ! "$PASSWORD" ]; then + echo set app-specific password in PASSWORD env var + exit 1 +fi + +USERNAME=takeshi.sone@gmail.com TEAM=42LGPQYC7M APPNAME=BLEUnlock @@ -25,21 +30,20 @@ notarize() { rm -f $TMPDIR/upload.zip ditto -c -k --keepParent $app $TMPDIR/upload.zip - xcrun altool --notarize-app --primary-bundle-id $id --username $USERNAME \ - --password "@keychain:AC_PASSWORD" --file $TMPDIR/upload.zip \ - --asc-provider $TEAM 2>&1 | tee $TMPDIR/altool.log - uuid=$(awk '/^RequestUUID/ { print $3 }' $TMPDIR/altool.log) + xcrun notarytool submit --apple-id $USERNAME --password "$PASSWORD" $TMPDIR/upload.zip \ + --team-id $TEAM 2>&1 | tee $TMPDIR/altool.log + uuid=$(awk '/ id:/ { print $2; exit }' $TMPDIR/altool.log) while true; do - sleep 5 - xcrun altool --notarization-info $uuid --username $USERNAME \ - --password "@keychain:AC_PASSWORD" --asc-provider $TEAM 2>&1 | + sleep 2 + xcrun notarytool info $uuid --apple-id $USERNAME \ + --password "$PASSWORD" --team-id $TEAM 2>&1 | tee $TMPDIR/altool.log || true - status=$(grep Status: $TMPDIR/altool.log | sed 's/^.*Status: *//') - if [ "$status" ] && [ "$status" != "in progress" ]; then + status=$(grep status: $TMPDIR/altool.log | sed 's/^.*status: *//') + if [ "$status" ] && [ "$status" != "In Progress" ]; then break fi done - if [ "$status" != "success" ]; then + if [ "$status" != "Accepted" ]; then exit 1 fi xcrun stapler staple $app