From c76b623ccdf386dc7f5663aa315a1186754dab63 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 28 Apr 2019 17:26:29 +0900 Subject: [PATCH 001/183] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 436e4437..1e62c420 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - Unlocks Mac for you when the device is near Mac, no need to enter password again - Optionally wakes from display sleep state - Password is securely stored in Keychain +- Uses Hardened Runtime and notarized by Apple ## Installation -Unzip and copy BLEUnlock to Applications folder. +Download zip file from [releases](https://github.com/ts1/BLEUnlock/releases), unzip and copy BLEUnlock to Applications folder. On the first launch, it asks your login password, which is required to unlock the lock screen. From cf926a8080eff05ea9f8db7180cdf937682ff0b0 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 28 Apr 2019 20:24:57 +0900 Subject: [PATCH 002/183] Update README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e62c420..f32f7397 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de ## Installation -Download zip file from [releases](https://github.com/ts1/BLEUnlock/releases), unzip and copy BLEUnlock to Applications folder. +Download zip file from [releases](https://github.com/ts1/BLEUnlock/releases), +unzip and copy to Applications folder. On the first launch, it asks your login password, which is required to unlock the lock screen. @@ -24,6 +25,10 @@ Then it asks for permission for Accessibility. In System Preferences unlock by clicking lock icon and turn BLEUnlock on. It is also required to unlock the lock screen. +Finally, from the status bar icon, select "Device". +It starts scanning nearby BLE devices. +Select your device, and you're done. + ## Troubleshooting If it fails to unlock, check BLEUnlock is turned on in "System Preferences" → "Security & Privacy" → "Privacy" → "Accessibility". From dcc194ecc0777f5e189ac4b5700046981e0870ba Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 11:58:18 +0900 Subject: [PATCH 003/183] Let scan timer expire while not scanning --- BLEUnlock/BLE.swift | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 30034ff4..1901a17f 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -77,16 +77,10 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func startScanning() { scanMode = true - for device in devices.values { - resetScanTimer(device: device) - } } func stopScanning() { scanMode = false - for device in devices.values { - device.scanTimer?.invalidate() - } } func startMonitor(uuid: UUID) { @@ -177,7 +171,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func resetScanTimer(device: Device) { device.scanTimer?.invalidate() - device.scanTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false, block: { _ in + device.scanTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in self.delegate?.removeDevice(device: device) if let p = device.peripheral { self.centralMgr.cancelPeripheralConnection(p) From 0e6b44603b35ab157e87d3badd058b2a95ec3ef4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 11:58:51 +0900 Subject: [PATCH 004/183] Remove extraneous permission dialog System dialog seems always work when outside sandbox. --- BLEUnlock.xcodeproj/project.pbxproj | 21 +++++++++++++++++++++ BLEUnlock/AppDelegate.swift | 9 ++------- BLEUnlock/Base.lproj/Localizable.strings | 2 -- BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 2 -- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 9760ab46..923f26d6 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -163,6 +163,7 @@ 3DD4B64D226C1C3200451B7B /* Frameworks */, 3DD4B64E226C1C3200451B7B /* Resources */, 3D600A5922701E5D0068FB7B /* CopyFiles */, + 3D7C0F8022769F1F005A4C43 /* ShellScript */, ); buildRules = ( ); @@ -253,6 +254,26 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 3D7C0F8022769F1F005A4C43 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 3D600A3C22701A5A0068FB7B /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 455a1d4a..7eab45da 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -315,7 +315,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate statusItem.menu = mainMenu } - func checkAccessibility() -> Bool { + func checkAccessibility() { let key = kAXTrustedCheckOptionPrompt.takeRetainedValue() as String if (!AXIsProcessTrustedWithOptions([key: true] as CFDictionary)) { // Sometimes Prompt option above doesn't work. @@ -324,10 +324,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate // "Fn" key down and up CGEvent(keyboardEventSource: src, virtualKey: 63, keyDown: true)?.post(tap: .cghidEventTap) CGEvent(keyboardEventSource: src, virtualKey: 63, keyDown: false)?.post(tap: .cghidEventTap) - return false } - return true - } func applicationDidFinishLaunching(_ aNotification: Notification) { @@ -351,9 +348,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate if fetchPassword() == nil { askPassword() } - if (!checkAccessibility()) { - errorModal(t("no-accessibility"), info: t("allow-accessibility")) - } + checkAccessibility() } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index c8e08595..75f6eb45 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -8,5 +8,3 @@ "error_lock_screen" = "Failed to lock screen"; "error_unlock_screen" = "Failed to unlock screen"; "Not detected" = "Signal not detected"; -"no-accessibility" = "BLEUnlock uses Accessibility feature to unlock your Mac."; -"allow-accessibility" = "Please allow BLEUnlock to access Accessibility."; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index c17b005f..5ec1f3d1 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 6 + 7 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index c9fe5c31..db2e97d1 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -19,5 +19,3 @@ "error_lock_screen" = "画面をロックできませんでした"; "error_unlock_screen" = "画面ロックを解除できませんでした"; "Device not set" = "デバイス未選択"; -"no-accessibility" = "BLEUnlockはMacのロックを解除するためにアクセシビリティ機能を使います。"; -"allow-accessibility" = "BLEUnlockを許可してください"; From 807c9a1e154a56c8ad1bd0a17b991979e9b6d690 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 12:17:54 +0900 Subject: [PATCH 005/183] Update README --- BLEUnlock/Info.plist | 2 +- README.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 5ec1f3d1..1186db30 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 7 + 8 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/README.md b/README.md index f32f7397..ec5ccf5b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - Password is securely stored in Keychain - Uses Hardened Runtime and notarized by Apple +## Requirements + +- Mac supporting Bluetooth 4.0 and up +- macOS 10.13 (High Sierra) and up +- iPhone 4s and up, Apple Watch (all), or other BLE device + ## Installation Download zip file from [releases](https://github.com/ts1/BLEUnlock/releases), From e20371e9ec4dd6a2d74c9658552eeb70c9d135ad Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 12:24:55 +0900 Subject: [PATCH 006/183] Release 1.1 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 1186db30..9d1d343b 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.1 CFBundleVersion - 8 + 9 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From a3d7ab7374cda0ec658242900ee0c22892e7c384 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 12:47:36 +0900 Subject: [PATCH 007/183] README: Add notarized image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec5ccf5b..5086bd8f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - Unlocks Mac for you when the device is near Mac, no need to enter password again - Optionally wakes from display sleep state - Password is securely stored in Keychain -- Uses Hardened Runtime and notarized by Apple +- Uses Hardened Runtime and notarized by Apple ![](https://i.imgur.com/i9Rj44q.png) ## Requirements From 514137031d4033fc3c9a1b262bfd6e7c62cfbf21 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 15:22:30 +0900 Subject: [PATCH 008/183] Add iTunes control --- BLEUnlock/AppDelegate.swift | 49 ++++++++++++++++++++++++++ BLEUnlock/BLEUnlock.entitlements | 5 ++- BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + README.md | 7 ++-- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 7eab45da..47fc0e53 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -31,6 +31,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var sleeping = false var connected = false var userNotification: NSUserNotification? + var iTunesWasPlaying = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -106,7 +107,38 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate NSUserNotificationCenter.default.scheduleNotification(un) userNotification = un } + + func runAppleScript(_ script: String) -> NSAppleEventDescriptor? { + guard let scriptObject = NSAppleScript(source: script) else { return nil } + var error: NSDictionary? + let output = scriptObject.executeAndReturnError(&error) + if let e = error { + debugPrint(e) + return nil + } + return output + } + + func isItunesPlaying() -> Bool { + let result = runAppleScript("tell application \"iTunes\" to return player state") + return result?.stringValue == "kPSP" + } + func pauseItunes() { + guard prefs.bool(forKey: "pauseItunes") else { return } + iTunesWasPlaying = isItunesPlaying() + if iTunesWasPlaying { + _ = runAppleScript("tell application \"iTunes\" to pause") + } + } + + func playItunes() { + guard prefs.bool(forKey: "pauseItunes") else { return } + if iTunesWasPlaying { + _ = runAppleScript("tell application \"iTunes\" to play") + } + } + func updatePresence(presence: Bool, reason: String) { if presence { if let un = userNotification { @@ -119,6 +151,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } unlockScreen() } else { + pauseItunes() if lockScreen() { self.notifyUser(reason) } else { @@ -153,6 +186,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate if locked == 1 { print("Entering password") fakeKeyStrokes(password) + playItunes() } } } @@ -281,6 +315,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate SMLoginItemSetEnabled(Bundle.main.bundleIdentifier! + ".Launcher" as CFString, launchAtLogin) } + @objc func togglePauseItunes(_ menuItem: NSMenuItem) { + let pauseItunes = !prefs.bool(forKey: "pauseItunes") + prefs.set(pauseItunes, forKey: "pauseItunes") + menuItem.state = pauseItunes ? .on : .off + if pauseItunes { + _ = isItunesPlaying() // Show permission dialog + } + } + func constructMenu() { monitorMenuItem = mainMenu.addItem(withTitle: t("Device not set"), action: nil, keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) @@ -305,6 +348,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate if prefs.bool(forKey: "wakeOnProximity") { item.state = .on } + + item = mainMenu.addItem(withTitle: t("Pause iTunes while locked"), action: #selector(togglePauseItunes), keyEquivalent: "") + if prefs.bool(forKey: "pauseItunes") { + item.state = .on + } + mainMenu.addItem(withTitle: t("Set password..."), action: #selector(askPassword), keyEquivalent: "") item = mainMenu.addItem(withTitle: t("Launch at login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") diff --git a/BLEUnlock/BLEUnlock.entitlements b/BLEUnlock/BLEUnlock.entitlements index 0c67376e..49ad0bb0 100644 --- a/BLEUnlock/BLEUnlock.entitlements +++ b/BLEUnlock/BLEUnlock.entitlements @@ -1,5 +1,8 @@ - + + com.apple.security.automation.apple-events + + diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 9d1d343b..8208eb29 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.1 CFBundleVersion - 9 + 24 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index db2e97d1..33294c0e 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -19,3 +19,4 @@ "error_lock_screen" = "画面をロックできませんでした"; "error_unlock_screen" = "画面ロックを解除できませんでした"; "Device not set" = "デバイス未選択"; +"Pause iTunes while locked" = "ロック中iTunesを一時停止する"; diff --git a/README.md b/README.md index 5086bd8f..27302ce7 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,15 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - Locks Mac when the specified device is away from Mac - Unlocks Mac for you when the device is near Mac, no need to enter password again - Optionally wakes from display sleep state +- Optionally pauses/unpauses iTunes when you're away and back - Password is securely stored in Keychain - Uses Hardened Runtime and notarized by Apple ![](https://i.imgur.com/i9Rj44q.png) ## Requirements -- Mac supporting Bluetooth 4.0 and up -- macOS 10.13 (High Sierra) and up -- iPhone 4s and up, Apple Watch (all), or other BLE device +- Mac supporting Bluetooth 4.0 or later +- macOS 10.13 (High Sierra) or later +- iPhone 4s or later, Apple Watch (all), or other BLE device ## Installation From 44365fa4a15ca308bcc09a137b9562a651b1de54 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 29 Apr 2019 17:58:18 +0900 Subject: [PATCH 009/183] Release 1.2 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8208eb29..01bc912e 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleVersion - 24 + 26 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 5918c96e6adf5a16c9fe1b7dcd5511addcb6db2a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 30 Apr 2019 00:23:48 +0900 Subject: [PATCH 010/183] Translations refactoring and refinement --- BLEUnlock.xcodeproj/project.pbxproj | 4 +-- BLEUnlock/AppDelegate.swift | 38 +++++++++++----------- BLEUnlock/Base.lproj/InfoPlist.strings | 1 + BLEUnlock/Base.lproj/Localizable.strings | 28 +++++++++++------ BLEUnlock/Info.plist | 2 +- BLEUnlock/en.lproj/InfoPlist.strings | 1 - BLEUnlock/en.lproj/Localizable.strings | 7 ----- BLEUnlock/en.lproj/MainMenu.strings | 1 - BLEUnlock/ja.lproj/InfoPlist.strings | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 40 +++++++++++------------- README.md | 6 ++-- 11 files changed, 64 insertions(+), 66 deletions(-) create mode 100644 BLEUnlock/Base.lproj/InfoPlist.strings delete mode 100644 BLEUnlock/en.lproj/InfoPlist.strings delete mode 100644 BLEUnlock/en.lproj/Localizable.strings delete mode 100644 BLEUnlock/en.lproj/MainMenu.strings diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 923f26d6..2a2a0131 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3D1B377B22772B740024E2E0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 3D600A36226CC1A40068FB7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; 3D600A38226CC1AA0068FB7B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; @@ -46,7 +47,6 @@ 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = appleDeviceNames.swift; sourceTree = ""; }; 3DC0BC6922728FF1001145EC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3DC0BC6E22729050001145EC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; - 3DC0BC7022729055001145EC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 3DD4B650226C1C3200451B7B /* BLEUnlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BLEUnlock.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3DD4B653226C1C3200451B7B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 3DD4B658226C1C3400451B7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -318,7 +318,7 @@ isa = PBXVariantGroup; children = ( 3DC0BC6E22729050001145EC /* ja */, - 3DC0BC7022729055001145EC /* en */, + 3D1B377B22772B740024E2E0 /* Base */, ); name = InfoPlist.strings; sourceTree = ""; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 47fc0e53..73e9da2f 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -86,7 +86,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate statusItem.button?.image = NSImage(named: "StatusBarConnected") } } else { - monitorMenuItem?.title = t("Not detected") + monitorMenuItem?.title = t("not_detected") if (connected) { connected = false statusItem.button?.image = NSImage(named: "StatusBarDisconnected") @@ -98,11 +98,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate let un = NSUserNotification() un.title = "BLEUnlock" if reason == "lost" { - un.subtitle = t("notification_lock_reason_lost_signal") + un.subtitle = t("notification_lost_signal") } else if reason == "away" { - un.subtitle = t("notification_lock_reason_device_away") + un.subtitle = t("notification_device_away") } - un.informativeText = t("notification_title_locked") + un.informativeText = t("notification_locked") un.deliveryDate = Date().addingTimeInterval(1) NSUserNotificationCenter.default.scheduleNotification(un) userNotification = un @@ -221,7 +221,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } func monitorDevice(uuid: UUID) { - monitorMenuItem?.title = t("Not detected") + monitorMenuItem?.title = t("not_detected") ble.startMonitor(uuid: uuid) } @@ -281,9 +281,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func askPassword() { let msg = NSAlert() msg.addButton(withTitle: "OK") - msg.addButton(withTitle: t("Cancel")) - msg.messageText = t("enter password") - msg.informativeText = t("password is safe") + msg.addButton(withTitle: t("cancel")) + msg.messageText = t("enter_password") + msg.informativeText = t("password_info") let txt = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 260, height: 20)) msg.accessoryView = txt @@ -325,42 +325,42 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } func constructMenu() { - monitorMenuItem = mainMenu.addItem(withTitle: t("Device not set"), action: nil, keyEquivalent: "") + monitorMenuItem = mainMenu.addItem(withTitle: t("device_not_set"), action: nil, keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) var item: NSMenuItem - item = mainMenu.addItem(withTitle: t("Device"), action: nil, keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("device"), action: nil, keyEquivalent: "") item.submenu = deviceMenu deviceMenu.delegate = self - deviceMenu.addItem(withTitle: t("Scanning..."), action: nil, keyEquivalent: "") + deviceMenu.addItem(withTitle: t("scanning"), action: nil, keyEquivalent: "") - let proximityItem = mainMenu.addItem(withTitle: t("Proximity RSSI"), action: nil, keyEquivalent: "") + let proximityItem = mainMenu.addItem(withTitle: t("proximity_rssi"), action: nil, keyEquivalent: "") proximityItem.submenu = proximityMenu - proximityMenu.addItem(withTitle: t("⬆Closer"), action: nil, keyEquivalent: "") + proximityMenu.addItem(withTitle: t("closer"), action: nil, keyEquivalent: "") for proximity in stride(from: -50, to: -100, by: -10) { let item = proximityMenu.addItem(withTitle: String(format: "%ddBm", proximity), action: #selector(setProximity), keyEquivalent: "") item.tag = proximity } - proximityMenu.addItem(withTitle: t("⬇Farther"), action: nil, keyEquivalent: "") + proximityMenu.addItem(withTitle: t("farther"), action: nil, keyEquivalent: "") proximityMenu.delegate = self - item = mainMenu.addItem(withTitle: t("Wake on proximity"), action: #selector(toggleWakeOnProximity), keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("wake_on_proximity"), action: #selector(toggleWakeOnProximity), keyEquivalent: "") if prefs.bool(forKey: "wakeOnProximity") { item.state = .on } - item = mainMenu.addItem(withTitle: t("Pause iTunes while locked"), action: #selector(togglePauseItunes), keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("pause_itunes"), action: #selector(togglePauseItunes), keyEquivalent: "") if prefs.bool(forKey: "pauseItunes") { item.state = .on } - mainMenu.addItem(withTitle: t("Set password..."), action: #selector(askPassword), keyEquivalent: "") + mainMenu.addItem(withTitle: t("set_password"), action: #selector(askPassword), keyEquivalent: "") - item = mainMenu.addItem(withTitle: t("Launch at login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("launch_at_login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") item.state = prefs.bool(forKey: "launchAtLogin") ? .on : .off mainMenu.addItem(NSMenuItem.separator()) - mainMenu.addItem(withTitle: t("Quit BLEUnlock"), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") + mainMenu.addItem(withTitle: t("quit"), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") statusItem.menu = mainMenu } diff --git a/BLEUnlock/Base.lproj/InfoPlist.strings b/BLEUnlock/Base.lproj/InfoPlist.strings new file mode 100644 index 00000000..03f48249 --- /dev/null +++ b/BLEUnlock/Base.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses iTunes on lock/unlock."; diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 75f6eb45..35c9c732 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -1,10 +1,18 @@ -"enter password" = "Enter password to unlock the lock screen."; -"password is safe" = "Your password will be securely stored in Keychain."; -"notification_title_locked" = "Your Mac has been locked by BLEUnlock"; -"notification_lock_reason_lost_signal" = "Signal is lost"; -"notification_lock_reason_device_away" = "Device is away"; -"error_get_permission" = "Failed to get permission to lock screen"; -"not_permitted" = "Not permitted"; -"error_lock_screen" = "Failed to lock screen"; -"error_unlock_screen" = "Failed to unlock screen"; -"Not detected" = "Signal not detected"; +"not_detected" = "Signal not detected"; +"notification_lost_signal" = "Signal is lost"; +"notification_device_away" = "Device is away"; +"notification_locked" = "This computer has been locked by BLEUnlock"; +"cancel" = "Cancel"; +"enter_password" = "Enter password to unlock the lock screen."; +"password_info" = "Your password will be securely stored in Keychain."; +"device_not_set" = "Device not selected"; +"device" = "Device"; +"scanning" = "Scanning…"; +"proximity_rssi" = "Proximity Threshold"; +"closer" = "⬆Closer"; +"farther" = "⬇Farther"; +"wake_on_proximity" = "Wake on proximity"; +"pause_itunes" = "Pause iTunes while locked"; +"set_password" = "Set password…"; +"launch_at_login" = "Launch at login"; +"quit" = "Quit BLEUnlock"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 01bc912e..686739f7 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2 CFBundleVersion - 26 + 36 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/en.lproj/InfoPlist.strings b/BLEUnlock/en.lproj/InfoPlist.strings deleted file mode 100644 index 655302b2..00000000 --- a/BLEUnlock/en.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -"NSAppleEventsUsageDescription" = "BLEUnlock requires permission to lock screen."; diff --git a/BLEUnlock/en.lproj/Localizable.strings b/BLEUnlock/en.lproj/Localizable.strings deleted file mode 100644 index 2ccc1722..00000000 --- a/BLEUnlock/en.lproj/Localizable.strings +++ /dev/null @@ -1,7 +0,0 @@ -/* - Localizable.strings - BLEUnlock - - Created by Takeshi Sone on 2019/04/22. - Copyright © 2019 Takeshi Sone. All rights reserved. -*/ diff --git a/BLEUnlock/en.lproj/MainMenu.strings b/BLEUnlock/en.lproj/MainMenu.strings deleted file mode 100644 index 8b137891..00000000 --- a/BLEUnlock/en.lproj/MainMenu.strings +++ /dev/null @@ -1 +0,0 @@ - diff --git a/BLEUnlock/ja.lproj/InfoPlist.strings b/BLEUnlock/ja.lproj/InfoPlist.strings index 8f471e84..1478220c 100644 --- a/BLEUnlock/ja.lproj/InfoPlist.strings +++ b/BLEUnlock/ja.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "画面をロックするために許可が必要です。"; +"NSAppleEventsUsageDescription" = "ロック/アンロック時に iTunes の一時停止/再生を行います。"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 33294c0e..61ae2c6e 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -1,22 +1,18 @@ -"Device" = "デバイス"; -"Not detected" = "信号未検出"; -"Scanning..." = "スキャン中..."; -"Proximity RSSI" = "信号強度しきい値"; -"⬆Closer" = "⬆近い"; -"⬇Farther" = "⬇遠い"; -"Wake on proximity" = "画面スリープから復帰させる"; -"Set password..." = "パスワードを設定..."; -"Launch at login" = "ログイン時に起動"; -"Quit BLEUnlock" = "BLEUnlockを終了"; -"enter password" = "ロック画面を解除するためのパスワードを入力してください。"; -"password is safe" = "パスワードはキーチェーンに安全に保存されます。"; -"Cancel" = "キャンセル"; -"notification_title_locked" = "MacはBLEUnlockによってロックされました"; -"notification_lock_reason_lost_signal" = "デバイスからの信号がありません"; -"notification_lock_reason_device_away" = "デバイスがMacから離れました"; -"error_get_permission" = "画面をロックする許可が得られませんでした"; -"not_permitted" = "許可がありません"; -"error_lock_screen" = "画面をロックできませんでした"; -"error_unlock_screen" = "画面ロックを解除できませんでした"; -"Device not set" = "デバイス未選択"; -"Pause iTunes while locked" = "ロック中iTunesを一時停止する"; +"not_detected" = "信号未検出"; +"cancel" = "キャンセル"; +"notification_lost_signal" = "デバイスからの信号がありません"; +"notification_device_away" = "デバイスが Mac から離れました"; +"notification_locked" = "Mac は BLEUnlock によってロックされました"; +"enter_password" = "ロック画面を解除するためのパスワードを入力してください。"; +"password_info" = "パスワードはキーチェーンに安全に保存されます。"; +"device_not_set" = "デバイス未選択"; +"device" = "デバイス"; +"scanning" = "スキャン中…"; +"proximity_rssi" = "信号強度しきい値"; +"closer" = "⬆近い"; +"farther" = "⬇遠い"; +"wake_on_proximity" = "画面スリープから復帰"; +"pause_itunes" = "ロック中 iTunes を一時停止"; +"set_password" = "パスワードを設定…"; +"launch_at_login" = "ログイン時に起動"; +"quit" = "BLEUnlock を終了"; diff --git a/README.md b/README.md index 27302ce7..e9a5d9f8 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - No iPhone app is required - Works with any BLE devices that periodically send signal -- Locks Mac when the specified device is away from Mac - Unlocks Mac for you when the device is near Mac, no need to enter password again +- Locks Mac when the device is away from Mac - Optionally wakes from display sleep state - Optionally pauses/unpauses iTunes when you're away and back - Password is securely stored in Keychain @@ -17,7 +17,7 @@ Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE de - Mac supporting Bluetooth 4.0 or later - macOS 10.13 (High Sierra) or later -- iPhone 4s or later, Apple Watch (all), or other BLE device +- iPhone 5s or later, Apple Watch (all), or other BLE device that transmits signal periodically ## Installation @@ -43,6 +43,8 @@ If it is already on, try turning it off and on again. If it asks for permission to access its own password, click "Always Allow", because it is needed while the screen is locked. +If "Signal is lost" occurs frequently, turn Bluetooth of Mac off then on. + ## Acknowledgement Icon is based on `lock-open.svg` downloaded from materialdesignicons.com. From 68f5ca1d1dafb61047897f7d2a716d9c4ae795e7 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 30 Apr 2019 13:46:29 +0900 Subject: [PATCH 011/183] Release 1.2.1 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 686739f7..99338bc3 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2 + 1.2.1 CFBundleVersion - 36 + 37 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From c5a48eb5e5a0608938727dca857abe15306a239c Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 30 Apr 2019 16:26:46 +0900 Subject: [PATCH 012/183] Add Lock now --- BLEUnlock/AppDelegate.swift | 47 +++++++++++++++++------- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/lowlevel.c | 10 ++--- BLEUnlock/lowlevel.h | 1 + 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 73e9da2f..3577675e 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -136,6 +136,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate guard prefs.bool(forKey: "pauseItunes") else { return } if iTunesWasPlaying { _ = runAppleScript("tell application \"iTunes\" to play") + iTunesWasPlaying = false } } @@ -151,11 +152,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } unlockScreen() } else { - pauseItunes() - if lockScreen() { - self.notifyUser(reason) - } else { - debugPrint("Failed to lock") + if (!isScreenLocked()) { + pauseItunes() + if lockScreen() { + self.notifyUser(reason) + } else { + print("Failed to lock") + } } } } @@ -175,20 +178,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate CGEvent(keyboardEventSource: src, virtualKey: 52, keyDown: false)?.post(tap: .cghidEventTap) } + func isScreenLocked() -> Bool { + if let dict = CGSessionCopyCurrentDictionary() as? [String : Any] { + if let locked = dict["CGSSessionScreenIsLocked"] as? Int { + return locked == 1 + } + } + return false + } + func unlockScreen() { if sleeping { print("Pending unlock") return } if let password = fetchPassword() { // Fetch password beforehand, as it may ask for permission in modal - if let dict = CGSessionCopyCurrentDictionary() as? [String : Any] { - if let locked = dict["CGSSessionScreenIsLocked"] as? Int { - if locked == 1 { - print("Entering password") - fakeKeyStrokes(password) - playItunes() - } - } + if isScreenLocked() { + print("Entering password") + fakeKeyStrokes(password) + playItunes() } } } @@ -324,11 +332,24 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } + @objc func lockNow() { + guard !isScreenLocked() else { return } + pauseItunes() + lockScreen() + Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in + sleepDisplay() + }) + } + func constructMenu() { monitorMenuItem = mainMenu.addItem(withTitle: t("device_not_set"), action: nil, keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) var item: NSMenuItem + + item = mainMenu.addItem(withTitle: t("lock_now"), action: #selector(lockNow), keyEquivalent: "") + mainMenu.addItem(NSMenuItem.separator()) + item = mainMenu.addItem(withTitle: t("device"), action: nil, keyEquivalent: "") item.submenu = deviceMenu deviceMenu.delegate = self diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 35c9c732..d49847fa 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -16,3 +16,4 @@ "set_password" = "Set password…"; "launch_at_login" = "Launch at login"; "quit" = "Quit BLEUnlock"; +"lock_now" = "Lock now"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 99338bc3..3b051490 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.1 CFBundleVersion - 37 + 47 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 61ae2c6e..a4049f24 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -16,3 +16,4 @@ "set_password" = "パスワードを設定…"; "launch_at_login" = "ログイン時に起動"; "quit" = "BLEUnlock を終了"; +"lock_now" = "今すぐロック"; diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index 4eed394a..42f9bb7a 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -46,18 +46,16 @@ void wakeDisplay(void) bool lockScreen(void) { -#if 1 // Go to lock screen by private API. Doesn't work in Sandbox. extern int SACLockScreenImmediate(void); return SACLockScreenImmediate() == 0; -#else - // Go to display sleep. User must set lock screen immediately after sleep in order to lock. +} + +void sleepDisplay(void) +{ io_registry_entry_t reg = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler"); if (reg) { IORegistryEntrySetCFProperty(reg, CFSTR("IORequestIdle"), kCFBooleanTrue); IOObjectRelease(reg); - return true; } - return false; -#endif } diff --git a/BLEUnlock/lowlevel.h b/BLEUnlock/lowlevel.h index 6860b61b..2cf20ed7 100644 --- a/BLEUnlock/lowlevel.h +++ b/BLEUnlock/lowlevel.h @@ -5,6 +5,7 @@ void setSleepNotification(void); void wakeDisplay(void); bool lockScreen(void); +void sleepDisplay(void); // callback void onDisplaySleep(void); From 9ad1f9f39539b068d9daf7b517b56defcaa93cb8 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 30 Apr 2019 21:06:03 +0900 Subject: [PATCH 013/183] Add about box --- BLEUnlock.xcodeproj/project.pbxproj | 18 + BLEUnlock/AboutBox.swift | 35 ++ BLEUnlock/AppDelegate.swift | 9 +- BLEUnlock/Base.lproj/AboutBox.xib | 448 ++++++++++++++++++ BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Base.lproj/MainMenu.xib | 3 +- .../AboutImage.imageset/Contents.json | 22 + .../AboutImage.imageset/app-128.png | Bin 0 -> 4332 bytes .../AboutImage.imageset/app-256.png | Bin 0 -> 9112 bytes BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/AboutBox.strings | 5 + BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/ja.lproj/MainMenu.strings | 1 - 13 files changed, 541 insertions(+), 4 deletions(-) create mode 100644 BLEUnlock/AboutBox.swift create mode 100644 BLEUnlock/Base.lproj/AboutBox.xib create mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json create mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png create mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/app-256.png create mode 100644 BLEUnlock/ja.lproj/AboutBox.strings delete mode 100644 BLEUnlock/ja.lproj/MainMenu.strings diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 2a2a0131..31dfca7e 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 3D600A4822701A5C0068FB7B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D600A4622701A5C0068FB7B /* MainMenu.xib */; }; 3D600A5A22701E740068FB7B /* Launcher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3D600A4022701A5A0068FB7B /* Launcher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */; }; + 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D95379322784D550017D8B9 /* AboutBox.swift */; }; + 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D953799227853E20017D8B9 /* AboutBox.xib */; }; 3DC0BC6D22729050001145EC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3DC0BC6F22729050001145EC /* InfoPlist.strings */; }; 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD4B653226C1C3200451B7B /* AppDelegate.swift */; }; 3DD4B659226C1C3400451B7B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3DD4B657226C1C3400451B7B /* MainMenu.xib */; }; @@ -45,6 +47,9 @@ 3D600A4922701A5C0068FB7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3D600A4A22701A5C0068FB7B /* Launcher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Launcher.entitlements; sourceTree = ""; }; 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = appleDeviceNames.swift; sourceTree = ""; }; + 3D95379322784D550017D8B9 /* AboutBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutBox.swift; sourceTree = ""; }; + 3D953798227853E20017D8B9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutBox.xib; sourceTree = ""; }; + 3D95379B227853EC0017D8B9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/AboutBox.strings; sourceTree = ""; }; 3DC0BC6922728FF1001145EC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3DC0BC6E22729050001145EC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; 3DD4B650226C1C3200451B7B /* BLEUnlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BLEUnlock.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -111,6 +116,8 @@ 3DD4B652226C1C3200451B7B /* BLEUnlock */ = { isa = PBXGroup; children = ( + 3D95379322784D550017D8B9 /* AboutBox.swift */, + 3D953799227853E20017D8B9 /* AboutBox.xib */, 3D600A35226CC1A40068FB7B /* Localizable.strings */, 3DD4B665226C1DF200451B7B /* BLE.swift */, 3DD4B653226C1C3200451B7B /* AppDelegate.swift */, @@ -247,6 +254,7 @@ files = ( 3D2FCF08226C99CB007A06E7 /* Images.xcassets in Resources */, 3D600A37226CC1A40068FB7B /* Localizable.strings in Resources */, + 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */, 3DD4B659226C1C3400451B7B /* MainMenu.xib in Resources */, 3DC0BC6D22729050001145EC /* InfoPlist.strings in Resources */, ); @@ -290,6 +298,7 @@ 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */, 3DD4B666226C1DF200451B7B /* BLE.swift in Sources */, 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */, + 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */, 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -314,6 +323,15 @@ name = MainMenu.xib; sourceTree = ""; }; + 3D953799227853E20017D8B9 /* AboutBox.xib */ = { + isa = PBXVariantGroup; + children = ( + 3D953798227853E20017D8B9 /* Base */, + 3D95379B227853EC0017D8B9 /* ja */, + ); + name = AboutBox.xib; + sourceTree = ""; + }; 3DC0BC6F22729050001145EC /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/BLEUnlock/AboutBox.swift b/BLEUnlock/AboutBox.swift new file mode 100644 index 00000000..e7ecb7f8 --- /dev/null +++ b/BLEUnlock/AboutBox.swift @@ -0,0 +1,35 @@ +import Cocoa + +private var aboutBox: AboutBox? = nil + +class AboutBox: NSWindowController, NSWindowDelegate { + @IBOutlet weak var versionLabel: NSTextField! + + convenience init() { + self.init(windowNibName: "AboutBox") + } + + override func windowDidLoad() { + super.windowDidLoad() + if let info = Bundle.main.infoDictionary { + if let version = info["CFBundleShortVersionString"] as? String { + versionLabel.stringValue = versionLabel.stringValue.replacingOccurrences(of: "#{version}", with: version) + } + } + } + + func windowWillClose(_ notification: Notification) { + aboutBox = nil + } + + static func showAboutBox() { + if (aboutBox == nil) { + aboutBox = AboutBox() + } + aboutBox?.showWindow(nil) + NSApp.activate(ignoringOtherApps: true) + aboutBox?.window?.orderFront(self) + } +} + + diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 3577675e..fa3ce1a0 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -32,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var connected = false var userNotification: NSUserNotification? var iTunesWasPlaying = false + var aboutBox: AboutBox? = nil func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -296,6 +297,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate let txt = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 260, height: 20)) msg.accessoryView = txt txt.becomeFirstResponder() + NSApp.activate(ignoringOtherApps: true) let response = msg.runModal() if (response == .alertFirstButtonReturn) { @@ -341,9 +343,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate }) } + @objc func showAboutBox() { + AboutBox.showAboutBox() + } + func constructMenu() { monitorMenuItem = mainMenu.addItem(withTitle: t("device_not_set"), action: nil, keyEquivalent: "") - mainMenu.addItem(NSMenuItem.separator()) var item: NSMenuItem @@ -380,6 +385,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate item = mainMenu.addItem(withTitle: t("launch_at_login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") item.state = prefs.bool(forKey: "launchAtLogin") ? .on : .off + mainMenu.addItem(NSMenuItem.separator()) + mainMenu.addItem(withTitle: t("about"), action: #selector(showAboutBox), keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) mainMenu.addItem(withTitle: t("quit"), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") statusItem.menu = mainMenu diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib new file mode 100644 index 00000000..468063df --- /dev/null +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -0,0 +1,448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
 +Included icon is derived from lock-open.svg downloaded from materialdesignicons.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index d49847fa..3b63a518 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -17,3 +17,4 @@ "launch_at_login" = "Launch at login"; "quit" = "Quit BLEUnlock"; "lock_now" = "Lock now"; +"about" = "About BLEUnlock"; diff --git a/BLEUnlock/Base.lproj/MainMenu.xib b/BLEUnlock/Base.lproj/MainMenu.xib index 336d4490..def5847a 100644 --- a/BLEUnlock/Base.lproj/MainMenu.xib +++ b/BLEUnlock/Base.lproj/MainMenu.xib @@ -1,6 +1,7 @@ + @@ -10,7 +11,7 @@ - + diff --git a/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json b/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json new file mode 100644 index 00000000..f6b54b35 --- /dev/null +++ b/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "app-128.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "app-256.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png b/BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png new file mode 100644 index 0000000000000000000000000000000000000000..597fa8610b7dceed790556ae3e440b179c658e7d GIT binary patch literal 4332 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

@E92iQMG_44g~rdWqZx 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 DKTRhw literal 0 HcmV?d00001 diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 3b051490..c15ff699 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.1 CFBundleVersion - 47 + 143 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/AboutBox.strings b/BLEUnlock/ja.lproj/AboutBox.strings new file mode 100644 index 00000000..17d6cbe8 --- /dev/null +++ b/BLEUnlock/ja.lproj/AboutBox.strings @@ -0,0 +1,5 @@ +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock バージョン #{version}"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "BLEUnlock について"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index a4049f24..abf79ae8 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -17,3 +17,4 @@ "launch_at_login" = "ログイン時に起動"; "quit" = "BLEUnlock を終了"; "lock_now" = "今すぐロック"; +"about" = "BLEUnlock について"; diff --git a/BLEUnlock/ja.lproj/MainMenu.strings b/BLEUnlock/ja.lproj/MainMenu.strings deleted file mode 100644 index 8b137891..00000000 --- a/BLEUnlock/ja.lproj/MainMenu.strings +++ /dev/null @@ -1 +0,0 @@ - From a8cf53e50ee097c0f00820e8b1c74046179941e4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 30 Apr 2019 22:38:05 +0900 Subject: [PATCH 014/183] Release 1.3 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index c15ff699..8df1dfa0 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.1 + 1.3 CFBundleVersion - 143 + 145 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From c25ae0bb4da669875afaa5949c723ca376b8032a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 1 May 2019 11:59:59 +0900 Subject: [PATCH 015/183] Fix menu item case --- BLEUnlock/Base.lproj/Localizable.strings | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 3b63a518..3a0b2481 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -11,10 +11,10 @@ "proximity_rssi" = "Proximity Threshold"; "closer" = "⬆Closer"; "farther" = "⬇Farther"; -"wake_on_proximity" = "Wake on proximity"; -"pause_itunes" = "Pause iTunes while locked"; -"set_password" = "Set password…"; -"launch_at_login" = "Launch at login"; +"wake_on_proximity" = "Wake on Proximity"; +"pause_itunes" = "Pause iTunes while Locked"; +"set_password" = "Set Password…"; +"launch_at_login" = "Launch at Login"; "quit" = "Quit BLEUnlock"; -"lock_now" = "Lock now"; +"lock_now" = "Lock Now"; "about" = "About BLEUnlock"; From 05c3c4a67f271bf3b06b433b51be3bcbe091f332 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 00:08:40 +0900 Subject: [PATCH 016/183] Rewrite Launcher in Objective-C, reduced app size --- BLEUnlock.xcodeproj/project.pbxproj | 38 ++++++++++++-------------- BLEUnlock/Info.plist | 4 +-- Launcher/AppDelegate.h | 4 +++ Launcher/AppDelegate.m | 29 ++++++++++++++++++++ Launcher/AppDelegate.swift | 21 -------------- Launcher/Info.plist | 2 +- Launcher/{Base.lproj => }/MainMenu.xib | 3 +- Launcher/main.m | 5 ++++ 8 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 Launcher/AppDelegate.h create mode 100644 Launcher/AppDelegate.m delete mode 100644 Launcher/AppDelegate.swift rename Launcher/{Base.lproj => }/MainMenu.xib (88%) create mode 100644 Launcher/main.m diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 31dfca7e..a46d4f3d 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -8,9 +8,10 @@ /* Begin PBXBuildFile section */ 3D2FCF08226C99CB007A06E7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D2FCF07226C99CB007A06E7 /* Images.xcassets */; }; + 3D4EE4112279E55A00AF9E93 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */; }; + 3D4EE4132279E56900AF9E93 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4122279E56900AF9E93 /* main.m */; }; + 3D4EE4152279E94B00AF9E93 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */; }; 3D600A37226CC1A40068FB7B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D600A35226CC1A40068FB7B /* Localizable.strings */; }; - 3D600A4322701A5B0068FB7B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D600A4222701A5B0068FB7B /* AppDelegate.swift */; }; - 3D600A4822701A5C0068FB7B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D600A4622701A5C0068FB7B /* MainMenu.xib */; }; 3D600A5A22701E740068FB7B /* Launcher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3D600A4022701A5A0068FB7B /* Launcher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */; }; 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D95379322784D550017D8B9 /* AboutBox.swift */; }; @@ -39,11 +40,13 @@ /* Begin PBXFileReference section */ 3D1B377B22772B740024E2E0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 3D4EE4122279E56900AF9E93 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 3D600A36226CC1A40068FB7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; 3D600A38226CC1AA0068FB7B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; 3D600A4022701A5A0068FB7B /* Launcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Launcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D600A4222701A5B0068FB7B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 3D600A4722701A5C0068FB7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 3D600A4922701A5C0068FB7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3D600A4A22701A5C0068FB7B /* Launcher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Launcher.entitlements; sourceTree = ""; }; 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = appleDeviceNames.swift; sourceTree = ""; }; @@ -85,9 +88,11 @@ 3D600A4122701A5B0068FB7B /* Launcher */ = { isa = PBXGroup; children = ( - 3D600A4222701A5B0068FB7B /* AppDelegate.swift */, - 3D600A4622701A5C0068FB7B /* MainMenu.xib */, + 3D4EE4122279E56900AF9E93 /* main.m */, + 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */, + 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */, 3D600A4922701A5C0068FB7B /* Info.plist */, + 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */, 3D600A4A22701A5C0068FB7B /* Launcher.entitlements */, ); path = Launcher; @@ -193,6 +198,7 @@ TargetAttributes = { 3D600A3F22701A5A0068FB7B = { CreatedOnToolsVersion = 10.2.1; + LastSwiftMigration = 1020; SystemCapabilities = { com.apple.GameCenter.Mac = { enabled = 0; @@ -244,7 +250,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3D600A4822701A5C0068FB7B /* MainMenu.xib in Resources */, + 3D4EE4152279E94B00AF9E93 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -287,7 +293,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3D600A4322701A5B0068FB7B /* AppDelegate.swift in Sources */, + 3D4EE4112279E55A00AF9E93 /* AppDelegate.m in Sources */, + 3D4EE4132279E56900AF9E93 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -315,14 +322,6 @@ name = Localizable.strings; sourceTree = ""; }; - 3D600A4622701A5C0068FB7B /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 3D600A4722701A5C0068FB7B /* Base */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; 3D953799227853E20017D8B9 /* AboutBox.xib */ = { isa = PBXVariantGroup; children = ( @@ -355,6 +354,7 @@ 3D600A4C22701A5C0068FB7B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -366,10 +366,10 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -377,6 +377,7 @@ 3D600A4D22701A5C0068FB7B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -389,7 +390,6 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -517,7 +517,6 @@ 3DD4B65F226C1C3400451B7B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; @@ -548,7 +547,6 @@ 3DD4B660226C1C3400451B7B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8df1dfa0..bc0e3191 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3 CFBundleVersion - 145 + 148 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion @@ -27,7 +27,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2019 Takeshi Sone. All rights reserved. + Copyright © 2019 Takeshi Sone. MIT Licensed. NSMainNibFile MainMenu NSPrincipalClass diff --git a/Launcher/AppDelegate.h b/Launcher/AppDelegate.h new file mode 100644 index 00000000..a59935ba --- /dev/null +++ b/Launcher/AppDelegate.h @@ -0,0 +1,4 @@ +#import + +@interface AppDelegate : NSObject +@end diff --git a/Launcher/AppDelegate.m b/Launcher/AppDelegate.m new file mode 100644 index 00000000..384d80dc --- /dev/null +++ b/Launcher/AppDelegate.m @@ -0,0 +1,29 @@ +#import "AppDelegate.h" + +@interface AppDelegate () +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + NSString *id = [[NSBundle mainBundle] bundleIdentifier]; + NSString *mainId = [id stringByReplacingOccurrencesOfString:@".Launcher" withString:@""]; + if ([NSRunningApplication runningApplicationsWithBundleIdentifier:mainId].count > 0) { + [NSApp terminate:self]; + } + + NSString *path = [[NSBundle mainBundle] bundlePath]; + NSMutableArray *components = [NSMutableArray arrayWithArray:[path pathComponents]]; + [components removeLastObject]; + [components removeLastObject]; + [components removeLastObject]; + [components removeLastObject]; + NSString *mainPath = [NSString pathWithComponents:components]; + [[NSWorkspace sharedWorkspace] launchApplication:mainPath]; + [NSApp terminate:self]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { +} + +@end diff --git a/Launcher/AppDelegate.swift b/Launcher/AppDelegate.swift deleted file mode 100644 index c3961ba8..00000000 --- a/Launcher/AppDelegate.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Cocoa - -@NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ aNotification: Notification) { - let mainId = Bundle.main.bundleIdentifier!.replacingOccurrences(of: ".Launcher", with: "") - guard NSRunningApplication.runningApplications(withBundleIdentifier: mainId).isEmpty else { - NSApp.terminate(nil) - return - } - let path = Bundle.main.bundlePath as NSString - var components = path.pathComponents - components.removeLast() - components.removeLast() - components.removeLast() - components.removeLast() - let mainPath = NSString.path(withComponents: components) - NSWorkspace.shared.launchApplication(mainPath) - NSApp.terminate(nil) - } -} diff --git a/Launcher/Info.plist b/Launcher/Info.plist index 41120f50..a1b38996 100644 --- a/Launcher/Info.plist +++ b/Launcher/Info.plist @@ -23,7 +23,7 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2019 Takeshi Sone. All rights reserved. + Copyright © 2019 Takeshi Sone. MIT licecnced. NSMainNibFile MainMenu NSPrincipalClass diff --git a/Launcher/Base.lproj/MainMenu.xib b/Launcher/MainMenu.xib similarity index 88% rename from Launcher/Base.lproj/MainMenu.xib rename to Launcher/MainMenu.xib index 26f73fba..0297fed8 100644 --- a/Launcher/Base.lproj/MainMenu.xib +++ b/Launcher/MainMenu.xib @@ -1,6 +1,7 @@ + @@ -11,7 +12,7 @@ - + diff --git a/Launcher/main.m b/Launcher/main.m new file mode 100644 index 00000000..8a6799b4 --- /dev/null +++ b/Launcher/main.m @@ -0,0 +1,5 @@ +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} From af3ead52e88d66dbb876e608ddd57e6ce1cb1800 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 00:17:53 +0900 Subject: [PATCH 017/183] Include build number in About box --- BLEUnlock/AboutBox.swift | 4 +++- BLEUnlock/Info.plist | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/AboutBox.swift b/BLEUnlock/AboutBox.swift index e7ecb7f8..46155c52 100644 --- a/BLEUnlock/AboutBox.swift +++ b/BLEUnlock/AboutBox.swift @@ -13,7 +13,9 @@ class AboutBox: NSWindowController, NSWindowDelegate { super.windowDidLoad() if let info = Bundle.main.infoDictionary { if let version = info["CFBundleShortVersionString"] as? String { - versionLabel.stringValue = versionLabel.stringValue.replacingOccurrences(of: "#{version}", with: version) + if let build = info["CFBundleVersion"] as? String { + versionLabel.stringValue = versionLabel.stringValue.replacingOccurrences(of: "#{version}", with: "\(version) (\(build))") + } } } } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index bc0e3191..9413b906 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3 CFBundleVersion - 148 + 151 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 7424e6930a421e4bfaa3b129624746bae6b3b437 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 11:55:11 +0900 Subject: [PATCH 018/183] Refine readme and add Japanese translation --- README.ja.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 41 ++++++++++++++++++++++--------------- 2 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 README.ja.md diff --git a/README.ja.md b/README.ja.md new file mode 100644 index 00000000..b92669e2 --- /dev/null +++ b/README.ja.md @@ -0,0 +1,58 @@ +# BLEUnlock + +BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low EnergyデバイスでMacをロック・アンロックする小さなメニューバーユーティリティーです。 + + +## 特徴 + +- iPhoneアプリは必要ありません +- 定期的に信号を送出するBLEデバイスなら何でも使えます +- デバイスがMacの近くにあればMacのロック画面を自動的に解除します - もうパスワードを入力する必要はありません +- デバイスがMacから離れると画面をロックします +- デバイスがMacに近づくと画面スリープを解除します (オプション) +- デバイスがMacから離れる/近づくとiTunesの再生を一時停止/再生します (オプション) +- パスワードはキーチェーンに安全に保管されます +- Hardened Runtimeを使いAppleによる公証を受けています ![](https://i.imgur.com/8AbtXEp.png) + +## 必要なもの + +- Bluetooth Low EnergyをサポートするMac +- macOS 10.13 (High Sierra) 以上 +- iPhone 5s以上, Apple Watch (すべて), または定期的に信号を送信するBLEデバイス + +## インストール + +[Releases](https://github.com/ts1/BLEUnlock/releases)からzipファイルをダウンロードし、解凍してアプリケーションフォルダにコピーします。 + +最初の起動であなたのログインパスワードを聞いてきます。 +これはロック画面を解除するために必要です。 +キーチェーンに保管されるため安全です。 + +次にアクセシビリティの許可を求めてきます。 +システム環境設定の画面でロックアイコンをクリックしてアンロックし、BLEUnlockをオンにしてください。 +これもロック画面を解除するために必要です。 + +最後に、メニューバーアイコンから "デバイス" を選択してください。 +近くにあるBLEデバイスのスキャンが始まります。 +使いたいデバイスを選べば完了です。 + +## トラブルシューティング + +ロック解除に失敗する場合、"システム環境設定" → "セキュリティとプライバシー" → +"プライバシー" → "アクセシビリティ" でBLEUnlockがオンになっているか確認してください。 +すでにオンになっている場合、一度オフにしてもう一度オンにしてみてください。 + +もしBLEUnlock自身のパスワードにアクセスする許可を求められた場合、"常に許可" してください。ロック画面中に必要になるためです。 + +もし "信号がありません" が頻発する場合、MacのBluetoothを一度オフにしてからオンにしてみてください。 +もしくはより頻繁に信号を送出するデバイスを使ってください。 + +## クレジット + +アイコンはmaterialdesignicons.comからダウンロードしたlock-open.svgをもとにしています。これはGoogleによってデザインされApache License version 2.0でライセンスされています。 + +## ライセンス + +MIT + +Copyright © 2019 Takeshi Sone. diff --git a/README.md b/README.md index e9a5d9f8..3f53a00e 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,31 @@ # BLEUnlock -Lock/Unlock your Mac with your iPhone, Apple Watch, or any other Bluetooth LE device. +BLEUnlock is a small menu bar utility that locks and unlocks your Mac with your iPhone, Apple Watch, or any other Bluetooth Low Energy device. + +This document is also available in [Japanese](README.ja.md). ## Features - No iPhone app is required - Works with any BLE devices that periodically send signal -- Unlocks Mac for you when the device is near Mac, no need to enter password again -- Locks Mac when the device is away from Mac +- Unlocks your Mac for you when the device is near Mac, no need to enter password again +- Locks your Mac when the device is away from Mac - Optionally wakes from display sleep state -- Optionally pauses/unpauses iTunes when you're away and back +- Optionally pauses and unpauses iTunes playback when you're away and back - Password is securely stored in Keychain -- Uses Hardened Runtime and notarized by Apple ![](https://i.imgur.com/i9Rj44q.png) +- Uses Hardened Runtime and notarized by Apple +![](https://i.imgur.com/i9Rj44q.png) ## Requirements -- Mac supporting Bluetooth 4.0 or later +- Mac with Bluetooth Low Energy support - macOS 10.13 (High Sierra) or later -- iPhone 5s or later, Apple Watch (all), or other BLE device that transmits signal periodically +- iPhone 5s or later, Apple Watch (all), or other BLE device that transmits +signal periodically ## Installation -Download zip file from [releases](https://github.com/ts1/BLEUnlock/releases), +Download zip file from [Releases](https://github.com/ts1/BLEUnlock/releases), unzip and copy to Applications folder. On the first launch, it asks your login password, @@ -29,28 +33,33 @@ which is required to unlock the lock screen. It's safe because it's stored in Keychain. Then it asks for permission for Accessibility. -In System Preferences unlock by clicking lock icon and turn BLEUnlock on. -It is also required to unlock the lock screen. +In System Preferences, click the lock icon to unlock and turn BLEUnlock on. +This permission is also required to unlock the lock screen. -Finally, from the status bar icon, select "Device". +Finally, from the menu bar icon, select "Device". It starts scanning nearby BLE devices. -Select your device, and you're done. +Select your device, and you're done! ## Troubleshooting -If it fails to unlock, check BLEUnlock is turned on in "System Preferences" → "Security & Privacy" → "Privacy" → "Accessibility". +If it fails to unlock, check BLEUnlock is turned on in "System Preferences" → +"Security & Privacy" → "Privacy" → "Accessibility". If it is already on, try turning it off and on again. -If it asks for permission to access its own password, click "Always Allow", because it is needed while the screen is locked. +If it asks for permission to access its own password, click "Always Allow", +because it is needed while the screen is locked. If "Signal is lost" occurs frequently, turn Bluetooth of Mac off then on. +Or use a device that sends signal more frequently. ## Acknowledgement -Icon is based on `lock-open.svg` downloaded from materialdesignicons.com. +Icon is based on `lock-open.svg` downloaded from materialdesignicons.com, +which is originally designed by Google LLC and licensed under Apache License +version 2.0. ## License MIT -Copyright © 2019 by Takeshi Sone. +Copyright © 2019 Takeshi Sone. From 26585738bf283793b873c753551e8f0c0ba5c273 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 13:28:11 +0900 Subject: [PATCH 019/183] Let about box close on esc key --- BLEUnlock/AboutBox.swift | 8 +++----- BLEUnlock/Base.lproj/AboutBox.xib | 6 ++---- BLEUnlock/Info.plist | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/BLEUnlock/AboutBox.swift b/BLEUnlock/AboutBox.swift index 46155c52..e56aabf2 100644 --- a/BLEUnlock/AboutBox.swift +++ b/BLEUnlock/AboutBox.swift @@ -2,7 +2,7 @@ import Cocoa private var aboutBox: AboutBox? = nil -class AboutBox: NSWindowController, NSWindowDelegate { +class AboutBox: NSWindowController { @IBOutlet weak var versionLabel: NSTextField! convenience init() { @@ -20,8 +20,8 @@ class AboutBox: NSWindowController, NSWindowDelegate { } } - func windowWillClose(_ notification: Notification) { - aboutBox = nil + override func cancelOperation(_ sender: Any?) { + close() } static func showAboutBox() { @@ -33,5 +33,3 @@ class AboutBox: NSWindowController, NSWindowDelegate { aboutBox?.window?.orderFront(self) } } - - diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 468063df..406a1c11 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -9,6 +9,7 @@ + @@ -48,7 +49,7 @@ - + @@ -436,9 +437,6 @@ Included icon is derived from lock-open.svg downloaded from materialdesignicons. - - - diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 9413b906..38c8b81c 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3 CFBundleVersion - 151 + 173 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From aed9c4ee4e898d9bcfd62df74dc6c7ccbe469f53 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 14:27:00 +0900 Subject: [PATCH 020/183] Release 1.3.1 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 38c8b81c..4845a30d 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3 + 1.3.1 CFBundleVersion - 173 + 174 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 7abf8a0524f0d54ba3d55a2ba1e3a6c643406fa6 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 14:40:05 +0900 Subject: [PATCH 021/183] Fix closing about box by esc key --- BLEUnlock/AboutBox.swift | 2 +- BLEUnlock/Base.lproj/AboutBox.xib | 5 ++++- BLEUnlock/Info.plist | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/AboutBox.swift b/BLEUnlock/AboutBox.swift index e56aabf2..22808aa8 100644 --- a/BLEUnlock/AboutBox.swift +++ b/BLEUnlock/AboutBox.swift @@ -2,7 +2,7 @@ import Cocoa private var aboutBox: AboutBox? = nil -class AboutBox: NSWindowController { +class AboutBox: NSWindowController, NSWindowDelegate { @IBOutlet weak var versionLabel: NSTextField! convenience init() { diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 406a1c11..8d55c543 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -49,7 +49,7 @@ - + @@ -437,6 +437,9 @@ Included icon is derived from lock-open.svg downloaded from materialdesignicons. + + + diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 4845a30d..8fedd262 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3.1 CFBundleVersion - 174 + 178 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 3020a130a72314b5ae75247efaf475b1ccb31a82 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 17:01:32 +0900 Subject: [PATCH 022/183] Changed signal timeout to 61 seconds --- BLEUnlock/BLE.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 1901a17f..69c9bb28 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -73,7 +73,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var presence = false var proximityRSSI = -70 var proximityTimeout = 10.0 - var signalTimeout = 31.0 + var signalTimeout = 61.0 func startScanning() { scanMode = true From e14be293324a4d0f17b1cdf865378c91116c8709 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 2 May 2019 17:12:22 +0900 Subject: [PATCH 023/183] Release 1.3.2 --- BLEUnlock/Info.plist | 4 ++-- release | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8fedd262..33667243 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.1 + 1.3.2 CFBundleVersion - 178 + 180 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/release b/release index 33b31d33..2bbd14e4 100755 --- a/release +++ b/release @@ -1,4 +1,4 @@ -#!/bin/bash -xe +#!/bin/bash -e set -o pipefail From 60bad96446997b54a9ea8dcd4f2b64f0c5957086 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 01:16:56 +0900 Subject: [PATCH 024/183] Use readRSSI for stable, accurate monitoring --- BLEUnlock.xcodeproj/project.pbxproj | 16 +-- BLEUnlock/AppDelegate.swift | 36 +++---- BLEUnlock/BLE.swift | 154 ++++++++++++++++++++-------- BLEUnlock/Info.plist | 2 +- BLEUnlock/lowlevel.c | 37 ------- BLEUnlock/lowlevel.h | 5 - 6 files changed, 132 insertions(+), 118 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index a46d4f3d..d1e34e04 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -355,8 +355,8 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; @@ -378,8 +378,8 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEVELOPMENT_TEAM = 376NR42P9A; @@ -520,8 +520,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; @@ -550,8 +550,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Mac Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index fa3ce1a0..b3e636ab 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -2,18 +2,6 @@ import Cocoa import Quartz import ServiceManagement -var appDelegate: AppDelegate? = nil - -@_cdecl("onDisplayWake") -func onDisplayWake() { - appDelegate?.onWake() -} - -@_cdecl("onDisplaySleep") -func onDisplaySleep() { - appDelegate?.onSleep() -} - func t(_ key: String) -> String { return NSLocalizedString(key, comment: "") } @@ -61,7 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate func newDevice(device: Device) { let menuItem = deviceMenu.addItem(withTitle: menuItemTitle(device: device), action:#selector(selectDevice), keyEquivalent: "") deviceDict[device.uuid] = menuItem - if (device.uuid == ble.monitorUUID) { + if (device.uuid == ble.monitoredUUID) { menuItem.state = .on } } @@ -202,21 +190,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } - func onWake() { - print("awake") + @objc func onDisplayWake() { + print("display wake") sleeping = false - Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false, block: { _ in + Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in if self.ble.presence { self.unlockScreen() } }) } - - func onSleep() { + + @objc func onDisplaySleep() { sleeping = true - print("sleep") + print("display sleep") } - + @objc func selectDevice(item: NSMenuItem) { for (uuid, menuItem) in deviceDict { if menuItem == item { @@ -419,9 +407,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate if proximity != 0 { ble.proximityRSSI = proximity } - appDelegate = self; - setSleepNotification() - + + let nc = NSWorkspace.shared.notificationCenter; + nc.addObserver(self, selector: #selector(onDisplaySleep), name: NSWorkspace.screensDidSleepNotification, object: nil) + nc.addObserver(self, selector: #selector(onDisplayWake), name: NSWorkspace.screensDidWakeNotification, object: nil) + if fetchPassword() == nil { askPassword() } diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 69c9bb28..31067b90 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -67,29 +67,32 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var devices : [UUID : Device] = [:] var delegate: BLEDelegate? var scanMode = false - var monitorUUID: UUID? + var monitoredUUID: UUID? + var monitoredPeripheral: CBPeripheral? var proximityTimer : Timer? var signalTimer: Timer? var presence = false var proximityRSSI = -70 - var proximityTimeout = 10.0 - var signalTimeout = 61.0 - + var proximityTimeout = 8.5 + var signalTimeout = 31.0 + var lastReadAt = 0.0 + func startScanning() { scanMode = true } - + func stopScanning() { scanMode = false } - + func startMonitor(uuid: UUID) { - monitorUUID = uuid + monitoredUUID = uuid proximityTimer?.invalidate() resetSignalTimer() - presence = false + presence = true + monitoredPeripheral = nil } - + func resetSignalTimer() { signalTimer?.invalidate() signalTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in @@ -104,48 +107,73 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { RunLoop.main.add(timer, forMode: .common) } } - + func centralManagerDidUpdateState(_ central: CBCentralManager) { - if central.state == .poweredOn { + switch central.state { + case .poweredOn: + print("central powered on") centralMgr.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]) + if let p = monitoredPeripheral { + central.cancelPeripheralConnection(p) + } + case .poweredOff: + print("central powered off") + //presence = false + default: + break } } - + + func updateMonitoredPeripheral(_ rssi: Int) { + if let p = monitoredPeripheral { + if (!scanMode && p.state == .disconnected) { + centralMgr.connect(p, options: nil) + } + } + delegate?.updateRSSI(rssi: rssi) + if (rssi > proximityRSSI) { + if (!presence) { + print("Device is close") + presence = true + delegate?.updatePresence(presence: presence, reason: "close") + } + if let timer = proximityTimer { + timer.invalidate() + print("Proximity timer canceled") + proximityTimer = nil + } + } else if (presence) { + if proximityTimer == nil { + proximityTimer = Timer.scheduledTimer(withTimeInterval: proximityTimeout, repeats: false, block: { _ in + print("Device is away") + self.presence = false + self.delegate?.updatePresence(presence: self.presence, reason: "away") + self.proximityTimer = nil + }) + if let timer = proximityTimer { + RunLoop.main.add(timer, forMode: .common) + print("Proximity timer started") + } + } + } + resetSignalTimer() + } + func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue - if let uuid = monitorUUID { + if let uuid = monitoredUUID { if peripheral.identifier.description == uuid.description { - delegate?.updateRSSI(rssi: rssi) - if (rssi > proximityRSSI) { - if (!presence) { - print("Device is close") - presence = true - delegate?.updatePresence(presence: presence, reason: "close") - } - if let timer = proximityTimer { - timer.invalidate() - print("Proximity timer canceled") - proximityTimer = nil - } - } else if (presence) { - if proximityTimer == nil { - proximityTimer = Timer.scheduledTimer(withTimeInterval: proximityTimeout, repeats: false, block: { _ in - print("Device is away") - self.presence = false - self.delegate?.updatePresence(presence: self.presence, reason: "away") - self.proximityTimer = nil - }) - if let timer = proximityTimer { - RunLoop.main.add(timer, forMode: .common) - print("Proximity timer started") - } - } + if monitoredPeripheral == nil { + monitoredPeripheral = peripheral + } + if peripheral.state != .connected { + updateMonitoredPeripheral(rssi) + //print("Discover \(rssi)dBm") } - resetSignalTimer() } } @@ -168,7 +196,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { resetScanTimer(device: device) } } - + func resetScanTimer(device: Device) { device.scanTimer?.invalidate() device.scanTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in @@ -182,14 +210,52 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { RunLoop.main.add(timer, forMode: .common) } } - + func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices([DeviceInformation]) + if peripheral == monitoredPeripheral { + peripheral.readRSSI() + lastReadAt = Date().timeIntervalSince1970 + } } - + + func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { + guard peripheral == monitoredPeripheral else { return } + central.connect(peripheral, options: nil) + } + + func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { + guard peripheral == monitoredPeripheral else { return } + central.connect(peripheral, options: nil) + } + + func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { + guard peripheral == monitoredPeripheral else { return } + let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue + updateMonitoredPeripheral(rssi) + //print("readRSSI \(rssi)dBm") + + var interval: TimeInterval + if lastReadAt > 0 { + interval = 2.0 - (Date().timeIntervalSince1970 - lastReadAt) + if interval < 0 { + interval = 0 + } + } else { + interval = 0 + } + let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false, block: { _ in + if peripheral.state == .connected { + peripheral.readRSSI() + self.lastReadAt = Date().timeIntervalSince1970 + } + }) + RunLoop.main.add(timer, forMode: .common) + } + func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let services = peripheral.services { @@ -200,7 +266,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } } - + func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) @@ -230,7 +296,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { device.model = s delegate?.updateDevice(device: device) } - if device.model != nil && device.model != nil { + if device.model != nil && device.model != nil && device.peripheral != monitoredPeripheral { centralMgr.cancelPeripheralConnection(peripheral) } } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 33667243..43291324 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3.2 CFBundleVersion - 180 + 289 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index 42f9bb7a..e3a943ba 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -1,42 +1,5 @@ #include "lowlevel.h" - -#include -#include -#include - -#include -#include -#include - #include -#include - -static void -sleepCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) -{ - switch (messageType) { - case kIOMessageDeviceWillPowerOff: - onDisplaySleep(); - break; - case kIOMessageDeviceHasPoweredOn: - onDisplayWake(); - break; - } -} - -void setSleepNotification(void) -{ - io_service_t displayWrangler; - IONotificationPortRef notificationPort; - io_object_t notification; - - displayWrangler = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching("IODisplayWrangler")); - notificationPort = IONotificationPortCreate(kIOMasterPortDefault); - IOServiceAddInterestNotification(notificationPort, displayWrangler, kIOGeneralInterest, sleepCallback, NULL, ¬ification); - - CFRunLoopAddSource (CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode); - IOObjectRelease (displayWrangler); -} void wakeDisplay(void) { diff --git a/BLEUnlock/lowlevel.h b/BLEUnlock/lowlevel.h index 2cf20ed7..0f96e9c9 100644 --- a/BLEUnlock/lowlevel.h +++ b/BLEUnlock/lowlevel.h @@ -2,14 +2,9 @@ #define lowlevel_h #include -void setSleepNotification(void); void wakeDisplay(void); bool lockScreen(void); void sleepDisplay(void); -// callback -void onDisplaySleep(void); -void onDisplayWake(void); - #endif /* lowlevel_h */ From 94d287cbe5cf1ccb804cb024346d80c94c4eeee0 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 11:55:32 +0900 Subject: [PATCH 025/183] Release 1.4 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 43291324..cef4e7a3 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.2 + 1.4 CFBundleVersion - 289 + 292 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From da3456b3a77cf43871d94c347ff2e1dedd1f08ca Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 19:52:59 +0900 Subject: [PATCH 026/183] Assume device is not present when Bluetooth is powered off --- BLEUnlock/AppDelegate.swift | 5 +++++ BLEUnlock/BLE.swift | 2 +- BLEUnlock/Base.lproj/InfoPlist.strings | 2 +- BLEUnlock/Base.lproj/Localizable.strings | 2 +- BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/InfoPlist.strings | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index b3e636ab..8f3a8d83 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -21,6 +21,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var userNotification: NSUserNotification? var iTunesWasPlaying = false var aboutBox: AboutBox? = nil + var unlockRequired = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -139,6 +140,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate print("Waking display") wakeDisplay() } + unlockRequired = true unlockScreen() } else { if (!isScreenLocked()) { @@ -177,6 +179,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } func unlockScreen() { + guard unlockRequired else { return } if sleeping { print("Pending unlock") return @@ -186,6 +189,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate print("Entering password") fakeKeyStrokes(password) playItunes() + unlockRequired = false } } } @@ -193,6 +197,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func onDisplayWake() { print("display wake") sleeping = false + unlockRequired = true Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in if self.ble.presence { self.unlockScreen() diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 31067b90..105f42e8 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -118,7 +118,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } case .poweredOff: print("central powered off") - //presence = false + presence = false default: break } diff --git a/BLEUnlock/Base.lproj/InfoPlist.strings b/BLEUnlock/Base.lproj/InfoPlist.strings index 03f48249..220d80bd 100644 --- a/BLEUnlock/Base.lproj/InfoPlist.strings +++ b/BLEUnlock/Base.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses iTunes on lock/unlock."; +"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses iTunes playback upon locking/unlocking screen."; diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 3a0b2481..1eda6471 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -16,5 +16,5 @@ "set_password" = "Set Password…"; "launch_at_login" = "Launch at Login"; "quit" = "Quit BLEUnlock"; -"lock_now" = "Lock Now"; +"lock_now" = "Lock Screen Now"; "about" = "About BLEUnlock"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index cef4e7a3..8a45ac90 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4 CFBundleVersion - 292 + 301 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/InfoPlist.strings b/BLEUnlock/ja.lproj/InfoPlist.strings index 1478220c..1919d2f4 100644 --- a/BLEUnlock/ja.lproj/InfoPlist.strings +++ b/BLEUnlock/ja.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "ロック/アンロック時に iTunes の一時停止/再生を行います。"; +"NSAppleEventsUsageDescription" = "画面ロック/解除時に iTunes の一時停止/解除を行います。"; From ff4c229e6a56b9551e19286eed33482c7dd2f2ad Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 23:08:05 +0900 Subject: [PATCH 027/183] Code cleanup --- BLEUnlock/AppDelegate.swift | 57 +++++++++++++++++++------------------ BLEUnlock/Info.plist | 2 +- BLEUnlock/lowlevel.c | 4 +-- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 8f3a8d83..1cabecdc 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -16,12 +16,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var deviceDict: [UUID: NSMenuItem] = [:] var monitorMenuItem : NSMenuItem? let prefs = UserDefaults.standard - var sleeping = false + var displaySleep = false + var systemSleep = false var connected = false var userNotification: NSUserNotification? var iTunesWasPlaying = false var aboutBox: AboutBox? = nil - var unlockRequired = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -136,12 +136,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate NSUserNotificationCenter.default.removeDeliveredNotification(un) userNotification = nil } - if prefs.bool(forKey: "wakeOnProximity") && sleeping { + if displaySleep && !systemSleep && prefs.bool(forKey: "wakeOnProximity") { print("Waking display") wakeDisplay() } - unlockRequired = true - unlockScreen() + tryUnlockScreen() } else { if (!isScreenLocked()) { pauseItunes() @@ -178,38 +177,40 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate return false } - func unlockScreen() { - guard unlockRequired else { return } - if sleeping { - print("Pending unlock") - return - } - if let password = fetchPassword() { // Fetch password beforehand, as it may ask for permission in modal - if isScreenLocked() { - print("Entering password") - fakeKeyStrokes(password) - playItunes() - unlockRequired = false - } - } + func tryUnlockScreen() { + guard ble.presence else { return } + guard !systemSleep else { return } + guard !displaySleep else { return } + guard isScreenLocked() else { return } + guard let password = fetchPassword() else { return } + + print("Entering password") + fakeKeyStrokes(password) + playItunes() } @objc func onDisplayWake() { print("display wake") - sleeping = false - unlockRequired = true - Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in - if self.ble.presence { - self.unlockScreen() - } - }) + displaySleep = false + tryUnlockScreen() } @objc func onDisplaySleep() { - sleeping = true print("display sleep") + displaySleep = true } + @objc func onSystemWake() { + print("system wake") + systemSleep = false + tryUnlockScreen() + } + + @objc func onSystemSleep() { + print("system sleep") + systemSleep = true + } + @objc func selectDevice(item: NSMenuItem) { for (uuid, menuItem) in deviceDict { if menuItem == item { @@ -416,6 +417,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate let nc = NSWorkspace.shared.notificationCenter; nc.addObserver(self, selector: #selector(onDisplaySleep), name: NSWorkspace.screensDidSleepNotification, object: nil) nc.addObserver(self, selector: #selector(onDisplayWake), name: NSWorkspace.screensDidWakeNotification, object: nil) + nc.addObserver(self, selector: #selector(onSystemSleep), name: NSWorkspace.willSleepNotification, object: nil) + nc.addObserver(self, selector: #selector(onSystemWake), name: NSWorkspace.didWakeNotification, object: nil) if fetchPassword() == nil { askPassword() diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8a45ac90..9ffefe2c 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4 CFBundleVersion - 301 + 315 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index e3a943ba..3e2c53a4 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -3,8 +3,8 @@ void wakeDisplay(void) { - IOPMAssertionID assertionID; - IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID); + static IOPMAssertionID assertionID; + IOPMAssertionDeclareUserActivity(CFSTR("BLEUnlock"), kIOPMUserActiveLocal, &assertionID); } bool lockScreen(void) From 7a05498b4b18b4faf8278f39e76fdb8cb49be599 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 23:09:22 +0900 Subject: [PATCH 028/183] Add description of system sleep to README --- README.ja.md | 11 +++++++++++ README.md | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/README.ja.md b/README.ja.md index b92669e2..e3dcb063 100644 --- a/README.ja.md +++ b/README.ja.md @@ -47,6 +47,17 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス もし "信号がありません" が頻発する場合、MacのBluetoothを一度オフにしてからオンにしてみてください。 もしくはより頻繁に信号を送出するデバイスを使ってください。 +### ロック解除に時間がかかる場合 + +短い答え: iPhoneのスリープを解除してください。 + +バージョン1.4.1から、BLEUnlockはシステムスリープ中Bluetoothがパワーオフしたとき (システムスリープから15〜30秒後)、デバイスが離れたと仮定するようになりました。 +これはセキュリティのために必要です。 +結果として、スリープ解除後、ロック解除されるまで最大で数秒かかるようになりました。 + +これはデバイスとのコネクションが切れ、デバイスが信号を送ってくるのを待っているためです。 +デバイス側もスリープ中は信号送出の間隔が長いので、デバイスのスリープを解除すれば、大抵の場合すぐに信号が送られ、ロック解除されます。 + ## クレジット アイコンはmaterialdesignicons.comからダウンロードしたlock-open.svgをもとにしています。これはGoogleによってデザインされApache License version 2.0でライセンスされています。 diff --git a/README.md b/README.md index 3f53a00e..9d214f30 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,23 @@ because it is needed while the screen is locked. If "Signal is lost" occurs frequently, turn Bluetooth of Mac off then on. Or use a device that sends signal more frequently. +### Why does it sometimes take long time to unlock? + +Short answer: wake your iPhone. + +From version 1.4.1, BLEUnlock assumes the device is away +when Mac enters system sleep and Bluetooth hardware is powered off +(typically 15-30 seconds after the lid is closed). +This is required for security. +Consequently, it sometimes takes up to several seconds to unlock when Mac wakes +from system sleep. + +This is because BLEUnlock has lost connection to the device and has to wait +for the device to send signal. +Usually, devices send signal less frequently when it is in sleep mode. +Thus, if you wake the device, in most cases it sends signal promptly, +and BLEUnlock unlocks. + ## Acknowledgement Icon is based on `lock-open.svg` downloaded from materialdesignicons.com, From 1bcc4ccdde55e2371c7ec831ff03b4895b79e096 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 23:35:09 +0900 Subject: [PATCH 029/183] Fix behaviour when switch monitored device --- BLEUnlock/BLE.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 105f42e8..ad7eb43a 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -86,6 +86,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } func startMonitor(uuid: UUID) { + if let p = monitoredPeripheral { + centralMgr.cancelPeripheralConnection(p) + } monitoredUUID = uuid proximityTimer?.invalidate() resetSignalTimer() From 4766c58e0f4fe870a4ee1cc3e9bc6338df3716fd Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 23:37:00 +0900 Subject: [PATCH 030/183] Replace off icon --- BLEUnlock/AppDelegate.swift | 4 +++- BLEUnlock/Base.lproj/AboutBox.xib | 20 +++--------------- .../Contents.json | 14 ++---------- .../lock-gray.pdf | Bin 1116 -> 0 bytes .../{lock-gray-1.pdf => unlock-off.pdf} | Bin 1117 -> 1125 bytes BLEUnlock/Info.plist | 2 +- README.ja.md | 2 +- README.md | 4 ++-- icons/.gitignore | 1 + icons/mkicons | 3 +++ icons/unlock-outline.svg | 1 + 11 files changed, 17 insertions(+), 34 deletions(-) delete mode 100644 BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/lock-gray.pdf rename BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/{lock-gray-1.pdf => unlock-off.pdf} (62%) create mode 100644 icons/unlock-outline.svg diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 1cabecdc..7b28784c 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -222,8 +222,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } } - + func monitorDevice(uuid: UUID) { + connected = false + statusItem.button?.image = NSImage(named: "StatusBarDisconnected") monitorMenuItem?.title = t("not_detected") ble.startMonitor(uuid: uuid) } diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 8d55c543..9c1d53f3 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -49,7 +49,7 @@ - + @@ -385,23 +385,9 @@ THE SOFTWARE IS PROVIDED "AS IS" - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 -Included icon is derived from lock-open.svg downloaded from materialdesignicons.com - - - - - - - - - - - - - - +Included icons are derived from SVGs downloaded from materialdesignicons.com. They are originally designed by Google LLC and licensed under Apache License version 2.0. diff --git a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/Contents.json b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/Contents.json index fe003bd5..67bf9093 100644 --- a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/Contents.json +++ b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/Contents.json @@ -2,17 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "lock-gray.pdf" - }, - { - "idiom" : "universal", - "filename" : "lock-gray-1.pdf", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ] + "filename" : "unlock-off.pdf" } ], "info" : { @@ -20,7 +10,7 @@ "author" : "xcode" }, "properties" : { - "template-rendering-intent" : "original", + "template-rendering-intent" : "template", "preserves-vector-representation" : true } } \ No newline at end of file diff --git a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/lock-gray.pdf b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/lock-gray.pdf deleted file mode 100644 index c32b61fe609992a91cca95c22ed38294924b6e47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1116 zcmZWpTSyd9816z6gY{6@g}_eH;w9Xf+1c6EMGf6uFQm4ulA?x9vuD>)n_14B^@cAA zQBMh$_@G2YN>5Q?B(C0Z$lEmTDH5@;8yb7tLju`|P*Ip3V~|Nr-WKV$N^ zin4gK$e8Yax%rE+0uE@Wo0!~Oz?MNZfE$5GATI*|V2hL%3=x@IBn(|p)_jo3&u4Uu zpmc_5A9Q=_)XfKye=lcropqer+9OA{rg&fWEcA>l_dD*DR!=P5&Pz?Qq;>7zTbKHv zqVsrf_}kwPAmPr7ySg5}|x$ozEM7Bo6q8-5pd z*S@{HL!a4LGj^`vby~{eK>5y16_eNepXY8k@`B-{g+sM{GnvAp?xy02OGzaI&2y$* zi>!D)bEa&vJMd{Z65e(_we&}1` z$3GrR-t9!QB|pb-BNM6qGrPTY@JH*Nl>u{g!}qI4d?|i~fvRtnI(eg_QKjfiw6a(^ z`kk(XxTJ=N?si;UiHU$9Hh{kY^)!s$aJ++d^!KVHinD3q;0Gpi=3Cq-1J_SJN<{#G@Tks`@oRwKvSvv4pU}FxyUk KCMNf>BIX|q#ZxN) diff --git a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/lock-gray-1.pdf b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf similarity index 62% rename from BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/lock-gray-1.pdf rename to BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf index e9efb1a5da32f87db8ac10b93fee05f13ab164ae..df4958765bd32bb8c5725c94d2559612446aed95 100644 GIT binary patch delta 391 zcmcc1@swkNPkrbF$GoEoJTBkuTJ;0E8YL{;`U6~Fy>HUFbuWI^TgCmW7R^XEHe)_? zbD#J|h350!`!w8N{yw<={Y-g#6;oLq_IQ~qZ{zR$n`m)om+vcH-K$1CYkg*I5m$e2 zEvxX&ly`FO))nVF6Qxd-J$^JRgk5p7!2eof(IqQ4o)Xh*~Z;KPMog)2;EJ+~-2UX+> zBdtZm`&IrmP5c?#TO{wW^Q>H5cNo`|Bf?^8I%g_gdiZ?1()682Wd`rxZKc+hKPUf- zsl6cmm+3HLzB*TGUP^IE(PSq^2}aA!(Ts-}IZX|Wjf{>nwANsa&tp74CNNaMw7W&JUJ~)xKveL{oS|#LW+`p delta 403 zcmaFLahGF)PrYlPW8M)1jy<2b>;kSOteJU<}v)wc;U+y3sq!nmh*(t(7wk2eDK zD(plY&F87_UE%Tg`}faJZl^zduAe#8y4)^ucF1i8(XC$+`PgP%X1u4oIVVAPQ`r~i zMkc?!eGfi7(Aul-b#|v1!y2hW3)aldI`ZyvON(sinOs@!tqauG)pK0XO*(s{CiT3@ zLaPg1iCFBundleShortVersionString 1.4 CFBundleVersion - 315 + 323 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/README.ja.md b/README.ja.md index e3dcb063..0d2a98a4 100644 --- a/README.ja.md +++ b/README.ja.md @@ -60,7 +60,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス ## クレジット -アイコンはmaterialdesignicons.comからダウンロードしたlock-open.svgをもとにしています。これはGoogleによってデザインされApache License version 2.0でライセンスされています。 +アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 ## ライセンス diff --git a/README.md b/README.md index 9d214f30..f7ba3f23 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ and BLEUnlock unlocks. ## Acknowledgement -Icon is based on `lock-open.svg` downloaded from materialdesignicons.com, -which is originally designed by Google LLC and licensed under Apache License +Icons are based on SVGs downloaded from materialdesignicons.com. +They are originally designed by Google LLC and licensed under Apache License version 2.0. ## License diff --git a/icons/.gitignore b/icons/.gitignore index e33609d2..66335542 100644 --- a/icons/.gitignore +++ b/icons/.gitignore @@ -1 +1,2 @@ *.png +*.pdf diff --git a/icons/mkicons b/icons/mkicons index dfb06cfe..ed1738ac 100755 --- a/icons/mkicons +++ b/icons/mkicons @@ -3,3 +3,6 @@ for s in 16 32 64 128 256 512 1024; do svg2png -w $s -h $s unlock-with-circle.svg app-$s.png done svg2png -w 1280 -h 800 unlock-with-circle.svg app-preview.png + +svg2pdf -w 18 -h -18 unlock.svg unlock-on.pdf +svg2pdf -w 18 -h -18 unlock-outline.svg unlock-off.pdf diff --git a/icons/unlock-outline.svg b/icons/unlock-outline.svg new file mode 100644 index 00000000..2be8c819 --- /dev/null +++ b/icons/unlock-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file From 2f3f2db1dd02a0df705d5c48ae11889f2f772b21 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 3 May 2019 23:47:56 +0900 Subject: [PATCH 031/183] Release 1.4.1 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index d430ecb6..45ae23b6 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4 + 1.4.1 CFBundleVersion - 323 + 324 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 138eed79b3ca96c9adec1fa68f270ee02913329f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 4 May 2019 23:02:48 +0900 Subject: [PATCH 032/183] Invalidate signal timer when powered off --- BLEUnlock/BLE.swift | 2 ++ BLEUnlock/Info.plist | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index ad7eb43a..263bc1f4 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -122,6 +122,8 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { case .poweredOff: print("central powered off") presence = false + signalTimer?.invalidate() + signalTimer = nil default: break } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 45ae23b6..84c588dc 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.1 CFBundleVersion - 324 + 325 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 76c35c5d200ea5e0ef583e09a48eebf1ded96050 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 5 May 2019 01:07:17 +0900 Subject: [PATCH 033/183] Add Bluetooth power warning --- BLEUnlock/AppDelegate.swift | 9 ++++++++- BLEUnlock/BLE.swift | 11 +++++++++-- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 7b28784c..051cb942 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -84,6 +84,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } + func bluetoothPowerWarn() { + errorModal(t("bluetooth_power_warn")) + } + func notifyUser(_ reason: String) { let un = NSUserNotification() un.title = "BLEUnlock" @@ -234,6 +238,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate let alert = NSAlert() alert.messageText = msg alert.informativeText = info ?? "" + alert.window.title = "BLEUnlock" + NSApp.activate(ignoringOtherApps: true) alert.runModal() } @@ -289,7 +295,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate msg.addButton(withTitle: t("cancel")) msg.messageText = t("enter_password") msg.informativeText = t("password_info") - + msg.window.title = "BLEUnlock" + let txt = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 260, height: 20)) msg.accessoryView = txt txt.becomeFirstResponder() diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 263bc1f4..c122f071 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -60,6 +60,7 @@ protocol BLEDelegate { func removeDevice(device: Device) func updateRSSI(rssi: Int?) func updatePresence(presence: Bool, reason: String) + func bluetoothPowerWarn() } class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { @@ -76,6 +77,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var proximityTimeout = 8.5 var signalTimeout = 31.0 var lastReadAt = 0.0 + var powerWarn = true func startScanning() { scanMode = true @@ -114,16 +116,21 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: - print("central powered on") + print("Bluetooth powered on") centralMgr.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]) if let p = monitoredPeripheral { central.cancelPeripheralConnection(p) } + powerWarn = false case .poweredOff: - print("central powered off") + print("Bluetooth powered off") presence = false signalTimer?.invalidate() signalTimer = nil + if powerWarn { + powerWarn = false + delegate?.bluetoothPowerWarn() + } default: break } diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 1eda6471..65b4b2e9 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -18,3 +18,4 @@ "quit" = "Quit BLEUnlock"; "lock_now" = "Lock Screen Now"; "about" = "About BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 84c588dc..288a4291 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.1 CFBundleVersion - 325 + 333 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index abf79ae8..5664887d 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -18,3 +18,4 @@ "quit" = "BLEUnlock を終了"; "lock_now" = "今すぐロック"; "about" = "BLEUnlock について"; +"bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; From 6ca55cff2b3e77a9046c95e6a8ce420ac8dfb6e5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 5 May 2019 01:28:25 +0900 Subject: [PATCH 034/183] Add password not set warning --- BLEUnlock/AppDelegate.swift | 7 +++++-- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 051cb942..25f34261 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -186,7 +186,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate guard !systemSleep else { return } guard !displaySleep else { return } guard isScreenLocked() else { return } - guard let password = fetchPassword() else { return } + guard let password = fetchPassword(warn: true) else { return } print("Entering password") fakeKeyStrokes(password) @@ -262,7 +262,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } - func fetchPassword() -> String? { + func fetchPassword(warn: Bool = false) -> String? { let query: [String: Any] = [ String(kSecClass): kSecClassGenericPassword, String(kSecAttrAccount): NSUserName(), @@ -275,6 +275,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate let status = SecItemCopyMatching(query as CFDictionary, &item) if (status == errSecItemNotFound) { print("Password is not stored") + if warn { + errorModal(t("password_not_set")) + } return nil } guard status == errSecSuccess else { diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 65b4b2e9..dbe5a727 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -19,3 +19,4 @@ "lock_now" = "Lock Screen Now"; "about" = "About BLEUnlock"; "bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; +"password_not_set" = "Password is not set."; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 288a4291..46bf37f3 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.1 CFBundleVersion - 333 + 335 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 5664887d..44015b1c 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -19,3 +19,4 @@ "lock_now" = "今すぐロック"; "about" = "BLEUnlock について"; "bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; +"password_not_set" = "パスワードが設定されていません。"; From f8c8008bf89ab38a3b19b9060355d2097c51e224 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 5 May 2019 01:59:41 +0900 Subject: [PATCH 035/183] Retry waking display --- BLEUnlock/AppDelegate.swift | 7 +++++++ BLEUnlock/Info.plist | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 25f34261..ab72890e 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -22,6 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var userNotification: NSUserNotification? var iTunesWasPlaying = false var aboutBox: AboutBox? = nil + var wakeTimer: Timer? func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -143,6 +144,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate if displaySleep && !systemSleep && prefs.bool(forKey: "wakeOnProximity") { print("Waking display") wakeDisplay() + wakeTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in + print("Retrying waking display") + wakeDisplay() + }) } tryUnlockScreen() } else { @@ -196,6 +201,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func onDisplayWake() { print("display wake") displaySleep = false + wakeTimer?.invalidate() + wakeTimer = nil tryUnlockScreen() } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 46bf37f3..b1475f58 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.1 CFBundleVersion - 335 + 337 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From d1deb049af38a220a19de2cb948517e2d72ab894 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 6 May 2019 22:07:01 +0900 Subject: [PATCH 036/183] Release 1.4.2 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index b1475f58..6f7fecc4 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.1 + 1.4.2 CFBundleVersion - 337 + 339 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 04a6e66f58e9047fd0e210a8dfd6749b805c75f4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 23 May 2019 19:55:08 +0900 Subject: [PATCH 037/183] Delay unlocking after system sleep --- BLEUnlock/AppDelegate.swift | 7 +++++-- BLEUnlock/Info.plist | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index ab72890e..f05c4dc3 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -213,8 +213,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func onSystemWake() { print("system wake") - systemSleep = false - tryUnlockScreen() + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in + print("delayed system wake job") + self.systemSleep = false + self.tryUnlockScreen() + }) } @objc func onSystemSleep() { diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 6f7fecc4..ed60f3c6 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.2 CFBundleVersion - 339 + 340 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 7b8218240dbc6c33dae4d63659e4509590347659 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 23 May 2019 20:35:12 +0900 Subject: [PATCH 038/183] Unlock Screen Now really locks screen --- BLEUnlock/AppDelegate.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index f05c4dc3..25f38022 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -23,6 +23,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate var iTunesWasPlaying = false var aboutBox: AboutBox? = nil var wakeTimer: Timer? + var manualLock = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -159,6 +160,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate print("Failed to lock") } } + manualLock = false } } @@ -190,6 +192,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate guard ble.presence else { return } guard !systemSleep else { return } guard !displaySleep else { return } + guard !manualLock else { return } guard isScreenLocked() else { return } guard let password = fetchPassword(warn: true) else { return } @@ -224,7 +227,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate print("system sleep") systemSleep = true } - + + @objc func onUnlock() { + manualLock = false + print("unlock") + } + @objc func selectDevice(item: NSMenuItem) { for (uuid, menuItem) in deviceDict { if menuItem == item { @@ -352,11 +360,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func lockNow() { guard !isScreenLocked() else { return } + manualLock = true pauseItunes() lockScreen() - Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in - sleepDisplay() - }) } @objc func showAboutBox() { @@ -442,6 +448,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate nc.addObserver(self, selector: #selector(onSystemSleep), name: NSWorkspace.willSleepNotification, object: nil) nc.addObserver(self, selector: #selector(onSystemWake), name: NSWorkspace.didWakeNotification, object: nil) + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) + if fetchPassword() == nil { askPassword() } From 73e3c8fca748061c37571790e66786db1c7ae527 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 23 May 2019 20:45:08 +0900 Subject: [PATCH 039/183] Release 1.4.3 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index ed60f3c6..00203450 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.2 + 1.4.3 CFBundleVersion - 340 + 344 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From ba679c7a6209d9d06fd8ce20e5902b06e5235cd5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 3 Jun 2019 18:28:21 +0900 Subject: [PATCH 040/183] Delay one second on all unlock events --- BLEUnlock/AppDelegate.swift | 23 +++++++++++------------ BLEUnlock/Info.plist | 2 +- release | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 25f38022..b73ade2b 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -189,16 +189,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } func tryUnlockScreen() { + guard !manualLock else { return } guard ble.presence else { return } guard !systemSleep else { return } guard !displaySleep else { return } - guard !manualLock else { return } - guard isScreenLocked() else { return } - guard let password = fetchPassword(warn: true) else { return } - - print("Entering password") - fakeKeyStrokes(password) - playItunes() + Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in + guard self.isScreenLocked() else { return } + guard let password = self.fetchPassword(warn: true) else { return } + + print("Entering password") + self.fakeKeyStrokes(password) + self.playItunes() + }) } @objc func onDisplayWake() { @@ -216,11 +218,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func onSystemWake() { print("system wake") - Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in - print("delayed system wake job") - self.systemSleep = false - self.tryUnlockScreen() - }) + self.systemSleep = false + self.tryUnlockScreen() } @objc func onSystemSleep() { diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 00203450..cf297e35 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.4.3 CFBundleVersion - 344 + 353 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/release b/release index 2bbd14e4..425b9a1c 100755 --- a/release +++ b/release @@ -30,7 +30,7 @@ notarize() { --asc-provider $TEAM 2>&1 | tee $TMPDIR/altool.log uuid=$(awk '/^RequestUUID/ { print $3 }' $TMPDIR/altool.log) while true; do - sleep 10 + sleep 15 xcrun altool --notarization-info $uuid --username $USERNAME \ --password "@keychain:AC_PASSWORD" --asc-provider 376NR42P9A 2>&1 | tee $TMPDIR/altool.log From b2be4402ec80793100bc4f776fddef4fe90d5580 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 8 Jun 2019 21:01:50 +0900 Subject: [PATCH 041/183] Separate lock and unlock RSSI --- BLEUnlock/AppDelegate.swift | 78 +++++++++++++++++------- BLEUnlock/BLE.swift | 31 +++++----- BLEUnlock/Base.lproj/Localizable.strings | 3 +- BLEUnlock/ja.lproj/Localizable.strings | 3 +- 4 files changed, 76 insertions(+), 39 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index b73ade2b..517d4459 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -7,12 +7,13 @@ func t(_ key: String) -> String { } @NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate { +class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemValidation, BLEDelegate { let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let ble = BLE() let mainMenu = NSMenu() let deviceMenu = NSMenu() - let proximityMenu = NSMenu() + let lockRSSIMenu = NSMenu() + let unlockRSSIMenu = NSMenu() var deviceDict: [UUID: NSMenuItem] = [:] var monitorMenuItem : NSMenuItem? let prefs = UserDefaults.standard @@ -28,9 +29,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { ble.startScanning() - } else if menu == proximityMenu { + } else if menu == lockRSSIMenu { for item in menu.items { - if item.tag == ble.proximityRSSI { + if item.tag == ble.lockRSSI { + item.state = .on + } else { + item.state = .off + } + } + } else if menu == unlockRSSIMenu { + for item in menu.items { + if item.tag == ble.unlockRSSI { item.state = .on } else { item.state = .off @@ -38,6 +47,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate } } } + + func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { + if menuItem.menu == lockRSSIMenu { + return menuItem.tag <= ble.unlockRSSI + } else if menuItem.menu == unlockRSSIMenu { + return menuItem.tag >= ble.lockRSSI + } + return true + } func menuDidClose(_ menu: NSMenu) { if menu == deviceMenu { @@ -335,10 +353,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate prefs.set(value, forKey: "wakeOnProximity") } - @objc func setProximity(_ menuItem: NSMenuItem) { + @objc func setLockRSSI(_ menuItem: NSMenuItem) { + let value = menuItem.tag + prefs.set(value, forKey: "lockRSSI") + ble.lockRSSI = value + } + + @objc func setUnlockRSSI(_ menuItem: NSMenuItem) { let value = menuItem.tag - prefs.set(value, forKey: "proximity") - ble.proximityRSSI = value + prefs.set(value, forKey: "unlockRSSI") + ble.unlockRSSI = value } @objc func toggleLaunchAtLogin(_ menuItem: NSMenuItem) { @@ -367,6 +391,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate @objc func showAboutBox() { AboutBox.showAboutBox() } + + func constructRSSIMenu(_ menu: NSMenu, _ action: Selector) { + menu.addItem(withTitle: t("closer"), action: nil, keyEquivalent: "") + for proximity in stride(from: -50, to: -100, by: -10) { + let item = menu.addItem(withTitle: String(format: "%ddBm", proximity), action: action, keyEquivalent: "") + item.tag = proximity + } + menu.addItem(withTitle: t("farther"), action: nil, keyEquivalent: "") + menu.delegate = self + } func constructMenu() { monitorMenuItem = mainMenu.addItem(withTitle: t("device_not_set"), action: nil, keyEquivalent: "") @@ -380,17 +414,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate item.submenu = deviceMenu deviceMenu.delegate = self deviceMenu.addItem(withTitle: t("scanning"), action: nil, keyEquivalent: "") - - let proximityItem = mainMenu.addItem(withTitle: t("proximity_rssi"), action: nil, keyEquivalent: "") - proximityItem.submenu = proximityMenu - proximityMenu.addItem(withTitle: t("closer"), action: nil, keyEquivalent: "") - for proximity in stride(from: -50, to: -100, by: -10) { - let item = proximityMenu.addItem(withTitle: String(format: "%ddBm", proximity), action: #selector(setProximity), keyEquivalent: "") - item.tag = proximity - } - proximityMenu.addItem(withTitle: t("farther"), action: nil, keyEquivalent: "") - proximityMenu.delegate = self - + + let unlockRSSIItem = mainMenu.addItem(withTitle: t("unlock_rssi"), action: nil, keyEquivalent: "") + unlockRSSIItem.submenu = unlockRSSIMenu + constructRSSIMenu(unlockRSSIMenu, #selector(setUnlockRSSI)) + + let lockRSSIItem = mainMenu.addItem(withTitle: t("lock_rssi"), action: nil, keyEquivalent: "") + lockRSSIItem.submenu = lockRSSIMenu + constructRSSIMenu(lockRSSIMenu, #selector(setLockRSSI)) + item = mainMenu.addItem(withTitle: t("wake_on_proximity"), action: #selector(toggleWakeOnProximity), keyEquivalent: "") if prefs.bool(forKey: "wakeOnProximity") { item.state = .on @@ -436,9 +468,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, BLEDelegate monitorDevice(uuid: uuid) } } - let proximity = prefs.integer(forKey: "proximity") - if proximity != 0 { - ble.proximityRSSI = proximity + let lockRSSI = prefs.integer(forKey: "lockRSSI") + if lockRSSI != 0 { + ble.lockRSSI = lockRSSI + } + let unlockRSSI = prefs.integer(forKey: "unlockRSSI") + if unlockRSSI != 0 { + ble.unlockRSSI = unlockRSSI } let nc = NSWorkspace.shared.notificationCenter; diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index c122f071..1606bd64 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -73,8 +73,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var proximityTimer : Timer? var signalTimer: Timer? var presence = false - var proximityRSSI = -70 - var proximityTimeout = 8.5 + var lockRSSI = -80 + var unlockRSSI = -60 + var proximityTimeout = 4.5 var signalTimeout = 31.0 var lastReadAt = 0.0 var powerWarn = true @@ -143,8 +144,8 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } delegate?.updateRSSI(rssi: rssi) - if (rssi > proximityRSSI) { - if (!presence) { + if rssi > lockRSSI { + if rssi > unlockRSSI && !presence { print("Device is close") presence = true delegate?.updatePresence(presence: presence, reason: "close") @@ -154,18 +155,16 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { print("Proximity timer canceled") proximityTimer = nil } - } else if (presence) { - if proximityTimer == nil { - proximityTimer = Timer.scheduledTimer(withTimeInterval: proximityTimeout, repeats: false, block: { _ in - print("Device is away") - self.presence = false - self.delegate?.updatePresence(presence: self.presence, reason: "away") - self.proximityTimer = nil - }) - if let timer = proximityTimer { - RunLoop.main.add(timer, forMode: .common) - print("Proximity timer started") - } + } else if presence && proximityTimer == nil { + proximityTimer = Timer.scheduledTimer(withTimeInterval: proximityTimeout, repeats: false, block: { _ in + print("Device is away") + self.presence = false + self.delegate?.updatePresence(presence: self.presence, reason: "away") + self.proximityTimer = nil + }) + if let timer = proximityTimer { + RunLoop.main.add(timer, forMode: .common) + print("Proximity timer started") } } resetSignalTimer() diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index dbe5a727..657c4dd9 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -8,7 +8,8 @@ "device_not_set" = "Device not selected"; "device" = "Device"; "scanning" = "Scanning…"; -"proximity_rssi" = "Proximity Threshold"; +"lock_rssi" = "Lock RSSI"; +"unlock_rssi" = "Unlock RSSI"; "closer" = "⬆Closer"; "farther" = "⬇Farther"; "wake_on_proximity" = "Wake on Proximity"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 44015b1c..c186c6c3 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -8,7 +8,8 @@ "device_not_set" = "デバイス未選択"; "device" = "デバイス"; "scanning" = "スキャン中…"; -"proximity_rssi" = "信号強度しきい値"; +"lock_rssi" = "ロック信号強度"; +"unlock_rssi" = "アンロック信号強度"; "closer" = "⬆近い"; "farther" = "⬇遠い"; "wake_on_proximity" = "画面スリープから復帰"; From 045ebecf1d16d840d468162bfb2070fd51ea4585 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 8 Jun 2019 22:13:50 +0900 Subject: [PATCH 042/183] Add web links to about box --- BLEUnlock/AboutBox.swift | 7 ++++ BLEUnlock/Base.lproj/AboutBox.xib | 54 +++++++++++++++++++++-------- BLEUnlock/ja.lproj/AboutBox.strings | 5 ++- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/BLEUnlock/AboutBox.swift b/BLEUnlock/AboutBox.swift index 22808aa8..86fe9d4f 100644 --- a/BLEUnlock/AboutBox.swift +++ b/BLEUnlock/AboutBox.swift @@ -5,6 +5,13 @@ private var aboutBox: AboutBox? = nil class AboutBox: NSWindowController, NSWindowDelegate { @IBOutlet weak var versionLabel: NSTextField! + @IBAction func visitHomepage(_ sender: Any) { + NSWorkspace.shared.open(URL(string: "https://github.com/ts1/BLEUnlock#readme")!) + } + + @IBAction func checkReleases(_ sender: Any) { + NSWorkspace.shared.open(URL(string: "https://github.com/ts1/BLEUnlock/releases")!) + } convenience init() { self.init(windowNibName: "AboutBox") } diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 9c1d53f3..ec9d9f2f 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -16,18 +16,18 @@ - + - + - + - + @@ -35,7 +35,7 @@ - + @@ -43,17 +43,17 @@ - + - + - + - + @@ -404,29 +404,53 @@ Included icons are derived from SVGs downloaded from materialdesignicons.com. Th - + + + - + + + + - + + + - + - - + diff --git a/BLEUnlock/ja.lproj/AboutBox.strings b/BLEUnlock/ja.lproj/AboutBox.strings index 17d6cbe8..e3db5e2f 100644 --- a/BLEUnlock/ja.lproj/AboutBox.strings +++ b/BLEUnlock/ja.lproj/AboutBox.strings @@ -1,5 +1,4 @@ -/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ "VLW-23-BX0.title" = "BLEUnlock バージョン #{version}"; - -/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ "hgX-Z1-8qq.title" = "BLEUnlock について"; +"fPY-L3-Edh.title" = "ホームページ"; +"WIg-Gy-UeI.title" = "リリース情報"; From 00e00a984ac944126e799edfeac835f85862945a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 10 Jun 2019 12:34:38 +0900 Subject: [PATCH 043/183] Add update check --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++ BLEUnlock/AppDelegate.swift | 36 ++++++++++++++++-- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/checkUpdate.swift | 48 ++++++++++++++++++++++++ BLEUnlock/ja.lproj/Localizable.strings | 1 + 5 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 BLEUnlock/checkUpdate.swift diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index d1e34e04..9398a735 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D95379322784D550017D8B9 /* AboutBox.swift */; }; 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D953799227853E20017D8B9 /* AboutBox.xib */; }; 3DC0BC6D22729050001145EC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3DC0BC6F22729050001145EC /* InfoPlist.strings */; }; + 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */; }; 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD4B653226C1C3200451B7B /* AppDelegate.swift */; }; 3DD4B659226C1C3400451B7B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3DD4B657226C1C3400451B7B /* MainMenu.xib */; }; 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DD4B663226C1CF000451B7B /* lowlevel.c */; }; @@ -55,6 +56,7 @@ 3D95379B227853EC0017D8B9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/AboutBox.strings; sourceTree = ""; }; 3DC0BC6922728FF1001145EC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3DC0BC6E22729050001145EC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; + 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = checkUpdate.swift; sourceTree = ""; }; 3DD4B650226C1C3200451B7B /* BLEUnlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BLEUnlock.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3DD4B653226C1C3200451B7B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 3DD4B658226C1C3400451B7B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -128,6 +130,7 @@ 3DD4B653226C1C3200451B7B /* AppDelegate.swift */, 3DC0BC6922728FF1001145EC /* Info.plist */, 3DC0BC6F22729050001145EC /* InfoPlist.strings */, + 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */, 3DD4B662226C1CEF00451B7B /* lowlevel.h */, 3DD4B663226C1CF000451B7B /* lowlevel.c */, 3DD4B657226C1C3400451B7B /* MainMenu.xib */, @@ -305,6 +308,7 @@ 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */, 3DD4B666226C1DF200451B7B /* BLE.swift in Sources */, 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */, + 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */, 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */, 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */, ); diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 517d4459..ca78b211 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -7,7 +7,7 @@ func t(_ key: String) -> String { } @NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemValidation, BLEDelegate { +class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemValidation, NSUserNotificationCenterDelegate, BLEDelegate { let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let ble = BLE() let mainMenu = NSMenu() @@ -122,6 +122,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa userNotification = un } + func userNotificationCenter(_ center: NSUserNotificationCenter, + shouldPresent notification: NSUserNotification) -> Bool { + return true + } + + func userNotificationCenter(_ center: NSUserNotificationCenter, + didActivate notification: NSUserNotification) { + if notification != userNotification { + NSWorkspace.shared.open(URL(string: "https://github.com/ts1/BLEUnlock/releases")!) + NSUserNotificationCenter.default.removeDeliveredNotification(notification) + } + } + func runAppleScript(_ script: String) -> NSAppleEventDescriptor? { guard let scriptObject = NSAppleScript(source: script) else { return nil } var error: NSDictionary? @@ -227,6 +240,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa wakeTimer?.invalidate() wakeTimer = nil tryUnlockScreen() + Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in + if self.displaySleep == false { + checkUpdate() + } + }) } @objc func onDisplaySleep() { @@ -236,8 +254,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onSystemWake() { print("system wake") - self.systemSleep = false - self.tryUnlockScreen() + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in + print("delayed system wake job") + self.systemSleep = false + self.tryUnlockScreen() + }) } @objc func onSystemSleep() { @@ -477,6 +498,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa ble.unlockRSSI = unlockRSSI } + NSUserNotificationCenter.default.delegate = self + let nc = NSWorkspace.shared.notificationCenter; nc.addObserver(self, selector: #selector(onDisplaySleep), name: NSWorkspace.screensDidSleepNotification, object: nil) nc.addObserver(self, selector: #selector(onDisplayWake), name: NSWorkspace.screensDidWakeNotification, object: nil) @@ -489,6 +512,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa askPassword() } checkAccessibility() + + Timer.scheduledTimer(withTimeInterval: 60*60, repeats: true, block: { _ in + if self.displaySleep == false { + checkUpdate() + } + }) + checkUpdate() } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 657c4dd9..ef926a75 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -21,3 +21,4 @@ "about" = "About BLEUnlock"; "bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; "password_not_set" = "Password is not set."; +"notification_update_available" = "Update is available."; diff --git a/BLEUnlock/checkUpdate.swift b/BLEUnlock/checkUpdate.swift new file mode 100644 index 00000000..a27aa663 --- /dev/null +++ b/BLEUnlock/checkUpdate.swift @@ -0,0 +1,48 @@ +import Cocoa + +private let KEY = "lastUpdateCheck" +private let INTERVAL = 24.0 * 60 * 60 +private var notified = false +private var lastCheckAt = UserDefaults.standard.double(forKey: KEY) + +func checkUpdate() { + guard !notified else { return } + let now = NSDate().timeIntervalSince1970 + guard now - lastCheckAt >= INTERVAL else { return } + doCheckUpdate() +} + +private func doCheckUpdate() { + var request = URLRequest(url: URL(string: "https://api.github.com/repos/ts1/BLEUnlock/releases/latest")!) + request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData + let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in + if let jsondata = data { + if let json = try? JSONSerialization.jsonObject(with: jsondata) { + if let dict = json as? [String:Any] { + if let version = dict["tag_name"] as? String { + lastCheckAt = NSDate().timeIntervalSince1970 + UserDefaults.standard.set(lastCheckAt, forKey: KEY) + compareVersionsAndNotify(version) + } + } + } + } + }) + task.resume() +} + +private func compareVersionsAndNotify(_ latestVersion: String) { + if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String { + if version != latestVersion { + notify() + notified = true + } + } +} + +private func notify() { + let un = NSUserNotification() + un.title = "BLEUnlock" + un.informativeText = t("notification_update_available") + NSUserNotificationCenter.default.deliver(un) +} diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index c186c6c3..8ad2cce1 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -21,3 +21,4 @@ "about" = "BLEUnlock について"; "bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; "password_not_set" = "パスワードが設定されていません。"; +"notification_update_available" = "アップデートが利用可能です。"; From 8078b72c485042c888f6c08dca4e3e371c6774f1 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 6 Jul 2019 16:48:41 +0900 Subject: [PATCH 044/183] Tweak update notification --- BLEUnlock/AppDelegate.swift | 19 +++++-------------- BLEUnlock/checkUpdate.swift | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 2 +- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index ca78b211..faf06449 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -224,7 +224,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard ble.presence else { return } guard !systemSleep else { return } guard !displaySleep else { return } - Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in guard self.isScreenLocked() else { return } guard let password = self.fetchPassword(warn: true) else { return } @@ -240,11 +240,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa wakeTimer?.invalidate() wakeTimer = nil tryUnlockScreen() - Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in - if self.displaySleep == false { - checkUpdate() - } - }) } @objc func onDisplaySleep() { @@ -254,7 +249,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onSystemWake() { print("system wake") - Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in + Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in print("delayed system wake job") self.systemSleep = false self.tryUnlockScreen() @@ -268,7 +263,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onUnlock() { manualLock = false - print("unlock") + Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { _ in + checkUpdate() + }) } @objc func selectDevice(item: NSMenuItem) { @@ -512,12 +509,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa askPassword() } checkAccessibility() - - Timer.scheduledTimer(withTimeInterval: 60*60, repeats: true, block: { _ in - if self.displaySleep == false { - checkUpdate() - } - }) checkUpdate() } diff --git a/BLEUnlock/checkUpdate.swift b/BLEUnlock/checkUpdate.swift index a27aa663..adb38d6c 100644 --- a/BLEUnlock/checkUpdate.swift +++ b/BLEUnlock/checkUpdate.swift @@ -43,6 +43,6 @@ private func compareVersionsAndNotify(_ latestVersion: String) { private func notify() { let un = NSUserNotification() un.title = "BLEUnlock" - un.informativeText = t("notification_update_available") + un.subtitle = t("notification_update_available") NSUserNotificationCenter.default.deliver(un) } diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 8ad2cce1..3797909a 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -21,4 +21,4 @@ "about" = "BLEUnlock について"; "bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; "password_not_set" = "パスワードが設定されていません。"; -"notification_update_available" = "アップデートが利用可能です。"; +"notification_update_available" = "アップデートが利用可能です"; From 690142927ca9fb8363599e467d8dfc2ca317c8ba Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 6 Jul 2019 17:08:15 +0900 Subject: [PATCH 045/183] Add passive mode --- BLEUnlock/AppDelegate.swift | 11 +++++++++++ BLEUnlock/BLE.swift | 7 +++++-- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/ja.lproj/Localizable.strings | 1 + 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index faf06449..ea6b03dd 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -399,6 +399,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } + @objc func togglePassiveMode(_ menuItem: NSMenuItem) { + let passiveMode = !prefs.bool(forKey: "passiveMode") + prefs.set(passiveMode, forKey: "passiveMode") + menuItem.state = passiveMode ? .on : .off + ble.passiveMode = passiveMode + } + @objc func lockNow() { guard !isScreenLocked() else { return } manualLock = true @@ -453,6 +460,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa mainMenu.addItem(withTitle: t("set_password"), action: #selector(askPassword), keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("passive_mode"), action: #selector(togglePassiveMode), keyEquivalent: "") + item.state = prefs.bool(forKey: "passiveMode") ? .on : .off + item = mainMenu.addItem(withTitle: t("launch_at_login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") item.state = prefs.bool(forKey: "launchAtLogin") ? .on : .off @@ -494,6 +504,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if unlockRSSI != 0 { ble.unlockRSSI = unlockRSSI } + ble.passiveMode = prefs.bool(forKey: "passiveMode") NSUserNotificationCenter.default.delegate = self diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 1606bd64..a2644347 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -79,6 +79,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var signalTimeout = 31.0 var lastReadAt = 0.0 var powerWarn = true + var passiveMode = false func startScanning() { scanMode = true @@ -139,7 +140,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func updateMonitoredPeripheral(_ rssi: Int) { if let p = monitoredPeripheral { - if (!scanMode && p.state == .disconnected) { + if !scanMode && p.state == .disconnected && !passiveMode { centralMgr.connect(p, options: nil) } } @@ -227,7 +228,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { { peripheral.delegate = self peripheral.discoverServices([DeviceInformation]) - if peripheral == monitoredPeripheral { + if peripheral == monitoredPeripheral && !passiveMode { peripheral.readRSSI() lastReadAt = Date().timeIntervalSince1970 } @@ -235,11 +236,13 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { guard peripheral == monitoredPeripheral else { return } + guard !passiveMode else { return } central.connect(peripheral, options: nil) } func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { guard peripheral == monitoredPeripheral else { return } + guard !passiveMode else { return } central.connect(peripheral, options: nil) } diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index ef926a75..5b3c16e5 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -22,3 +22,4 @@ "bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; "password_not_set" = "Password is not set."; "notification_update_available" = "Update is available."; +"passive_mode" = "Passive Mode"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 3797909a..f0a9a3d8 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -22,3 +22,4 @@ "bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; "password_not_set" = "パスワードが設定されていません。"; "notification_update_available" = "アップデートが利用可能です"; +"passive_mode" = "パッシブモード"; From cc85b8d60a84a190b7383f951b4bc8234311e74e Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 6 Jul 2019 17:24:25 +0900 Subject: [PATCH 046/183] Update README --- README.ja.md | 12 ++++++++++++ README.md | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/README.ja.md b/README.ja.md index 0d2a98a4..8e48eb66 100644 --- a/README.ja.md +++ b/README.ja.md @@ -58,6 +58,18 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス これはデバイスとのコネクションが切れ、デバイスが信号を送ってくるのを待っているためです。 デバイス側もスリープ中は信号送出の間隔が長いので、デバイスのスリープを解除すれば、大抵の場合すぐに信号が送られ、ロック解除されます。 +### パッシブモードとBluetoothインターネット共有 + +BLEUnlockはデフォルトで能動的にデバイスに接続して信号強度を読み取ります。 +これはiPhoneなどサポートされているデバイスでは最も安定して信号強度を取得する方法です。 +しかしながら、この方法はBluetoothインターネット共有と相性良くありません。 + +パッシブモードではデバイスから発せられる信号を受動的に受け取ります。 +これはBluetoothインターネット共有と干渉しません。 + +Bluetoothインターネット共有を使う場合、パッシブモードをオンにしてください。 +そうでなければオフにしてください。 + ## クレジット アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 diff --git a/README.md b/README.md index f7ba3f23..0fd40321 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,21 @@ Usually, devices send signal less frequently when it is in sleep mode. Thus, if you wake the device, in most cases it sends signal promptly, and BLEUnlock unlocks. +### Passive mode and Bluetooth Internet Sharing + +By default BLEUnlock actively connects to the device to read RSSI +(signal strength). +It is the best way to steadily read RSSI for devices such as iPhone that +support it. +However, it does not play nice with Bluetooth Internet Sharing. + +With Passive Mode, BLEUnlock only passively receives signals that the deveice +broadcasts. +That does not interfere with Bluetooth Internet Sharing. + +If you use Bluetooth Internet Sharing on the same device, turn Passive Mode on. +If you don't, turn it off. + ## Acknowledgement Icons are based on SVGs downloaded from materialdesignicons.com. From a3def9d2a7301fadfbb37356fc7f8a2a33cd5433 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 6 Jul 2019 18:17:57 +0900 Subject: [PATCH 047/183] 1.5.0 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index cf297e35..a3ff576b 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.3 + 1.5.0 CFBundleVersion - 353 + 473 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 7bfe96dacf75bb04a387570a976bb30ef3b5796e Mon Sep 17 00:00:00 2001 From: peiit Date: Thu, 18 Jul 2019 13:41:10 +0800 Subject: [PATCH 048/183] Improve Chinese translation --- .gitignore | 55 ++++++++++++++++++++- BLEUnlock.xcodeproj/project.pbxproj | 9 ++++ BLEUnlock/BLE.swift | 5 ++ BLEUnlock/Info.plist | 2 +- BLEUnlock/zh-Hans.lproj/AboutBox.strings | 15 ++++++ BLEUnlock/zh-Hans.lproj/InfoPlist.strings | 1 + BLEUnlock/zh-Hans.lproj/Localizable.strings | 25 ++++++++++ BLEUnlock/zh-Hans.lproj/MainMenu.strings | 1 + 8 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 BLEUnlock/zh-Hans.lproj/AboutBox.strings create mode 100644 BLEUnlock/zh-Hans.lproj/InfoPlist.strings create mode 100644 BLEUnlock/zh-Hans.lproj/Localizable.strings create mode 100644 BLEUnlock/zh-Hans.lproj/MainMenu.strings diff --git a/.gitignore b/.gitignore index 73ef9b50..414ba236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,53 @@ -/build -/BLEUnlock.xcodeproj/xcuserdata +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md + +fastlane/report.xml +fastlane/screenshots diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 9398a735..92797fde 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -66,6 +66,10 @@ 3DD4B663226C1CF000451B7B /* lowlevel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = lowlevel.c; sourceTree = ""; }; 3DD4B665226C1DF200451B7B /* BLE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLE.swift; sourceTree = ""; }; 3DD4B668226C1E3700451B7B /* login.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = login.framework; path = ../../../../System/Library/PrivateFrameworks/login.framework; sourceTree = ""; }; + B1EFF91422E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutBox.strings"; sourceTree = ""; }; + B1EFF91522E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; + B1EFF91622E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + B1EFF91722E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -236,6 +240,7 @@ en, Base, ja, + "zh-Hans", ); mainGroup = 3DD4B647226C1C3200451B7B; productRefGroup = 3DD4B651226C1C3200451B7B /* Products */; @@ -322,6 +327,7 @@ children = ( 3D600A36226CC1A40068FB7B /* Base */, 3D600A38226CC1AA0068FB7B /* ja */, + B1EFF91622E010F50010DB0A /* zh-Hans */, ); name = Localizable.strings; sourceTree = ""; @@ -331,6 +337,7 @@ children = ( 3D953798227853E20017D8B9 /* Base */, 3D95379B227853EC0017D8B9 /* ja */, + B1EFF91422E010F50010DB0A /* zh-Hans */, ); name = AboutBox.xib; sourceTree = ""; @@ -340,6 +347,7 @@ children = ( 3DC0BC6E22729050001145EC /* ja */, 3D1B377B22772B740024E2E0 /* Base */, + B1EFF91722E010F50010DB0A /* zh-Hans */, ); name = InfoPlist.strings; sourceTree = ""; @@ -348,6 +356,7 @@ isa = PBXVariantGroup; children = ( 3DD4B658226C1C3400451B7B /* Base */, + B1EFF91522E010F50010DB0A /* zh-Hans */, ); name = MainMenu.xib; sourceTree = ""; diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index a2644347..aa66cda2 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -223,6 +223,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } + //MARK:- CBCentralManagerDelegate start func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { @@ -246,6 +247,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { central.connect(peripheral, options: nil) } + //MARK:CBCentralManagerDelegate end - + + //MARK:- CBPeripheralDelegate start func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { guard peripheral == monitoredPeripheral else { return } let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue @@ -323,6 +327,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { { peripheral.discoverServices([DeviceInformation]) } + //MARK:CBPeripheralDelegate end - override init() { super.init() diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index a3ff576b..542afff0 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.5.0 CFBundleVersion - 473 + 489 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/zh-Hans.lproj/AboutBox.strings b/BLEUnlock/zh-Hans.lproj/AboutBox.strings new file mode 100644 index 00000000..ada6a31c --- /dev/null +++ b/BLEUnlock/zh-Hans.lproj/AboutBox.strings @@ -0,0 +1,15 @@ + +/* Class = "NSTextFieldCell"; title = "Copyright © 2019 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ +"GN4-8z-o8I.title" = "Copyright © 2019 Takeshi Sone"; + +/* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ +"VLW-23-BX0.title" = "BLEUnlock 版本 #{version}"; + +/* Class = "NSButtonCell"; title = "Check Releases"; ObjectID = "WIg-Gy-UeI"; */ +"WIg-Gy-UeI.title" = "版本检查"; + +/* Class = "NSButtonCell"; title = "Visit Homepage"; ObjectID = "fPY-L3-Edh"; */ +"fPY-L3-Edh.title" = "访问主页"; + +/* Class = "NSWindow"; title = "About BLEUnlock"; ObjectID = "hgX-Z1-8qq"; */ +"hgX-Z1-8qq.title" = "关于BLEUnlock"; diff --git a/BLEUnlock/zh-Hans.lproj/InfoPlist.strings b/BLEUnlock/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 00000000..9192c666 --- /dev/null +++ b/BLEUnlock/zh-Hans.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"NSAppleEventsUsageDescription" = "锁定/解锁屏幕时,BLEUnlock将暂停/继续iTunes播放。"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings new file mode 100644 index 00000000..33203180 --- /dev/null +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,25 @@ +"not_detected" = "未检测到信息"; +"notification_lost_signal" = "信号丢失"; +"notification_device_away" = "设备掉线"; +"notification_locked" = "本电脑已被BLEUnlock锁定"; +"cancel" = "取消"; +"enter_password" = "输入密码以解锁电脑。"; +"password_info" = "您的密码将安全地存储在钥匙串中。"; +"device_not_set" = "未选择设备"; +"device" = "设备列表"; +"scanning" = "扫描中…"; +"lock_rssi" = "锁定 RSSI"; +"unlock_rssi" = "解锁 RSSI"; +"closer" = "⬆接近"; +"farther" = "⬇远离"; +"wake_on_proximity" = "靠近唤醒"; +"pause_itunes" = "锁定时暂停iTunes"; +"set_password" = "设置密码…"; +"launch_at_login" = "开机启动"; +"quit" = "退出BLEUnlock"; +"lock_now" = "立刻锁定屏幕"; +"about" = "关于BLEUnlock"; +"bluetooth_power_warn" = "蓝牙已关闭。要使BLEUnlock正常工作,请将其打开。"; +"password_not_set" = "密码未设置。"; +"notification_update_available" = "发现新版本。"; +"passive_mode" = "被动模式"; diff --git a/BLEUnlock/zh-Hans.lproj/MainMenu.strings b/BLEUnlock/zh-Hans.lproj/MainMenu.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/BLEUnlock/zh-Hans.lproj/MainMenu.strings @@ -0,0 +1 @@ + From a41b223e006d8e53a31b794be5207152b5a91fb0 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 4 Aug 2019 16:00:20 +0900 Subject: [PATCH 049/183] Removed unnecessary file --- BLEUnlock.xcodeproj/project.pbxproj | 2 -- BLEUnlock/Info.plist | 2 +- BLEUnlock/zh-Hans.lproj/MainMenu.strings | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 BLEUnlock/zh-Hans.lproj/MainMenu.strings diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 92797fde..dedf08b7 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -67,7 +67,6 @@ 3DD4B665226C1DF200451B7B /* BLE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLE.swift; sourceTree = ""; }; 3DD4B668226C1E3700451B7B /* login.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = login.framework; path = ../../../../System/Library/PrivateFrameworks/login.framework; sourceTree = ""; }; B1EFF91422E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutBox.strings"; sourceTree = ""; }; - B1EFF91522E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; B1EFF91622E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; B1EFF91722E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -356,7 +355,6 @@ isa = PBXVariantGroup; children = ( 3DD4B658226C1C3400451B7B /* Base */, - B1EFF91522E010F50010DB0A /* zh-Hans */, ); name = MainMenu.xib; sourceTree = ""; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 542afff0..01bdc593 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.5.0 CFBundleVersion - 489 + 491 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/zh-Hans.lproj/MainMenu.strings b/BLEUnlock/zh-Hans.lproj/MainMenu.strings deleted file mode 100644 index 8b137891..00000000 --- a/BLEUnlock/zh-Hans.lproj/MainMenu.strings +++ /dev/null @@ -1 +0,0 @@ - From 488eec4d457c89f321a4e07e69cf0e1a759517fd Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 4 Aug 2019 16:01:24 +0900 Subject: [PATCH 050/183] 1.5.1 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 01bdc593..db164c03 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.0 + 1.5.1 CFBundleVersion - 491 + 492 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 8edfb209852a49a386eeb8f92e1a403894b587a5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 4 Aug 2019 16:02:35 +0900 Subject: [PATCH 051/183] Update README --- README.ja.md | 2 ++ README.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.ja.md b/README.ja.md index 8e48eb66..3703df18 100644 --- a/README.ja.md +++ b/README.ja.md @@ -74,6 +74,8 @@ Bluetoothインターネット共有を使う場合、パッシブモードを アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 +中国語の翻訳は @peiit さんによって行われました。 + ## ライセンス MIT diff --git a/README.md b/README.md index 0fd40321..24f68bee 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,14 @@ That does not interfere with Bluetooth Internet Sharing. If you use Bluetooth Internet Sharing on the same device, turn Passive Mode on. If you don't, turn it off. -## Acknowledgement +## Credits Icons are based on SVGs downloaded from materialdesignicons.com. They are originally designed by Google LLC and licensed under Apache License version 2.0. +Chinese translation by @peiit. + ## License MIT From 742676846f93f3a24befe9ae8df0c4cc23d394fd Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Oct 2019 18:13:35 +0900 Subject: [PATCH 052/183] Control Music.app instead of iTunes on Catalina (#2) --- BLEUnlock/AppDelegate.swift | 20 ++++++++++++++++---- BLEUnlock/Base.lproj/InfoPlist.strings | 2 +- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/InfoPlist.strings | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/zh-Hans.lproj/InfoPlist.strings | 2 +- BLEUnlock/zh-Hans.lproj/Localizable.strings | 1 + 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index ea6b03dd..8aae1bb6 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -146,8 +146,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa return output } + func iTunesName() -> String { + if #available(OSX 10.15, *) { + return "Music" + } else { + return "iTunes" + } + } + func isItunesPlaying() -> Bool { - let result = runAppleScript("tell application \"iTunes\" to return player state") + let result = runAppleScript("tell application \"\(iTunesName())\" to return player state") return result?.stringValue == "kPSP" } @@ -155,14 +163,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard prefs.bool(forKey: "pauseItunes") else { return } iTunesWasPlaying = isItunesPlaying() if iTunesWasPlaying { - _ = runAppleScript("tell application \"iTunes\" to pause") + _ = runAppleScript("tell application \"\(iTunesName())\" to pause") } } func playItunes() { guard prefs.bool(forKey: "pauseItunes") else { return } if iTunesWasPlaying { - _ = runAppleScript("tell application \"iTunes\" to play") + _ = runAppleScript("tell application \"\(iTunesName())\" to play") iTunesWasPlaying = false } } @@ -453,7 +461,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .on } - item = mainMenu.addItem(withTitle: t("pause_itunes"), action: #selector(togglePauseItunes), keyEquivalent: "") + var pause_itunes = "pause_itunes" + if #available(OSX 10.15, *) { + pause_itunes = "pause_music" + } + item = mainMenu.addItem(withTitle: t(pause_itunes), action: #selector(togglePauseItunes), keyEquivalent: "") if prefs.bool(forKey: "pauseItunes") { item.state = .on } diff --git a/BLEUnlock/Base.lproj/InfoPlist.strings b/BLEUnlock/Base.lproj/InfoPlist.strings index 220d80bd..c7181a34 100644 --- a/BLEUnlock/Base.lproj/InfoPlist.strings +++ b/BLEUnlock/Base.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses iTunes playback upon locking/unlocking screen."; +"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses Music (iTunes) playback upon locking/unlocking screen."; diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 5b3c16e5..c50f9f86 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -14,6 +14,7 @@ "farther" = "⬇Farther"; "wake_on_proximity" = "Wake on Proximity"; "pause_itunes" = "Pause iTunes while Locked"; +"pause_music" = "Pause Music while Locked"; "set_password" = "Set Password…"; "launch_at_login" = "Launch at Login"; "quit" = "Quit BLEUnlock"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index db164c03..0afefdd7 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.5.1 CFBundleVersion - 492 + 496 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/InfoPlist.strings b/BLEUnlock/ja.lproj/InfoPlist.strings index 1919d2f4..be05ed8b 100644 --- a/BLEUnlock/ja.lproj/InfoPlist.strings +++ b/BLEUnlock/ja.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "画面ロック/解除時に iTunes の一時停止/解除を行います。"; +"NSAppleEventsUsageDescription" = "画面ロック/解除時にミュージック(iTunes)の一時停止/解除を行います。"; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index f0a9a3d8..e6b81657 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -14,6 +14,7 @@ "farther" = "⬇遠い"; "wake_on_proximity" = "画面スリープから復帰"; "pause_itunes" = "ロック中 iTunes を一時停止"; +"pause_music" = "ロック中ミュージックを一時停止"; "set_password" = "パスワードを設定…"; "launch_at_login" = "ログイン時に起動"; "quit" = "BLEUnlock を終了"; diff --git a/BLEUnlock/zh-Hans.lproj/InfoPlist.strings b/BLEUnlock/zh-Hans.lproj/InfoPlist.strings index 9192c666..f9daf712 100644 --- a/BLEUnlock/zh-Hans.lproj/InfoPlist.strings +++ b/BLEUnlock/zh-Hans.lproj/InfoPlist.strings @@ -1 +1 @@ -"NSAppleEventsUsageDescription" = "锁定/解锁屏幕时,BLEUnlock将暂停/继续iTunes播放。"; +"NSAppleEventsUsageDescription" = "锁定/解锁屏幕时,BLEUnlock将暂停/继续Music (iTunes)播放。"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 33203180..380708ca 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -14,6 +14,7 @@ "farther" = "⬇远离"; "wake_on_proximity" = "靠近唤醒"; "pause_itunes" = "锁定时暂停iTunes"; +"pause_music" = "锁定时暂停Music"; "set_password" = "设置密码…"; "launch_at_login" = "开机启动"; "quit" = "退出BLEUnlock"; From 914be66557aaadb33ce9272d36705d01f3a98f27 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Oct 2019 23:23:06 +0900 Subject: [PATCH 053/183] 1.5.2 --- BLEUnlock/Info.plist | 4 ++-- release | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 0afefdd7..5b7da9a8 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.1 + 1.5.2 CFBundleVersion - 496 + 498 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/release b/release index 425b9a1c..7399c35e 100755 --- a/release +++ b/release @@ -30,12 +30,12 @@ notarize() { --asc-provider $TEAM 2>&1 | tee $TMPDIR/altool.log uuid=$(awk '/^RequestUUID/ { print $3 }' $TMPDIR/altool.log) while true; do - sleep 15 + sleep 5 xcrun altool --notarization-info $uuid --username $USERNAME \ --password "@keychain:AC_PASSWORD" --asc-provider 376NR42P9A 2>&1 | - tee $TMPDIR/altool.log + tee $TMPDIR/altool.log || true status=$(grep Status: $TMPDIR/altool.log | sed 's/^.*Status: *//') - if [ "$status" != "in progress" ]; then + if [ "$status" ] && [ "$status" != "in progress" ]; then break fi done From 8754a4edd3ef1f38ffde4f2e0941db8fcb616e71 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 1 Nov 2019 19:20:34 +0800 Subject: [PATCH 054/183] change build config --- BLEUnlock.xcodeproj/project.pbxproj | 24 ++++++++++++------------ BLEUnlock/Info.plist | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index dedf08b7..310a0b53 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -366,10 +366,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = P89FPAP885; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -377,7 +377,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; + PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -389,11 +389,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = P89FPAP885; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -401,7 +401,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; + PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -531,10 +531,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = P89FPAP885; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -542,7 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; + PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "BLEUnlock/BLEUnlock-Bridging-Header.h"; @@ -561,10 +561,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = P89FPAP885; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -572,7 +572,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; + PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "BLEUnlock/BLEUnlock-Bridging-Header.h"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 0afefdd7..fb8f4552 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.5.1 CFBundleVersion - 496 + 497 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From ae9d1222c3ffb64e9764820cd48b713b5d0370ee Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Sat, 2 Nov 2019 11:49:24 +0800 Subject: [PATCH 055/183] support set minimum rssi and use moving average of rssi --- BLEUnlock.xcodeproj/project.pbxproj | 8 ++-- BLEUnlock/AppDelegate.swift | 35 ++++++++++++++-- BLEUnlock/BLE.swift | 46 +++++++++++++++++---- BLEUnlock/Base.lproj/Localizable.strings | 4 ++ BLEUnlock/Info.plist | 2 +- BLEUnlock/zh-Hans.lproj/Localizable.strings | 4 ++ 6 files changed, 82 insertions(+), 17 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 310a0b53..54bcc7e5 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -369,7 +369,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = P89FPAP885; + DEVELOPMENT_TEAM = 6F43YBKR55; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -393,7 +393,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = P89FPAP885; + DEVELOPMENT_TEAM = 6F43YBKR55; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -534,7 +534,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = P89FPAP885; + DEVELOPMENT_TEAM = 6F43YBKR55; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -564,7 +564,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = P89FPAP885; + DEVELOPMENT_TEAM = 6F43YBKR55; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 8aae1bb6..87b200f3 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -355,7 +355,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func askPassword() { let msg = NSAlert() - msg.addButton(withTitle: "OK") + msg.addButton(withTitle: t("ok")) msg.addButton(withTitle: t("cancel")) msg.messageText = t("enter_password") msg.informativeText = t("password_info") @@ -372,6 +372,28 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa storePassword(pw) } } + + @objc func setRSSIThreshold() { + let msg = NSAlert() + msg.addButton(withTitle: t("ok")) + msg.addButton(withTitle: t("cancel")) + msg.messageText = t("enter_rssi_threshold") + msg.informativeText = t("enter_rssi_threshold_info") + msg.window.title = "BLEUnlock" + + let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 260, height: 20)) + txt.placeholderString = String(ble.thresholdRSSI) + msg.accessoryView = txt + txt.becomeFirstResponder() + NSApp.activate(ignoringOtherApps: true) + let response = msg.runModal() + + if (response == .alertFirstButtonReturn) { + let val = txt.intValue + ble.thresholdRSSI = Int(val) + prefs.set(val, forKey: "thresholdRSSI") + } + } @objc func toggleWakeOnProximity(_ menuItem: NSMenuItem) { let value = !prefs.bool(forKey: "wakeOnProximity") @@ -427,7 +449,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func constructRSSIMenu(_ menu: NSMenu, _ action: Selector) { menu.addItem(withTitle: t("closer"), action: nil, keyEquivalent: "") - for proximity in stride(from: -50, to: -100, by: -10) { + for proximity in stride(from: -50, to: -100, by: -5) { let item = menu.addItem(withTitle: String(format: "%ddBm", proximity), action: action, keyEquivalent: "") item.tag = proximity } @@ -477,9 +499,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item = mainMenu.addItem(withTitle: t("launch_at_login"), action: #selector(toggleLaunchAtLogin), keyEquivalent: "") item.state = prefs.bool(forKey: "launchAtLogin") ? .on : .off + + mainMenu.addItem(withTitle: t("set_rssi_threshold"), action: #selector(setRSSIThreshold), + keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) - mainMenu.addItem(withTitle: t("about"), action: #selector(showAboutBox), keyEquivalent: "") + // mainMenu.addItem(withTitle: t("about"), action: #selector(showAboutBox), keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) mainMenu.addItem(withTitle: t("quit"), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") statusItem.menu = mainMenu @@ -517,6 +542,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa ble.unlockRSSI = unlockRSSI } ble.passiveMode = prefs.bool(forKey: "passiveMode") + let thresholdRSSI = prefs.integer(forKey: "thresholdRSSI") + if thresholdRSSI != 0 { + ble.thresholdRSSI = thresholdRSSI + } NSUserNotificationCenter.default.delegate = self diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index aa66cda2..55eeb611 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -1,5 +1,6 @@ import Foundation import CoreBluetooth +import Accelerate class Device: NSObject { let uuid : UUID! @@ -80,6 +81,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var lastReadAt = 0.0 var powerWarn = true var passiveMode = false + var thresholdRSSI = -80 + var latestRSSIs: [Double] = [] + var latestN: Int = 5 func startScanning() { scanMode = true @@ -137,16 +141,38 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { break } } + + func getEstimatedRSSI(rssi: Int) -> Double { + if (latestRSSIs.count < latestN - 1) { + latestRSSIs.append(Double(rssi)) + return 1.0 + Double(unlockRSSI) + } + if (latestRSSIs.count < latestN) { + latestRSSIs.append(Double(rssi)) + } else { + latestRSSIs.removeFirst() + latestRSSIs.append(Double(rssi)) + } + var mean: Double = 0.0 + var sddev: Double = 0.0 + vDSP_normalizeD(latestRSSIs, 1, nil, 1, &mean, &sddev, vDSP_Length(latestRSSIs.count)) + // sddev *= sqrt(Double(latestRSSIs.count)/Double(latestRSSIs.count - 1)) + return mean + } func updateMonitoredPeripheral(_ rssi: Int) { + if let p = monitoredPeripheral { if !scanMode && p.state == .disconnected && !passiveMode { centralMgr.connect(p, options: nil) } } - delegate?.updateRSSI(rssi: rssi) - if rssi > lockRSSI { - if rssi > unlockRSSI && !presence { + + // print(String(format: "rssi: %d", rssi)) + let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) + delegate?.updateRSSI(rssi: estimatedRSSI) + if estimatedRSSI >= lockRSSI { + if estimatedRSSI >= unlockRSSI && !presence { print("Device is close") presence = true delegate?.updatePresence(presence: presence, reason: "close") @@ -194,12 +220,14 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var device: Device if (dev == nil) { device = Device(uuid: peripheral.identifier) - device.peripheral = peripheral - device.rssi = rssi - device.advData = advertisementData["kCBAdvDataManufacturerData"] as? Data - devices[peripheral.identifier] = device - central.connect(peripheral, options: nil) - delegate?.newDevice(device: device) + if (rssi >= thresholdRSSI) { + device.peripheral = peripheral + device.rssi = rssi + device.advData = advertisementData["kCBAdvDataManufacturerData"] as? Data + devices[peripheral.identifier] = device + central.connect(peripheral, options: nil) + delegate?.newDevice(device: device) + } } else { device = dev! device.rssi = rssi diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index c50f9f86..d50288e9 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -3,6 +3,7 @@ "notification_device_away" = "Device is away"; "notification_locked" = "This computer has been locked by BLEUnlock"; "cancel" = "Cancel"; +"ok" = "OK"; "enter_password" = "Enter password to unlock the lock screen."; "password_info" = "Your password will be securely stored in Keychain."; "device_not_set" = "Device not selected"; @@ -20,6 +21,9 @@ "quit" = "Quit BLEUnlock"; "lock_now" = "Lock Screen Now"; "about" = "About BLEUnlock"; +"set_rssi_threshold" = "Set Minimum RSSI"; +"enter_rssi_threshold" = "Enter Minimum RSSI"; +"enter_rssi_threshold_info" = "Device whose RSSI is less than this value will be ignored while scanning."; "bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; "password_not_set" = "Password is not set."; "notification_update_available" = "Update is available."; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index fb8f4552..5a75acd3 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.5.1 CFBundleVersion - 497 + 499 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 380708ca..b7e731e9 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -2,6 +2,7 @@ "notification_lost_signal" = "信号丢失"; "notification_device_away" = "设备掉线"; "notification_locked" = "本电脑已被BLEUnlock锁定"; +"ok" = "确定"; "cancel" = "取消"; "enter_password" = "输入密码以解锁电脑。"; "password_info" = "您的密码将安全地存储在钥匙串中。"; @@ -20,6 +21,9 @@ "quit" = "退出BLEUnlock"; "lock_now" = "立刻锁定屏幕"; "about" = "关于BLEUnlock"; +"set_rssi_threshold" = "设置最小RSSI"; +"enter_rssi_threshold" = "输入最小RSSI"; +"enter_rssi_threshold_info" = "扫描设备时会忽略RSSI值低于该值的设备。"; "bluetooth_power_warn" = "蓝牙已关闭。要使BLEUnlock正常工作,请将其打开。"; "password_not_set" = "密码未设置。"; "notification_update_available" = "发现新版本。"; From df9028f2c36b9e17238d8282d9f21b8c7aed75ae Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Sat, 2 Nov 2019 11:58:08 +0800 Subject: [PATCH 056/183] reset project.pbxproj for pull request --- BLEUnlock.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 54bcc7e5..dedf08b7 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -366,10 +366,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 6F43YBKR55; + DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -377,7 +377,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock.Launcher; + PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -389,11 +389,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 6F43YBKR55; + DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -401,7 +401,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock.Launcher; + PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock.Launcher; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -531,10 +531,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 6F43YBKR55; + DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -542,7 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock; + PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "BLEUnlock/BLEUnlock-Bridging-Header.h"; @@ -561,10 +561,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 6F43YBKR55; + DEVELOPMENT_TEAM = 376NR42P9A; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -572,7 +572,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = ch.william.BLEUnlock; + PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "BLEUnlock/BLEUnlock-Bridging-Header.h"; From a4e9c6ccd5cca11498dedc8695e8c7e92efa818a Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Sat, 2 Nov 2019 14:36:19 +0800 Subject: [PATCH 057/183] uncomment about info --- BLEUnlock/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 87b200f3..7a9ed1a0 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -504,7 +504,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) - // mainMenu.addItem(withTitle: t("about"), action: #selector(showAboutBox), keyEquivalent: "") + mainMenu.addItem(withTitle: t("about"), action: #selector(showAboutBox), keyEquivalent: "") mainMenu.addItem(NSMenuItem.separator()) mainMenu.addItem(withTitle: t("quit"), action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") statusItem.menu = mainMenu From 1126f9384aa852e3a73a08135831fb88b5a8a872 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 28 Nov 2019 19:29:03 +0900 Subject: [PATCH 058/183] Fix translations --- BLEUnlock/Base.lproj/Localizable.strings | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 4 ++++ BLEUnlock/zh-Hans.lproj/Localizable.strings | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index d50288e9..266a9a14 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -21,7 +21,7 @@ "quit" = "Quit BLEUnlock"; "lock_now" = "Lock Screen Now"; "about" = "About BLEUnlock"; -"set_rssi_threshold" = "Set Minimum RSSI"; +"set_rssi_threshold" = "Set Minimum RSSI…"; "enter_rssi_threshold" = "Enter Minimum RSSI"; "enter_rssi_threshold_info" = "Device whose RSSI is less than this value will be ignored while scanning."; "bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index e6b81657..58023b31 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -1,5 +1,6 @@ "not_detected" = "信号未検出"; "cancel" = "キャンセル"; +"ok" = "OK"; "notification_lost_signal" = "デバイスからの信号がありません"; "notification_device_away" = "デバイスが Mac から離れました"; "notification_locked" = "Mac は BLEUnlock によってロックされました"; @@ -20,6 +21,9 @@ "quit" = "BLEUnlock を終了"; "lock_now" = "今すぐロック"; "about" = "BLEUnlock について"; +"set_rssi_threshold" = "最小RSSIを設定…"; +"enter_rssi_threshold" = "最小RSSIを入力"; +"enter_rssi_threshold_info" = "この値よりRSSIが小さいデバイスはスキャンに表示されません。"; "bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; "password_not_set" = "パスワードが設定されていません。"; "notification_update_available" = "アップデートが利用可能です"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index b7e731e9..e4d0f5d9 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -21,7 +21,7 @@ "quit" = "退出BLEUnlock"; "lock_now" = "立刻锁定屏幕"; "about" = "关于BLEUnlock"; -"set_rssi_threshold" = "设置最小RSSI"; +"set_rssi_threshold" = "设置最小RSSI…"; "enter_rssi_threshold" = "输入最小RSSI"; "enter_rssi_threshold_info" = "扫描设备时会忽略RSSI值低于该值的设备。"; "bluetooth_power_warn" = "蓝牙已关闭。要使BLEUnlock正常工作,请将其打开。"; From 9e8eecd6d508786edb156a2c9ebd7681a1519cd2 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 28 Nov 2019 19:08:01 +0900 Subject: [PATCH 059/183] Add new Apple device names --- BLEUnlock/appleDeviceNames.swift | 61 ++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index 5f37f12c..df83cb8e 100644 --- a/BLEUnlock/appleDeviceNames.swift +++ b/BLEUnlock/appleDeviceNames.swift @@ -12,29 +12,36 @@ let appleDeviceNames = [ "iPhone5,4": "iPhone 5c", "iPhone6,1": "iPhone 5s", "iPhone6,2": "iPhone 5s", - "iPhone7,2": "iPhone 6", "iPhone7,1": "iPhone 6 Plus", + "iPhone7,2": "iPhone 6", "iPhone8,1": "iPhone 6s", "iPhone8,2": "iPhone 6s Plus", "iPhone8,4": "iPhone SE", "iPhone9,1": "iPhone 7", - "iPhone9,3": "iPhone 7", "iPhone9,2": "iPhone 7 Plus", + "iPhone9,3": "iPhone 7", "iPhone9,4": "iPhone 7 Plus", "iPhone10,1": "iPhone 8", - "iPhone10,4": "iPhone 8", "iPhone10,2": "iPhone 8 Plus", - "iPhone10,5": "iPhone 8 Plus", "iPhone10,3": "iPhone X", + "iPhone10,4": "iPhone 8", + "iPhone10,5": "iPhone 8 Plus", "iPhone10,6": "iPhone X", - "iPhone11,8": "iPhone XR", "iPhone11,2": "iPhone XS", "iPhone11,6": "iPhone XS Max", + "iPhone11,8": "iPhone XR", + "iPhone12,1": "iPhone 11", + "iPhone12,3": "iPhone 11 Pro", + "iPhone12,5": "iPhone 11 Pro Max", + "iPad1,1": "iPad", "iPad2,1": "iPad 2", "iPad2,2": "iPad 2", "iPad2,3": "iPad 2", "iPad2,4": "iPad 2", + "iPad2,5": "iPad mini", + "iPad2,6": "iPad mini", + "iPad2,7": "iPad mini", "iPad3,1": "iPad 3", "iPad3,2": "iPad 3", "iPad3,3": "iPad 3", @@ -44,12 +51,20 @@ let appleDeviceNames = [ "iPad4,1": "iPad Air", "iPad4,2": "iPad Air", "iPad4,3": "iPad Air", + "iPad4,4": "iPad mini 2", + "iPad4,5": "iPad mini 2", + "iPad4,6": "iPad mini 2", + "iPad4,7": "iPad mini 3", + "iPad4,8": "iPad mini 3", + "iPad4,9": "iPad mini 3", + "iPad5,1": "iPad mini 4", + "iPad5,2": "iPad mini 4", "iPad5,3": "iPad Air 2", "iPad5,4": "iPad Air 2", - "iPad6,7": "iPad Pro (12.9 inch)", - "iPad6,8": "iPad Pro (12.9 inch)", "iPad6,3": "iPad Pro (9.7 inch)", "iPad6,4": "iPad Pro (9.7 inch)", + "iPad6,7": "iPad Pro (12.9 inch)", + "iPad6,8": "iPad Pro (12.9 inch)", "iPad6,11": "iPad (5th generation)", "iPad6,12": "iPad (5th generation)", "iPad7,1": "iPad Pro (12.9-inch, 2nd generation)", @@ -58,23 +73,29 @@ let appleDeviceNames = [ "iPad7,4": "iPad Pro (10.5-inch)", "iPad7,5": "iPad (6th generation)", "iPad7,6": "iPad (6th generation)", - "iPad2,5": "iPad mini", - "iPad2,6": "iPad mini", - "iPad2,7": "iPad mini", - "iPad4,4": "iPad mini 2", - "iPad4,5": "iPad mini 2", - "iPad4,6": "iPad mini 2", - "iPad4,7": "iPad mini 3", - "iPad4,8": "iPad mini 3", - "iPad4,9": "iPad mini 3", - "iPad5,1": "iPad mini 4", - "iPad5,2": "iPad mini 4", + "iPad7,11": "iPad 7th Gen 10.2-inch (WiFi)", + "iPad7,12": "iPad 7th Gen 10.2-inch (WiFi+Cellular)", + "iPad8,1": "iPad Pro 3rd Gen (11 inch, WiFi)", + "iPad8,2": "iPad Pro 3rd Gen (11 inch, 1TB, WiFi)", + "iPad8,3": "iPad Pro 3rd Gen (11 inch, WiFi+Cellular)", + "iPad8,4": "iPad Pro 3rd Gen (11 inch, 1TB, WiFi+Cellular)", + "iPad8,5": "iPad Pro 3rd Gen (12.9 inch, WiFi)", + "iPad8,6": "iPad Pro 3rd Gen (12.9 inch, 1TB, WiFi)", + "iPad8,7": "iPad Pro 3rd Gen (12.9 inch, WiFi+Cellular)", + "iPad8,8": "iPad Pro 3rd Gen (12.9 inch, 1TB, WiFi+Cellular)", + "iPad11,1": "iPad mini 5th Gen (WiFi)", + "iPad11,2": "iPad mini 5th Gen", + "iPad11,3": "iPad Air 3rd Gen (WiFi)", + "iPad11,4": "iPad Air 3rd Gen", + "iPod1,1": "iPod touch", "iPod2,1": "iPod touch 2G", "iPod3,1": "iPod touch 3G", "iPod4,1": "iPod touch 4G", "iPod5,1": "iPod touch 5G", "iPod7,1": "iPod touch 6G", + "iPod9,1": "iPod touch 7G", + "Watch1,1": "Apple Watch (1st gen.)", "Watch1,2": "Apple Watch (1st gen.)", "Watch2,6": "Apple Watch Series 1", @@ -89,4 +110,8 @@ let appleDeviceNames = [ "Watch4,2": "Apple Watch Series 4", "Watch4,3": "Apple Watch Series 4", "Watch4,4": "Apple Watch Series 4", + "Watch5,1": "Apple Watch Series 5 40mm case (GPS)", + "Watch5,2": "Apple Watch Series 5 44mm case (GPS)", + "Watch5,3": "Apple Watch Series 5 40mm case (GPS+Cellular)", + "Watch5,4": "Apple Watch Series 5 44mm case (GPS+Cellular)", ] From d2b63044696354cde53972f806c6aba2326bf39c Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 28 Nov 2019 19:45:28 +0900 Subject: [PATCH 060/183] Update README --- README.ja.md | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.ja.md b/README.ja.md index 3703df18..cdcfb087 100644 --- a/README.ja.md +++ b/README.ja.md @@ -12,7 +12,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス - デバイスがMacに近づくと画面スリープを解除します (オプション) - デバイスがMacから離れる/近づくとiTunesの再生を一時停止/再生します (オプション) - パスワードはキーチェーンに安全に保管されます -- Hardened Runtimeを使いAppleによる公証を受けています ![](https://i.imgur.com/8AbtXEp.png) +- Hardened Runtimeを使いAppleによる公証を受けています ## 必要なもの diff --git a/README.md b/README.md index 24f68bee..898d1f20 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ This document is also available in [Japanese](README.ja.md). - Optionally pauses and unpauses iTunes playback when you're away and back - Password is securely stored in Keychain - Uses Hardened Runtime and notarized by Apple -![](https://i.imgur.com/i9Rj44q.png) ## Requirements From 8e64bae028e94c104ec285dc346d55c8979b8dde Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 28 Nov 2019 20:24:17 +0900 Subject: [PATCH 061/183] 1.6.0 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 3016bba3..13887115 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.2 + 1.6.0 CFBundleVersion - 499 + 504 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 20aac57203177ca47d36645a03d938d152b2ef18 Mon Sep 17 00:00:00 2001 From: Stephen Groat Date: Tue, 21 Jan 2020 15:35:27 -0500 Subject: [PATCH 062/183] use local codesigning for debug by default --- .gitignore | 2 ++ BLEUnlock.xcodeproj/project.pbxproj | 12 ++++++------ BLEUnlock/Info.plist | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 414ba236..209d67aa 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,5 @@ Carthage/Build fastlane/report.xml fastlane/screenshots + +.DS_Store diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index dedf08b7..f77a9b3e 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -366,10 +366,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -531,10 +531,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 13887115..73a77076 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.6.0 CFBundleVersion - 504 + 507 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From a4963b0924c8e2bb92fb231048d53d7d732d659b Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 22 Jan 2020 15:56:23 -0500 Subject: [PATCH 063/183] Create test.yml --- .github/workflows/test.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..4bee3ac5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,16 @@ +name: CI + +on: + push: + branches: + - master + +jobs: + build: + + runs-on: macOS-latest + + steps: + - uses: actions/checkout@master + - name: Start xcodebuild test + run: xcodebuild clean test -project BLEUnlock.xcodeproj -scheme BLEUnlock From 0127bb29cb7adee3140fb4b81a3d4c0912659248 Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 22 Jan 2020 16:04:52 -0500 Subject: [PATCH 064/183] Update test.yml --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bee3ac5..48fae7c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,6 @@ name: CI -on: - push: - branches: - - master +on: [push] jobs: build: From addaeda6e7118d23be4ffa9fa2f6996313af6d26 Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 22 Jan 2020 16:07:12 -0500 Subject: [PATCH 065/183] build, don't test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48fae7c5..f06d168d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,4 +10,4 @@ jobs: steps: - uses: actions/checkout@master - name: Start xcodebuild test - run: xcodebuild clean test -project BLEUnlock.xcodeproj -scheme BLEUnlock + run: xcodebuild clean build -project BLEUnlock.xcodeproj -scheme BLEUnlock From fd80c53e096795a09ecaf90765c07331523988cf Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 23 Jan 2020 13:44:44 +0900 Subject: [PATCH 066/183] Update README --- README.ja.md | 6 ++++-- README.md | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.ja.md b/README.ja.md index cdcfb087..02234d04 100644 --- a/README.ja.md +++ b/README.ja.md @@ -72,9 +72,11 @@ Bluetoothインターネット共有を使う場合、パッシブモードを ## クレジット -アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 +- peiit: 中国語の翻訳 +- wenmin-wu: 最小RSSIと移動平均 +- stephengroat: CI -中国語の翻訳は @peiit さんによって行われました。 +アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 ## ライセンス diff --git a/README.md b/README.md index 898d1f20..cbb4a3ef 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BLEUnlock -BLEUnlock is a small menu bar utility that locks and unlocks your Mac with your iPhone, Apple Watch, or any other Bluetooth Low Energy device. +BLEUnlock is a small menu bar utility that locks and unlocks your Mac with your iPhone, Apple Watch, or any other Bluetooth Low Energy devices. This document is also available in [Japanese](README.ja.md). @@ -85,12 +85,14 @@ If you don't, turn it off. ## Credits +- peiit: Chinese translation +- wenmin-wu: Minimum RSSI and moving average +- stephengroat: CI + Icons are based on SVGs downloaded from materialdesignicons.com. They are originally designed by Google LLC and licensed under Apache License version 2.0. -Chinese translation by @peiit. - ## License MIT From 8acbc2d8976871fb0c2bbf2e55e39f3fc2140fe5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 16 Feb 2020 22:04:32 +0900 Subject: [PATCH 067/183] Add option to disable lock --- .../xcschemes/xcschememanagement.plist | 13 +------------ BLEUnlock/AppDelegate.swift | 4 +++- BLEUnlock/BLE.swift | 3 ++- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/zh-Hans.lproj/Localizable.strings | 1 + 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist b/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist index 88e50b68..d16b1f25 100644 --- a/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist @@ -18,17 +18,6 @@ SuppressBuildableAutocreation - - 3D600A3F22701A5A0068FB7B - - primary - - - 3DD4B64F226C1C3200451B7B - - primary - - - + diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 7a9ed1a0..2db3dbdb 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -191,7 +191,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } tryUnlockScreen() } else { - if (!isScreenLocked()) { + if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { pauseItunes() if lockScreen() { self.notifyUser(reason) @@ -477,6 +477,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let lockRSSIItem = mainMenu.addItem(withTitle: t("lock_rssi"), action: nil, keyEquivalent: "") lockRSSIItem.submenu = lockRSSIMenu constructRSSIMenu(lockRSSIMenu, #selector(setLockRSSI)) + item = lockRSSIMenu.addItem(withTitle: t("disabled"), action: #selector(setLockRSSI), keyEquivalent: "") + item.tag = ble.LOCK_DISABLED item = mainMenu.addItem(withTitle: t("wake_on_proximity"), action: #selector(toggleWakeOnProximity), keyEquivalent: "") if prefs.bool(forKey: "wakeOnProximity") { diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 55eeb611..1883f09b 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -65,6 +65,7 @@ protocol BLEDelegate { } class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { + let LOCK_DISABLED = -100 var centralMgr : CBCentralManager! var devices : [UUID : Device] = [:] var delegate: BLEDelegate? @@ -171,7 +172,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { // print(String(format: "rssi: %d", rssi)) let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) delegate?.updateRSSI(rssi: estimatedRSSI) - if estimatedRSSI >= lockRSSI { + if estimatedRSSI >= (lockRSSI == LOCK_DISABLED ? unlockRSSI : lockRSSI) { if estimatedRSSI >= unlockRSSI && !presence { print("Device is close") presence = true diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 266a9a14..2de2145b 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -13,6 +13,7 @@ "unlock_rssi" = "Unlock RSSI"; "closer" = "⬆Closer"; "farther" = "⬇Farther"; +"disabled" = "Disable"; "wake_on_proximity" = "Wake on Proximity"; "pause_itunes" = "Pause iTunes while Locked"; "pause_music" = "Pause Music while Locked"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 73a77076..f9dfb729 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.6.0 CFBundleVersion - 507 + 513 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 58023b31..1ff4a7cd 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -13,6 +13,7 @@ "unlock_rssi" = "アンロック信号強度"; "closer" = "⬆近い"; "farther" = "⬇遠い"; +"disabled" = "無効にする"; "wake_on_proximity" = "画面スリープから復帰"; "pause_itunes" = "ロック中 iTunes を一時停止"; "pause_music" = "ロック中ミュージックを一時停止"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index e4d0f5d9..7761103f 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -13,6 +13,7 @@ "unlock_rssi" = "解锁 RSSI"; "closer" = "⬆接近"; "farther" = "⬇远离"; +"disabled" = "禁用"; "wake_on_proximity" = "靠近唤醒"; "pause_itunes" = "锁定时暂停iTunes"; "pause_music" = "锁定时暂停Music"; From 546eb02e5710f49b08db82a126496750e6a710fc Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 16 Feb 2020 22:29:36 +0900 Subject: [PATCH 068/183] 1.7.0 --- .../ts1.xcuserdatad/xcschemes/xcschememanagement.plist | 8 +++++++- BLEUnlock/Info.plist | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist b/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist index d16b1f25..641b2f7a 100644 --- a/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/BLEUnlock.xcodeproj/xcuserdata/ts1.xcuserdatad/xcschemes/xcschememanagement.plist @@ -18,6 +18,12 @@ SuppressBuildableAutocreation - + + 3DD4B64F226C1C3200451B7B + + primary + + + diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index f9dfb729..1ebd2094 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.6.0 + 1.7.0 CFBundleVersion - 513 + 520 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 56e4475cd57b6f1880cf993bb4a6facff8f1f51b Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Apr 2020 17:10:08 +0900 Subject: [PATCH 069/183] Change signal timeout to 61 seconds --- BLEUnlock/BLE.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 1883f09b..0fbdc023 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -78,7 +78,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var lockRSSI = -80 var unlockRSSI = -60 var proximityTimeout = 4.5 - var signalTimeout = 31.0 + var signalTimeout = 61.0 var lastReadAt = 0.0 var powerWarn = true var passiveMode = false From 9e49525de2f7126f234aae6c8ec4d21e14800365 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Apr 2020 17:27:34 +0900 Subject: [PATCH 070/183] Add option to disable unlock --- BLEUnlock/AppDelegate.swift | 26 +++++++++++++++----------- BLEUnlock/BLE.swift | 3 ++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 2db3dbdb..48a717da 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -177,19 +177,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func updatePresence(presence: Bool, reason: String) { if presence { - if let un = userNotification { - NSUserNotificationCenter.default.removeDeliveredNotification(un) - userNotification = nil - } - if displaySleep && !systemSleep && prefs.bool(forKey: "wakeOnProximity") { - print("Waking display") - wakeDisplay() - wakeTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in - print("Retrying waking display") + if ble.unlockRSSI != ble.UNLOCK_DISABLED { + if let un = userNotification { + NSUserNotificationCenter.default.removeDeliveredNotification(un) + userNotification = nil + } + if displaySleep && !systemSleep && prefs.bool(forKey: "wakeOnProximity") { + print("Waking display") wakeDisplay() - }) + wakeTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in + print("Retrying waking display") + wakeDisplay() + }) + } + tryUnlockScreen() } - tryUnlockScreen() } else { if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { pauseItunes() @@ -472,6 +474,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let unlockRSSIItem = mainMenu.addItem(withTitle: t("unlock_rssi"), action: nil, keyEquivalent: "") unlockRSSIItem.submenu = unlockRSSIMenu + item = unlockRSSIMenu.addItem(withTitle: t("disabled"), action: #selector(setUnlockRSSI), keyEquivalent: "") + item.tag = ble.UNLOCK_DISABLED constructRSSIMenu(unlockRSSIMenu, #selector(setUnlockRSSI)) let lockRSSIItem = mainMenu.addItem(withTitle: t("lock_rssi"), action: nil, keyEquivalent: "") diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 0fbdc023..3c73f3b1 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -65,6 +65,7 @@ protocol BLEDelegate { } class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { + let UNLOCK_DISABLED = 1 let LOCK_DISABLED = -100 var centralMgr : CBCentralManager! var devices : [UUID : Device] = [:] @@ -173,7 +174,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) delegate?.updateRSSI(rssi: estimatedRSSI) if estimatedRSSI >= (lockRSSI == LOCK_DISABLED ? unlockRSSI : lockRSSI) { - if estimatedRSSI >= unlockRSSI && !presence { + if estimatedRSSI >= (unlockRSSI == UNLOCK_DISABLED ? lockRSSI : unlockRSSI) && !presence { print("Device is close") presence = true delegate?.updatePresence(presence: presence, reason: "close") From 7feee8a70a9c89e8dfa8f821acede40fe97101ae Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Apr 2020 17:41:28 +0900 Subject: [PATCH 071/183] v1.8.0 --- BLEUnlock.xcodeproj/project.pbxproj | 2 ++ BLEUnlock/Info.plist | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index f77a9b3e..5aa75676 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -542,6 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MARKETING_VERSION = 1.8.0; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -572,6 +573,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MARKETING_VERSION = 1.8.0; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 1ebd2094..8355da9f 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.7.0 + $(MARKETING_VERSION) CFBundleVersion - 520 + 524 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 652964d5a78113932ccfdb6f55ecb1368121f60a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 18 Apr 2020 17:43:21 +0900 Subject: [PATCH 072/183] Unlock faster bypassing moving average --- BLEUnlock/BLE.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 3c73f3b1..167dbe85 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -163,7 +163,6 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } func updateMonitoredPeripheral(_ rssi: Int) { - if let p = monitoredPeripheral { if !scanMode && p.state == .disconnected && !passiveMode { centralMgr.connect(p, options: nil) @@ -171,14 +170,16 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } // print(String(format: "rssi: %d", rssi)) + if rssi >= (unlockRSSI == UNLOCK_DISABLED ? lockRSSI : unlockRSSI) && !presence { + print("Device is close") + presence = true + delegate?.updatePresence(presence: presence, reason: "close") + } + let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) delegate?.updateRSSI(rssi: estimatedRSSI) + if estimatedRSSI >= (lockRSSI == LOCK_DISABLED ? unlockRSSI : lockRSSI) { - if estimatedRSSI >= (unlockRSSI == UNLOCK_DISABLED ? lockRSSI : unlockRSSI) && !presence { - print("Device is close") - presence = true - delegate?.updatePresence(presence: presence, reason: "close") - } if let timer = proximityTimer { timer.invalidate() print("Proximity timer canceled") From c47ad7716ddc5fc816c12d8494864ea0bd269d02 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 18 Apr 2020 17:52:14 +0900 Subject: [PATCH 073/183] 1.8.1 --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- BLEUnlock/Info.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 5aa75676..6afc7e08 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -542,7 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.8.1; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -573,7 +573,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.8.1; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8355da9f..e2f031cc 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 524 + 526 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From af00b045df1e8aa3bb64e4fc501e6343672ecbcf Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 21 Apr 2020 14:57:25 +0900 Subject: [PATCH 074/183] Don't ask for password when unlock is disabled Closes #10 --- BLEUnlock/AppDelegate.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 48a717da..2aa1c2b3 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -232,6 +232,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func tryUnlockScreen() { guard !manualLock else { return } guard ble.presence else { return } + guard ble.unlockRSSI != ble.UNLOCK_DISABLED else { return } guard !systemSleep else { return } guard !displaySleep else { return } Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in @@ -563,7 +564,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa DistributedNotificationCenter.default().addObserver(self, selector: #selector(onUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) - if fetchPassword() == nil { + if ble.unlockRSSI != ble.UNLOCK_DISABLED && fetchPassword() == nil { askPassword() } checkAccessibility() From 783f920dbf9145ade8534638e40e357b93556174 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 21 Apr 2020 15:34:04 +0900 Subject: [PATCH 075/183] 1.8.2 --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- BLEUnlock/Info.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 6afc7e08..1923c9fe 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -542,7 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.1; + MARKETING_VERSION = 1.8.2; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -573,7 +573,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.1; + MARKETING_VERSION = 1.8.2; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index e2f031cc..e64129fc 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 526 + 537 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From bf17751d27268f59bc44fa723adc460e2dd27629 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 5 Sep 2020 21:09:34 +0900 Subject: [PATCH 076/183] Add no-signal timeout menu --- BLEUnlock.xcodeproj/project.pbxproj | 2 +- BLEUnlock/AppDelegate.swift | 28 ++++++++++++ BLEUnlock/BLE.swift | 2 +- BLEUnlock/Base.lproj/Localizable.strings | 48 +++++++++++--------- BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 50 +++++++++++---------- BLEUnlock/zh-Hans.lproj/Localizable.strings | 48 +++++++++++--------- 7 files changed, 110 insertions(+), 70 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 1923c9fe..dcac88b5 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -503,7 +503,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 2aa1c2b3..57fd3661 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -14,6 +14,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let deviceMenu = NSMenu() let lockRSSIMenu = NSMenu() let unlockRSSIMenu = NSMenu() + let timeoutMenu = NSMenu() var deviceDict: [UUID: NSMenuItem] = [:] var monitorMenuItem : NSMenuItem? let prefs = UserDefaults.standard @@ -45,6 +46,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .off } } + } else if menu == timeoutMenu { + for item in menu.items { + if item.tag == Int(ble.signalTimeout) { + item.state = .on + } else { + item.state = .off + } + } } } @@ -416,6 +425,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa ble.unlockRSSI = value } + @objc func setTimeout(_ menuItem: NSMenuItem) { + let value = menuItem.tag + prefs.set(value, forKey: "timeout") + ble.signalTimeout = Double(value) + } + @objc func toggleLaunchAtLogin(_ menuItem: NSMenuItem) { let launchAtLogin = !prefs.bool(forKey: "launchAtLogin") prefs.set(launchAtLogin, forKey: "launchAtLogin") @@ -485,6 +500,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item = lockRSSIMenu.addItem(withTitle: t("disabled"), action: #selector(setLockRSSI), keyEquivalent: "") item.tag = ble.LOCK_DISABLED + let timeoutItem = mainMenu.addItem(withTitle: t("timeout"), action: nil, keyEquivalent: "") + timeoutItem.submenu = timeoutMenu + timeoutMenu.addItem(withTitle: "30 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 30 + timeoutMenu.addItem(withTitle: "1 " + t("minute"), action: #selector(setTimeout), keyEquivalent: "").tag = 60 + timeoutMenu.addItem(withTitle: "2 " + t("minutes"), action: #selector(setTimeout), keyEquivalent: "").tag = 120 + timeoutMenu.addItem(withTitle: "5 " + t("minutes"), action: #selector(setTimeout), keyEquivalent: "").tag = 300 + timeoutMenu.addItem(withTitle: "10 " + t("minutes"), action: #selector(setTimeout), keyEquivalent: "").tag = 600 + timeoutMenu.delegate = self + item = mainMenu.addItem(withTitle: t("wake_on_proximity"), action: #selector(toggleWakeOnProximity), keyEquivalent: "") if prefs.bool(forKey: "wakeOnProximity") { item.state = .on @@ -548,6 +572,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if unlockRSSI != 0 { ble.unlockRSSI = unlockRSSI } + let timeout = prefs.integer(forKey: "timeout") + if timeout != 0 { + ble.signalTimeout = Double(timeout) + } ble.passiveMode = prefs.bool(forKey: "passiveMode") let thresholdRSSI = prefs.integer(forKey: "thresholdRSSI") if thresholdRSSI != 0 { diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 167dbe85..bb063535 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -79,7 +79,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var lockRSSI = -80 var unlockRSSI = -60 var proximityTimeout = 4.5 - var signalTimeout = 61.0 + var signalTimeout = 30.0 var lastReadAt = 0.0 var powerWarn = true var passiveMode = false diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 2de2145b..97cac79d 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -1,31 +1,35 @@ +"about" = "About BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; +"cancel" = "Cancel"; +"closer" = "⬆Closer"; +"device" = "Device"; +"device_not_set" = "Device not selected"; +"disabled" = "Disable"; +"enter_password" = "Enter password to unlock the lock screen."; +"enter_rssi_threshold" = "Enter Minimum RSSI"; +"enter_rssi_threshold_info" = "Device whose RSSI is less than this value will be ignored while scanning."; +"farther" = "⬇Farther"; +"launch_at_login" = "Launch at Login"; +"lock_now" = "Lock Screen Now"; +"lock_rssi" = "Lock RSSI"; +"minute" = "minute"; +"minutes" = "minutes"; "not_detected" = "Signal not detected"; -"notification_lost_signal" = "Signal is lost"; "notification_device_away" = "Device is away"; "notification_locked" = "This computer has been locked by BLEUnlock"; -"cancel" = "Cancel"; +"notification_lost_signal" = "Signal is lost"; +"notification_update_available" = "Update is available."; "ok" = "OK"; -"enter_password" = "Enter password to unlock the lock screen."; +"passive_mode" = "Passive Mode"; "password_info" = "Your password will be securely stored in Keychain."; -"device_not_set" = "Device not selected"; -"device" = "Device"; -"scanning" = "Scanning…"; -"lock_rssi" = "Lock RSSI"; -"unlock_rssi" = "Unlock RSSI"; -"closer" = "⬆Closer"; -"farther" = "⬇Farther"; -"disabled" = "Disable"; -"wake_on_proximity" = "Wake on Proximity"; +"password_not_set" = "Password is not set."; "pause_itunes" = "Pause iTunes while Locked"; "pause_music" = "Pause Music while Locked"; -"set_password" = "Set Password…"; -"launch_at_login" = "Launch at Login"; "quit" = "Quit BLEUnlock"; -"lock_now" = "Lock Screen Now"; -"about" = "About BLEUnlock"; +"scanning" = "Scanning…"; +"seconds" = "seconds"; +"set_password" = "Set Password…"; "set_rssi_threshold" = "Set Minimum RSSI…"; -"enter_rssi_threshold" = "Enter Minimum RSSI"; -"enter_rssi_threshold_info" = "Device whose RSSI is less than this value will be ignored while scanning."; -"bluetooth_power_warn" = "Bluetooth is turned off. For BLEUnlock to work, turn it on."; -"password_not_set" = "Password is not set."; -"notification_update_available" = "Update is available."; -"passive_mode" = "Passive Mode"; +"timeout" = "No-Signal Timeout"; +"unlock_rssi" = "Unlock RSSI"; +"wake_on_proximity" = "Wake on Proximity"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index e64129fc..82fb5878 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 537 + 550 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 1ff4a7cd..d323c9aa 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -1,31 +1,35 @@ -"not_detected" = "信号未検出"; +"about" = "BLEUnlock について"; +"bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; "cancel" = "キャンセル"; -"ok" = "OK"; -"notification_lost_signal" = "デバイスからの信号がありません"; +"closer" = "⬆近い"; +"device" = "デバイス"; +"device_not_set" = "デバイス未選択"; +"disabled" = "無効にする"; +"enter_password" = "ロック画面を解除するためのパスワードを入力してください。"; +"enter_rssi_threshold" = "最小RSSIを入力"; +"enter_rssi_threshold_info" = "この値よりRSSIが小さいデバイスはスキャンに表示されません。"; +"farther" = "⬇遠い"; +"launch_at_login" = "ログイン時に起動"; +"lock_now" = "今すぐロック"; +"lock_rssi" = "ロック信号強度"; +"minute" = "分"; +"minutes" = "分"; +"not_detected" = "信号未検出"; "notification_device_away" = "デバイスが Mac から離れました"; "notification_locked" = "Mac は BLEUnlock によってロックされました"; -"enter_password" = "ロック画面を解除するためのパスワードを入力してください。"; +"notification_lost_signal" = "デバイスからの信号がありません"; +"notification_update_available" = "アップデートが利用可能です"; +"ok" = "OK"; +"passive_mode" = "パッシブモード"; "password_info" = "パスワードはキーチェーンに安全に保存されます。"; -"device_not_set" = "デバイス未選択"; -"device" = "デバイス"; -"scanning" = "スキャン中…"; -"lock_rssi" = "ロック信号強度"; -"unlock_rssi" = "アンロック信号強度"; -"closer" = "⬆近い"; -"farther" = "⬇遠い"; -"disabled" = "無効にする"; -"wake_on_proximity" = "画面スリープから復帰"; +"password_not_set" = "パスワードが設定されていません。"; "pause_itunes" = "ロック中 iTunes を一時停止"; "pause_music" = "ロック中ミュージックを一時停止"; -"set_password" = "パスワードを設定…"; -"launch_at_login" = "ログイン時に起動"; "quit" = "BLEUnlock を終了"; -"lock_now" = "今すぐロック"; -"about" = "BLEUnlock について"; +"scanning" = "スキャン中…"; +"seconds" = "秒"; +"set_password" = "パスワードを設定…"; "set_rssi_threshold" = "最小RSSIを設定…"; -"enter_rssi_threshold" = "最小RSSIを入力"; -"enter_rssi_threshold_info" = "この値よりRSSIが小さいデバイスはスキャンに表示されません。"; -"bluetooth_power_warn" = "Bluetooth がオフになっています。 BLEUnlock を動作させるためにはオンにしてください。"; -"password_not_set" = "パスワードが設定されていません。"; -"notification_update_available" = "アップデートが利用可能です"; -"passive_mode" = "パッシブモード"; +"timeout" = "無信号タイムアウト"; +"unlock_rssi" = "アンロック信号強度"; +"wake_on_proximity" = "画面スリープから復帰"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 7761103f..9d81ee78 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -1,31 +1,35 @@ +"about" = "关于BLEUnlock"; +"bluetooth_power_warn" = "蓝牙已关闭。要使BLEUnlock正常工作,请将其打开。"; +"cancel" = "取消"; +"closer" = "⬆接近"; +"device" = "设备列表"; +"device_not_set" = "未选择设备"; +"disabled" = "禁用"; +"enter_password" = "输入密码以解锁电脑。"; +"enter_rssi_threshold" = "输入最小RSSI"; +"enter_rssi_threshold_info" = "扫描设备时会忽略RSSI值低于该值的设备。"; +"farther" = "⬇远离"; +"launch_at_login" = "开机启动"; +"lock_now" = "立刻锁定屏幕"; +"lock_rssi" = "锁定 RSSI"; +"minute" = "分钟"; +"minutes" = "分钟"; "not_detected" = "未检测到信息"; -"notification_lost_signal" = "信号丢失"; "notification_device_away" = "设备掉线"; "notification_locked" = "本电脑已被BLEUnlock锁定"; +"notification_lost_signal" = "信号丢失"; +"notification_update_available" = "发现新版本。"; "ok" = "确定"; -"cancel" = "取消"; -"enter_password" = "输入密码以解锁电脑。"; +"passive_mode" = "被动模式"; "password_info" = "您的密码将安全地存储在钥匙串中。"; -"device_not_set" = "未选择设备"; -"device" = "设备列表"; -"scanning" = "扫描中…"; -"lock_rssi" = "锁定 RSSI"; -"unlock_rssi" = "解锁 RSSI"; -"closer" = "⬆接近"; -"farther" = "⬇远离"; -"disabled" = "禁用"; -"wake_on_proximity" = "靠近唤醒"; +"password_not_set" = "密码未设置。"; "pause_itunes" = "锁定时暂停iTunes"; "pause_music" = "锁定时暂停Music"; -"set_password" = "设置密码…"; -"launch_at_login" = "开机启动"; "quit" = "退出BLEUnlock"; -"lock_now" = "立刻锁定屏幕"; -"about" = "关于BLEUnlock"; +"scanning" = "扫描中…"; +"seconds" = "秒钟"; +"set_password" = "设置密码…"; "set_rssi_threshold" = "设置最小RSSI…"; -"enter_rssi_threshold" = "输入最小RSSI"; -"enter_rssi_threshold_info" = "扫描设备时会忽略RSSI值低于该值的设备。"; -"bluetooth_power_warn" = "蓝牙已关闭。要使BLEUnlock正常工作,请将其打开。"; -"password_not_set" = "密码未设置。"; -"notification_update_available" = "发现新版本。"; -"passive_mode" = "被动模式"; +"timeout" = "无信号超时"; +"unlock_rssi" = "解锁 RSSI"; +"wake_on_proximity" = "靠近唤醒"; From 3788ef301534b17f62f3de86490e624f7232e7af Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 5 Sep 2020 21:16:41 +0900 Subject: [PATCH 077/183] Update copyright year --- BLEUnlock/Base.lproj/AboutBox.xib | 22 +++++++++++----------- BLEUnlock/Info.plist | 2 +- BLEUnlock/zh-Hans.lproj/AboutBox.strings | 4 ---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index ec9d9f2f..dc824aeb 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -1,8 +1,8 @@ - + - + @@ -17,7 +17,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -35,25 +35,25 @@ - - + + - + - + - + - + @@ -404,7 +404,7 @@ Included icons are derived from SVGs downloaded from materialdesignicons.com. Th - + diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 82fb5878..8b7513ba 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 550 + 551 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/zh-Hans.lproj/AboutBox.strings b/BLEUnlock/zh-Hans.lproj/AboutBox.strings index ada6a31c..08a046ac 100644 --- a/BLEUnlock/zh-Hans.lproj/AboutBox.strings +++ b/BLEUnlock/zh-Hans.lproj/AboutBox.strings @@ -1,7 +1,3 @@ - -/* Class = "NSTextFieldCell"; title = "Copyright © 2019 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ -"GN4-8z-o8I.title" = "Copyright © 2019 Takeshi Sone"; - /* Class = "NSTextFieldCell"; title = "BLEUnlock version #{version}"; ObjectID = "VLW-23-BX0"; */ "VLW-23-BX0.title" = "BLEUnlock 版本 #{version}"; From 41b151a12c1839c53fc5611b3235742abd55f4b4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 5 Sep 2020 21:54:38 +0900 Subject: [PATCH 078/183] Run scripts on events --- BLEUnlock.xcodeproj/project.pbxproj | 2 +- BLEUnlock/AppDelegate.swift | 17 ++++++++++++++++- BLEUnlock/Info.plist | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index dcac88b5..d7b7889c 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -199,7 +199,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1020; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1170; ORGANIZATIONNAME = "Takeshi Sone"; TargetAttributes = { 3D600A3F22701A5A0068FB7B = { diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 57fd3661..5cbe76d9 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -26,6 +26,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var aboutBox: AboutBox? = nil var wakeTimer: Timer? var manualLock = false + var unlockedAt = 0.0 func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -144,6 +145,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } + func runScript(_ name: String) { + guard let directory = try? FileManager.default.url(for: .applicationScriptsDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { return } + let file = directory.appendingPathComponent(name) + let process = Process() + process.executableURL = file + try? process.run() + } + func runAppleScript(_ script: String) -> NSAppleEventDescriptor? { guard let scriptObject = NSAppleScript(source: script) else { return nil } var error: NSDictionary? @@ -205,7 +214,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { pauseItunes() if lockScreen() { - self.notifyUser(reason) + notifyUser(reason) + runScript("locked") } else { print("Failed to lock") } @@ -251,6 +261,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa print("Entering password") self.fakeKeyStrokes(password) self.playItunes() + self.runScript("unlocked") + self.unlockedAt = Date().timeIntervalSince1970 }) } @@ -282,6 +294,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } @objc func onUnlock() { + if Date().timeIntervalSince1970 >= unlockedAt + 10 { + runScript("intruded") + } manualLock = false Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { _ in checkUpdate() diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8b7513ba..b9de1eec 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 551 + 557 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From e11d8b15698ee0353e65cf376780fec91383a8fa Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 15:10:36 +0900 Subject: [PATCH 079/183] Improve running script --- BLEUnlock/AppDelegate.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 5cbe76d9..a4bafa1d 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -145,11 +145,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } - func runScript(_ name: String) { + func runScript(_ arg: String) { guard let directory = try? FileManager.default.url(for: .applicationScriptsDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { return } - let file = directory.appendingPathComponent(name) + let file = directory.appendingPathComponent("event") let process = Process() process.executableURL = file + process.arguments = [arg] try? process.run() } @@ -215,7 +216,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa pauseItunes() if lockScreen() { notifyUser(reason) - runScript("locked") + runScript(reason) } else { print("Failed to lock") } @@ -268,6 +269,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onDisplayWake() { print("display wake") + unlockedAt = Date().timeIntervalSince1970 displaySleep = false wakeTimer?.invalidate() wakeTimer = nil @@ -294,7 +296,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } @objc func onUnlock() { - if Date().timeIntervalSince1970 >= unlockedAt + 10 { + if Date().timeIntervalSince1970 >= unlockedAt + 2 { runScript("intruded") } manualLock = false From 2596c357124c9027bc4f96b784ce7361d6221202 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 17:21:24 +0900 Subject: [PATCH 080/183] Update README --- README.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cbb4a3ef..2203e61f 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,78 @@ That does not interfere with Bluetooth Internet Sharing. If you use Bluetooth Internet Sharing on the same device, turn Passive Mode on. If you don't, turn it off. +## Run script on lock/unlock + +On locking and unlocking, BLEUnlock runs a script located here: + +``` +~/Library/Application Scripts/jp.sone.BLEUnlock/event +``` + +An argument is passed depending on the type of event: + +|Event|Argument| +|-----|--------| +|Locked by BLEUnlock because of low RSSI|`away`| +|Locked by BLEUnlock because of no signal|`lost`| +|Unlocked by BLEUnlock|`unlocked`| +|Unlocked manually|`intruded`| + +> NOTE: for `intruded` event works properly, you have to set *Require password immediately after sleep* in Security & Privacy. + +### Example + +Here is an example script which sends LINE Notify message, with a photo of the person in front of Mac when unlocked manually. + +```sh +#!/bin/bash + +set -eo pipefail + +LINE_TOKEN=xxxxx + +notify() { + local message=$1 + local image=$2 + if [ "$image" ]; then + img_arg="-F imageFile=@$image" + else + img_arg="" + fi + curl -X POST -H "Authorization: Bearer $LINE_TOKEN" -F "message=$message" \ + $img_arg https://notify-api.line.me/api/notify +} + +capture() { + open -Wa SnapshotUnlocker + ls -t /tmp/unlock-*.jpg | head -1 +} + +case $1 in + away) + notify "$(hostname -s) is locked by BLEUnlock because iPhone is away." + ;; + lost) + notify "$(hostname -s) is locked by BLEUnlock because signal is lost." + ;; + unlocked) + #notify "$(hostname -s) is unlocked by BLEUnlock." + ;; + intruded) + notify "$(hostname -s) is manually unlocked." $(capture) + ;; +esac +``` + +`SnapshotUnlocker` is an .app created with Script Editor with this script: + +``` +do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y /tmp/unlock-$(date +%Y%m%d_%H%M%S).jpg" +``` + +This is required because BLEUnlock does not have Camera permission. +Giving permission to this app resolve the problem. + ## Credits - peiit: Chinese translation @@ -97,4 +169,4 @@ version 2.0. MIT -Copyright © 2019 Takeshi Sone. +Copyright © 2019-2020 Takeshi Sone. From a8c99d7c1d3758e02bd58d5551d8cefd9472cb1b Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 18:02:48 +0900 Subject: [PATCH 081/183] Add name resolution with IOBluetooth --- BLEUnlock/BLE.swift | 16 ++++++++++++++++ BLEUnlock/Info.plist | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index bb063535..61fdd898 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -1,7 +1,16 @@ import Foundation import CoreBluetooth +import IOBluetooth import Accelerate +func getIOBDevice(_ uuid: String) -> IOBluetoothDevice? { + guard let plist = NSDictionary(contentsOfFile: "/Library/Preferences/com.apple.Bluetooth.plist") else { return nil } + guard let cbcache = plist["CoreBluetoothCache"] as? NSDictionary else { return nil } + guard let device = cbcache[uuid] as? NSDictionary else { return nil } + guard let addr = device["DeviceAddress"] as? String else {return nil } + return IOBluetoothDevice(addressString: addr) +} + class Device: NSObject { let uuid : UUID! var peripheral : CBPeripheral? @@ -10,9 +19,16 @@ class Device: NSObject { var advData: Data? var rssi: Int = 0 var scanTimer: Timer? + var iobDevice: IOBluetoothDevice? override var description: String { get { + if iobDevice == nil { + iobDevice = getIOBDevice(uuid.description) + } + if let name = iobDevice?.nameOrAddress { + return name + } if let manu = manufacture { if let mod = model { if manu == "Apple Inc." && appleDeviceNames[mod] != nil { diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index b9de1eec..23d65361 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 557 + 564 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 28a2917386fc9ee222ea43b013fa7d4074ca5d62 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 18:38:30 +0900 Subject: [PATCH 082/183] Update README.ja --- README.ja.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/README.ja.md b/README.ja.md index 02234d04..3fc2f385 100644 --- a/README.ja.md +++ b/README.ja.md @@ -70,6 +70,78 @@ BLEUnlockはデフォルトで能動的にデバイスに接続して信号強 Bluetoothインターネット共有を使う場合、パッシブモードをオンにしてください。 そうでなければオフにしてください。 +## ロック・アンロック時にスクリプトを実行する + +BLEUnlockはロック・アンロック時に以下のスクリプトを実行します。 + +``` +~/Library/Application Scripts/jp.sone.BLEUnlock/event +``` + +スクリプトにはイベントに応じて以下の引数の一つが渡されます。 + +|Event|Argument| +|-----|--------| +|信号強度のためBLEUnlockによりロックされた|`away`| +|無信号のためBLEUnlockによりロックされた|`lost`| +|BLEUnlockによりアンロックされた|`unlocked`| +|手動でアンロックされた|`intruded`| + +> 注意: `intruded` イベントが正常に働くには、 *セキュリティとプライバシー* で *スリープとスクリーンセーバの解除にパスワードを要求* を **すぐに** に設定してください。 + +### サンプル + +例としてLINE Notifyにメッセージを送るスクリプトを示します。 +手動でアンロックされた場合Macの前にいる人の写真を添付します。 + +```sh +#!/bin/bash + +set -eo pipefail + +LINE_TOKEN=xxxxx + +notify() { + local message=$1 + local image=$2 + if [ "$image" ]; then + img_arg="-F imageFile=@$image" + else + img_arg="" + fi + curl -X POST -H "Authorization: Bearer $LINE_TOKEN" -F "message=$message" \ + $img_arg https://notify-api.line.me/api/notify +} + +capture() { + open -Wa SnapshotUnlocker + ls -t /tmp/unlock-*.jpg | head -1 +} + +case $1 in + away) + notify "$(hostname -s) is locked by BLEUnlock because iPhone is away." + ;; + lost) + notify "$(hostname -s) is locked by BLEUnlock because signal is lost." + ;; + unlocked) + #notify "$(hostname -s) is unlocked by BLEUnlock." + ;; + intruded) + notify "$(hostname -s) is manually unlocked." $(capture) + ;; +esac +``` + +`SnapshotUnlocker` は Script Editor で作られた .app で、内容は以下のとおりです。 + +``` +do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y /tmp/unlock-$(date +%Y%m%d_%H%M%S).jpg" +``` + +これはBLEUnlockにカメラのパーミッションがないため必要となります。このappにパーミッションを与えることによりパーミッションの問題を回避できます。 + ## クレジット - peiit: 中国語の翻訳 From 27220cac3c8c361bb6850fb3b02b5d2ab72a72f5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 18:53:10 +0900 Subject: [PATCH 083/183] v1.9.0-beta --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++++++ BLEUnlock.xcodeproj/project.pbxproj | 4 +-- BLEUnlock/Info.plist | 4 +-- ExportOptions.plist | 4 +-- 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..1e3d6560 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,54 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + build: + runs-on: macOS-latest + steps: + - uses: actions/checkout@master + - name: Start xcodebuild test + run: | + APPNAME=BLEUnlock + + BASEDIR=$(pwd) + BUILDDIR=$BASEDIR/build + + xcodebuild \ + -scheme $APPNAME \ + -archivePath $BUILDDIR/$APPNAME.xcarchive \ + archive + + xcodebuild \ + -exportArchive \ + -archivePath $BUILDDIR/$APPNAME.xcarchive \ + -exportOptionsPlist $BASEDIR/ExportOptions.plist \ + -exportPath $BUILDDIR + + ditto -c -k --keepParent $BUILDDIR/$APPNAME.app \ + $BUILDDIR/$APPNAME-${{ github.ref }}.zip + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/BLEUnlock-${{ github.ref }}.zip + asset_name: BLEUnlock-${{ github.ref }}.zip + asset_content_type: application/zip + diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index d7b7889c..a16b6c2d 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -542,7 +542,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.2; + MARKETING_VERSION = "1.9.0-beta"; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -573,7 +573,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.2; + MARKETING_VERSION = "1.9.0-beta"; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 23d65361..5a79020e 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 564 + 568 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion @@ -27,7 +27,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2019 Takeshi Sone. MIT Licensed. + Copyright © 2019-2020 Takeshi Sone. MIT Licensed. NSMainNibFile MainMenu NSPrincipalClass diff --git a/ExportOptions.plist b/ExportOptions.plist index fea2fcac..6bc71d2a 100644 --- a/ExportOptions.plist +++ b/ExportOptions.plist @@ -5,10 +5,8 @@ destination export method - developer-id + mac-application signingStyle automatic - teamID - 376NR42P9A From 09bb7ec557f648341ca2517d4ffc8e6c7ca06f9e Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 19:11:01 +0900 Subject: [PATCH 084/183] Fix releasing procedure --- .github/workflows/release.yml | 54 ----------------------------- BLEUnlock.xcodeproj/project.pbxproj | 8 ++--- BLEUnlock/Info.plist | 2 +- release | 4 +-- 4 files changed, 7 insertions(+), 61 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1e3d6560..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v*' - -jobs: - build: - runs-on: macOS-latest - steps: - - uses: actions/checkout@master - - name: Start xcodebuild test - run: | - APPNAME=BLEUnlock - - BASEDIR=$(pwd) - BUILDDIR=$BASEDIR/build - - xcodebuild \ - -scheme $APPNAME \ - -archivePath $BUILDDIR/$APPNAME.xcarchive \ - archive - - xcodebuild \ - -exportArchive \ - -archivePath $BUILDDIR/$APPNAME.xcarchive \ - -exportOptionsPlist $BASEDIR/ExportOptions.plist \ - -exportPath $BUILDDIR - - ditto -c -k --keepParent $BUILDDIR/$APPNAME.app \ - $BUILDDIR/$APPNAME-${{ github.ref }}.zip - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./build/BLEUnlock-${{ github.ref }}.zip - asset_name: BLEUnlock-${{ github.ref }}.zip - asset_content_type: application/zip - diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index a16b6c2d..1a661eb3 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -389,11 +389,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = 42LGPQYC7M; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -562,10 +562,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 376NR42P9A; + DEVELOPMENT_TEAM = 42LGPQYC7M; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 5a79020e..3f601960 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 568 + 572 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/release b/release index 7399c35e..97250a5d 100755 --- a/release +++ b/release @@ -60,8 +60,8 @@ xcodebuild \ -exportOptionsPlist $BASEDIR/ExportOptions.plist \ -exportPath $RELEASEDIR -notarize $RELEASEDIR/$APPNAME.app/Contents/Library/LoginItems/Launcher.app -notarize $RELEASEDIR/$APPNAME.app +#notarize $RELEASEDIR/$APPNAME.app/Contents/Library/LoginItems/Launcher.app +#notarize $RELEASEDIR/$APPNAME.app version=$(extract_info $RELEASEDIR/$APPNAME.app CFBundleShortVersionString) From 6e6f729e965cfa7eab3d8e150b4bfa4313c7b8e9 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 19:39:23 +0900 Subject: [PATCH 085/183] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..711ecdc1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ["https://paypal.me/takeshisone"] From d6e7b6d56680142e5d87392c87da599e16176a82 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 19 Sep 2020 19:40:11 +0900 Subject: [PATCH 086/183] Fix readme --- README.ja.md | 1 - README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/README.ja.md b/README.ja.md index 3fc2f385..83b32661 100644 --- a/README.ja.md +++ b/README.ja.md @@ -12,7 +12,6 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス - デバイスがMacに近づくと画面スリープを解除します (オプション) - デバイスがMacから離れる/近づくとiTunesの再生を一時停止/再生します (オプション) - パスワードはキーチェーンに安全に保管されます -- Hardened Runtimeを使いAppleによる公証を受けています ## 必要なもの diff --git a/README.md b/README.md index 2203e61f..8c46a2b1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ This document is also available in [Japanese](README.ja.md). - Optionally wakes from display sleep state - Optionally pauses and unpauses iTunes playback when you're away and back - Password is securely stored in Keychain -- Uses Hardened Runtime and notarized by Apple ## Requirements From 08382cb74368cbfb1c86decf31529de3e8134c24 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 24 Sep 2020 16:10:41 +0900 Subject: [PATCH 087/183] Display MAC address, more detailed listing --- BLEUnlock/AppDelegate.swift | 8 +++++++- BLEUnlock/BLE.swift | 36 +++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index a4bafa1d..6257ca8f 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -74,7 +74,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } func menuItemTitle(device: Device) -> String { - return String(format: "%@ (%ddBm)", device.description, device.rssi) + var desc : String! + if let mac = device.macAddr { + desc = String(format: "%@ (%@)", device.description, mac) + } else { + desc = device.description + } + return String(format: "%@ (%ddBm)", desc, device.rssi) } func newDevice(device: Device) { diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 61fdd898..8135030c 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -1,14 +1,19 @@ import Foundation import CoreBluetooth -import IOBluetooth import Accelerate -func getIOBDevice(_ uuid: String) -> IOBluetoothDevice? { +func getMACFromUUID(_ uuid: String) -> String? { guard let plist = NSDictionary(contentsOfFile: "/Library/Preferences/com.apple.Bluetooth.plist") else { return nil } guard let cbcache = plist["CoreBluetoothCache"] as? NSDictionary else { return nil } guard let device = cbcache[uuid] as? NSDictionary else { return nil } - guard let addr = device["DeviceAddress"] as? String else {return nil } - return IOBluetoothDevice(addressString: addr) + return device["DeviceAddress"] as? String +} + +func getNameFromMAC(_ mac: String) -> String? { + guard let plist = NSDictionary(contentsOfFile: "/Library/Preferences/com.apple.Bluetooth.plist") else { return nil } + guard let devcache = plist["DeviceCache"] as? NSDictionary else { return nil } + guard let device = devcache[mac] as? NSDictionary else { return nil } + return device["Name"] as? String } class Device: NSObject { @@ -19,15 +24,21 @@ class Device: NSObject { var advData: Data? var rssi: Int = 0 var scanTimer: Timer? - var iobDevice: IOBluetoothDevice? + var macAddr: String? + var blName: String? override var description: String { get { - if iobDevice == nil { - iobDevice = getIOBDevice(uuid.description) + if macAddr == nil { + macAddr = getMACFromUUID(uuid.description) } - if let name = iobDevice?.nameOrAddress { - return name + if let mac = macAddr { + blName = getNameFromMAC(mac) + if let name = blName { + if name != "iPhone" && name != "iPad" { + return name + } + } } if let manu = manufacture { if let mod = model { @@ -47,6 +58,7 @@ class Device: NSObject { if let mod = model { return mod } + // iBeacon if let adv = advData { var iBeaconPrefix : [uint16] = [0x004c, 0x01502] if adv[0...3] == Data(bytes: &iBeaconPrefix, count: 4) { @@ -58,6 +70,12 @@ class Device: NSObject { return "iBeacon [\(major), \(minor)] \(d)m" } } + if let name = blName { + return name + } + if let mac = macAddr { + return mac // better than uuid + } return uuid.description } } From 737d41800149f95092682bbcd1b1a2dce18a5306 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 10 Oct 2020 13:34:38 +0900 Subject: [PATCH 088/183] Fix timeout while scanning --- BLEUnlock/BLE.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 8135030c..d4849fa2 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -245,7 +245,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if monitoredPeripheral == nil { monitoredPeripheral = peripheral } - if peripheral.state != .connected { + if passiveMode || peripheral.state != .connected { updateMonitoredPeripheral(rssi) //print("Discover \(rssi)dBm") } From 282bf1983ab810c963e818007d5bd24228892a49 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 10 Oct 2020 23:11:10 +0900 Subject: [PATCH 089/183] Improve active mode --- BLEUnlock/AppDelegate.swift | 8 ++-- BLEUnlock/BLE.swift | 73 ++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 6257ca8f..1e7e3156 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -76,7 +76,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func menuItemTitle(device: Device) -> String { var desc : String! if let mac = device.macAddr { - desc = String(format: "%@ (%@)", device.description, mac) + let prettifiedMac = mac.replacingOccurrences(of: "-", with: ":").uppercased() + desc = String(format: "%@ (%@)", device.description, prettifiedMac) } else { desc = device.description } @@ -474,7 +475,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let passiveMode = !prefs.bool(forKey: "passiveMode") prefs.set(passiveMode, forKey: "passiveMode") menuItem.state = passiveMode ? .on : .off - ble.passiveMode = passiveMode + ble.setPassiveMode(passiveMode) } @objc func lockNow() { @@ -525,6 +526,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let timeoutItem = mainMenu.addItem(withTitle: t("timeout"), action: nil, keyEquivalent: "") timeoutItem.submenu = timeoutMenu + timeoutMenu.addItem(withTitle: "15 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 15 timeoutMenu.addItem(withTitle: "30 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 30 timeoutMenu.addItem(withTitle: "1 " + t("minute"), action: #selector(setTimeout), keyEquivalent: "").tag = 60 timeoutMenu.addItem(withTitle: "2 " + t("minutes"), action: #selector(setTimeout), keyEquivalent: "").tag = 120 @@ -599,7 +601,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if timeout != 0 { ble.signalTimeout = Double(timeout) } - ble.passiveMode = prefs.bool(forKey: "passiveMode") + ble.setPassiveMode(prefs.bool(forKey: "passiveMode")) let thresholdRSSI = prefs.integer(forKey: "thresholdRSSI") if thresholdRSSI != 0 { ble.thresholdRSSI = thresholdRSSI diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index d4849fa2..99073833 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -117,16 +117,36 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var lastReadAt = 0.0 var powerWarn = true var passiveMode = false - var thresholdRSSI = -80 + var thresholdRSSI = -70 var latestRSSIs: [Double] = [] var latestN: Int = 5 + var activeModeTimer : Timer? = nil + + func scanForPeripherals() { + guard !centralMgr.isScanning else { return } + centralMgr.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]) + //print("Start scanning") + } func startScanning() { scanMode = true + scanForPeripherals() } func stopScanning() { scanMode = false + if activeModeTimer != nil { + centralMgr.stopScan() + } + } + + func setPassiveMode(_ mode: Bool) { + passiveMode = mode + if passiveMode { + activeModeTimer?.invalidate() + activeModeTimer = nil + } + scanForPeripherals() } func startMonitor(uuid: UUID) { @@ -138,6 +158,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { resetSignalTimer() presence = true monitoredPeripheral = nil + activeModeTimer?.invalidate() + activeModeTimer = nil + scanForPeripherals() } func resetSignalTimer() { @@ -159,7 +182,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { switch central.state { case .poweredOn: print("Bluetooth powered on") - centralMgr.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]) + scanForPeripherals() if let p = monitoredPeripheral { central.cancelPeripheralConnection(p) } @@ -198,7 +221,8 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func updateMonitoredPeripheral(_ rssi: Int) { if let p = monitoredPeripheral { - if !scanMode && p.state == .disconnected && !passiveMode { + if !passiveMode && activeModeTimer == nil { + //print("connecting") centralMgr.connect(p, options: nil) } } @@ -245,7 +269,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if monitoredPeripheral == nil { monitoredPeripheral = peripheral } - if passiveMode || peripheral.state != .connected { + if activeModeTimer == nil { updateMonitoredPeripheral(rssi) //print("Discover \(rssi)dBm") } @@ -289,29 +313,19 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } //MARK:- CBCentralManagerDelegate start + func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self - peripheral.discoverServices([DeviceInformation]) + if scanMode { + peripheral.discoverServices([DeviceInformation]) + } if peripheral == monitoredPeripheral && !passiveMode { peripheral.readRSSI() - lastReadAt = Date().timeIntervalSince1970 } } - func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { - guard peripheral == monitoredPeripheral else { return } - guard !passiveMode else { return } - central.connect(peripheral, options: nil) - } - - func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { - guard peripheral == monitoredPeripheral else { return } - guard !passiveMode else { return } - central.connect(peripheral, options: nil) - } - //MARK:CBCentralManagerDelegate end - //MARK:- CBPeripheralDelegate start @@ -320,23 +334,16 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue updateMonitoredPeripheral(rssi) //print("readRSSI \(rssi)dBm") + centralMgr.cancelPeripheralConnection(peripheral) - var interval: TimeInterval - if lastReadAt > 0 { - interval = 2.0 - (Date().timeIntervalSince1970 - lastReadAt) - if interval < 0 { - interval = 0 - } - } else { - interval = 0 + if activeModeTimer == nil && !passiveMode{ + print("Entering active mode") + centralMgr.stopScan() + activeModeTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { _ in + self.centralMgr.connect(peripheral, options: nil) + }) + RunLoop.main.add(activeModeTimer!, forMode: .common) } - let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false, block: { _ in - if peripheral.state == .connected { - peripheral.readRSSI() - self.lastReadAt = Date().timeIntervalSince1970 - } - }) - RunLoop.main.add(timer, forMode: .common) } func peripheral(_ peripheral: CBPeripheral, From d7656a03146d2ad46fb1031d16474b8475972921 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 12 Oct 2020 22:11:44 +0900 Subject: [PATCH 090/183] Fall back to passive mode when reading RSSI times out --- BLEUnlock/BLE.swift | 59 +++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 99073833..b2deb580 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -121,6 +121,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var latestRSSIs: [Double] = [] var latestN: Int = 5 var activeModeTimer : Timer? = nil + var connectionTimer : Timer? = nil func scanForPeripherals() { guard !centralMgr.isScanning else { return } @@ -182,9 +183,8 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { switch central.state { case .poweredOn: print("Bluetooth powered on") - scanForPeripherals() - if let p = monitoredPeripheral { - central.cancelPeripheralConnection(p) + if activeModeTimer == nil { + scanForPeripherals() } powerWarn = false case .poweredOff: @@ -220,13 +220,6 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } func updateMonitoredPeripheral(_ rssi: Int) { - if let p = monitoredPeripheral { - if !passiveMode && activeModeTimer == nil { - //print("connecting") - centralMgr.connect(p, options: nil) - } - } - // print(String(format: "rssi: %d", rssi)) if rssi >= (unlockRSSI == UNLOCK_DISABLED ? lockRSSI : unlockRSSI) && !presence { print("Device is close") @@ -250,14 +243,28 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { self.delegate?.updatePresence(presence: self.presence, reason: "away") self.proximityTimer = nil }) - if let timer = proximityTimer { - RunLoop.main.add(timer, forMode: .common) - print("Proximity timer started") - } + RunLoop.main.add(proximityTimer!, forMode: .common) + print("Proximity timer started") } resetSignalTimer() } + func connectMonitoredPeripheral() { + if let p = monitoredPeripheral { + guard p.state == .disconnected else { return } + //print("connecting") + centralMgr.connect(p, options: nil) + connectionTimer?.invalidate() + connectionTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in + if p.state == .connecting { + print("Connection timeout") + self.centralMgr.cancelPeripheralConnection(p) + } + }) + RunLoop.main.add(connectionTimer!, forMode: .common) + } + } + func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], @@ -270,8 +277,11 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { monitoredPeripheral = peripheral } if activeModeTimer == nil { - updateMonitoredPeripheral(rssi) //print("Discover \(rssi)dBm") + updateMonitoredPeripheral(rssi) + if !passiveMode { + connectMonitoredPeripheral() + } } } } @@ -322,6 +332,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { peripheral.discoverServices([DeviceInformation]) } if peripheral == monitoredPeripheral && !passiveMode { + connectionTimer?.invalidate() + connectionTimer = nil + //print("reading RSSI") peripheral.readRSSI() } } @@ -329,18 +342,28 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { //MARK:CBCentralManagerDelegate end - //MARK:- CBPeripheralDelegate start + func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { guard peripheral == monitoredPeripheral else { return } let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue - updateMonitoredPeripheral(rssi) //print("readRSSI \(rssi)dBm") + updateMonitoredPeripheral(rssi) centralMgr.cancelPeripheralConnection(peripheral) + lastReadAt = Date().timeIntervalSince1970 if activeModeTimer == nil && !passiveMode{ print("Entering active mode") centralMgr.stopScan() - activeModeTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { _ in - self.centralMgr.connect(peripheral, options: nil) + activeModeTimer = Timer.scheduledTimer(withTimeInterval: 4, repeats: true, block: { _ in + if Date().timeIntervalSince1970 > self.lastReadAt + 16 { + print("Falling back to passive mode") + self.centralMgr.cancelPeripheralConnection(peripheral) + self.activeModeTimer?.invalidate() + self.activeModeTimer = nil + self.scanForPeripherals() + } else { + self.connectMonitoredPeripheral() + } }) RunLoop.main.add(activeModeTimer!, forMode: .common) } From 993cbde2b9de1de9bfb4b42eaa19bc1978994d42 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 14 Oct 2020 18:18:21 +0900 Subject: [PATCH 091/183] Cleanup --- BLEUnlock/AppDelegate.swift | 1 - BLEUnlock/BLE.swift | 38 ++++++++++++++++++------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 1e7e3156..74c89558 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -526,7 +526,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let timeoutItem = mainMenu.addItem(withTitle: t("timeout"), action: nil, keyEquivalent: "") timeoutItem.submenu = timeoutMenu - timeoutMenu.addItem(withTitle: "15 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 15 timeoutMenu.addItem(withTitle: "30 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 30 timeoutMenu.addItem(withTitle: "1 " + t("minute"), action: #selector(setTimeout), keyEquivalent: "").tag = 60 timeoutMenu.addItem(withTitle: "2 " + t("minutes"), action: #selector(setTimeout), keyEquivalent: "").tag = 120 diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index b2deb580..c1b76d07 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -249,10 +249,24 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { resetSignalTimer() } + func resetScanTimer(device: Device) { + device.scanTimer?.invalidate() + device.scanTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in + self.delegate?.removeDevice(device: device) + if let p = device.peripheral { + self.centralMgr.cancelPeripheralConnection(p) + } + self.devices.removeValue(forKey: device.uuid) + }) + if let timer = device.scanTimer { + RunLoop.main.add(timer, forMode: .common) + } + } + func connectMonitoredPeripheral() { if let p = monitoredPeripheral { guard p.state == .disconnected else { return } - //print("connecting") + print("Connecting") centralMgr.connect(p, options: nil) connectionTimer?.invalidate() connectionTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in @@ -265,6 +279,8 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } + //MARK:- CBCentralManagerDelegate start + func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], @@ -277,7 +293,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { monitoredPeripheral = peripheral } if activeModeTimer == nil { - //print("Discover \(rssi)dBm") + print("Discover \(rssi)dBm") updateMonitoredPeripheral(rssi) if !passiveMode { connectMonitoredPeripheral() @@ -308,22 +324,6 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } - func resetScanTimer(device: Device) { - device.scanTimer?.invalidate() - device.scanTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in - self.delegate?.removeDevice(device: device) - if let p = device.peripheral { - self.centralMgr.cancelPeripheralConnection(p) - } - self.devices.removeValue(forKey: device.uuid) - }) - if let timer = device.scanTimer { - RunLoop.main.add(timer, forMode: .common) - } - } - - //MARK:- CBCentralManagerDelegate start - func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { @@ -334,7 +334,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if peripheral == monitoredPeripheral && !passiveMode { connectionTimer?.invalidate() connectionTimer = nil - //print("reading RSSI") + print("reading RSSI") peripheral.readRSSI() } } From 1d58a618e229bff69fe7cc8cbbc497bdde36b790 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 14 Oct 2020 21:31:31 +0900 Subject: [PATCH 092/183] Keep connection while active mode --- BLEUnlock/BLE.swift | 18 +++++++++++------- BLEUnlock/Info.plist | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index c1b76d07..bf3b1134 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -113,7 +113,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var lockRSSI = -80 var unlockRSSI = -60 var proximityTimeout = 4.5 - var signalTimeout = 30.0 + var signalTimeout = 60.0 var lastReadAt = 0.0 var powerWarn = true var passiveMode = false @@ -146,6 +146,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if passiveMode { activeModeTimer?.invalidate() activeModeTimer = nil + if let p = monitoredPeripheral { + centralMgr.cancelPeripheralConnection(p) + } } scanForPeripherals() } @@ -293,7 +296,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { monitoredPeripheral = peripheral } if activeModeTimer == nil { - print("Discover \(rssi)dBm") + //print("Discover \(rssi)dBm") updateMonitoredPeripheral(rssi) if !passiveMode { connectMonitoredPeripheral() @@ -334,7 +337,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if peripheral == monitoredPeripheral && !passiveMode { connectionTimer?.invalidate() connectionTimer = nil - print("reading RSSI") + //print("reading RSSI") peripheral.readRSSI() } } @@ -348,19 +351,20 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { let rssi = RSSI.intValue > 0 ? 0 : RSSI.intValue //print("readRSSI \(rssi)dBm") updateMonitoredPeripheral(rssi) - centralMgr.cancelPeripheralConnection(peripheral) lastReadAt = Date().timeIntervalSince1970 - if activeModeTimer == nil && !passiveMode{ + if activeModeTimer == nil && !passiveMode { print("Entering active mode") centralMgr.stopScan() - activeModeTimer = Timer.scheduledTimer(withTimeInterval: 4, repeats: true, block: { _ in - if Date().timeIntervalSince1970 > self.lastReadAt + 16 { + activeModeTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { _ in + if Date().timeIntervalSince1970 > self.lastReadAt + 10 { print("Falling back to passive mode") self.centralMgr.cancelPeripheralConnection(peripheral) self.activeModeTimer?.invalidate() self.activeModeTimer = nil self.scanForPeripherals() + } else if peripheral.state == .connected { + peripheral.readRSSI() } else { self.connectMonitoredPeripheral() } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 3f601960..9b431ea0 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 572 + 616 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From f6ece6492ac3532aff866860da4459790059fe63 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 14 Oct 2020 21:37:42 +0900 Subject: [PATCH 093/183] 1.9.0 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 9b431ea0..556efc53 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - $(MARKETING_VERSION) + 1.9.0 CFBundleVersion - 616 + 618 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From c60fdde1649bfbcd6ba033c725c2854ad7569181 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 15 Oct 2020 12:16:26 +0900 Subject: [PATCH 094/183] Password was truncated to 20 characters --- BLEUnlock/AppDelegate.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 74c89558..3dc733ed 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -234,13 +234,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func fakeKeyStrokes(_ string: String) { let src = CGEventSource(stateID: .hidSystemState) - let pressEvent = CGEvent(keyboardEventSource: src, virtualKey: 49, keyDown: true) - let len = string.count - let buffer = UnsafeMutablePointer.allocate(capacity: len) - NSString(string:string).getCharacters(buffer) - pressEvent?.keyboardSetUnicodeString(stringLength: len, unicodeString: buffer) - pressEvent?.post(tap: .cghidEventTap) - CGEvent(keyboardEventSource: src, virtualKey: 49, keyDown: false)?.post(tap: .cghidEventTap) + // Send key event once by one character so the string longer than 20 characters can be entered + for c in string { + let pressEvent = CGEvent(keyboardEventSource: src, virtualKey: 49, keyDown: true) + let char = Array(c.utf16) + pressEvent?.keyboardSetUnicodeString(stringLength: 1, unicodeString: char) + pressEvent?.post(tap: .cghidEventTap) + CGEvent(keyboardEventSource: src, virtualKey: 49, keyDown: false)?.post(tap: .cghidEventTap) + } // Return key CGEvent(keyboardEventSource: src, virtualKey: 52, keyDown: true)?.post(tap: .cghidEventTap) @@ -265,6 +266,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in guard self.isScreenLocked() else { return } guard let password = self.fetchPassword(warn: true) else { return } + print(password) print("Entering password") self.fakeKeyStrokes(password) From e9ebc8f5053e5a75eb79bbb6ddfeb96e72f94778 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 15 Oct 2020 12:48:01 +0900 Subject: [PATCH 095/183] Remove debug print --- BLEUnlock/AppDelegate.swift | 1 - README.ja.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 3dc733ed..0b6f0f81 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -266,7 +266,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in guard self.isScreenLocked() else { return } guard let password = self.fetchPassword(warn: true) else { return } - print(password) print("Entering password") self.fakeKeyStrokes(password) diff --git a/README.ja.md b/README.ja.md index 83b32661..7c0c098f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -153,4 +153,4 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y MIT -Copyright © 2019 Takeshi Sone. +Copyright © 2019-2020 Takeshi Sone. From 1422bc3884c2c36c2b4293cf3d237d7ec558b209 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 16 Oct 2020 19:55:27 +0900 Subject: [PATCH 096/183] Change maximum RSSI to -30dBm --- BLEUnlock/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 0b6f0f81..31b16c94 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -492,7 +492,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func constructRSSIMenu(_ menu: NSMenu, _ action: Selector) { menu.addItem(withTitle: t("closer"), action: nil, keyEquivalent: "") - for proximity in stride(from: -50, to: -100, by: -5) { + for proximity in stride(from: -30, to: -100, by: -5) { let item = menu.addItem(withTitle: String(format: "%ddBm", proximity), action: action, keyEquivalent: "") item.tag = proximity } From a3ae84e22099551cd750b28896def62c61da8e5e Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 17 Oct 2020 13:01:38 +0900 Subject: [PATCH 097/183] Exclude Exposure Notification See #20 --- BLEUnlock.xcodeproj/project.pbxproj | 4 +++- BLEUnlock/BLE.swift | 24 ++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 1a661eb3..af23ef8b 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -199,7 +199,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1020; - LastUpgradeCheck = 1170; + LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Takeshi Sone"; TargetAttributes = { 3D600A3F22701A5A0068FB7B = { @@ -435,6 +435,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -497,6 +498,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index bf3b1134..460c0ebe 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -2,6 +2,11 @@ import Foundation import CoreBluetooth import Accelerate +let DeviceInformation = CBUUID(string:"180A") +let ManufacturerName = CBUUID(string:"2A29") +let ModelName = CBUUID(string:"2A24") +let ExposureNotification = CBUUID(string:"FD6F") + func getMACFromUUID(_ uuid: String) -> String? { guard let plist = NSDictionary(contentsOfFile: "/Library/Preferences/com.apple.Bluetooth.plist") else { return nil } guard let cbcache = plist["CoreBluetoothCache"] as? NSDictionary else { return nil } @@ -79,16 +84,12 @@ class Device: NSObject { return uuid.description } } - + init(uuid _uuid: UUID) { uuid = _uuid } } -let DeviceInformation = CBUUID(string:"180A") -let ManufacturerName = CBUUID(string:"2A29") -let ModelName = CBUUID(string:"2A24") - protocol BLEDelegate { func newDevice(device: Device) func updateDevice(device: Device) @@ -306,6 +307,14 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } if (scanMode) { + if let uuids = advertisementData["kCBAdvDataServiceUUIDs"] as? [CBUUID] { + for uuid in uuids { + if uuid == ExposureNotification { + //print("Device \(peripheral.identifier) Exposure Notification") + return + } + } + } let dev = devices[peripheral.identifier] var device: Device if (dev == nil) { @@ -330,6 +339,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { + //print("didConnect") peripheral.delegate = self if scanMode { peripheral.discoverServices([DeviceInformation]) @@ -355,7 +365,9 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { if activeModeTimer == nil && !passiveMode { print("Entering active mode") - centralMgr.stopScan() + if !scanMode { + centralMgr.stopScan() + } activeModeTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { _ in if Date().timeIntervalSince1970 > self.lastReadAt + 10 { print("Falling back to passive mode") From 7e862a3c0e0607f29539834cc18396e0dd5a2e2f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 17 Oct 2020 13:27:21 +0900 Subject: [PATCH 098/183] Update README --- README.ja.md | 1 + README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.ja.md b/README.ja.md index 7c0c098f..afde9442 100644 --- a/README.ja.md +++ b/README.ja.md @@ -9,6 +9,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス - 定期的に信号を送出するBLEデバイスなら何でも使えます - デバイスがMacの近くにあればMacのロック画面を自動的に解除します - もうパスワードを入力する必要はありません - デバイスがMacから離れると画面をロックします +- ロック・アンロック時にスクリプトを実行することができます - デバイスがMacに近づくと画面スリープを解除します (オプション) - デバイスがMacから離れる/近づくとiTunesの再生を一時停止/再生します (オプション) - パスワードはキーチェーンに安全に保管されます diff --git a/README.md b/README.md index 8c46a2b1..80d3357c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ This document is also available in [Japanese](README.ja.md). - Works with any BLE devices that periodically send signal - Unlocks your Mac for you when the device is near Mac, no need to enter password again - Locks your Mac when the device is away from Mac +- Optionally runs your script on lock/unlock - Optionally wakes from display sleep state - Optionally pauses and unpauses iTunes playback when you're away and back - Password is securely stored in Keychain From e412a4d599e99add68d74cd39004ff7ec1547ba6 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 17 Oct 2020 13:31:14 +0900 Subject: [PATCH 099/183] Release 1.9.1 This closes #19, closes #20, and closes #22. --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 556efc53..800070f4 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.9.0 + 1.9.1 CFBundleVersion - 618 + 652 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 29be62432e2f221e0254572ef3c26d8ca9e2af0d Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 20 Oct 2020 22:04:14 +0900 Subject: [PATCH 100/183] Unlock from screensaver --- BLEUnlock/AppDelegate.swift | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 31b16c94..bd2dd03f 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -27,6 +27,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var wakeTimer: Timer? var manualLock = false var unlockedAt = 0.0 + var inScreensaver = false func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -263,6 +264,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard ble.unlockRSSI != ble.UNLOCK_DISABLED else { return } guard !systemSleep else { return } guard !displaySleep else { return } + + if inScreensaver { + // In screensaver, make sure Login panel is displayed + let src = CGEventSource(stateID: .hidSystemState) + // Shift key down and up + CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: true)?.post(tap: .cghidEventTap) + CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: false)?.post(tap: .cghidEventTap) + } + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in guard self.isScreenLocked() else { return } guard let password = self.fetchPassword(warn: true) else { return } @@ -313,6 +323,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa }) } + @objc func onScreensaverStart() { + print("screensaver start") + inScreensaver = true + } + + @objc func onScreensaverStop() { + print("screensaver stop") + inScreensaver = false + } + @objc func selectDevice(item: NSMenuItem) { for (uuid, menuItem) in deviceDict { if menuItem == item { @@ -615,7 +635,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa nc.addObserver(self, selector: #selector(onSystemSleep), name: NSWorkspace.willSleepNotification, object: nil) nc.addObserver(self, selector: #selector(onSystemWake), name: NSWorkspace.didWakeNotification, object: nil) - DistributedNotificationCenter.default().addObserver(self, selector: #selector(onUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) + let dnc = DistributedNotificationCenter.default + dnc.addObserver(self, selector: #selector(onUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) + 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 { askPassword() From e26897c3ca3c64dae79688fff0f54d829456dcbf Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 22 Oct 2020 19:02:40 +0900 Subject: [PATCH 101/183] Add iPhone models --- BLEUnlock/appleDeviceNames.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index df83cb8e..7d05863c 100644 --- a/BLEUnlock/appleDeviceNames.swift +++ b/BLEUnlock/appleDeviceNames.swift @@ -33,6 +33,11 @@ let appleDeviceNames = [ "iPhone12,1": "iPhone 11", "iPhone12,3": "iPhone 11 Pro", "iPhone12,5": "iPhone 11 Pro Max", + "iPhone12,8": "iPhone SE 2nd Gen", + "iPhone13,1": "iPhone 12 Mini", + "iPhone13,2": "iPhone 12", + "iPhone13,3": "iPhone 12 Pro", + "iPhone13,4": "iPhone 12 Pro Max", "iPad1,1": "iPad", "iPad2,1": "iPad 2", From 1ff9476e4f5b9583e9eb840dc4196bd9ff343e14 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 29 Oct 2020 22:03:45 +0900 Subject: [PATCH 102/183] Update funding information --- .github/FUNDING.yml | 2 +- README.ja.md | 9 +++++++++ README.md | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 711ecdc1..c97ded96 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ["https://paypal.me/takeshisone"] +custom: ["https://www.buymeacoffee.com/tsone", "https://paypal.me/takeshisone"] diff --git a/README.ja.md b/README.ja.md index afde9442..01eb48d9 100644 --- a/README.ja.md +++ b/README.ja.md @@ -142,6 +142,15 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y これはBLEUnlockにカメラのパーミッションがないため必要となります。このappにパーミッションを与えることによりパーミッションの問題を回避できます。 +## FUNDING + +1.9.0以降のバイナリリリースはAppleによって公証されていません。 +このため、**起動するには右クリックして開き、Keychain等のパーミッションを再認証する必要があります**。 + +現在私の会社ではMacやiOSのアプリを開発していないため、有料のApple Developerアカウントにアクセスできません。 + +このアプリを気に入っていただけたら、Apple Developer Programの費用を自分で払うことができるよう、[Buy Me a Coffee](https://www.buymeacoffee.com/tsone) もしくは [PayPal.Me](https://paypal.me/takeshisone) で寄付をいただけるとありがたいです。 + ## クレジット - peiit: 中国語の翻訳 diff --git a/README.md b/README.md index 80d3357c..46fb68ea 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,15 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y This is required because BLEUnlock does not have Camera permission. Giving permission to this app resolve the problem. +## Funding + +Since 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.** + +My company is not developing iOS/Mac app at the moment, so I don't have access to a paid Apple Developer account. + +If you find this app is useful, please help me pay for Apple Developer Program by donating via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone)! + ## Credits - peiit: Chinese translation From fdc51c0b58408d2392fdff5689625d88815d471b Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 31 Oct 2020 16:59:05 +0900 Subject: [PATCH 103/183] Replace iTunes/Music with Now Playing --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++ BLEUnlock/AppDelegate.swift | 59 ++++++++------------- BLEUnlock/Base.lproj/Localizable.strings | 3 +- BLEUnlock/Info.plist | 2 +- BLEUnlock/NowPlaying.swift | 40 ++++++++++++++ BLEUnlock/ja.lproj/Localizable.strings | 3 +- BLEUnlock/zh-Hans.lproj/Localizable.strings | 3 +- 7 files changed, 69 insertions(+), 45 deletions(-) create mode 100644 BLEUnlock/NowPlaying.swift diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index af23ef8b..513cd890 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 3D2FCF08226C99CB007A06E7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D2FCF07226C99CB007A06E7 /* Images.xcassets */; }; + 3D43E004254D3946009D5EEB /* NowPlaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D43E003254D3946009D5EEB /* NowPlaying.swift */; }; 3D4EE4112279E55A00AF9E93 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */; }; 3D4EE4132279E56900AF9E93 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4122279E56900AF9E93 /* main.m */; }; 3D4EE4152279E94B00AF9E93 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */; }; @@ -41,6 +42,7 @@ /* Begin PBXFileReference section */ 3D1B377B22772B740024E2E0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 3D43E003254D3946009D5EEB /* NowPlaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NowPlaying.swift; sourceTree = ""; }; 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 3D4EE4122279E56900AF9E93 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -126,6 +128,7 @@ 3DD4B652226C1C3200451B7B /* BLEUnlock */ = { isa = PBXGroup; children = ( + 3D43E003254D3946009D5EEB /* NowPlaying.swift */, 3D95379322784D550017D8B9 /* AboutBox.swift */, 3D953799227853E20017D8B9 /* AboutBox.xib */, 3D600A35226CC1A40068FB7B /* Localizable.strings */, @@ -311,6 +314,7 @@ files = ( 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */, 3DD4B666226C1DF200451B7B /* BLE.swift in Sources */, + 3D43E004254D3946009D5EEB /* NowPlaying.swift in Sources */, 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */, 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */, 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */, diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index bd2dd03f..6da5089c 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -22,7 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var systemSleep = false var connected = false var userNotification: NSUserNotification? - var iTunesWasPlaying = false + var nowPlayingWasPlaying = false var aboutBox: AboutBox? = nil var wakeTimer: Timer? var manualLock = false @@ -173,32 +173,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa return output } - func iTunesName() -> String { - if #available(OSX 10.15, *) { - return "Music" - } else { - return "iTunes" - } - } - - func isItunesPlaying() -> Bool { - let result = runAppleScript("tell application \"\(iTunesName())\" to return player state") - return result?.stringValue == "kPSP" - } - - func pauseItunes() { + func pauseNowPlaying() { guard prefs.bool(forKey: "pauseItunes") else { return } - iTunesWasPlaying = isItunesPlaying() - if iTunesWasPlaying { - _ = runAppleScript("tell application \"\(iTunesName())\" to pause") - } + NowPlayingIsPlaying({ (playing) in + self.nowPlayingWasPlaying = playing + if self.nowPlayingWasPlaying { + print("pause") + NowPlayingPause() + } + }) } - func playItunes() { + func playNowPlaying() { guard prefs.bool(forKey: "pauseItunes") else { return } - if iTunesWasPlaying { - _ = runAppleScript("tell application \"\(iTunesName())\" to play") - iTunesWasPlaying = false + if nowPlayingWasPlaying { + print("play") + NowPlayingPlay() } } @@ -221,7 +211,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } else { if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { - pauseItunes() + pauseNowPlaying() if lockScreen() { notifyUser(reason) runScript(reason) @@ -279,7 +269,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa print("Entering password") self.fakeKeyStrokes(password) - self.playItunes() + self.playNowPlaying() self.runScript("unlocked") self.unlockedAt = Date().timeIntervalSince1970 }) @@ -483,13 +473,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa SMLoginItemSetEnabled(Bundle.main.bundleIdentifier! + ".Launcher" as CFString, launchAtLogin) } - @objc func togglePauseItunes(_ menuItem: NSMenuItem) { - let pauseItunes = !prefs.bool(forKey: "pauseItunes") - prefs.set(pauseItunes, forKey: "pauseItunes") - menuItem.state = pauseItunes ? .on : .off - if pauseItunes { - _ = isItunesPlaying() // Show permission dialog - } + @objc func togglePauseNowPlaying(_ menuItem: NSMenuItem) { + let pauseNowPlaying = !prefs.bool(forKey: "pauseItunes") + prefs.set(pauseNowPlaying, forKey: "pauseItunes") + menuItem.state = pauseNowPlaying ? .on : .off } @objc func togglePassiveMode(_ menuItem: NSMenuItem) { @@ -502,7 +489,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func lockNow() { guard !isScreenLocked() else { return } manualLock = true - pauseItunes() + pauseNowPlaying() lockScreen() } @@ -559,11 +546,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .on } - var pause_itunes = "pause_itunes" - if #available(OSX 10.15, *) { - pause_itunes = "pause_music" - } - item = mainMenu.addItem(withTitle: t(pause_itunes), action: #selector(togglePauseItunes), keyEquivalent: "") + item = mainMenu.addItem(withTitle: t("pause_now_playing"), action: #selector(togglePauseNowPlaying), keyEquivalent: "") if prefs.bool(forKey: "pauseItunes") { item.state = .on } diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 97cac79d..3da0da46 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -23,8 +23,7 @@ "passive_mode" = "Passive Mode"; "password_info" = "Your password will be securely stored in Keychain."; "password_not_set" = "Password is not set."; -"pause_itunes" = "Pause iTunes while Locked"; -"pause_music" = "Pause Music while Locked"; +"pause_now_playing" = "Pause \"Now Playing\" while Locked"; "quit" = "Quit BLEUnlock"; "scanning" = "Scanning…"; "seconds" = "seconds"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 800070f4..f868f904 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 652 + 657 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/NowPlaying.swift b/BLEUnlock/NowPlaying.swift new file mode 100644 index 00000000..d66a93f1 --- /dev/null +++ b/BLEUnlock/NowPlaying.swift @@ -0,0 +1,40 @@ +import Foundation +import CoreFoundation + +private func getMediaRemoteBundle() -> CFBundle { + return CFBundleCreate( + kCFAllocatorDefault, + NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework") + ) +} + +private func sendCommand(_ command: Int) { + typealias funcType = @convention(c) (Int, NSDictionary?) -> Void + guard let ptr = CFBundleGetFunctionPointerForName( + getMediaRemoteBundle(), + "MRMediaRemoteSendCommand" as CFString + ) else { return } + let MRMediaRemoteSendCommand = unsafeBitCast(ptr, to: funcType.self) + MRMediaRemoteSendCommand(command, nil) +} + +func NowPlayingIsPlaying(_ closure: @escaping (Bool) -> Void) { + typealias funcType = @convention(c) (DispatchQueue, @escaping (Bool) -> Void) -> Void + guard let ptr = CFBundleGetFunctionPointerForName( + getMediaRemoteBundle(), + "MRMediaRemoteGetNowPlayingApplicationIsPlaying" as CFString + ) else { + closure(false) + return + } + let MRMediaRemoteIsPlaying = unsafeBitCast(ptr, to: funcType.self) + MRMediaRemoteIsPlaying(DispatchQueue.main, closure) +} + +func NowPlayingPlay() { + sendCommand(0) // MRMediaRemoteCommandPlay +} + +func NowPlayingPause() { + sendCommand(1) // MRMediaRemoteCommandPause +} diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index d323c9aa..7908d4c5 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -23,8 +23,7 @@ "passive_mode" = "パッシブモード"; "password_info" = "パスワードはキーチェーンに安全に保存されます。"; "password_not_set" = "パスワードが設定されていません。"; -"pause_itunes" = "ロック中 iTunes を一時停止"; -"pause_music" = "ロック中ミュージックを一時停止"; +"pause_now_playing" = "ロック中 \"再生中\" を一時停止"; "quit" = "BLEUnlock を終了"; "scanning" = "スキャン中…"; "seconds" = "秒"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 9d81ee78..4add0da1 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -23,8 +23,7 @@ "passive_mode" = "被动模式"; "password_info" = "您的密码将安全地存储在钥匙串中。"; "password_not_set" = "密码未设置。"; -"pause_itunes" = "锁定时暂停iTunes"; -"pause_music" = "锁定时暂停Music"; +"pause_now_playing" = "锁定时暂停\"播放中\""; "quit" = "退出BLEUnlock"; "scanning" = "扫描中…"; "seconds" = "秒钟"; From 4ffd228201bf045a18715a4f7db5930241e859f7 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 31 Oct 2020 19:52:07 +0900 Subject: [PATCH 104/183] Optionally launch screensaver to lock --- BLEUnlock/AppDelegate.swift | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 6da5089c..d2df093d 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -162,17 +162,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa try? process.run() } - func runAppleScript(_ script: String) -> NSAppleEventDescriptor? { - guard let scriptObject = NSAppleScript(source: script) else { return nil } - var error: NSDictionary? - let output = scriptObject.executeAndReturnError(&error) - if let e = error { - debugPrint(e) - return nil - } - return output - } - func pauseNowPlaying() { guard prefs.bool(forKey: "pauseItunes") else { return } NowPlayingIsPlaying({ (playing) in @@ -192,6 +181,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } } + func lockOrSaveScreen() -> Bool { + if prefs.bool(forKey: "screensaver") { + NSWorkspace.shared.launchApplication("ScreenSaverEngine") + return true // Really!? + } else { + return lockScreen() + } + } + func updatePresence(presence: Bool, reason: String) { if presence { if ble.unlockRSSI != ble.UNLOCK_DISABLED { @@ -212,7 +210,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } else { if (!isScreenLocked() && ble.lockRSSI != ble.LOCK_DISABLED) { pauseNowPlaying() - if lockScreen() { + if lockOrSaveScreen() { notifyUser(reason) runScript(reason) } else { @@ -490,7 +488,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard !isScreenLocked() else { return } manualLock = true pauseNowPlaying() - lockScreen() + lockOrSaveScreen() } @objc func showAboutBox() { From 28b09e50d70030ba15bd33d5b69ee29ccf9d3298 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 31 Oct 2020 20:28:20 +0900 Subject: [PATCH 105/183] Add menu item to set screensaver option --- BLEUnlock/AppDelegate.swift | 11 +++++++++++ BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/zh-Hans.lproj/Localizable.strings | 1 + 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index d2df093d..c0c413d0 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -477,6 +477,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa menuItem.state = pauseNowPlaying ? .on : .off } + @objc func toggleUseScreensaver(_ menuItem: NSMenuItem) { + let value = !prefs.bool(forKey: "screensaver") + prefs.set(value, forKey: "screensaver") + menuItem.state = value ? .on : .off + } + @objc func togglePassiveMode(_ menuItem: NSMenuItem) { let passiveMode = !prefs.bool(forKey: "passiveMode") prefs.set(passiveMode, forKey: "passiveMode") @@ -548,6 +554,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if prefs.bool(forKey: "pauseItunes") { item.state = .on } + + item = mainMenu.addItem(withTitle: t("use_screensaver_to_lock"), action: #selector(toggleUseScreensaver), keyEquivalent: "") + if prefs.bool(forKey: "screensaver") { + item.state = .on + } mainMenu.addItem(withTitle: t("set_password"), action: #selector(askPassword), keyEquivalent: "") diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 3da0da46..7d6a9e59 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -31,4 +31,5 @@ "set_rssi_threshold" = "Set Minimum RSSI…"; "timeout" = "No-Signal Timeout"; "unlock_rssi" = "Unlock RSSI"; +"use_screensaver_to_lock" = "Use Screensaver to Lock"; "wake_on_proximity" = "Wake on Proximity"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index f868f904..8eac83b3 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 657 + 676 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index 7908d4c5..fdd45c8f 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -31,4 +31,5 @@ "set_rssi_threshold" = "最小RSSIを設定…"; "timeout" = "無信号タイムアウト"; "unlock_rssi" = "アンロック信号強度"; +"use_screensaver_to_lock" = "スクリーンセーバーでロック"; "wake_on_proximity" = "画面スリープから復帰"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 4add0da1..02e4093c 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -31,4 +31,5 @@ "set_rssi_threshold" = "设置最小RSSI…"; "timeout" = "无信号超时"; "unlock_rssi" = "解锁 RSSI"; +"use_screensaver_to_lock" = "用屏保来锁定它"; "wake_on_proximity" = "靠近唤醒"; From 211d8e248d468b274d6a5979030e094009996907 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 1 Nov 2020 12:30:19 +0900 Subject: [PATCH 106/183] Cleanup --- BLEUnlock.xcodeproj/project.pbxproj | 32 ++++++----------------- BLEUnlock/AppDelegate.swift | 2 +- BLEUnlock/Base.lproj/InfoPlist.strings | 1 - BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/InfoPlist.strings | 1 - BLEUnlock/lowlevel.c | 9 ------- BLEUnlock/lowlevel.h | 2 -- BLEUnlock/zh-Hans.lproj/InfoPlist.strings | 1 - README.md | 2 +- 9 files changed, 11 insertions(+), 41 deletions(-) delete mode 100644 BLEUnlock/Base.lproj/InfoPlist.strings delete mode 100644 BLEUnlock/ja.lproj/InfoPlist.strings delete mode 100644 BLEUnlock/zh-Hans.lproj/InfoPlist.strings diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 513cd890..22330f26 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */; }; 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D95379322784D550017D8B9 /* AboutBox.swift */; }; 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D953799227853E20017D8B9 /* AboutBox.xib */; }; - 3DC0BC6D22729050001145EC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3DC0BC6F22729050001145EC /* InfoPlist.strings */; }; 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */; }; 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD4B653226C1C3200451B7B /* AppDelegate.swift */; }; 3DD4B659226C1C3400451B7B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3DD4B657226C1C3400451B7B /* MainMenu.xib */; }; @@ -40,7 +39,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3D1B377B22772B740024E2E0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 3D43E003254D3946009D5EEB /* NowPlaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NowPlaying.swift; sourceTree = ""; }; 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -57,7 +55,6 @@ 3D953798227853E20017D8B9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutBox.xib; sourceTree = ""; }; 3D95379B227853EC0017D8B9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/AboutBox.strings; sourceTree = ""; }; 3DC0BC6922728FF1001145EC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3DC0BC6E22729050001145EC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = checkUpdate.swift; sourceTree = ""; }; 3DD4B650226C1C3200451B7B /* BLEUnlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BLEUnlock.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3DD4B653226C1C3200451B7B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -70,7 +67,6 @@ 3DD4B668226C1E3700451B7B /* login.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = login.framework; path = ../../../../System/Library/PrivateFrameworks/login.framework; sourceTree = ""; }; B1EFF91422E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutBox.strings"; sourceTree = ""; }; B1EFF91622E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; - B1EFF91722E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -135,7 +131,6 @@ 3DD4B665226C1DF200451B7B /* BLE.swift */, 3DD4B653226C1C3200451B7B /* AppDelegate.swift */, 3DC0BC6922728FF1001145EC /* Info.plist */, - 3DC0BC6F22729050001145EC /* InfoPlist.strings */, 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */, 3DD4B662226C1CEF00451B7B /* lowlevel.h */, 3DD4B663226C1CF000451B7B /* lowlevel.c */, @@ -272,7 +267,6 @@ 3D600A37226CC1A40068FB7B /* Localizable.strings in Resources */, 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */, 3DD4B659226C1C3400451B7B /* MainMenu.xib in Resources */, - 3DC0BC6D22729050001145EC /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -345,16 +339,6 @@ name = AboutBox.xib; sourceTree = ""; }; - 3DC0BC6F22729050001145EC /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 3DC0BC6E22729050001145EC /* ja */, - 3D1B377B22772B740024E2E0 /* Base */, - B1EFF91722E010F50010DB0A /* zh-Hans */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; 3DD4B657226C1C3400451B7B /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( @@ -370,10 +354,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42LGPQYC7M; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Launcher/Info.plist; INSTALL_PATH = ""; @@ -537,10 +521,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42LGPQYC7M; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = BLEUnlock/Info.plist; @@ -548,7 +532,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = "1.9.0-beta"; + MARKETING_VERSION = ""; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -579,7 +563,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = "1.9.0-beta"; + MARKETING_VERSION = ""; PRODUCT_BUNDLE_IDENTIFIER = jp.sone.BLEUnlock; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index c0c413d0..aa045681 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -494,7 +494,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard !isScreenLocked() else { return } manualLock = true pauseNowPlaying() - lockOrSaveScreen() + _ = lockOrSaveScreen() } @objc func showAboutBox() { diff --git a/BLEUnlock/Base.lproj/InfoPlist.strings b/BLEUnlock/Base.lproj/InfoPlist.strings deleted file mode 100644 index c7181a34..00000000 --- a/BLEUnlock/Base.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -"NSAppleEventsUsageDescription" = "BLEUnlock pauses/unpauses Music (iTunes) playback upon locking/unlocking screen."; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8eac83b3..78bd5c68 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 676 + 684 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/InfoPlist.strings b/BLEUnlock/ja.lproj/InfoPlist.strings deleted file mode 100644 index be05ed8b..00000000 --- a/BLEUnlock/ja.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -"NSAppleEventsUsageDescription" = "画面ロック/解除時にミュージック(iTunes)の一時停止/解除を行います。"; diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index 3e2c53a4..2f70ccb0 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -13,12 +13,3 @@ bool lockScreen(void) extern int SACLockScreenImmediate(void); return SACLockScreenImmediate() == 0; } - -void sleepDisplay(void) -{ - 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 0f96e9c9..eec0625a 100644 --- a/BLEUnlock/lowlevel.h +++ b/BLEUnlock/lowlevel.h @@ -4,7 +4,5 @@ void wakeDisplay(void); bool lockScreen(void); -void sleepDisplay(void); - #endif /* lowlevel_h */ diff --git a/BLEUnlock/zh-Hans.lproj/InfoPlist.strings b/BLEUnlock/zh-Hans.lproj/InfoPlist.strings deleted file mode 100644 index f9daf712..00000000 --- a/BLEUnlock/zh-Hans.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -"NSAppleEventsUsageDescription" = "锁定/解锁屏幕时,BLEUnlock将暂停/继续Music (iTunes)播放。"; diff --git a/README.md b/README.md index 46fb68ea..4aa0ffad 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Because of this, **you have to right-click and Open to start, and you have to re My company is not developing iOS/Mac app at the moment, so I don't have access to a paid Apple Developer account. -If you find this app is useful, please help me pay for Apple Developer Program by donating via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone)! +If you find this app useful, please help me pay for Apple Developer Program by donating via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone)! ## Credits From 2b1566781627d1e29a4c1924273660b4ddf68ee1 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 1 Nov 2020 12:45:36 +0900 Subject: [PATCH 107/183] Show active mode status in menu item --- BLEUnlock/AppDelegate.swift | 4 ++-- BLEUnlock/BLE.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index aa045681..35d18e04 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -106,9 +106,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa deviceDict.removeValue(forKey: device.uuid) } - func updateRSSI(rssi: Int?) { + func updateRSSI(rssi: Int?, active: Bool) { if let r = rssi { - monitorMenuItem?.title = String(format:"%ddBm", r) + monitorMenuItem?.title = String(format:"%ddBm", r) + (active ? " (Active)" : "") if (!connected) { connected = true statusItem.button?.image = NSImage(named: "StatusBarConnected") diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 460c0ebe..4fa0169e 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -94,7 +94,7 @@ protocol BLEDelegate { func newDevice(device: Device) func updateDevice(device: Device) func removeDevice(device: Device) - func updateRSSI(rssi: Int?) + func updateRSSI(rssi: Int?, active: Bool) func updatePresence(presence: Bool, reason: String) func bluetoothPowerWarn() } @@ -172,7 +172,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { signalTimer?.invalidate() signalTimer = Timer.scheduledTimer(withTimeInterval: signalTimeout, repeats: false, block: { _ in print("Device is lost") - self.delegate?.updateRSSI(rssi: nil) + self.delegate?.updateRSSI(rssi: nil, active: false) if self.presence { self.presence = false self.delegate?.updatePresence(presence: self.presence, reason: "lost") @@ -232,7 +232,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) - delegate?.updateRSSI(rssi: estimatedRSSI) + delegate?.updateRSSI(rssi: estimatedRSSI, active: activeModeTimer != nil) if estimatedRSSI >= (lockRSSI == LOCK_DISABLED ? unlockRSSI : lockRSSI) { if let timer = proximityTimer { From e626427f23108446b2bc7f054f4a3b7d38fbfad0 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 1 Nov 2020 14:21:30 +0900 Subject: [PATCH 108/183] Update Apple device names --- BLEUnlock/Info.plist | 2 +- BLEUnlock/appleDeviceNames.swift | 266 +++++++++++++++++-------------- apple-device-names | 28 ++++ 3 files changed, 173 insertions(+), 123 deletions(-) create mode 100755 apple-device-names diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 78bd5c68..ed13b00c 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 684 + 687 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index 7d05863c..e49eefaf 100644 --- a/BLEUnlock/appleDeviceNames.swift +++ b/BLEUnlock/appleDeviceNames.swift @@ -1,122 +1,144 @@ -let appleDeviceNames = [ - "iPhone1,1": "iPhone", - "iPhone1,2": "iPhone 3G", - "iPhone2,1": "iPhone 3GS", - "iPhone3,1": "iPhone 4", - "iPhone3,2": "iPhone 4", - "iPhone3,3": "iPhone 4", - "iPhone4,1": "iPhone 4S", - "iPhone5,1": "iPhone 5", - "iPhone5,2": "iPhone 5", - "iPhone5,3": "iPhone 5c", - "iPhone5,4": "iPhone 5c", - "iPhone6,1": "iPhone 5s", - "iPhone6,2": "iPhone 5s", - "iPhone7,1": "iPhone 6 Plus", - "iPhone7,2": "iPhone 6", - "iPhone8,1": "iPhone 6s", - "iPhone8,2": "iPhone 6s Plus", - "iPhone8,4": "iPhone SE", - "iPhone9,1": "iPhone 7", - "iPhone9,2": "iPhone 7 Plus", - "iPhone9,3": "iPhone 7", - "iPhone9,4": "iPhone 7 Plus", - "iPhone10,1": "iPhone 8", - "iPhone10,2": "iPhone 8 Plus", - "iPhone10,3": "iPhone X", - "iPhone10,4": "iPhone 8", - "iPhone10,5": "iPhone 8 Plus", - "iPhone10,6": "iPhone X", - "iPhone11,2": "iPhone XS", - "iPhone11,6": "iPhone XS Max", - "iPhone11,8": "iPhone XR", - "iPhone12,1": "iPhone 11", - "iPhone12,3": "iPhone 11 Pro", - "iPhone12,5": "iPhone 11 Pro Max", - "iPhone12,8": "iPhone SE 2nd Gen", - "iPhone13,1": "iPhone 12 Mini", - "iPhone13,2": "iPhone 12", - "iPhone13,3": "iPhone 12 Pro", - "iPhone13,4": "iPhone 12 Pro Max", - - "iPad1,1": "iPad", - "iPad2,1": "iPad 2", - "iPad2,2": "iPad 2", - "iPad2,3": "iPad 2", - "iPad2,4": "iPad 2", - "iPad2,5": "iPad mini", - "iPad2,6": "iPad mini", - "iPad2,7": "iPad mini", - "iPad3,1": "iPad 3", - "iPad3,2": "iPad 3", - "iPad3,3": "iPad 3", - "iPad3,4": "iPad 4", - "iPad3,5": "iPad 4", - "iPad3,6": "iPad 4", - "iPad4,1": "iPad Air", - "iPad4,2": "iPad Air", - "iPad4,3": "iPad Air", - "iPad4,4": "iPad mini 2", - "iPad4,5": "iPad mini 2", - "iPad4,6": "iPad mini 2", - "iPad4,7": "iPad mini 3", - "iPad4,8": "iPad mini 3", - "iPad4,9": "iPad mini 3", - "iPad5,1": "iPad mini 4", - "iPad5,2": "iPad mini 4", - "iPad5,3": "iPad Air 2", - "iPad5,4": "iPad Air 2", - "iPad6,3": "iPad Pro (9.7 inch)", - "iPad6,4": "iPad Pro (9.7 inch)", - "iPad6,7": "iPad Pro (12.9 inch)", - "iPad6,8": "iPad Pro (12.9 inch)", - "iPad6,11": "iPad (5th generation)", - "iPad6,12": "iPad (5th generation)", - "iPad7,1": "iPad Pro (12.9-inch, 2nd generation)", - "iPad7,2": "iPad Pro (12.9-inch, 2nd generation)", - "iPad7,3": "iPad Pro (10.5-inch)", - "iPad7,4": "iPad Pro (10.5-inch)", - "iPad7,5": "iPad (6th generation)", - "iPad7,6": "iPad (6th generation)", - "iPad7,11": "iPad 7th Gen 10.2-inch (WiFi)", - "iPad7,12": "iPad 7th Gen 10.2-inch (WiFi+Cellular)", - "iPad8,1": "iPad Pro 3rd Gen (11 inch, WiFi)", - "iPad8,2": "iPad Pro 3rd Gen (11 inch, 1TB, WiFi)", - "iPad8,3": "iPad Pro 3rd Gen (11 inch, WiFi+Cellular)", - "iPad8,4": "iPad Pro 3rd Gen (11 inch, 1TB, WiFi+Cellular)", - "iPad8,5": "iPad Pro 3rd Gen (12.9 inch, WiFi)", - "iPad8,6": "iPad Pro 3rd Gen (12.9 inch, 1TB, WiFi)", - "iPad8,7": "iPad Pro 3rd Gen (12.9 inch, WiFi+Cellular)", - "iPad8,8": "iPad Pro 3rd Gen (12.9 inch, 1TB, WiFi+Cellular)", - "iPad11,1": "iPad mini 5th Gen (WiFi)", - "iPad11,2": "iPad mini 5th Gen", - "iPad11,3": "iPad Air 3rd Gen (WiFi)", - "iPad11,4": "iPad Air 3rd Gen", - - "iPod1,1": "iPod touch", - "iPod2,1": "iPod touch 2G", - "iPod3,1": "iPod touch 3G", - "iPod4,1": "iPod touch 4G", - "iPod5,1": "iPod touch 5G", - "iPod7,1": "iPod touch 6G", - "iPod9,1": "iPod touch 7G", - - "Watch1,1": "Apple Watch (1st gen.)", - "Watch1,2": "Apple Watch (1st gen.)", - "Watch2,6": "Apple Watch Series 1", - "Watch2,7": "Apple Watch Series 1", - "Watch2,3": "Apple Watch Series 2", - "Watch2,4": "Apple Watch Series 2", - "Watch3,1": "Apple Watch Series 3", - "Watch3,2": "Apple Watch Series 3", - "Watch3,3": "Apple Watch Series 3", - "Watch3,4": "Apple Watch Series 3", - "Watch4,1": "Apple Watch Series 4", - "Watch4,2": "Apple Watch Series 4", - "Watch4,3": "Apple Watch Series 4", - "Watch4,4": "Apple Watch Series 4", - "Watch5,1": "Apple Watch Series 5 40mm case (GPS)", - "Watch5,2": "Apple Watch Series 5 44mm case (GPS)", - "Watch5,3": "Apple Watch Series 5 40mm case (GPS+Cellular)", - "Watch5,4": "Apple Watch Series 5 44mm case (GPS+Cellular)", -] +let appleDeviceNames = [ + "iPhone1,1": "iPhone", + "iPhone1,2": "iPhone 3G", + "iPhone2,1": "iPhone 3GS", + "iPhone3,1": "iPhone 4 (GSM)", + "iPhone3,2": "iPhone 4 (GSM Rev A)", + "iPhone3,3": "iPhone 4 (CDMA)", + "iPhone4,1": "iPhone 4S", + "iPhone5,1": "iPhone 5", + "iPhone5,2": "iPhone 5", + "iPhone5,3": "iPhone 5c", + "iPhone5,4": "iPhone 5c", + "iPhone6,1": "iPhone 5s", + "iPhone6,2": "iPhone 5s", + "iPhone7,1": "iPhone 6 Plus", + "iPhone7,2": "iPhone 6", + "iPhone8,1": "iPhone 6s", + "iPhone8,2": "iPhone 6s Plus", + "iPhone8,4": "iPhone SE", + "iPhone9,1": "iPhone 7", + "iPhone9,3": "iPhone 7", + "iPhone9,2": "iPhone 7 Plus", + "iPhone9,4": "iPhone 7 Plus", + "iPhone10,1": "iPhone 8", + "iPhone10,4": "iPhone 8", + "iPhone10,2": "iPhone 8 Plus", + "iPhone10,5": "iPhone 8 Plus", + "iPhone12,8": "iPhone SE", + "iPhone10,3": "iPhone X", + "iPhone10,6": "iPhone X", + "iPhone11,2": "iPhone XS", + "iPhone11,4": "iPhone XS Max", + "iPhone11,6": "iPhone XS Max", + "iPhone11,8": "iPhone XR", + "iPhone12,3": "iPhone 11 Pro", + "iPhone12,5": "iPhone 11 Pro Max", + "iPhone13,1": "iPhone 12 mini", + "iPhone13,2": "iPhone 12", + "iPhone13,3": "iPhone 12 Pro", + "iPhone13,4": "iPhone 12 Pro Max", + "iPhone12,1": "iPhone 11", + "iPod1,1": "iPod touch (1st generation)", + "iPod2,1": "iPod touch (2nd generation)", + "iPod3,1": "iPod touch (3rd generation)", + "iPod4,1": "iPod touch (4th generation)", + "iPod5,1": "iPod touch (5th generation)", + "iPod7,1": "iPod touch (6th generation)", + "iPod9,1": "iPod touch (7th generation)", + "iPad1,1": "iPad", + "iPad2,1": "iPad 2", + "iPad2,2": "iPad 2 Wi-Fi + 3G (GSM)", + "iPad2,3": "iPad 2 Wi-Fi + 3G (CDMA)", + "iPad2,4": "iPad 2 (Rev A)", + "iPad3,1": "iPad (3rd generation)", + "iPad3,2": "iPad Wi-Fi + 4G (LTE/CDMA)", + "iPad3,3": "iPad Wi-Fi + 4G (LTE/GSM)", + "iPad3,4": "iPad (4th generation)", + "iPad3,5": "iPad (4th generation)", + "iPad3,6": "iPad (4th generation)", + "iPad4,1": "iPad Air", + "iPad4,2": "iPad Air", + "iPad4,3": "iPad Air", + "iPad5,3": "iPad Air 2", + "iPad5,4": "iPad Air 2", + "iPad6,11": "iPad (5th generation)", + "iPad6,12": "iPad (5th generation)", + "iPad11,3": "iPad Air (3rd generation)", + "iPad11,4": "iPad Air (3rd generation)", + "iPad13,1": "iPad Air (4th generation)", + "iPad13,2": "iPad Air (4th generation)", + "iPad7,5": "iPad (6th generation)", + "iPad7,6": "iPad (6th generation)", + "iPad2,5": "iPad mini", + "iPad2,6": "iPad mini", + "iPad2,7": "iPad mini", + "iPad4,4": "iPad mini 2", + "iPad4,5": "iPad mini 2", + "iPad4,6": "iPad mini 2", + "iPad4,7": "iPad mini 3", + "iPad4,8": "iPad mini 3", + "iPad4,9": "iPad mini 3", + "iPad5,1": "iPad mini 4", + "iPad5,2": "iPad mini 4", + "iPad11,1": "iPad mini (5th generation)", + "iPad11,2": "iPad mini (5th generation)", + "iPad6,7": "iPad Pro (12.9-inch)", + "iPad6,8": "iPad Pro (12.9-inch)", + "iPad6,3": "iPad Pro (9.7-inch)", + "iPad6,4": "iPad Pro (9.7-inch)", + "iPad7,1": "iPad Pro (12.9-inch, 2nd generation)", + "iPad7,2": "iPad Pro (12.9-inch, 2nd generation)", + "iPad7,3": "iPad Pro (10.5-inch)", + "iPad7,4": "iPad Pro (10.5-inch)", + "iPad8,1": "iPad Pro (11-inch)", + "iPad8,2": "iPad Pro (11-inch)", + "iPad8,3": "iPad Pro (11-inch)", + "iPad8,4": "iPad Pro (11-inch)", + "iPad8,5": "iPad Pro (12.9-inch) (3rd generation)", + "iPad8,6": "iPad Pro (12.9-inch) (3rd generation)", + "iPad8,7": "iPad Pro (12.9-inch) (3rd generation)", + "iPad8,8": "iPad Pro (12.9-inch) (3rd generation)", + "iPad8,9": "iPad Pro (11-inch) (2nd generation)", + "iPad8,10": "iPad Pro (11-inch) (2nd generation)", + "iPad8,11": "iPad Pro (12.9-inch) (4th generation)", + "iPad8,12": "iPad Pro (12.9-inch) (4th generation)", + "iPad7,11": "iPad (7th generation)", + "iPad7,12": "iPad (7th generation)", + "iPad11,6": "iPad (8th generation)", + "iPad11,7": "iPad (8th generation)", + "Watch1,1": "Apple Watch 38mm", + "Watch1,2": "Apple Watch 42mm", + "Watch2,6": "Apple Watch Series 1", + "Watch2,7": "Apple Watch Series 1", + "Watch2,3": "Apple Watch Series 2", + "Watch2,4": "Apple Watch Series 2", + "Watch3,1": "Apple Watch Series 3 (GPS + Cellular)", + "Watch3,2": "Apple Watch Series 3 (GPS + Cellular)", + "Watch3,3": "Apple Watch Series 3 (GPS)", + "Watch3,4": "Apple Watch Series 3 (GPS)", + "Watch4,1": "Apple Watch Series 4", + "Watch4,2": "Apple Watch Series 4", + "Watch4,3": "Apple Watch Series 4", + "Watch4,4": "Apple Watch Series 4", + "Watch5,1": "Apple Watch Series 5", + "Watch5,2": "Apple Watch Series 5", + "Watch5,3": "Apple Watch Series 5", + "Watch5,4": "Apple Watch Series 5", + "Watch6,1": "Apple Watch Series 6", + "Watch6,2": "Apple Watch Series 6", + "Watch6,3": "Apple Watch Series 6", + "Watch6,4": "Apple Watch Series 6", + "Watch5,9": "Apple Watch SE", + "Watch5,10": "Apple Watch SE", + "Watch5,11": "Apple Watch SE", + "Watch5,12": "Apple Watch SE", + "AppleTV2,1": "Apple TV (2nd generation)", + "AppleTV3,1": "Apple TV (3rd generation)", + "AppleTV3,2": "Apple TV (3rd generation Rev A)", + "AppleTV5,3": "Apple TV (4th generation)", + "AppleTV6,2": "Apple TV 4K", + "AudioAccessory1,1": "HomePod", + "AudioAccessory1,2": "HomePod", + "AudioAccessory5,1": "HomePod mini", +] diff --git a/apple-device-names b/apple-device-names new file mode 100755 index 00000000..12237d5c --- /dev/null +++ b/apple-device-names @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +import re, json +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 + +print('let appleDeviceNames = [') +for code, name in mapping.items(): + print(f' "{code}": "{name}",') +print(']') From 22085db9b4547056eec2f78a9470f3c21b0ee7a6 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 1 Nov 2020 14:53:01 +0900 Subject: [PATCH 109/183] Fix problem entering active mode --- BLEUnlock/BLE.swift | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 4fa0169e..b5f9510a 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -268,19 +268,19 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } func connectMonitoredPeripheral() { - if let p = monitoredPeripheral { - guard p.state == .disconnected else { return } - print("Connecting") - centralMgr.connect(p, options: nil) - connectionTimer?.invalidate() - connectionTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in - if p.state == .connecting { - print("Connection timeout") - self.centralMgr.cancelPeripheralConnection(p) - } - }) - RunLoop.main.add(connectionTimer!, forMode: .common) - } + guard let p = monitoredPeripheral else { return } + p.readRSSI() // Idk why but this works like a charm when 'didConnect' won't get called! + guard p.state == .disconnected else { return } + print("Connecting") + centralMgr.connect(p, options: nil) + connectionTimer?.invalidate() + connectionTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { _ in + if p.state == .connecting { + print("Connection timeout") + self.centralMgr.cancelPeripheralConnection(p) + } + }) + RunLoop.main.add(connectionTimer!, forMode: .common) } //MARK:- CBCentralManagerDelegate start @@ -339,15 +339,14 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - //print("didConnect") peripheral.delegate = self if scanMode { peripheral.discoverServices([DeviceInformation]) } if peripheral == monitoredPeripheral && !passiveMode { + print("Connected") connectionTimer?.invalidate() connectionTimer = nil - //print("reading RSSI") peripheral.readRSSI() } } From 2ef9036a038fa128a2aaa9f3b8a70849ceef738a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 12:58:07 +0900 Subject: [PATCH 110/183] Link MediaRemote framework directly --- BLEUnlock.xcodeproj/project.pbxproj | 8 +++--- BLEUnlock/AppDelegate.swift | 17 +++++++----- BLEUnlock/BLE.swift | 6 +++- BLEUnlock/BLEUnlock-Bridging-Header.h | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/MediaRemote.h | 10 +++++++ BLEUnlock/NowPlaying.swift | 40 --------------------------- README.md | 2 +- 8 files changed, 32 insertions(+), 54 deletions(-) create mode 100644 BLEUnlock/MediaRemote.h delete mode 100644 BLEUnlock/NowPlaying.swift diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 22330f26..a472d890 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 3D2CF85B255102B300157996 /* MediaRemote.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D2CF85A255102B300157996 /* MediaRemote.framework */; }; 3D2FCF08226C99CB007A06E7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D2FCF07226C99CB007A06E7 /* Images.xcassets */; }; - 3D43E004254D3946009D5EEB /* NowPlaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D43E003254D3946009D5EEB /* NowPlaying.swift */; }; 3D4EE4112279E55A00AF9E93 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */; }; 3D4EE4132279E56900AF9E93 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4EE4122279E56900AF9E93 /* main.m */; }; 3D4EE4152279E94B00AF9E93 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */; }; @@ -39,8 +39,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3D2CF85A255102B300157996 /* MediaRemote.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaRemote.framework; path = ../../../../System/Library/PrivateFrameworks/MediaRemote.framework; sourceTree = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 3D43E003254D3946009D5EEB /* NowPlaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NowPlaying.swift; sourceTree = ""; }; 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 3D4EE4122279E56900AF9E93 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -81,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3D2CF85B255102B300157996 /* MediaRemote.framework in Frameworks */, 3DD4B669226C1E3700451B7B /* login.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -124,7 +125,6 @@ 3DD4B652226C1C3200451B7B /* BLEUnlock */ = { isa = PBXGroup; children = ( - 3D43E003254D3946009D5EEB /* NowPlaying.swift */, 3D95379322784D550017D8B9 /* AboutBox.swift */, 3D953799227853E20017D8B9 /* AboutBox.xib */, 3D600A35226CC1A40068FB7B /* Localizable.strings */, @@ -146,6 +146,7 @@ 3DD4B667226C1E3700451B7B /* Frameworks */ = { isa = PBXGroup; children = ( + 3D2CF85A255102B300157996 /* MediaRemote.framework */, 3DD4B668226C1E3700451B7B /* login.framework */, ); name = Frameworks; @@ -308,7 +309,6 @@ files = ( 3DD4B654226C1C3200451B7B /* AppDelegate.swift in Sources */, 3DD4B666226C1DF200451B7B /* BLE.swift in Sources */, - 3D43E004254D3946009D5EEB /* NowPlaying.swift in Sources */, 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */, 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */, 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */, diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 35d18e04..10e6f6ab 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -164,20 +164,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func pauseNowPlaying() { guard prefs.bool(forKey: "pauseItunes") else { return } - NowPlayingIsPlaying({ (playing) in - self.nowPlayingWasPlaying = playing - if self.nowPlayingWasPlaying { - print("pause") - NowPlayingPause() + MRMediaRemoteGetNowPlayingApplicationIsPlaying( + DispatchQueue.main, + { (playing) in + self.nowPlayingWasPlaying = playing + if self.nowPlayingWasPlaying { + print("pause") + MRMediaRemoteSendCommand(MRCommandPause, nil) + } } - }) + ) } func playNowPlaying() { guard prefs.bool(forKey: "pauseItunes") else { return } if nowPlayingWasPlaying { print("play") - NowPlayingPlay() + MRMediaRemoteSendCommand(MRCommandPlay, nil) } } diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index b5f9510a..40c2e3d4 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -269,7 +269,11 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { func connectMonitoredPeripheral() { guard let p = monitoredPeripheral else { return } - p.readRSSI() // Idk why but this works like a charm when 'didConnect' won't get called! + + // Idk why but this works like a charm when 'didConnect' won't get called. + // However, this generates warnings in the log. + p.readRSSI() + guard p.state == .disconnected else { return } print("Connecting") centralMgr.connect(p, options: nil) diff --git a/BLEUnlock/BLEUnlock-Bridging-Header.h b/BLEUnlock/BLEUnlock-Bridging-Header.h index 5b7e1380..c4b6a0ea 100644 --- a/BLEUnlock/BLEUnlock-Bridging-Header.h +++ b/BLEUnlock/BLEUnlock-Bridging-Header.h @@ -3,3 +3,4 @@ // #include "lowlevel.h" +#include "MediaRemote.h" diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index ed13b00c..4e7a515c 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 687 + 697 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/MediaRemote.h b/BLEUnlock/MediaRemote.h new file mode 100644 index 00000000..0999d757 --- /dev/null +++ b/BLEUnlock/MediaRemote.h @@ -0,0 +1,10 @@ +#include + +typedef enum { + MRCommandPlay, + MRCommandPause, +} MRCommand; + +typedef void (^MRMediaRemoteGetNowPlayingApplicationIsPlayingCompletion)(Boolean isPlaying); +void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, MRMediaRemoteGetNowPlayingApplicationIsPlayingCompletion completion); +Boolean MRMediaRemoteSendCommand(MRCommand command, id userInfo); diff --git a/BLEUnlock/NowPlaying.swift b/BLEUnlock/NowPlaying.swift deleted file mode 100644 index d66a93f1..00000000 --- a/BLEUnlock/NowPlaying.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Foundation -import CoreFoundation - -private func getMediaRemoteBundle() -> CFBundle { - return CFBundleCreate( - kCFAllocatorDefault, - NSURL(fileURLWithPath: "/System/Library/PrivateFrameworks/MediaRemote.framework") - ) -} - -private func sendCommand(_ command: Int) { - typealias funcType = @convention(c) (Int, NSDictionary?) -> Void - guard let ptr = CFBundleGetFunctionPointerForName( - getMediaRemoteBundle(), - "MRMediaRemoteSendCommand" as CFString - ) else { return } - let MRMediaRemoteSendCommand = unsafeBitCast(ptr, to: funcType.self) - MRMediaRemoteSendCommand(command, nil) -} - -func NowPlayingIsPlaying(_ closure: @escaping (Bool) -> Void) { - typealias funcType = @convention(c) (DispatchQueue, @escaping (Bool) -> Void) -> Void - guard let ptr = CFBundleGetFunctionPointerForName( - getMediaRemoteBundle(), - "MRMediaRemoteGetNowPlayingApplicationIsPlaying" as CFString - ) else { - closure(false) - return - } - let MRMediaRemoteIsPlaying = unsafeBitCast(ptr, to: funcType.self) - MRMediaRemoteIsPlaying(DispatchQueue.main, closure) -} - -func NowPlayingPlay() { - sendCommand(0) // MRMediaRemoteCommandPlay -} - -func NowPlayingPause() { - sendCommand(1) // MRMediaRemoteCommandPause -} diff --git a/README.md b/README.md index 4aa0ffad..02b8ca11 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Because of this, **you have to right-click and Open to start, and you have to re My company is not developing iOS/Mac app at the moment, so I don't have access to a paid Apple Developer account. -If you find this app useful, please help me pay for Apple Developer Program by donating via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone)! +If you like this, I'd appreciate it if you could make a donation via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone) so that I can pay for the Apple Developer Program myself! ## Credits From b3725291729c62605f16d0eaec084f83c109dbc4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 13:05:26 +0900 Subject: [PATCH 111/183] Trying to fix CI --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index a472d890..751549a9 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; @@ -521,7 +521,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; From 645eb87c64cd8148980d8311dec9ef87709f4f4f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 17:14:39 +0900 Subject: [PATCH 112/183] Add Delay to Lock menu --- BLEUnlock/AppDelegate.swift | 34 ++++++++++++++++++++- BLEUnlock/BLE.swift | 20 +++++------- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/zh-Hans.lproj/Localizable.strings | 1 + 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 10e6f6ab..37c41af2 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -15,6 +15,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let lockRSSIMenu = NSMenu() let unlockRSSIMenu = NSMenu() let timeoutMenu = NSMenu() + let lockDelayMenu = NSMenu() var deviceDict: [UUID: NSMenuItem] = [:] var monitorMenuItem : NSMenuItem? let prefs = UserDefaults.standard @@ -56,6 +57,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item.state = .off } } + } else if menu == lockDelayMenu { + for item in menu.items { + if item.tag == Int(ble.proximityTimeout) { + item.state = .on + } else { + item.state = .off + } + } } } @@ -181,6 +190,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if nowPlayingWasPlaying { print("play") MRMediaRemoteSendCommand(MRCommandPlay, nil) + nowPlayingWasPlaying = false } } @@ -466,7 +476,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa prefs.set(value, forKey: "timeout") ble.signalTimeout = Double(value) } - + + @objc func setLockDelay(_ menuItem: NSMenuItem) { + let value = menuItem.tag + prefs.set(value, forKey: "lockDelay") + ble.proximityTimeout = Double(value) + } + @objc func toggleLaunchAtLogin(_ menuItem: NSMenuItem) { let launchAtLogin = !prefs.bool(forKey: "launchAtLogin") prefs.set(launchAtLogin, forKey: "launchAtLogin") @@ -539,6 +555,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa item = lockRSSIMenu.addItem(withTitle: t("disabled"), action: #selector(setLockRSSI), keyEquivalent: "") item.tag = ble.LOCK_DISABLED + 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: "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: "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 + lockDelayMenu.addItem(withTitle: "5 " + t("minutes"), action: #selector(setLockDelay), keyEquivalent: "").tag = 300 + lockDelayMenu.delegate = self + let timeoutItem = mainMenu.addItem(withTitle: t("timeout"), action: nil, keyEquivalent: "") timeoutItem.submenu = timeoutMenu timeoutMenu.addItem(withTitle: "30 " + t("seconds"), action: #selector(setTimeout), keyEquivalent: "").tag = 30 @@ -621,6 +649,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if thresholdRSSI != 0 { ble.thresholdRSSI = thresholdRSSI } + let lockDelay = prefs.integer(forKey: "lockDelay") + if lockDelay != 0 { + ble.proximityTimeout = Double(lockDelay) + } NSUserNotificationCenter.default.delegate = self diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index 40c2e3d4..ba298345 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -113,7 +113,7 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var presence = false var lockRSSI = -80 var unlockRSSI = -60 - var proximityTimeout = 4.5 + var proximityTimeout = 5.0 var signalTimeout = 60.0 var lastReadAt = 0.0 var powerWarn = true @@ -205,22 +205,15 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { } } - func getEstimatedRSSI(rssi: Int) -> Double { - if (latestRSSIs.count < latestN - 1) { - latestRSSIs.append(Double(rssi)) - return 1.0 + Double(unlockRSSI) - } - if (latestRSSIs.count < latestN) { - latestRSSIs.append(Double(rssi)) - } else { + func getEstimatedRSSI(rssi: Int) -> Int { + if latestRSSIs.count >= latestN { latestRSSIs.removeFirst() - latestRSSIs.append(Double(rssi)) } + latestRSSIs.append(Double(rssi)) var mean: Double = 0.0 var sddev: Double = 0.0 vDSP_normalizeD(latestRSSIs, 1, nil, 1, &mean, &sddev, vDSP_Length(latestRSSIs.count)) - // sddev *= sqrt(Double(latestRSSIs.count)/Double(latestRSSIs.count - 1)) - return mean + return Int(mean) } func updateMonitoredPeripheral(_ rssi: Int) { @@ -229,9 +222,10 @@ class BLE: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { print("Device is close") presence = true delegate?.updatePresence(presence: presence, reason: "close") + latestRSSIs.removeAll() // Avoid bouncing } - let estimatedRSSI = Int(getEstimatedRSSI(rssi: rssi)) + let estimatedRSSI = getEstimatedRSSI(rssi: rssi) delegate?.updateRSSI(rssi: estimatedRSSI, active: activeModeTimer != nil) if estimatedRSSI >= (lockRSSI == LOCK_DISABLED ? unlockRSSI : lockRSSI) { diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 7d6a9e59..0d16039e 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -10,6 +10,7 @@ "enter_rssi_threshold_info" = "Device whose RSSI is less than this value will be ignored while scanning."; "farther" = "⬇Farther"; "launch_at_login" = "Launch at Login"; +"lock_delay" = "Delay to Lock"; "lock_now" = "Lock Screen Now"; "lock_rssi" = "Lock RSSI"; "minute" = "minute"; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 4e7a515c..938f8089 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.9.1 CFBundleVersion - 697 + 708 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index fdd45c8f..9a4f6cf4 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -10,6 +10,7 @@ "enter_rssi_threshold_info" = "この値よりRSSIが小さいデバイスはスキャンに表示されません。"; "farther" = "⬇遠い"; "launch_at_login" = "ログイン時に起動"; +"lock_delay" = "ロックするまでの遅延"; "lock_now" = "今すぐロック"; "lock_rssi" = "ロック信号強度"; "minute" = "分"; diff --git a/BLEUnlock/zh-Hans.lproj/Localizable.strings b/BLEUnlock/zh-Hans.lproj/Localizable.strings index 02e4093c..ae958fa9 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -10,6 +10,7 @@ "enter_rssi_threshold_info" = "扫描设备时会忽略RSSI值低于该值的设备。"; "farther" = "⬇远离"; "launch_at_login" = "开机启动"; +"lock_delay" = "延迟锁定"; "lock_now" = "立刻锁定屏幕"; "lock_rssi" = "锁定 RSSI"; "minute" = "分钟"; From 887ed81daefd8de7fc68e26e5655cd5cd7135199 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 17:19:08 +0900 Subject: [PATCH 113/183] 1.10.0 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 938f8089..f71be4b2 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.9.1 + 1.10.0 CFBundleVersion - 708 + 709 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 0770135e7e3262ea229c43eab53dfc6252158513 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 18:02:11 +0900 Subject: [PATCH 114/183] Update README --- README.ja.md | 14 +++++++++++++- README.md | 19 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/README.ja.md b/README.ja.md index 01eb48d9..2a61d161 100644 --- a/README.ja.md +++ b/README.ja.md @@ -70,6 +70,18 @@ BLEUnlockはデフォルトで能動的にデバイスに接続して信号強 Bluetoothインターネット共有を使う場合、パッシブモードをオンにしてください。 そうでなければオフにしてください。 +### "再生中" について + +BLEUnlockはシステムの再生・一時停止コントロール (Media Remoteフレームワーク) に対応したアプリをロック時に一時停止できます。 +対応アプリにはApple Music, QuickTime Player, Spotify, Safari上のYouTubeなどがあります。 +残念ながら、今のところGoogle Chromeは一時停止・再生コマンドに反応しないようです。 + +アプリが「再生中」ウィジェット、もしくはキーボードの ⏯️ キーに対応していれば、BLEUnlockでも制御できるはずです。 + +### スクリーンセーバーによるロック + +「スクリーンセーバーでロック」オプションを使用する場合、正常にロックするには、システム環境設定の *セキュリティとプライバシー* で *スリープとスクリーンセーバの解除にパスワードを要求* を **すぐに** に設定してください。 + ## ロック・アンロック時にスクリプトを実行する BLEUnlockはロック・アンロック時に以下のスクリプトを実行します。 @@ -87,7 +99,7 @@ BLEUnlockはロック・アンロック時に以下のスクリプトを実行 |BLEUnlockによりアンロックされた|`unlocked`| |手動でアンロックされた|`intruded`| -> 注意: `intruded` イベントが正常に働くには、 *セキュリティとプライバシー* で *スリープとスクリーンセーバの解除にパスワードを要求* を **すぐに** に設定してください。 +> 注意: `intruded` イベントが正常に働くには、システム環境設定の *セキュリティとプライバシー* で *スリープとスクリーンセーバの解除にパスワードを要求* を **すぐに** に設定してください。 ### サンプル diff --git a/README.md b/README.md index 02b8ca11..f8dd4649 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,20 @@ broadcasts. That does not interfere with Bluetooth Internet Sharing. If you use Bluetooth Internet Sharing on the same device, turn Passive Mode on. -If you don't, turn it off. +Otherwise, turn it off. + +### Notes on Now Playing + +BLEUnlock can pause apps that support Media Remote framework while locking. +These apps include Apple Music, QuickTime Player, Spotify, and Youtube on Safari. +Unfortunately Google Chrome doesn't seem to react to play/pause command at the moment. + +If an app can be controlled via "Now Playing" widget, or by the ⏯️ key on the keyboard, BLEUnlock should be able to control it. + + +### Using screensaver to lock + +If you turn on Use Screensaver to Lock option, to lock the computer properly, you have to set *Require pasword **immediately** after sleep or screen saver begins* option in *Security & Privacy* preference pane. ## Run script on lock/unlock @@ -100,7 +113,7 @@ An argument is passed depending on the type of event: |Unlocked by BLEUnlock|`unlocked`| |Unlocked manually|`intruded`| -> NOTE: for `intruded` event works properly, you have to set *Require password immediately after sleep* in Security & Privacy. +> NOTE: for `intruded` event works properly, you have to set *Require password **immediately** after sleep* in *Security & Privacy* preference pane. ### Example @@ -162,7 +175,7 @@ Because of this, **you have to right-click and Open to start, and you have to re My company is not developing iOS/Mac app at the moment, so I don't have access to a paid Apple Developer account. -If you like this, I'd appreciate it if you could make a donation via [Buy Me a Coffee](https://www.buymeacoffee.com/tsone) or [PayPal.Me](https://paypal.me/takeshisone) 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://paypal.me/takeshisone) so that I can pay for the Apple Developer Program myself! ## Credits From 9be669ffd8d5a140a515c2d6dbad6dbdbd91742f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 18:15:09 +0900 Subject: [PATCH 115/183] Update README --- README.ja.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.ja.md b/README.ja.md index 2a61d161..a80b1d1d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -11,7 +11,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス - デバイスがMacから離れると画面をロックします - ロック・アンロック時にスクリプトを実行することができます - デバイスがMacに近づくと画面スリープを解除します (オプション) -- デバイスがMacから離れる/近づくとiTunesの再生を一時停止/再生します (オプション) +- デバイスがMacから離れる/近づくと音楽や動画の再生を一時停止/再生します (オプション) - パスワードはキーチェーンに安全に保管されます ## 必要なもの diff --git a/README.md b/README.md index f8dd4649..fdc4d455 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This document is also available in [Japanese](README.ja.md). - Locks your Mac when the device is away from Mac - Optionally runs your script on lock/unlock - Optionally wakes from display sleep state -- Optionally pauses and unpauses iTunes playback when you're away and back +- Optionally pauses and unpauses music/video playback when you're away and back - Password is securely stored in Keychain ## Requirements From d4e0faded349e423a30ffae0094b831c6398a59c Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Tue, 3 Nov 2020 18:39:15 +0900 Subject: [PATCH 116/183] Add badges --- README.ja.md | 3 +++ README.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.ja.md b/README.ja.md index a80b1d1d..a9b6b96d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,5 +1,8 @@ # BLEUnlock +![CI](https://github.com/ts1/BLEUnlock/workflows/CI/badge.svg) +![Github All Releases](https://img.shields.io/github/downloads/ts1/BLEUnlock/total.svg) + BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low EnergyデバイスでMacをロック・アンロックする小さなメニューバーユーティリティーです。 diff --git a/README.md b/README.md index fdc4d455..98d5f0fb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # BLEUnlock +![CI](https://github.com/ts1/BLEUnlock/workflows/CI/badge.svg) +![Github All Releases](https://img.shields.io/github/downloads/ts1/BLEUnlock/total.svg) + BLEUnlock is a small menu bar utility that locks and unlocks your Mac with your iPhone, Apple Watch, or any other Bluetooth Low Energy devices. This document is also available in [Japanese](README.ja.md). From 4cfba93ea6ddb25823446808f8c28e68e0f71e00 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 11 Nov 2020 18:13:18 +0900 Subject: [PATCH 117/183] Update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 98d5f0fb..7b4206b7 100644 --- a/README.md +++ b/README.md @@ -71,19 +71,19 @@ Usually, devices send signal less frequently when it is in sleep mode. Thus, if you wake the device, in most cases it sends signal promptly, and BLEUnlock unlocks. -### Passive mode and Bluetooth Internet Sharing +### Passive mode and Bluetooth Personal Hotspot By default BLEUnlock actively connects to the device to read RSSI (signal strength). It is the best way to steadily read RSSI for devices such as iPhone that support it. -However, it does not play nice with Bluetooth Internet Sharing. +However, it does not play nice with Bluetooth Personal Hotspot. With Passive Mode, BLEUnlock only passively receives signals that the deveice broadcasts. -That does not interfere with Bluetooth Internet Sharing. +That does not interfere with Bluetooth Personal Hotspot. -If you use Bluetooth Internet Sharing on the same device, turn Passive Mode on. +If you use Bluetooth Personal Hotspot on the same device, turn Passive Mode on. Otherwise, turn it off. ### Notes on Now Playing From 57d45933f8f39dc74d85e9693a652d382b027f8a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Dec 2020 11:56:18 +0900 Subject: [PATCH 118/183] Delay unpause to allow Chrome to unpause video playback --- BLEUnlock/AppDelegate.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 37c41af2..68edd3ee 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -189,8 +189,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard prefs.bool(forKey: "pauseItunes") else { return } if nowPlayingWasPlaying { print("play") - MRMediaRemoteSendCommand(MRCommandPlay, nil) - nowPlayingWasPlaying = false + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in + MRMediaRemoteSendCommand(MRCommandPlay, nil) + self.nowPlayingWasPlaying = false + }) } } @@ -317,6 +319,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onUnlock() { if Date().timeIntervalSince1970 >= unlockedAt + 2 { runScript("intruded") + self.playNowPlaying() } manualLock = false Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { _ in From 051aa7215b181f829aadf5b53837f6534b54d198 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Dec 2020 12:07:33 +0900 Subject: [PATCH 119/183] Use Esc key to escape from screen saver Fixes #25 --- BLEUnlock/AppDelegate.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 68edd3ee..3dd592e5 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -271,9 +271,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa if inScreensaver { // In screensaver, make sure Login panel is displayed let src = CGEventSource(stateID: .hidSystemState) - // Shift key down and up - CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: true)?.post(tap: .cghidEventTap) - CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: false)?.post(tap: .cghidEventTap) + // Esc key down and up + CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: true)?.post(tap: .cghidEventTap) + CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: false)?.post(tap: .cghidEventTap) } Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in From 7fe644f893da1f871b2bb0147f42a493bd434d25 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Dec 2020 12:43:23 +0900 Subject: [PATCH 120/183] Add NSBluetoothAlwaysUsageDescription Needed to run on M1 Mac --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- BLEUnlock/Info.plist | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 751549a9..a472d890 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; @@ -521,7 +521,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index f71be4b2..e48c31e6 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,13 +19,15 @@ CFBundleShortVersionString 1.10.0 CFBundleVersion - 709 + 714 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement + NSBluetoothAlwaysUsageDescription + BLEUnlock uses Bluetooth to detect devices. NSHumanReadableCopyright Copyright © 2019-2020 Takeshi Sone. MIT Licensed. NSMainNibFile From a3a1cf173346d37d48bbb3ac0ca0e07bb3f43585 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 23 Dec 2020 12:50:56 +0900 Subject: [PATCH 121/183] 1.10.1 --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- BLEUnlock/Info.plist | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index a472d890..c736e5d9 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; @@ -521,7 +521,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index e48c31e6..c04d67de 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.0 + 1.10.1 CFBundleVersion - 714 + 716 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 7043c9397ed97011f6f24e036773b35604bd0117 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Wed, 24 Feb 2021 23:05:28 +0900 Subject: [PATCH 122/183] Update README.md --- README.md | 148 +++++++++++++++++++++++-------------------- img/buymeacoffee.svg | 5 ++ 2 files changed, 86 insertions(+), 67 deletions(-) create mode 100644 img/buymeacoffee.svg diff --git a/README.md b/README.md index 7b4206b7..4cf2eaf7 100644 --- a/README.md +++ b/README.md @@ -2,102 +2,116 @@ ![CI](https://github.com/ts1/BLEUnlock/workflows/CI/badge.svg) ![Github All Releases](https://img.shields.io/github/downloads/ts1/BLEUnlock/total.svg) +[![Buy me a coffee](img/buymeacoffee.svg)](https://www.buymeacoffee.com/tsone) -BLEUnlock is a small menu bar utility that locks and unlocks your Mac with your iPhone, Apple Watch, or any other Bluetooth Low Energy devices. +BLEUnlock is a small menu bar utility that locks and unlocks your Mac by proximity of your iPhone, Apple Watch, or any other Bluetooth Low Energy device. -This document is also available in [Japanese](README.ja.md). +This document is also available in [Japanese (日本語版はこちら)](README.ja.md). ## Features - No iPhone app is required -- Works with any BLE devices that periodically send signal -- Unlocks your Mac for you when the device is near Mac, no need to enter password again -- Locks your Mac when the device is away from Mac -- Optionally runs your script on lock/unlock -- Optionally wakes from display sleep state +- Works with any BLE devices that periodically transmits signal from [static MAC address](#notes-on-mac-address) +- Unlocks your Mac for you when the BLE device is near your Mac, without entering password +- Locks your Mac when the BLE device is away from your Mac +- Optionally runs your own script upon lock/unlock +- Optionally wakes from display sleep - Optionally pauses and unpauses music/video playback when you're away and back - Password is securely stored in Keychain ## Requirements -- Mac with Bluetooth Low Energy support +- A Mac with Bluetooth Low Energy support - macOS 10.13 (High Sierra) or later -- iPhone 5s or later, Apple Watch (all), or other BLE device that transmits -signal periodically +- iPhone 5s or newer, Apple Watch (all), or another BLE device that has [static MAC address](#notes-on-mac-address) and transmits signal periodically ## Installation -Download zip file from [Releases](https://github.com/ts1/BLEUnlock/releases), -unzip and copy to Applications folder. +### Using Homebrew Cask -On the first launch, it asks your login password, -which is required to unlock the lock screen. -It's safe because it's stored in Keychain. +``` +$ brew install bleunlock +``` + +### Manual installation + +Download the zip file from [Releases](https://github.com/ts1/BLEUnlock/releases), unzip and move to the Applications folder. + +## Setting up + +On the first launch, it asks for the following permissions, which you must grant: -Then it asks for permission for Accessibility. -In System Preferences, click the lock icon to unlock and turn BLEUnlock on. -This permission is also required to unlock the lock screen. +Permission | Description +-----------|--- +Bluetooth | Obviously, Bluetooth access is required. Choose *OK*. +Accessibility | This is required to unlock the locked screen. Click *Open System Preferences*, click the lock icon on the bottom left to unlock, and turn on BLEUnlock. +Keychain | (Not always asked) If asked, you have to choose **Always Allow** because it is required while the screen is locked. +Notification | (Optional) BLEUnlock shows a message on the lock screen when it locks the screen. It is helpful to know if it's working properly. Additionally, to see the message on the lock screen, you need to set *Show previews* to *always* in the *Notification* preference pane. -Finally, from the menu bar icon, select "Device". +> NOTE: The number of permissions required increases with each version of macOS, so if you are using an older OS, you may not be asked for one or more permissions. + +Then it asks your login password to unlock the lock screen. +It will be stored safely in Keychain. + +Finally, from the menu bar icon, select *Device*. It starts scanning nearby BLE devices. Select your device, and you're done! -## Troubleshooting +## Options + +Option | Description +-------|--- +Unlock RSSI | Bluetooth signal strength to unlock. Larger value indicates that the BLE device needs to be closer to the Mac to unlock. Choose *Disable* to disable unlocking. +Lock RSSI | Bluetooth signal strength to lock. Smaller value indicates that the BLE device needs to be farther away from the Mac to lock. Choose *Disable* to disable locking. +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. +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. +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. +Set Minimum RSSI | Devices with RSSI below this value will not be displayed in the device scan list. -If it fails to unlock, check BLEUnlock is turned on in "System Preferences" → -"Security & Privacy" → "Privacy" → "Accessibility". -If it is already on, try turning it off and on again. +## Troubleshooting -If it asks for permission to access its own password, click "Always Allow", -because it is needed while the screen is locked. +### Can't find my device in the list -If "Signal is lost" occurs frequently, turn Bluetooth of Mac off then on. -Or use a device that sends signal more frequently. +If your BLE device is not from Apple, BLEUnlock may not able to find the device name. +If that is the case, your device is displayed as a UUID (long hexadecimal numbers and hyphens). +To identify the device, try moving the device closer to or farther away from the Mac and see if the RSSI (dB value) changes accordingly. -### Why does it sometimes take long time to unlock? +### It fails to unlock -Short answer: wake your iPhone. +Make sure BLEUnlock is turned on in *System Preferences* > *Security & Privacy* > *Privacy* > *Accessibility*. +If it is already on, try turning it off and on again. -From version 1.4.1, BLEUnlock assumes the device is away -when Mac enters system sleep and Bluetooth hardware is powered off -(typically 15-30 seconds after the lid is closed). -This is required for security. -Consequently, it sometimes takes up to several seconds to unlock when Mac wakes -from system sleep. +If it asks for permission to access its own password in Keychain, you must choose *Always Allow*, because it is needed while the screen is locked. -This is because BLEUnlock has lost connection to the device and has to wait -for the device to send signal. -Usually, devices send signal less frequently when it is in sleep mode. -Thus, if you wake the device, in most cases it sends signal promptly, -and BLEUnlock unlocks. +### "Signal is lost" occurs frequently -### Passive mode and Bluetooth Personal Hotspot +Increase *No-Signal Timeout*. +Or try *Passive Mode*. -By default BLEUnlock actively connects to the device to read RSSI -(signal strength). -It is the best way to steadily read RSSI for devices such as iPhone that -support it. -However, it does not play nice with Bluetooth Personal Hotspot. +### My Bluetooth keyboard, mouse, Personal Hotspot, or whatever Bluetooth, went nuts! -With Passive Mode, BLEUnlock only passively receives signals that the deveice -broadcasts. -That does not interfere with Bluetooth Personal Hotspot. +Turn on *Passive Mode*! -If you use Bluetooth Personal Hotspot on the same device, turn Passive Mode on. -Otherwise, turn it off. +## Notes on MAC address -### Notes on Now Playing +Unlike classic Bluetooth, Bluetooth Low Energy devices can use *private* MAC address. +That private address can be random, and can be changed from time to time. -BLEUnlock can pause apps that support Media Remote framework while locking. -These apps include Apple Music, QuickTime Player, Spotify, and Youtube on Safari. -Unfortunately Google Chrome doesn't seem to react to play/pause command at the moment. +Recent smart devices, both iOS and Android, tend to use private addresses that change every 15 minutes or so. This is probably to prevent tracking. -If an app can be controlled via "Now Playing" widget, or by the ⏯️ key on the keyboard, BLEUnlock should be able to control it. +On the other hand, in order for BLEUnlock to track your device, its MAC address must be static. +Fortunately, on Apple devices, if you are signed in with the same Apple ID as your Mac, the MAC address is resolved to the true (public) address. -### Using screensaver to lock +For other devices, including Android, the way to resolve the address is unknown. +If your non-Apple device changes its MAC address over time, unfortunately BLEUnlock can't support it. -If you turn on Use Screensaver to Lock option, to lock the computer properly, you have to set *Require pasword **immediately** after sleep or screen saver begins* option in *Security & Privacy* preference pane. +To check if the MAC address is resolved correctly, compare the MAC address displayed in the *Device* scan list of BLEUnlock with the one that is displayed on your device. ## Run script on lock/unlock @@ -120,7 +134,7 @@ An argument is passed depending on the type of event: ### Example -Here is an example script which sends LINE Notify message, with a photo of the person in front of Mac when unlocked manually. +Here is an example script which sends a LINE Notify message, with a photo of the person in front of the Mac when it is unlocked manually. ```sh #!/bin/bash @@ -168,30 +182,30 @@ esac do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y /tmp/unlock-$(date +%Y%m%d_%H%M%S).jpg" ``` -This is required because BLEUnlock does not have Camera permission. -Giving permission to this app resolve the problem. +This app is required because BLEUnlock does not have Camera permission. +Giving permission to this app resolves the problem. ## Funding -Since 1.9.0, binary releases are not notarized by Apple. +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.** -My company is not developing iOS/Mac app at the moment, so I don't have access to a paid Apple Developer account. +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) or [PayPal.Me](https://paypal.me/takeshisone) 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) so that I can pay for the Apple Developer Program myself! ## Credits - peiit: Chinese translation - wenmin-wu: Minimum RSSI and moving average - stephengroat: CI +- joeyhoer: Homebrew Cask Icons are based on SVGs downloaded from materialdesignicons.com. -They are originally designed by Google LLC and licensed under Apache License -version 2.0. +They are originally designed by Google LLC and licensed under Apache License version 2.0. ## License MIT -Copyright © 2019-2020 Takeshi Sone. +Copyright © 2019-2021 Takeshi Sone. diff --git a/img/buymeacoffee.svg b/img/buymeacoffee.svg new file mode 100644 index 00000000..d2d50b01 --- /dev/null +++ b/img/buymeacoffee.svg @@ -0,0 +1,5 @@ + + + + + From 2aa0122806522405f98a292e6ce86b2eee4b63a2 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 14 Mar 2021 11:46:35 +0900 Subject: [PATCH 123/183] Update READMEs --- README.ja.md | 117 ++++++++++++++++++++++++++++++--------------------- README.md | 1 + 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/README.ja.md b/README.ja.md index a9b6b96d..d2e695c0 100644 --- a/README.ja.md +++ b/README.ja.md @@ -2,88 +2,106 @@ ![CI](https://github.com/ts1/BLEUnlock/workflows/CI/badge.svg) ![Github All Releases](https://img.shields.io/github/downloads/ts1/BLEUnlock/total.svg) +[![Buy me a coffee](img/buymeacoffee.svg)](https://www.buymeacoffee.com/tsone) -BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low EnergyデバイスでMacをロック・アンロックする小さなメニューバーユーティリティーです。 - +BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイスの距離によってMacをロック・アンロックする小さなメニューバーユーティリティーです。 ## 特徴 - iPhoneアプリは必要ありません -- 定期的に信号を送出するBLEデバイスなら何でも使えます -- デバイスがMacの近くにあればMacのロック画面を自動的に解除します - もうパスワードを入力する必要はありません -- デバイスがMacから離れると画面をロックします +- 定期的に信号を送出するBLEデバイスなら何でも使えます ([静的なMACアドレス](#notes-on-mac-address)である必要があります) +- BLEデバイスがMacの近くにあればMacのロック画面を自動的に解除します +- BLEデバイスがMacから離れると画面をロックします - ロック・アンロック時にスクリプトを実行することができます -- デバイスがMacに近づくと画面スリープを解除します (オプション) -- デバイスがMacから離れる/近づくと音楽や動画の再生を一時停止/再生します (オプション) +- BLEデバイスがMacに近づくと画面スリープを解除することができます +- BLEデバイスがMacから離れる/近づくと音楽や動画の再生を一時停止/再生することができます - パスワードはキーチェーンに安全に保管されます ## 必要なもの - Bluetooth Low EnergyをサポートするMac - macOS 10.13 (High Sierra) 以上 -- iPhone 5s以上, Apple Watch (すべて), または定期的に信号を送信するBLEデバイス +- iPhone 5s以上, Apple Watch (すべて), または定期的に信号を[静的なMACアドレス](#notes-on-mac-address)から送信するBLEデバイス ## インストール -[Releases](https://github.com/ts1/BLEUnlock/releases)からzipファイルをダウンロードし、解凍してアプリケーションフォルダにコピーします。 +### Homebrew Caskを使う方法 + +``` +$ brew install bleunlock +``` + +### 手動でインストールする方法 + +[Releases](https://github.com/ts1/BLEUnlock/releases)からzipファイルをダウンロードし、解凍してアプリケーションフォルダに移動します。 -最初の起動であなたのログインパスワードを聞いてきます。 -これはロック画面を解除するために必要です。 -キーチェーンに保管されるため安全です。 +## セットアップ -次にアクセシビリティの許可を求めてきます。 -システム環境設定の画面でロックアイコンをクリックしてアンロックし、BLEUnlockをオンにしてください。 -これもロック画面を解除するために必要です。 +初回起動時、以下の許可を要求します。適切に許可してください。 -最後に、メニューバーアイコンから "デバイス" を選択してください。 -近くにあるBLEデバイスのスキャンが始まります。 -使いたいデバイスを選べば完了です。 +許可 | 説明 +---|--- +Bluetooth | 当然ながら、Bluetoothへのアクセスが必要です。 +アクセシビリティ | ロック画面を解除するために必要です。システム環境設定の画面で左下のロックアイコンをクリックしてアンロックし、BLEUnlockをオンにしてください。 +キーチェイン | (要求されない場合もあります) 要求された場合、必ず**常に許可**を選んでください。ロック中に必要になるためです。 +通知 | (任意) BLEUnlockはロック中に通知メッセージを表示します。正しく動作しているか確認するのに役立ちます。Big Sur以降、デフォルトでロック画面ではメッセージが表示されません。メッセージを表示するには*通知*環境設定パネルで*プレビューを表示*を*常に*に設定してください。 + +| 必要になる許可はmacOSのバージョンが上がるにつれ増えています。古いmacOSをお使いの場合は上に挙げた許可が表示されない場合があります。 + +次にあなたのログインパスワードを聞いてきます。これはロック画面を解除するために必要です。キーチェインに安全に保存されます。 + +最後に、メニューバーアイコンから*デバイス*を選択してください。近くにあるBLEデバイスのスキャンが始まります。使いたいデバイスを選べば完了です。 + +## オプション + +オプション | 説明 +---|--- +今すぐロック | BLEデバイスが近くにあるかどうかに関わらず、画面をロックします。BLEデバイスが一度遠ざかり、再び近づくと解除されます。席を離れる前に確実にロックするのに有効です。 +アンロック信号強度 | ロック解除に必要なBluetooth信号の強度です。値が大きいほどBLEデバイスがMacの近くにないとロック解除しません。*無効にする*を選ぶとBLEデバイスが近くにあってもロック解除をしません。 +ロック信号強度 | MacをロックするBluetooth信号の強度の閾値です。値が小さいほどBLEデバイスがMacから遠い時点でロックをします。*無効にする*を選ぶとBLEデバイスが遠くなるもしくは信号がなくなってもロックをしません。 +ロックするまでの遅延 | BLEデバイスが遠ざかってから実際にロックをするまでの時間です。BLEデバイスがこの時間内に再び近づくと、ロックは行われません。 +無信号タイムアウト | 最後に信号を受信してからロックするまでの時間です。意図せず「デバイスからの信号がありません」でロックされる場合、この値を増やしてください。 +画面スリープから復帰 | ロック中にBLEデバイスが近づいてきたとき、ディスプレイをスリープ画面から復帰させます。 +ロック中 "再生中" を一時停止 | ロック時に音楽や動画の再生を一時停止し、ロック解除時に再開します。対応しているのはApple Music, QuickTime Player, Spotifyなど、*再生中*ウィジェットやキーボードの⏯キーで制御できるアプリです。 +スクリーンセーバーでロック | ロック時にスクリーンセーバーを起動します。このオプションが正しく動作するには、*セキュリティとプライバシー*システム環境パネルで*スリープとスクリーンセーバーの解除にパスワードを要求*を*すぐに*に設定する必要があります。 +パスワードを設定... | Macのログインパスワードを変更したときに、このオプションを使って変更してください。 +パッシブモード | デフォルトでBLEUnlockはBLEデバイスに接続を確立し信号強度を読み取ろうとします。これはサポートされているデバイスでは最も安定して信号強度を読み取ることができる方法です。しかしながら、キーボード、マウス、トラックパッドや特にインターネット共有など、他のBluetooth機器を使用している場合、このモードが干渉することがあります。2.4GHz帯のWiFiも干渉する可能性があります。Bluetoothが不安定になる場合は、パッシブモードを有効にしてください。 ## トラブルシューティング -ロック解除に失敗する場合、"システム環境設定" → "セキュリティとプライバシー" → -"プライバシー" → "アクセシビリティ" でBLEUnlockがオンになっているか確認してください。 -すでにオンになっている場合、一度オフにしてもう一度オンにしてみてください。 +### デバイスがリストに表示されない + +Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取得できない場合があります。その場合、デバイスはUUID(ハイフンで区切られた長い16進数)で表示されます。 -もしBLEUnlock自身のパスワードにアクセスする許可を求められた場合、"常に許可" してください。ロック画面中に必要になるためです。 +デバイスを識別するには、Macから遠ざけたり近づけたりして、信号強度(dB値)がそれに応じて変化するかどうかを確認してください。 -もし "信号がありません" が頻発する場合、MacのBluetoothを一度オフにしてからオンにしてみてください。 -もしくはより頻繁に信号を送出するデバイスを使ってください。 +### アンロックされない -### ロック解除に時間がかかる場合 +*システム環境設定* > *セキュリティとプライバシー* > *アクセシビリティ* でBLEUnlockがオンになっているか確認してください。すでにオンになっている場合、一度オフにしてもう一度オンにしてみてください。 -短い答え: iPhoneのスリープを解除してください。 +もしキーチェインの許可を求められた場合、*常に許可*を選択してください。ロック中に必要になるためです。 -バージョン1.4.1から、BLEUnlockはシステムスリープ中Bluetoothがパワーオフしたとき (システムスリープから15〜30秒後)、デバイスが離れたと仮定するようになりました。 -これはセキュリティのために必要です。 -結果として、スリープ解除後、ロック解除されるまで最大で数秒かかるようになりました。 +### *デバイスからの信号がありません*が頻繁に発生する場合 -これはデバイスとのコネクションが切れ、デバイスが信号を送ってくるのを待っているためです。 -デバイス側もスリープ中は信号送出の間隔が長いので、デバイスのスリープを解除すれば、大抵の場合すぐに信号が送られ、ロック解除されます。 +*無信号タイムアウト*を増やしてください。それでも解決しない場合、*パッシブモード*を試してください。 -### パッシブモードとBluetoothインターネット共有 +### Bluetoothキーボード、マウス、インターネット共有その他Bluetoothがおかしくなった -BLEUnlockはデフォルトで能動的にデバイスに接続して信号強度を読み取ります。 -これはiPhoneなどサポートされているデバイスでは最も安定して信号強度を取得する方法です。 -しかしながら、この方法はBluetoothインターネット共有と相性良くありません。 +*パッシブモード*をオンにしてください。 -パッシブモードではデバイスから発せられる信号を受動的に受け取ります。 -これはBluetoothインターネット共有と干渉しません。 +## MACアドレスについて -Bluetoothインターネット共有を使う場合、パッシブモードをオンにしてください。 -そうでなければオフにしてください。 +クラシックBluetoothと違い、Bluetooth Low Energyデバイスは*プライベート*MACアドレスを使うことができます。プライベートアドレスはランダムで、時間が経つと変わることがあります。 -### "再生中" について +最近のスマートデバイスは、iOSとAndroidともに、15分ほどで変わるランダムアドレスを使う傾向にあります。おそらくトラッキング防止のためだと思われます。 -BLEUnlockはシステムの再生・一時停止コントロール (Media Remoteフレームワーク) に対応したアプリをロック時に一時停止できます。 -対応アプリにはApple Music, QuickTime Player, Spotify, Safari上のYouTubeなどがあります。 -残念ながら、今のところGoogle Chromeは一時停止・再生コマンドに反応しないようです。 +一方で、BLEUnlockは、BLEデバイスをトラッキングするために、MACアドレスは静的である必要があります。 -アプリが「再生中」ウィジェット、もしくはキーボードの ⏯️ キーに対応していれば、BLEUnlockでも制御できるはずです。 +幸運なことに、Appleのデバイスでは、Macと同じApple IDでサインインしていれば、真の(パブリック)MACアドレスが取得できます。 -### スクリーンセーバーによるロック +Android等、その他のデバイスに関しては、今のところMACアドレスを解決する方法は分かりません。非Apple製デバイスでMACアドレスが時間が経つと変わる場合、残念ながらBLEUnlockはサポートできません。 -「スクリーンセーバーでロック」オプションを使用する場合、正常にロックするには、システム環境設定の *セキュリティとプライバシー* で *スリープとスクリーンセーバの解除にパスワードを要求* を **すぐに** に設定してください。 +MACアドレスが正しく解決されているかチェックするには、*デバイス*のリストに表示されるMACアドレスと、BLEデバイスのアドレスを比較してください。 ## ロック・アンロック時にスクリプトを実行する @@ -149,13 +167,13 @@ case $1 in esac ``` -`SnapshotUnlocker` は Script Editor で作られた .app で、内容は以下のとおりです。 +`SnapshotUnlocker` はスクリプトエディタで作った .app で、内容は以下のとおりです。 ``` do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y /tmp/unlock-$(date +%Y%m%d_%H%M%S).jpg" ``` -これはBLEUnlockにカメラのパーミッションがないため必要となります。このappにパーミッションを与えることによりパーミッションの問題を回避できます。 +このappはBLEUnlockにカメラのパーミッションがないため必要となります。このappにパーミッションを与えることによりパーミッションの問題を回避できます。 ## FUNDING @@ -171,6 +189,7 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y - peiit: 中国語の翻訳 - wenmin-wu: 最小RSSIと移動平均 - stephengroat: CI +- joeyhoer: Homebrew Cask アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 @@ -178,4 +197,4 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y MIT -Copyright © 2019-2020 Takeshi Sone. +Copyright © 2019-2021 Takeshi Sone. diff --git a/README.md b/README.md index 4cf2eaf7..98275b65 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Select your device, and you're done! Option | Description -------|--- +Lock Screen Now | It locks the screen regardless of whether the BLE device is nearby or not; it will unlock once the BLE device moves away and then moves closer again. This is useful to ensure that the screen is locked before you leave your seat. Unlock RSSI | Bluetooth signal strength to unlock. Larger value indicates that the BLE device needs to be closer to the Mac to unlock. Choose *Disable* to disable unlocking. Lock RSSI | Bluetooth signal strength to lock. Smaller value indicates that the BLE device needs to be farther away from the Mac to lock. Choose *Disable* to disable locking. 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. From 8d00ddb2af66f72dd2b6bb6086035b64ae753ba8 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 14 Mar 2021 11:57:18 +0900 Subject: [PATCH 124/183] Update README.ja.md --- README.ja.md | 64 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/README.ja.md b/README.ja.md index d2e695c0..3761242a 100644 --- a/README.ja.md +++ b/README.ja.md @@ -9,7 +9,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス ## 特徴 - iPhoneアプリは必要ありません -- 定期的に信号を送出するBLEデバイスなら何でも使えます ([静的なMACアドレス](#notes-on-mac-address)である必要があります) +- 定期的に信号を送出するBLEデバイスなら何でも使えます ([静的なMACアドレス](#macアドレスについて)である必要があります) - BLEデバイスがMacの近くにあればMacのロック画面を自動的に解除します - BLEデバイスがMacから離れると画面をロックします - ロック・アンロック時にスクリプトを実行することができます @@ -21,7 +21,7 @@ BLEUnlockはiPhone, Apple Watchやその他のBluetooth Low Energyデバイス - Bluetooth Low EnergyをサポートするMac - macOS 10.13 (High Sierra) 以上 -- iPhone 5s以上, Apple Watch (すべて), または定期的に信号を[静的なMACアドレス](#notes-on-mac-address)から送信するBLEデバイス +- iPhone 5s以上, Apple Watch (すべて), または定期的に信号を[静的なMACアドレス](#macアドレスについて)から送信するBLEデバイス ## インストール @@ -54,26 +54,54 @@ Bluetooth | 当然ながら、Bluetoothへのアクセスが必要です。 ## オプション -オプション | 説明 ----|--- -今すぐロック | BLEデバイスが近くにあるかどうかに関わらず、画面をロックします。BLEデバイスが一度遠ざかり、再び近づくと解除されます。席を離れる前に確実にロックするのに有効です。 -アンロック信号強度 | ロック解除に必要なBluetooth信号の強度です。値が大きいほどBLEデバイスがMacの近くにないとロック解除しません。*無効にする*を選ぶとBLEデバイスが近くにあってもロック解除をしません。 -ロック信号強度 | MacをロックするBluetooth信号の強度の閾値です。値が小さいほどBLEデバイスがMacから遠い時点でロックをします。*無効にする*を選ぶとBLEデバイスが遠くなるもしくは信号がなくなってもロックをしません。 -ロックするまでの遅延 | BLEデバイスが遠ざかってから実際にロックをするまでの時間です。BLEデバイスがこの時間内に再び近づくと、ロックは行われません。 -無信号タイムアウト | 最後に信号を受信してからロックするまでの時間です。意図せず「デバイスからの信号がありません」でロックされる場合、この値を増やしてください。 -画面スリープから復帰 | ロック中にBLEデバイスが近づいてきたとき、ディスプレイをスリープ画面から復帰させます。 -ロック中 "再生中" を一時停止 | ロック時に音楽や動画の再生を一時停止し、ロック解除時に再開します。対応しているのはApple Music, QuickTime Player, Spotifyなど、*再生中*ウィジェットやキーボードの⏯キーで制御できるアプリです。 -スクリーンセーバーでロック | ロック時にスクリーンセーバーを起動します。このオプションが正しく動作するには、*セキュリティとプライバシー*システム環境パネルで*スリープとスクリーンセーバーの解除にパスワードを要求*を*すぐに*に設定する必要があります。 -パスワードを設定... | Macのログインパスワードを変更したときに、このオプションを使って変更してください。 -パッシブモード | デフォルトでBLEUnlockはBLEデバイスに接続を確立し信号強度を読み取ろうとします。これはサポートされているデバイスでは最も安定して信号強度を読み取ることができる方法です。しかしながら、キーボード、マウス、トラックパッドや特にインターネット共有など、他のBluetooth機器を使用している場合、このモードが干渉することがあります。2.4GHz帯のWiFiも干渉する可能性があります。Bluetoothが不安定になる場合は、パッシブモードを有効にしてください。 +### 今すぐロック +BLEデバイスが近くにあるかどうかに関わらず、画面をロックします。BLEデバイスが一度遠ざかり、再び近づくと解除されます。席を離れる前に確実にロックするのに有効です。 + +### アンロック信号強度 +ロック解除に必要なBluetooth信号の強度です。値が大きいほどBLEデバイスがMacの近くにないとロック解除しません。*無効にする*を選ぶとBLEデバイスが近くにあってもロック解除をしません。 + +### ロック信号強度 +MacをロックするBluetooth信号の強度の閾値です。値が小さいほどBLEデバイスがMacから遠い時点でロックをします。*無効にする*を選ぶとBLEデバイスが遠くなるもしくは信号がなくなってもロックをしません。 + +### ロックするまでの遅延 + +BLEデバイスが遠ざかってから実際にロックをするまでの時間です。BLEデバイスがこの時間内に再び近づくと、ロックは行われません。 + +### 無信号タイムアウト + +最後に信号を受信してからロックするまでの時間です。意図せず「デバイスからの信号がありません」でロックされる場合、この値を増やしてください。 + +### 画面スリープから復帰 + +ロック中にBLEデバイスが近づいてきたとき、ディスプレイをスリープ画面から復帰させます。 + +### ロック中 "再生中" を一時停止 + +ロック時に音楽や動画の再生を一時停止し、ロック解除時に再開します。対応しているのはApple Music, QuickTime Player, Spotifyなど、*再生中*ウィジェットやキーボードの⏯キーで制御できるアプリです。 + +### スクリーンセーバーでロック + +ロック時にスクリーンセーバーを起動します。このオプションが正しく動作するには、*セキュリティとプライバシー*システム環境パネルで*スリープとスクリーンセーバーの解除にパスワードを要求*を*すぐに*に設定する必要があります。 + +### パスワードを設定... + +Macのログインパスワードを変更したときに、このオプションを使って変更してください。 + +### パッシブモード + +デフォルトでBLEUnlockはBLEデバイスに接続を確立し信号強度を読み取ろうとします。これはサポートされているデバイスでは最も安定して信号強度を読み取ることができる方法です。しかしながら、キーボード、マウス、トラックパッドや特にインターネット共有など、他のBluetooth機器を使用している場合、このモードが干渉することがあります。2.4GHz帯のWiFiも干渉する可能性があります。Bluetoothが不安定になる場合は、パッシブモードを有効にしてください。 + +### 最小RSSIを設定 + +このRSSI未満のデバイスはデバイススキャンリストに表示されません。 ## トラブルシューティング ### デバイスがリストに表示されない -Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取得できない場合があります。その場合、デバイスはUUID(ハイフンで区切られた長い16進数)で表示されます。 +Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取得できない場合があります。その場合、デバイスはUUID(ハイフンで区切られた長い16進数)で表示されます。 -デバイスを識別するには、Macから遠ざけたり近づけたりして、信号強度(dB値)がそれに応じて変化するかどうかを確認してください。 +デバイスを識別するには、Macから遠ざけたり近づけたりして、信号強度(dB値)がそれに応じて変化するかどうかを確認してください。 ### アンロックされない @@ -81,9 +109,9 @@ Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取 もしキーチェインの許可を求められた場合、*常に許可*を選択してください。ロック中に必要になるためです。 -### *デバイスからの信号がありません*が頻繁に発生する場合 +### "デバイスからの信号がありません" が頻繁に発生する場合 -*無信号タイムアウト*を増やしてください。それでも解決しない場合、*パッシブモード*を試してください。 +*無信号タイムアウト*を大きくしてください。それでも解決しない場合、*パッシブモード*を試してください。 ### Bluetoothキーボード、マウス、インターネット共有その他Bluetoothがおかしくなった From c9ae8481236acdbdb7c06af8a014e9c419d51c41 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 9 Apr 2021 21:48:28 +0900 Subject: [PATCH 125/183] Send upto 20 characters at once --- BLEUnlock/AppDelegate.swift | 16 ++++++++++++---- BLEUnlock/Info.plist | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 3dd592e5..0f8fa822 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -238,11 +238,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func fakeKeyStrokes(_ string: String) { let src = CGEventSource(stateID: .hidSystemState) - // Send key event once by one character so the string longer than 20 characters can be entered - for c in string { + // Send 20 characters per keyboard event. That seems to be the limit. + let PER = 20 + let uniCharCount = string.utf16.count + var strIndex = string.utf16.startIndex + for offset in stride(from: 0, to: uniCharCount, by: PER) { let pressEvent = CGEvent(keyboardEventSource: src, virtualKey: 49, keyDown: true) - let char = Array(c.utf16) - pressEvent?.keyboardSetUnicodeString(stringLength: 1, unicodeString: char) + let len = offset + PER < uniCharCount ? PER : uniCharCount - offset + let buffer = UnsafeMutablePointer.allocate(capacity: len) + for i in 0..CFBundleShortVersionString 1.10.1 CFBundleVersion - 716 + 720 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From da376cba114a6c5670f6e063eb06a172bb9e8b18 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 9 Apr 2021 21:57:34 +0900 Subject: [PATCH 126/183] Bump year --- BLEUnlock/Base.lproj/AboutBox.xib | 2 +- BLEUnlock/Info.plist | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index dc824aeb..4c4cbe86 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -36,7 +36,7 @@ - + diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 6131aa79..07cf05ed 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.10.1 CFBundleVersion - 720 + 721 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion @@ -29,7 +29,7 @@ NSBluetoothAlwaysUsageDescription BLEUnlock uses Bluetooth to detect devices. NSHumanReadableCopyright - Copyright © 2019-2020 Takeshi Sone. MIT Licensed. + Copyright © 2019-2021 Takeshi Sone. MIT Licensed. NSMainNibFile MainMenu NSPrincipalClass From 3f964daa01fd7804baaba332ac8212bf8d90f010 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 29 Apr 2021 12:12:03 +0900 Subject: [PATCH 127/183] Pass RSSI as second argument to script (#31) --- BLEUnlock/AppDelegate.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 0f8fa822..9661171e 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -29,6 +29,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var manualLock = false var unlockedAt = 0.0 var inScreensaver = false + var lastRSSI: Int? = nil func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { @@ -117,6 +118,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa func updateRSSI(rssi: Int?, active: Bool) { if let r = rssi { + lastRSSI = r monitorMenuItem?.title = String(format:"%ddBm", r) + (active ? " (Active)" : "") if (!connected) { connected = true @@ -167,7 +169,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa let file = directory.appendingPathComponent("event") let process = Process() process.executableURL = file - process.arguments = [arg] + if let r = lastRSSI { + process.arguments = [arg, String(r)] + } else { + process.arguments = [arg] + } try? process.run() } From 7d2bbbb6d6f6183de83a8d8ae623ad9f4333e7b0 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 30 Apr 2021 20:32:54 +0900 Subject: [PATCH 128/183] Update Apple device names --- BLEUnlock/appleDeviceNames.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index e49eefaf..f60522cb 100644 --- a/BLEUnlock/appleDeviceNames.swift +++ b/BLEUnlock/appleDeviceNames.swift @@ -103,6 +103,14 @@ let appleDeviceNames = [ "iPad8,10": "iPad Pro (11-inch) (2nd generation)", "iPad8,11": "iPad Pro (12.9-inch) (4th generation)", "iPad8,12": "iPad Pro (12.9-inch) (4th generation)", + "iPad13,4": "iPad Pro (11-inch) (3rd generation)", + "iPad13,5": "iPad Pro (11-inch) (3rd generation)", + "iPad13,6": "iPad Pro (11-inch) (3rd generation)", + "iPad13,7": "iPad Pro (11-inch) (3rd generation)", + "iPad13,8": "iPad Pro (12.9-inch) (5th generation)", + "iPad13,9": "iPad Pro (12.9-inch) (5th generation)", + "iPad13,10": "iPad Pro (12.9-inch) (5th generation)", + "iPad13,11": "iPad Pro (12.9-inch) (5th generation)", "iPad7,11": "iPad (7th generation)", "iPad7,12": "iPad (7th generation)", "iPad11,6": "iPad (8th generation)", @@ -138,6 +146,7 @@ let appleDeviceNames = [ "AppleTV3,2": "Apple TV (3rd generation Rev A)", "AppleTV5,3": "Apple TV (4th generation)", "AppleTV6,2": "Apple TV 4K", + "AppleTV11,1": "Apple TV 4K (2nd generation)", "AudioAccessory1,1": "HomePod", "AudioAccessory1,2": "HomePod", "AudioAccessory5,1": "HomePod mini", From 0fc1f2d96cc8c1ae0a942a40ebc4d16e880683c2 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 30 Apr 2021 20:34:52 +0900 Subject: [PATCH 129/183] Nortalize with personal account --- BLEUnlock.xcodeproj/project.pbxproj | 4 ++-- release | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index c736e5d9..65bb8e80 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -377,7 +377,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; @@ -552,7 +552,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; diff --git a/release b/release index 97250a5d..6465c953 100755 --- a/release +++ b/release @@ -2,8 +2,8 @@ set -o pipefail -USERNAME=apple@bounce-inc.jp -TEAM=376NR42P9A +USERNAME=takeshisone@gmail.com +TEAM=42LGPQYC7M APPNAME=BLEUnlock BASEDIR=$(cd $(dirname $0); pwd) @@ -32,7 +32,7 @@ notarize() { while true; do sleep 5 xcrun altool --notarization-info $uuid --username $USERNAME \ - --password "@keychain:AC_PASSWORD" --asc-provider 376NR42P9A 2>&1 | + --password "@keychain:AC_PASSWORD" --asc-provider $TEAM 2>&1 | tee $TMPDIR/altool.log || true status=$(grep Status: $TMPDIR/altool.log | sed 's/^.*Status: *//') if [ "$status" ] && [ "$status" != "in progress" ]; then @@ -60,8 +60,8 @@ xcodebuild \ -exportOptionsPlist $BASEDIR/ExportOptions.plist \ -exportPath $RELEASEDIR -#notarize $RELEASEDIR/$APPNAME.app/Contents/Library/LoginItems/Launcher.app -#notarize $RELEASEDIR/$APPNAME.app +notarize $RELEASEDIR/$APPNAME.app/Contents/Library/LoginItems/Launcher.app +notarize $RELEASEDIR/$APPNAME.app version=$(extract_info $RELEASEDIR/$APPNAME.app CFBundleShortVersionString) From 6b1db35504bc3aa2f02b80be58d4625566857971 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 30 Apr 2021 20:43:32 +0900 Subject: [PATCH 130/183] 1.10.2 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 07cf05ed..5e1ac220 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.1 + 1.10.2 CFBundleVersion - 721 + 736 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 723f733b2397dba65ed46fc6d1db5f109806403a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Fri, 30 Apr 2021 20:43:32 +0900 Subject: [PATCH 131/183] Change icon to Big Sur style. --- .../AboutImage.imageset/Contents.json | 4 ++-- .../AboutImage.imageset/app-128.png | Bin 4332 -> 0 bytes .../AboutImage.imageset/app-256.png | Bin 9112 -> 0 bytes .../AboutImage.imageset/mac-128x128.png | Bin 0 -> 5171 bytes .../AboutImage.imageset/mac-128x128@2x.png | Bin 0 -> 9365 bytes .../AppIcon.appiconset/Contents.json | 20 +++++++++--------- .../AppIcon.appiconset/app-1024.png | Bin 40907 -> 0 bytes .../AppIcon.appiconset/app-128.png | Bin 4332 -> 0 bytes .../AppIcon.appiconset/app-16.png | Bin 548 -> 0 bytes .../AppIcon.appiconset/app-256.png | Bin 9112 -> 0 bytes .../AppIcon.appiconset/app-257.png | Bin 9112 -> 0 bytes .../AppIcon.appiconset/app-32.png | Bin 1086 -> 0 bytes .../AppIcon.appiconset/app-33.png | Bin 1086 -> 0 bytes .../AppIcon.appiconset/app-512.png | Bin 19127 -> 0 bytes .../AppIcon.appiconset/app-513.png | Bin 19127 -> 0 bytes .../AppIcon.appiconset/app-64.png | Bin 2104 -> 0 bytes .../AppIcon.appiconset/mac-128x128.png | Bin 0 -> 5171 bytes .../AppIcon.appiconset/mac-128x128@2x.png | Bin 0 -> 9365 bytes .../AppIcon.appiconset/mac-16x16.png | Bin 0 -> 1619 bytes .../AppIcon.appiconset/mac-16x16@2x.png | Bin 0 -> 2089 bytes .../AppIcon.appiconset/mac-256x256.png | Bin 0 -> 9365 bytes .../AppIcon.appiconset/mac-256x256@2x.png | Bin 0 -> 20870 bytes .../AppIcon.appiconset/mac-32x32.png | Bin 0 -> 2089 bytes .../AppIcon.appiconset/mac-32x32@2x.png | Bin 0 -> 3031 bytes .../AppIcon.appiconset/mac-512x512.png | Bin 0 -> 20870 bytes .../AppIcon.appiconset/mac-512x512@2x.png | Bin 0 -> 43743 bytes BLEUnlock/Info.plist | 4 ++-- 27 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png delete mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/app-256.png create mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/mac-128x128.png create mode 100644 BLEUnlock/Images.xcassets/AboutImage.imageset/mac-128x128@2x.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-1024.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-128.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-16.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-256.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-257.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-32.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-33.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-512.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-513.png delete mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/app-64.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-128x128.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-128x128@2x.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16@2x.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256@2x.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-32x32.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-32x32@2x.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-512x512.png create mode 100644 BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-512x512@2x.png diff --git a/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json b/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json index f6b54b35..3b5e6e1e 100644 --- a/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json +++ b/BLEUnlock/Images.xcassets/AboutImage.imageset/Contents.json @@ -2,12 +2,12 @@ "images" : [ { "idiom" : "universal", - "filename" : "app-128.png", + "filename" : "mac-128x128.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "app-256.png", + "filename" : "mac-128x128@2x.png", "scale" : "2x" }, { diff --git a/BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png b/BLEUnlock/Images.xcassets/AboutImage.imageset/app-128.png deleted file mode 100644 index 597fa8610b7dceed790556ae3e440b179c658e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4332 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

@E92iQMG_44g~rdWqZx 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 DKTRhw diff --git a/BLEUnlock/Images.xcassets/AboutImage.imageset/mac-128x128.png b/BLEUnlock/Images.xcassets/AboutImage.imageset/mac-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8a7ab47677a1c9793c962b16c798a347b62d97 GIT binary patch literal 5171 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 Ml9pnHym{FF0$JKjmjD0& literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/Contents.json b/BLEUnlock/Images.xcassets/AppIcon.appiconset/Contents.json index adf23089..b91a0506 100644 --- a/BLEUnlock/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/BLEUnlock/Images.xcassets/AppIcon.appiconset/Contents.json @@ -3,61 +3,61 @@ { "size" : "16x16", "idiom" : "mac", - "filename" : "app-16.png", + "filename" : "mac-16x16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", - "filename" : "app-32.png", + "filename" : "mac-16x16@2x.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "app-33.png", + "filename" : "mac-32x32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "app-64.png", + "filename" : "mac-32x32@2x.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "app-128.png", + "filename" : "mac-128x128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "app-256.png", + "filename" : "mac-128x128@2x.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "app-257.png", + "filename" : "mac-256x256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "app-513.png", + "filename" : "mac-256x256@2x.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "app-512.png", + "filename" : "mac-512x512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "app-1024.png", + "filename" : "mac-512x512@2x.png", "scale" : "2x" } ], diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-1024.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-1024.png deleted file mode 100644 index 136ac2ecc6b8ecadd86f6582cb636792eb99d7b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40907 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

vccNr@EI=SnSGYz?qG9#A!`5a#t=G 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�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 b={e_ruQ|n^@^c@0L@~GT)_$yI{onrs0bjzv diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-128.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-128.png deleted file mode 100644 index 597fa8610b7dceed790556ae3e440b179c658e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4332 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

@E92iQMG_44g~rdWqZx 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 DKTRhw diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-257.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-257.png deleted file mode 100644 index 4e5fae2caf2159370990c2c70759a4b6a8d6e651..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9112 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

@E92iQMG_44g~rdWqZx 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 DKTRhw diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-32.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/app-32.png deleted file mode 100644 index d159a50fce1f8756f2711630be657d7484968ab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1086 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

` 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

` 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 Ml9pnHym{FF0$JKjmjD0& literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..37ce87a6dec48a00bf93749a413e2753516bca78 GIT binary patch literal 1619 zcmV-Z2CVssP)BE1ZQLxAY_vg;$?V;oX(lt{{G@3qJ`_O^!KmPq;Qw2}Cn0L9?Tam?khm;cmfj@-EE=vY<#k5o0f^m&*sM6pBI0EL{uRZtU4R5Z z`^X8`V)zNfJ8YaF;u6EP09P2E5Ad?jnUOMxUqvh^s*L``+lU9}BlcPZ2XpY-jyyG} zttleQMaed+noc9ry@UIIwng1*#M^^UkFzj+6yF25_LnMhmf?pGpH<}CbcnAmX|u%; z|7E#p3GpKUUAw%6atiT2#A8)^yd3Cssj4^8z$>TJnP(8+gLrkpJj%xIMEtceUz`ec zeo~wa<_{hK;NmJRuzL3+9o0iVC08s$)vACv7|KExH6jmbd4gn8^0FkllzkRT6fd|P0n zrwSVAXrbt(XrcJk;OyVRDf#{Yw^VBW$m{6QFhZvPBk<6w!tZERBfJcLd1$M3&)TehHS09YRtkN(Z(GDi^F2;vKBX;HY+Q*UEnTqUvgh@A9J5aYSek}G;8j! zqp=F+X_rylpF&#LEj%b>g+0Q~5^H*5N-?MwSLAe5T{?GXqX;9WfX;*4pXw6&V*io?m|<6;W0GAb4f<-njQ>$>Q>9MPr@ zwU=n+0I+*9IKpp9{{0u*d8K7@p9VPmCp-6AwmA>rtOL;dXv;Q$XXCx^0IWWyc=l3w zC&Xgk0yx!!!@*Zi8qcdA>-AfBZYACT*nGWS|NH%Vee(m<-vGFv{{#Oecnig2*2@3@ z061k>NoGw=04e|g00;m9hiL!=000010000Q0000000N)_00aO40096101%)900aO4 z0096101yBG000-AQ^)`S0lY~>K~y*qty8;B0#Oh>%K{BhK-ADfATdzsNBH;xDtdxg z(eV*Re}RSsIx8EXA~X~fBt)Pgq0m^+Zdlec@##Tg%D{U%QUS?RKj#Mgz z;c$q-V1RzVkM(+uY&HvK6_+EiH5ruEX!(gm0?lSq+bNgJ*laeK&7{j?IT@f{ucKHj z9(g8{NwnMT!#31zX7Drakm7lATR^MT(noX|>?t{{zy>i_R;^Yo$6yu+6%D{4dhA^% zBzrI#jj-Kr?X``^W9?sTvJ(_PTq>Ata|?ySD}(4F$+Sa9n^3No3nW)Ab9kH2=XVmw zv_OvkK;W6r6&+=jEm;&HQas`l>J92zv8h0eD*Pud%#s+)d`b3X`^@=YzTfX6x^)RU R@NED9002ovPDHLkV1gE+`B4A> literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16@2x.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-16x16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7cdc43ab91018ea037ce6ebee0296f4b914480 GIT binary patch literal 2089 zcmV+^2-f$BP)BE1ZQLxAY_vg;$?V;oX(lt{{G@3qJ`_O^!KmPq;Qw2}Cn0L9?Tam?khm;cmfj@-EE=vY<#k5o0f^m&*sM6pBI0EL{uRZtU4R5Z z`^X8`V)zNfJ8YaF;u6EP09P2E5Ad?jnUOMxUqvh^s*L``+lU9}BlcPZ2XpY-jyyG} zttleQMaed+noc9ry@UIIwng1*#M^^UkFzj+6yF25_LnMhmf?pGpH<}CbcnAmX|u%; z|7E#p3GpKUUAw%6atiT2#A8)^yd3Cssj4^8z$>TJnP(8+gLrkpJj%xIMEtceUz`ec zeo~wa<_{hK;NmJRuzL3+9o0iVC08s$)vACv7|KExH6jmbd4gn8^0FkllzkRT6fd|P0n zrwSVAXrbt(XrcJk;OyVRDf#{Yw^VBW$m{6QFhZvPBk<6w!tZERBfJcLd1$M3&)TehHS09YRtkN(Z(GDi^F2;vKBX;HY+Q*UEnTqUvgh@A9J5aYSek}G;8j! zqp=F+X_rylpF&#LEj%b>g+0Q~5^H*5N-?MwSLAe5T{?GXqX;9WfX;*4pXw6&V*io?m|<6;W0GAb4f<-njQ>$>Q>9MPr@ zwU=n+0I+*9IKpp9{{0u*d8K7@p9VPmCp-6AwmA>rtOL;dXv;Q$XXCx^0IWWyc=l3w zC&Xgk0yx!!!@*Zi8qcdA>-AfBZYACT*nGWS|NH%Vee(m<-vGFv{{#Oecnig2*2@3@ z061k>NoGw=04e|g00;m9hiL!=000010000Q0000000N)_00aO40096103e_P00aO4 z0096103ZMW0056pK*<0A1CmKZK~z`??N&QWTtO5*>pn1osB0sLSSc|Zkad%+Ni>a* zCKi%P1XHB6uuNmG$oAI$0>LJo1_^OvFvKFesEDLVrEJoOuDiPVzVY76%(##1B1z+c zVb08)$2W5x_ln%pe|~{L75|~0o}NTQL&Kx1tE=`vAP|BqNw|If=r1s z2dIei+~wtEwxy-z8>m1bo5})!<0OowAp8<@1BR{VYYh+z3G}xx-tX=0ZP|)9Ecf>I zK7;TF2)5I}U66Bu-*}DJhlhu0-KGH`7K=p+g~CTDytpgpt}}vw&94a8D}czm``1Bq zRu>l+aj?&8IcLnz&(9^F&#OV+$phE!joGpJG!zOY^g||=M@X6&7ECg4&t~vK(%RZ8 zlarH@OeTdl+uq)mwY4?L<#G}Zhiw&a4cP#IKE{TZ3kCojMuWj1pUP9xnPRah-QC@? zyu2)ZeSNaOzpswR$H!%AYD#8jXXWVVNN`JC&EuUgM7<3#MhGBK5k5iD@Z#d4baZsc z%*>2TOiZXTJv}XLZEdo&v?P&8q~ZnW71vTL0CX@vKQCirV`YuH0O@dUZce(ox@2Ktp<*@YplSl3ao%cZXh<2>mCNzbXjF!W zhm{UcNSy@@RwO>2Wj2uA57b{t;_LZ}B zs>veEh7aQf(8Y2rJDpfY%<9$ERT&u>(T^(f>+9=Er@~h;rz;w*%J~=MFO#uP1%hBZ z_Csu36<(o)OAhJ(hDHeRDgBNcf8nidqCxg~Kck#;$L0>cs@J9CI54hse0+R~PWA)B zZ!jL%AnIMyIYRpf;NPNW{M79j0C<4ks$clI_ypmHSaxBCen7RPn^cdE6_=EaP zA3EK9Yv(kPNQ6&MPoJP{VldE-In3wxLo7AO7T`RGIfLBU!KeJfkM6z9KYxM0tVD5F Tex8YH00000NkvXXu0mjfF1zl3 literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..5bc2c9f7ee5b2c1bf67a9b555ebdf9d5a197d457 GIT binary patch literal 9365 zcmd6NbyQSe^zWTvq@+QS5Qc6+kdkhY5RsA^knZl70TqxArAtx)0coU$PH72=p+RYe zj^WMs{nlIWzxVfB>#lXrntji?XUF;My+3z8YHO(w6Veg_06?s!s;C11AnXtX;NxO% zCLUjH002T_ub`mqsGy?Y>g4M2+Reh!TE*JM+QZ&bM@1e0#A0LhOzr6PsH8KSYIs;j z-c1!3Cy`);Kh-+vjKBZ0jj^tAUf@l3s+CI^*j>!FXBS$#DYWO3rjjG;_`00Dd}rLj z!Q@c37tNPntDO&d$9XY!rSfHhnqa3%sHQo&(Xn-C;c_$UAY^OFbARc6LL;wn$`Tux zm$rg$V{)~oZ?n7mxmQQ<7;*3zA5Yw8?$7KXy0y;sKYItz-ldtdhVwhyoeFIgP~_r= z*>bT#=2dF|^Ps2N6akUD?kAXedqL1G;gkhkrRb^y3?Ai>9#)U0CkjTwlz2HyV=&$G zk$77yXGRb_&%^mRN#U>GxtNEP21Lp-v{)uv-(AD&l@=Yl=ij}IuMr5QLg6%?rE+_u ztVEFI>a8z$MOHqsujwJXvu?5{IAQ@zn_E735FSZ)Tj~Y2&~JNvD-|6_@{~-a+mfN) z`FVFp4)*RV-qbyXWf6LZ=wuOXk}?3xtK5850p~0XRhstPkwdWtb|QVpkr$1d4{gA~ z`+T+{)q`M8i<)q5r!uT4fdfBAH&9aER`USjiPOp3nveWTb zlf(?F0_rne9I10&N^)KfKnwWN0!^vucY0yIno&?Jt5*H$rBgDxuRsVqqC!qgP&(y%$^6k7(_QK>*qF4d;axe zwWW37^XmUImF+Ql*9qI2se)~Z8WCaeLU7`M zENTk!-w5kpffYIywNP5B&XE}sm`uWEA^M#55nIQSnvntj5@9*VbkHi|QI!Uwcbf zDaiehXakwYx$|?onSQ*Oq%PHN1E>lV6zg_j_$W8u-NZ+T^$B5X-CT#cTSD{T-cU?*`QXlyX)J4U^LDY z3TVu`!?|PgyWe9uJ%j}aF>6CL8%<4s8#~4aKoRx;wjbO8jnA z=Yaa@y`u#SxwMW5zCNpSovXH1Fd31Rf;ln8oq?Q|y7mg{IVM!b+``mAbS;Mz__LDXoqReOyJBZjbq$eWei)-8SqHxzjk@n zHCC+9^l5b;#QppC@6WGH(SzQDB+)PIDPj|Qb2YYa{Qdo5tWhs6;`?M-p_u*99YXpI zu|c=xX#+ox$|G0}u8+ud3`2je?2j9vqaS!r_B-X|E+Bo%3$S-+LWa}RQ#x~><)2o= z23(Rwatu%3D(?LG^M?t4>CvZ$mTnB;st2(UZ&pe@7v*2AT3jUe?n69!SUHHRHh<9A zo70S--QZ6g-bbhK87p_Fepf34YQ>JaDPO9v#wiS&hFRhZ!gri-M{{1&l@S`sL8?2E zLK&n~QNOFUy-h|-%gQnv>?hepxOA*_*nrQUKWCFmpyb~NQzwd2;LZPy_;ma&O6nfF zE|?0;K=afvs`iBSiY!O|`|_IhW06|P2Veoau{?&z!jH>mIP}2-FN-9(`XNeC6{7l% zk-rzW*LnhSbBv#*K0ZWXZm)f16&E5cDM~L|b^oY*j(IU;S|~hCIQvHIf&Bq!8@X9X zp;`_QJrXdNTt?kHR3Lz{h3F)kd)#J|gG1CBOAIQ_UT+PjPi}Jzi$w^yg9Y4{kvTvJ zzu+AtOs_ZI^Y}$ODuJXXG|2i6bK6jE+U(~-zfycnQdj}{ zn$j?usb?*PA2!0&`$Oml6_9eHJu(M;9cFB1Wg)#}N+1vQ7K1(P3soR4DY_ys<{Zsm zmmy5QQ?tfs9}enRg3DW(`En6xPtMsmVV|})TdV8v3_vi-_y|F`?hk%n78WF!)V`gkm<&ap&av!fB@B?9_HZPU=DnBFy;4bs ztow9B8Y4Q=UwPvz>*L67wlff7ZSDRq89h!R%di|S)S$c(t?B@9-o}RmykW zzos_3Ga-Tw#cu?eqnf~pQ$WIHd(kjs>N*EpQ4{e0LVs_?`_)C42{0mcZn9r;-pUE) z5mkc5K?HM=cb6Ay@dtK$GM*r0aZDF^|D@h&L!3hswJn&F>UyPY_QQ0@Gim^GNC>H4 zacP`hNeN^olK}Tq8@ve|)CI;aXK+C`9G@}M+RkosK)ApjOtE^4Us@DgYP+N$|<=CY(bzP}*LUkl>?y+fYr z6C=4DHnijkbfLLKwa3lrfPl7GTrFYR0(mGa)_N<`YaDMb7movj*luWe4s51N?3S-v`|WD1 z4Gs;N_QgFmEz!zuDJU-=uCwp5pcz>~m(RBbHk5t)<}Ts0dpI&XJ7F;r&H3&6 zFOg7?scH6ny>sS#qkBPDj*rXHYL5#xAMGE&@0z>Vzb`dYQKf4WoU8_fI?hzwGV2t&~a)>M15o#t3{pJ=Vp}!iOg)T=|PdQjm}Lm=Nb^e z#ar9<)e1ov4PjYNqY8t6_wCJ=jPK*;UYrZn2}^}yropGbU9L|4?zf;v&<@nMJ z-n=>_NclcGX4HT%U_k>QJT@h0`^0w%KTghVGegR! zX_rMqb=H5n^>o^Jf6*6p8h+@JCDYDr6+t>lAu=k~){afJ6`oroOVkmQFJ%^YyyzwU zs?K3Gal&XoLXhKS&MwvyTBB0J1eu^slQl|_jNo4=9Ck3`eg7}no6GX<9Gd}|pbIzO z^F?3k>=iM|^Nm!PUSmT{xom-#x!RD*wl$u%b%OGN1)H0Y;sv&X* z4e!A(Rw7IfmRd9YN-yVYZO?Y+_Rr>BeP2qQeo9T}YRS4y{dEK5=)Q?xMcQ7HIFe=E zyGU)n>1jP476>w zG70{LC_**fvpX4D%#HdymAEnd^Uy)e`}`Vp*Vc?R=iDdGOiu~QkY~2qI{F%&d$;So>5lKl6k?;o(qm0hOkGpY(M}SHFI9Npr^{eDSDHAfw#Ji7aFwumvZQq`w4!mhSMx=${v*E&E5n%?uWzb8n zZ5Jk+&i4c^vsD&mwpL6BJK5&=Y4`xuufk$-A_@uPS-Q~_?q6^GsNw}) z+!o(->*VTPQk(g8EP739R{Z?=^P5U$%jmiI53bjLgfFu&_^F7`K&}4i-B_d*uJmS_ zWy|GF1ST0(3A?+RarskgZ)Pl0s?ojCvpJHz^BZ!p(NQUWcY|L1`l_IXX*2}I7rLcM zs7VQuwMr*)P}Rv_hSGrjBw)W}0bxqz`JnUg7_-&=FP-WLy!#9r}6~}}SMI2*}Q`=8c z4&#i!4UEY75FL9KGIpJdjj0OZ-fG|9e?tjE_?|w1a*PtBIwoq5zUfVtd~`s%##nH)#u8 z?@w%#JR4VGZ0ci#w~RBjwDA%iS)8f4&ABmzXjGcDwpjOw$;kDlAU**l2p6j=dd~V#CUoMnlh`W$FS;7z6>j08)9pXHuUx)BaeA$Z*)wuh^$lZ%$OX>ad-C9~C6vviVr#oAk?~jc=57)wTq_Pl@WjFROiv#s{fhXIE4Rlno2C(4Y zUWKrw9LH6;vt3VOa7bPMOw}fW*y=8f_hDz}<$AIem+6P~8Vbbf#m21& zrSN={#(KywRujrM+&a6nM2Bem_#JW`z-kv?71%Wl{5U^)wzw;#s&fE;@`4bFh56vv zqpfb{s6q#ziM6XW1$aMnp0_tgi^x@y5VMQC@51`SbeJ8OkyuWwv{2zNc%1Zu1q!ON z+zVVG@hN2U_v0B4#KDtJkN(}SB0`;0RAz>@R+_?B%LkqpS@{8Z?; zIE(8k*r;@fv-3@c5E|v)_jrGhr_$*xP_z$l^L$Ato5T*LS@|RcB2NGy5J5)Av_w?M zwX>+}Qj3j@?-82Ok(l;TGU*$s*PI{%*)QvaK`&{3%DM&cQGx&A;NL6beGqCzmH@D_ zu4t{}Lo)70>g#sDO2>2~B|YVSo?w@U{A=m((Ix+_>>S`Q z11}w0{`CYOcHp^S>4=JoiYMfmg`1R1Vv;N%eCWfpS@FPwEYA`5|MEMFa*Vc`9(2Zi ztylPpoyN0oh3j>3W0U7rfO`3Bs8tk8o-{Uk{9S7X@46T{&nlPa?vLxkUZ5Jr%I0ynXOC5VpyU8+Alk%#*zDC&J#*nrhZILAWJ?z z5?8U*8rVGV+HP7DLQa3%HuwifPnKGfEq24&uyf9T4}(qR%@wB24=tk2_}>2~yf(8U z^E#weKLyS zX<<%Kzy`$sV>wC?^2S;7&y%`OMThKRSX4uFr+&<1dKo4Ak-UlmLD5yZ93?CGUyjNE z@RC9mut7AzY+lVPmf|Ii^j@&aXG3n(r0~93<*RJ<*)uXQHQn?7nxEf!dVoS1j|6Rx zeOXGHAHUMAFeVou2g;EB*}VjAp0RIgY?}T~mNb9nF<_`Z!bL4-TuU~#=v!MCHE(7( zE<7+W`9ds4+m~M2%&rL(|ImGFiF+*3;vgw;88!Dk@?e5Qgut#u z;|kx90NHl(CE7KzQDvptdIX)m zg3p9Yx);_dg*?)WbgnHc^-gmX+adQ%;JN=J!R8@NT)`KjyLF_NV-NPGs6hp)&%AmS zfds#2BuJ=^_=M~2)#;brl&;5qb`2tH&mvV7I5Z$g0ZF#KsT`?56K7{34DxQ!T{mYUl{S9 zJt7Qa6gtNPr2@~0iXI{-a(yO=(@)5-QLr3cc+xFr&SdfO_Hd4UUQ#QQ}HIKk@Yfr`PsF`?4}Tnw^YC3Skq{T zI+c0S@~iK^%>W0b`Us#WwbD> zltOwR`wC8c;~Usp@8IM0f&Kb<7j)@<$Q9&*7q!(~tDuK%oqWGYKO+Zp*}CX*^zHi9 zm!)h>MD7)9W^Rs1qBR;^QPKtm23!Ajc3LqY{UiUX+AS$epDlL&O7q~&UoB4kGO#1P z?EAmlxnUQlrmc21YBq?Vm2mA)WbKiZFsbdTMl>~_n3T&zv5+bj6 z|K7g8*v!#cd2H1P(YOLSiNlZ6!;+p5%u+h9Dti`92!ycDs)OnGr6J=~_&kSA0DGuCYtTM*)ju zp_TtL3(*Rr)4k;aGrJ`K4#O=X147t`fZvaavY%k-^kXLT>U+tbf!&oBPa1Nv&>Q@` zv70`Vy&DGp3oa}yg;AD*sOXgfeoztQAvNLwbNFSpeT-s7VV43sEtqS|HOW@P0Z?VA z8)r>CG_h78rv5ejq?9~Iy}F4DY&9y@@0B5ZYPh;@n=yp#$`paB5feV}=hFP7k8uQU zSVH2Xy1D(4z?=}t1UnGJIOS5xw>qE*kdc+9t7Z0rji!m&Td*kgjV9sVTR)rA+}+8v z%QrYk3KzYHV%S18bS@MbPudIZ*n@5I$v1BT+Or>3q0->Qnk|D#CwI3u3;5TqTVpkDnI`CK`pY&HVXe)L=Kxe13x- zZT3Y4&+uGgRU7yWMo1oCVcRMGs%57p-ir*4W!_HfeOyXb(t7nK5T5A#a^k8(DS>4a zci>e`>n)cvTGXnkVkNdJ#F!8nRhxooN=(+wTX-A&TQ_}`plVq*v(Yb_!W^fIux4Sf z%mxgw!9GkR1cTVflVkcdtc-e!7SuqXhe={7M)!IODHB5Fyf0X?M7UlXDt%v2V+L7a zXioymtN$a!%%>jqfq1L`Hzig`-cJ?hIQ`wYVQ%oi2An`JCixpE&)45)jRnxSaSbc3 zH4kbv?g7DoYkrhv_0+2gW*FuHsQ%8vtbx^n8%S%aBMLS7ZiKL=8v0&HCc89SkLCL= zQ!v605X5vMZs`T7tU$^u-i9PuXfnsr&0!a5gVEKDF7Qa;#BHRLL#{?7j!mP94Xi|w z_GbOM4JJyB(8_+^iU-4J)&VH@B5EBC;P;&-c7q|mpGR(jtIRvX0w8DZ1PE{4q1HwU zb2Hf-KOZa`#x4YSil^NN9;;9;)6jp*dYpFOV1_w?6sx&Zn#!@A^eFDF1}hj5N|MVA zjknL&7#ruhPEy9Q+aVSkgP+y&4+B}jNJPHSp~V!q{u>vWQti;MN94;SvhA^hGKmC$ z1Bi}%8>J59M+G#|35RMB`{8b4YAhuwD6t%4Eg*2O4sl8ms=8_xr>p_k!|{%hi?0G$ zbU?DrC4`Tm$psK-gA$Yw%zKh>b^!bFwkLygVe}`>H(-p?ak|`}7HU(bAVA0Vyd+ze z9o-(!w#E?iygczf0MG*o#!O)2DXPfl;{Cxa;O1Aix$mDoO+5Ha9&N2xR$A(#tE(FX z0bM*~Mml}bDd04-4lz1VC6)X{ceLRvakqR1{BR@`r^f#i9U`NVTzU4iE17m1iH5zhN%217gJL!pwc(RIFjqqqw zt+Mj+-5BnPXuyZP8>(q9Yr*vjoDO!zaR8`Q7I#nrgl{>3lVt*qtT8&v0xmFzI)qj^ zJ%r;UR!AM{Qz3dW+`qtn58JOe{ zyc6obnPEn|MwQ9bV4|!!%%?qxM{lps??{lChY8K!M8*>0LR8eCud%IIX0!fxqn^L5 zc=;zU+g$qEa1-drrf^fR-Pr~_@iHFg5Ja=x6u)f*875fQy=;FsJW3w&@iHX)E3K^7 za6P?%IntX9F(7tjz92gS`$+|&1ES6i8}5BY^+g`D-qqDRI2yWiFxDSXMZGAz4gs$EN(uLgXg8 zlr2I2@1tJ$^x^Z@VpGdY4)o}wK&>+U?@Jx)*?eYh$kPX{@U%=A-WZ5LV;4y`P zgn&b|r{j%FtRusPzoPohi1pl|@3(y7XA8^28*69D!bDoI{jML3Gm-~uG$hk&CK2@k zk4H>A${udf-c)oDP44xtOOW-M#MHCb7<=c1e6nVSg8#p}mNATMAWw?-ct45$`$vG9 Ml9pnHym{FF0$JKjmjD0& literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256@2x.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-256x256@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..32eb06fcaaa191930a174df37ec5449f97f02fa1 GIT binary patch literal 20870 zcmeEu^+S|Vx9&Sb4IL5+0wW=f7=VJnASzgZC>?{+jdU}Vs7MGRBB>xqPXFc1u%8K%oWXxm`1W`UvxT^v|aPSchA&9|W z+b%ta5JX62CMTzCAtx_qV`*de%=U$Wk-U+$k)4@=iu@f2x)~g-s%v^tm0migv_OEX z-LEGlC7kM1^kbo=%3uGX`BS3;s}aEn%Rv_+cFk*U)P8l8JnW~S_NP&9 zxFBEPraVs5&hxX8}vOv-i-IB zzlPtC%yI5&5t7)i4~N_qP0QxvXua1gmia}Iv%Avo$h-I+*5cQ&pHgUnh$sCR!Oz_& ze!EW-fiwxK(<2UVa)r$b8fb7vrDmiXoRDsLMSvaIhV(B3RriXE^A715;(u!dQhTCJ zXvX|p9BXg4G_Kw~F1m`2C3EyriKUpOW`o8j`GxY__Bh${o|y5!X$@9475lyUX8-3b zqcPlbRmj9lqQCRjcjI@mf~!GOo3v`F(!bQx4JpoL248@Z)k>%~$yG>JGK?;JX* z$3-J1!*4N>Gr#A)qE@tVr^#Erh$oo_UF_f;IyutxWH_P)mQbC*Jfj@J9DT%1reF7a$CteftEIV&iB52-Ow(-=&@bh?KN5jJeahidSfewq7$bM@*2~TVFpz`}_ z3FUqE3l3Z+%;rK4O+!6#b{)7n^jv=)dQM!6;*`XTz)H*A)!d{XWV2>bPe&5&XblM6<~0nEs;jRve55qb@YMiGd;5~OzHN^)>#F86Ee^5eBxh>TLZkj0IIaUEb)iTgY#vntX2-S6K9A z@m+oM^wdkT+-v-d6f-r6yh~;CBM^O#3+ zPpkMA^>`LR=aFfv7{+9(at+FoPI1{)apC#(atwa6*RQ@-U5U9PlYMDN+r^rD6dLfT z)WaQ}xOr|7^o&726LADMZWra43=BKcDem@`^-NLl6pja98%Z3v2~T8hcT-bZ;ZV(D+(2*M%URz~-8bZWw^l5d2G}^>F_s3rU zif55$ahKkbpsj`t&fB2Ym&jrT_xJ)65^B;QI)BT~P)5?6-CXi@Z87J0i#h`8KqJzC)!Yc))G^orelZ7r{##(g2Js^NY-G1lQT z&ALY7cg&@|a8bW1q-EKD%VgEa#_p&t+OxF0#4|DN>$d^$I=->pnzv z>Mi>VDnVgkzPzF=Q;oqa#+0WYijR+$9sMis4}=zRR7*1-RS>Qrkjhy#RkA)p)h)Yx z_RA+7yVxmgnWOvLXT%K|mrcCjnCXxAYHvMFvbnBQ{MtL?1TkjhP2F_mv0p;eI4g{> zMJUf1rGLb%>vHqA^ijmgNll`xpPtL|kKaRON8d&?UwW?4AR$u{y^ld5=dXV2E8A1b zyU|g^KC-c^n8a=b3uA;MD(<0RK6*|f-^!_thnB>hEGF4M32J@|B9EpMMnPfgaAqmD zZI{+>%qP^tkC?WJ&dDFTl8(}%kfd-j!W)BCX4xkvVY*jrwXLjHl43a&?kp6>lE5*a zbSF;>{d7B>D|CPDJvh1)XC1wePVsmCx2|AvCC(chz!qTr>XcYaYSkEZ3Ot<^GxZ3 z4{+8%CTdeg(;@Rx9q7WM1WmsBIqp<$A zd{W{$=D2I=E5pO%JlISML;W(!{(HDQS@lS0uwythk)6`}7ZJ)1febtEqk~KgWX4(@ zH7qR`*7*K16ehT^G4m=ZotJ-URi>H$ID5COQ&059X*#diwYLy_f^44e+c_(57sa*F z%9t{Th0SkL_5QGcI#KFAMQRjb6A7z>a&BW|qjyQN@Q-(j&fbKxTs{zd;auKHk~O=> z!0P$^&2RUDZsgHCdjsEw{NH4%4)>n?);6lDFDpsWY8btK-@BDx#DyY?w{WRm}+937KR7voZw_J)%7|8cI*4ZXAQ*A?OA5NuHit z7Hx1NnANta3HN0(Z1g2WKx8lZJyHPN#_Kce_U}r(eqC)VK6@90@Lh&Z=^mo)kf;B8T1WyawG^I;MoR<|gSrb}B+UyV7&DNq31TD^4B;Jo&LamYqc${4ys6z2JFsfCAzgyeDkn{PJl*@hl_4&$O%U|F zVZ-v&g(o{#tT4q-iG)854kDl-UbjqzeyzK%Y{A|v${xYsIxZCI$b8r)H2<%rO~DI4 z_shsfZ~wqkAuT>;mSG%DV+tpfsr-Ju|J^zCC9pgX&gCIdN=B@=J@(gSvb2iK)w?#M z{Eum%t??<+c4JFTPRP_}l%tU# zd(t#90Vd?}M;_P$g7eYlo$UYE9l=BiAwEZi3(9|bk^>tE9BSOl&B@BD807z>(0;V$ zSMN;=9=lEYTM)|A$FXNe;nr~c7i{R(ZJcL{R5_m?4m<9k~&WFQ)i#wDoT zIzJ`T*rL#dU*IM5ZQ#uH55sdKC6}GI4XVTxEPEAfYM~*jp`ndtae)qT=Hlr06(pz0GT#r3?2KDWu z?@yr{nHw_jruHWvkgo3AJsB&UBQiYnU3*Ldao&G_-fCxij80FznGL)NgStSJwb4JM zhy{S6X{2Mds1`W`CHe)QJ5dcHJeuQ|TOn0>8y$~R>8j&H^G1}wK<@eYMZNH+pOHlmUuMpLiMAkI$>?~N)jD@G z4)@}$#Azx<x(k{?9y3w0fdhnyCi_?ax>J;;-2+02K%$YUCPdcRB+3bIiPrJch52s`He#b|@xfT`; z)mU%c>?m`&acY0JkjoGMlrS-vg@AC^&P%gaXTF0!cOen;o;0?;b?=|_$WO9h#xzZ@ zQLE5~NV%S+3S;rjb1)3sk*Pe+;O1e+ok(LA{EWJYva-{Pmm1oIJSi76uYpTMkLNT$ zh%BPr7P)WX(dp&6Jh7?wf;trQo{&B#j@>i&Q^9qHMEC*Ma&HS`%%SeTnjh>1oE zUrib%fk5WWqy1tx>8Sb_bKOh41ML!S|J+ZnvMJt!Vd8MOu(g1uUA%)LKI-b{?;xpp zkF?Ge-pM6^9C0x_FGx2p3!E8&l&L9>*ku^x>!AuRYQvFIxda#xqm$-OZPZWnU*I=j zCY;q$IdSixVndGvQnS@#itk{Eu#yAgo@DGKn%>1EuD+V%%-XJ`gl_DIer3JDcyS3o zE1Gq6r1QkSZ8^(05?#_Y4AqcX`c_g+FM6Xhg@*&c(EJ7TrJ%SKYsiM03*5cD=pBb! zyJ)?mzj8J$Gcu8An6S?%1Z7Rt(3WhQ=nlTk9tc}I({b*2@u0@-<5;oV&SFXBf*keflrEH*DY`;m>?4#P*OedH1X7!&5D)fzCwsBo}^e4%+sBLtqQ(~(%wb`$NLs=MhWJM`8 zpXis-QdGs2^}BqSnWDFR>&AmL-swxO@PkGz%Hp^L2L=X)oRpMC%JU*fmz{;~-#e?= z;hnXyv7N=F0jrNK;imka`TELBb#-m+rPtK#tM^0MRzQI>*BHvaHo99}LKIxV?8Z71 zWieFdm^qwp^oWL{EnaF^Nats#kVamOkY2@21YM6o;x+=&!rkr`&XqV84~JUM!BF93 z#%mP=PnFR}-YcUC8{%*?#?BD=;LH?jY*}eZ^~uUA>zYfT}ogbHo(+W`}6kxQ$gTz7R&U zzZl^*SO{g6+$FR)#uDa+A{{(!M^g&uvt}9;C+Fy^~xi?2e5aB5U7}4-3e@ z{B?<#&HMP(-nbt%aV;iCNVzkS=^WhbToX|q?w0rI@e**;lbQa^*IoBv$8Q{Ojeck4 z1K-m&p&fI{cN{+AzEXQOJ^6Bj@2AEUq2W#~>0Zv%C8E`8oY!7d^>KrcZrP2`x~YiI zGzvi|VQBvc5tQ)YQT%XAsyYm_10AD>$JbtwC1$^zJK{p5P4v5N*ZPr^UvIxz|0GVL zH<($JpH!1vh?+&*n!=6H>tqRC9;N5Tchf47jruZSxNss=GxTXpTg2VCYfH=Yv|=%+ zpl+eVwee0+{J|t$lk*Psg=@d!Ja-3cAI<$O5Lr}djJdZ+-Jpu}1U;)ai!&jtk~-C1C%*UBlh{v8 zZDDchESl4eO;cbAon(#%J4V`~ukXDVQl2*d%<9ugx-ue*NomtZu~ljt=t=3@;dBJ zAnr%llE3(?`ksh`CU5s%3EZh9xdH)6+|y^&q!U{l{H}(&O?LCzD?0N>DC~*(@z_p| zbE%ET6w4~`Nvc(>jYVmeXD+cfpkr1&*W44@3(8!#W=jtb51)^ogcaqtw(QPQ39~Fa zZ4Fy}YzvmQEK!1?c*r5)k~_@faI(sp1;-z3EnG=W9r&K0?zTC4x1wHgQ?}x#f zjc!DV!DlV5ChP8URk|`K-ZNrm%eEg3(6|J@hnO#xmij!G-zschz29(U$ciqtD>w(gu2Q!&w33-FCI_wV|T#=`To zGjH7AN?ne8@6*M}j|zW_Mvl#Rw_W}HbIlufRLkqNG1roNasGMD1D?3)?Ke{<37&lb zt>efEPxvRFTL!aP8xgo*fnOcoN^i1diQY~jsVRPj#l06Ay6>~)=t|@eSzUJG=x{j2 zjvELDq)G*8Ua=Hh;!py2yoD_^>mKnTVVm!ns`E#Egp=)Nv-RDLHvVkpdJfX~nzq3M zAqtA~R>et8dC{!2Igoq1Qo>oqu8bx! z2#QdnwIq2Ag%#e}xFNH+2`GZ{@{L9oze}D{*tTCbpMsAjtjiWs#xFf2ga4F6Zrq!_ zN1UC#F)MN1MiHj*Cu5SgEoe;roA{kzZ!4?pqju-2DMf%)cjrXOqnCz?GRsd7m*V0x z6Mdo(@OC#;BjRuXKEm2OTD+LT7>d5FdOXYJp8p4|Oqn9!;P z`bcZt)W{7Ut}ZP-)0A4=n8^l(oO&KcbDL;8WG1kwC6S*hN*_#58)o^(&WnFr`J3E* z;lQe!wqVs%Ve3Od%lsFMoL9reR`%4l*D*^Y(LS<|(l`XWiZSBOF<^NHLjJ^zzAs*4 zx4+AsNc=1}GpDS@bDV(v4ga81ssTadO?Tr`?NYn%+CN?fxbO6;V;6ffu_;etMm`B? z$4YhIHa#w~VlLCb&M!{P4;Gk+41}x}^ryWhAfa-o52U7!*Z=%_9OZ26v7|j_2jJ5| zN&0|G7FBLRCtxaD6EkXT1BZs+x}J+HQl8l7C5DNHnDfePMR)upzfvLc`XYUNGP8%& z4U^fnXzRxJ7i6b$@7sbqss4nu7HH z&7K0v?SawLlOvK=t_^l6pQp~I9+T%15NrZj_`XJ8e@tZV?eeT#fGu#we35H znAgdVSJ*q~@Rt6-v3oaPUt=VY&8Wgxd}T|Ha0M!mzM`ud>(XdDtM^~j6Z?K|IBmm-W-3F zC2^v3YI?ayHRwIh9Ta6kJj!_~yF>zO6Q8^Q?xJXaC3C!9FWX<$KDCCO4~$knsrne7 z!`_)34@nVB_^}@+N>0Av^nFJ9A_cu%hTH!wL(D14-*Lbe`m(?F7%@%sJaAducFdQE zT>3Lk@Xy-lZHgwRE_scKiYtEtWJe<|C9c}Uk@x#~I-E+9MlkEeb@v@uQF>kXJlr0C zDel-U-@D%`D~Z|>=6zD>P>Wq2EQn*)no&wvaFA6g!~TAM;oN(A-YYY~q&~ZdY2T)X zzN6jh6-_0TPdl?rFNmg|lss19muKAiuqJ!h6;(wayQJt_D|5O*y%&*i{7VYNm0ErA zvLRija_$!@(6^`&J(r1qaZ#^hm%WWp@7p&$!~H$QWv&WBOt8jv`sJCdTgxK!E}K zy3i74N4qZ;46dQ-DD`8&i??|0m5)(czYP))XYM}*sgkWa+Ox=gHrm9O)6x+=SH?co zqE@>w>rUjbG?8$+%~JvblK6`{!qm*IsV@!><#Ve~cXE9Uv;?O%Ik2CI>~@Ub#=(Ck z&D`qRNe2j`OJG~U(EeaylBeVWtuj+Fvu2z@e}ng{CqYbCs6q~H%2$fVM~d3NrxyKa zNnq|=c^i6rQ=tq1mWeIQf*gT|r zq&{Oj*0pl2+OTR#rv1a|YWbS9^Xb9d+AZ~L;u|XwPM;Tj`}Oalk0+V@a$JIyXnrR@ z<5Aanb?muG zsRmH1r^`Cm3eZ#lwfU;soU17l84yZoQO6Eu?3#hFv2D8?}#7+b`8QC>d|lsUdMe_3g% z^z`L~1CQT*#){+3R(xioy*#-Rk6y?uNqrP0T)PsqqIXFEh5Qa5C4^=>L?OOx|5rxZ zOsDBrm80@TlVSnn&Em5p67>U~3B3c}5?!+er`obIiM_~NkA{=YsOt9;2grH%HTN+V z9n%Z0lU#35cL@Jk&Hp)db-~vpT!f+o-}x4?7NOpiS7Z`5Za!gT!BrFq+x2)J^y@n7 z3d&;Od3u#@ucS~_D3^{{V57_ZkYUc@r>a_s!+y^NbHZ=SZvB3aJv-SG_DQRbyt)Dv z+zf>C%oIeh>VrQvb&t{lM3;(dK<>)vGHF1L-lBycom)@I-nb0q3B12WlvlmjGG{~E zsV`YJqZC>kY(A846m$f}e(`pGRAaTKt~1HJ;(Lpn(#z-5QV(L*aa{)S3q**q!23bHtg*^x=1=uH;!@T3X03d-v1N%ArA}_}jO-l5h#QQhnd zY4*oxYag8g<3I5mG+NO`S?D;B{>Y-zYQji;1VI!++g2oE(hNQ58=wV+4Oh- zq%ktnVR9oc0$CZIRB8`J^Ap;wGTpW3%A$uDl`$zVK6vkBqKl;LFU%?{r@TTsSa6(! zYtpS7hZ@Sf^s3a%DCM@!blP#}Act-`c7 z+>a7er67aG_Y)zQE>g{F0eD9%F5rd~UJ$*%)>=lR)Zs6))ANk;!LE(>k+!V`i~2EB zM|Kdr*L`!v##{2dK;phAeTn~lB99LD(Q|`lfs=N>M-cv;AhPJ+W|*M)p@t>|tzB&S z*vaa6ac*7-V6>jp{;l?f!;bNUOtMC*7?|V|3;u}j!zEH3+-9w+-{`_HUr!pY8$UTu4@ScfO zA6fI-D_2;(hVV3?3&+6I83fdR;)(dLbEfO#wbsY`8zYo7Wj9EHivdw_&&bd zB2t28f`x!^v5n$vv(m>R=s8f3@*BFXWHpq05NO2MpB(K`JF@6`9n6ehEnc+JCzD6{ z^cGtU`^xhNM$}dJWpq=XyZm@u0weN4)0qP_rs$qh7;*^0SfiIra{vftary2@?+zGP zw|){Dkz>oqr`$7xeR{1y<(|hz1UZ1|=vw1fFB!b1cx-wG#()jv1Jz9OfAe(A!K*}q_%M%`$X21;;$5@g1N zH1qv^yzdA!&oV5*AMF&jeZHD>P)~CTQdD)@A=CaG-Ah?TdQRki?%Qw~~5pmyL9`~&&@z6}6c34zMf_D)pUshI& zPyjE!%Say0ABcft2&iw1^lac*n3;VW6)1_H-GDE`G)k=Fw96dK(nzT;-VE!O_)1o= z`I86%O$x)hsGp$Z=Z4+}-ZFD21MSz&6ZXrS(o5}QRB1u5`dDI$^I-iii!6oc0_WZ3 zKoYzlP0PHwe1vnP&v5VV<-K~36KR2U{o!%2seJDFdYVKsDx4I?4j%3ys9MZN)7dV$ z%!cXLbN{n&12n%1BNd$q()PI9($UpkBSRPduO}ns)1J)DIMV8Wj3)9yq%g$9#)d7kQ=0(p#GNh>g zjee+5kI^GW4_I{j6YDLTd%!rNqM~2p>s6teeOxyEoi}JcsB;T)fEjBpXbF7DE&XL@teILNH7&cg)60 z*V7oM(HF#!`SArl;i`9+-LX{X`;zrui!3Q=92i$3==s6cNu%HTu83TQP{GtNR7EdG z+o~%OR7QntyD{0zxswIF#;}surR?N@4O;nC|HbrtUAR#t;AaECc<5V_X@f9jHuV(H z%Ryc`Jc-@H1)h0XLG7#?fB~c`FwAqUX$C+w18V;oh*cj+!+XzIU}V^3V1*lzjIL+w zH!ATl*iuHmhN5TGEh6~=nE8|M_w+zd0Nl+R0bUr5)T9LDFkpzy=#Y@YGqVrs_~-uT zwGo0aO6a+|I@6+Zr4|mb^bti9)5Q4Ol`&8vir4s26sFM@U2GZc4)_+s1Qc;J7t@z&rDj~rD)+{`t7F8hm_vht zbKg9;0NOo#HT5~eq)n5}44vH&ld#UbaK-u^apSi0fMwPG#8aNFS2+X%C$9#RJg!;D z+Ya=4vw>X;SL;jon3QP}Id_@R9zQJm3DW-d^AR)SKB`mYhoTyRPhHc4<>wtpuqL7~ zc(NXqsEM(Fz6kA;|1)PB@KnQ;v_a*6M)(5m_@6KTA0s?BG=4Owe1*_yZFI$HWr(3- zTl|?A(Q^`~<$(zahj|)i)b9{hEbtUR2dzwt7QfkEfwp7+;;&gZ3xGRD3mtA-{&d<6 z4i3%%Vuu`1ndg{v#OJ8z>YdT`-+cLFj8(0sC571izG(kmayhSF2X}hn$C?F)hx{C^`;<=I0|nmFwf`fU-FKCU&3v6f zFbjB@)ZU2wYIbRqQrp#ueg*A)jn5J zz!yhXx>G|Xm$C{*Zq3C`QQcmzxt0~Filhmy$v0{p(RIeo0Fs=o#A!uKT~(FgNnJ8s z28QZpREt8N@g;sM@2pSj0aF8Wv)-xzaRi|`#?G{)M6`I{Rov>Jby`}te+{7Q`YW}q5~Ke zu1t2J!%uksj4S>grT66(so0n^toK!~W3TIF(6~cFZ^{X;#aFez-36^1UB_P49Q(QU z3;p_Ao=nQD>bmN2b8gVsBgundc_{4o+NMO{B9#;fuGsm%NtL6DVpKjAn8mWi+o|wP zj*8#hAm&BgULcl7mGz_d4z6DcQWK1lgwv8ha$Lz+~8h_Qzkl(eapZh?eFo7G%&>aJx~D!hzR*%4h01LLMk z+-=}a*l=(h^NteM9RdC0Yr?g&CaNg>_a}^>Y2L^Il%;a)qHuC^%SVGWni*lJa`r78 z^O(__L+lHLq-_YRd~BMk{_I)(lt+R3w&g2DRA_#m4Fp{@)?? zCJ`vqCmsI#dllenVHuMg1^?{#Q67ZB+aXy*{{qN!VBF-4pG|`QY&}DZgm^UBT($JpXm`FJ5I@gP|G4FR!2$p_ao6T2pg#J3J+e=MZzN4l{=HUif9^=292u zmX!_dEDv^5f_@Qb?{)U}^!!welW;9qu|&WTg@RzrotuoCeDDhl_#g|aV>>{oCdb^e zPtmS$`7vB*7GYs&=|F~XS~*m28v!DZ#M<1fEQg|^BJ1g<536x&9;uSR<*~osp3|Lc zkGsy_WXw4ZQY6gSOlzN!vi_ehqN{t=xJvhNACeJ3AL{7p#y#UiKYerX8t{WN~d#$qaxiSk-<1z+f6phlsBCBQMP3rLtz&5q%H}FgvHH{-y zj*oQ4FHa7bKFaQG^9A^K()o9XKvbQwI4@KEeUV6cw1^UK&&MD;HvB|hdIx!4+2mkz z=IzD()a=Q>HK-OzKX?&$!ye)n4?y+=DL&55j2e(s0Xg)N$}mA?xQk4DwF5L4D_t(kC|9-7?)76|#Jg}V8ii!wqZ~m2 zk1vhKzV2bYW~g9J0L@4}kvsovj;84g__FU|?8=j1O#4@*&mxzKV3^JLS&Q>3&z?5% z!nw&nd4u>39)C#$Gy&8B=wvKG4yS1F2K;U;JtNAxCtjeMvrVS6?)u14-TDROAUvv- zkOVIGw!j+R$U4Ajq1P85Sxa%6aFcBV7-h9@yP95>ptpA_@ZXDy_rB6!(-c0tq>rPo zajTYeFZ9X$-+qJmnG_N}DKvixi*vFyWn^he(#INrfVOBj?Jo_p>} zQdFeneTu>^fm+|nHm9L}t{5N_-Dz)}I3e zVh+B;q!TNP=~83+$V7a?X8kKV zf=%C8_|`hL_h>j5J2H&I=b7TI1|S7##=6eMt>%=k+6ky-+2Vynpb%;IoeIB}p5rBY z4BXrSs6w6ap+*vP_;hpiDj#aG1F(Z$=(gxgl%=(yZ3fT#42<=sVbSCcNjr29_-@E}k#;Lna{b zPnq|;CPu#F9OR-UHzi5uq43wYht#p(e}f_@!~+5rbGDis@DE%msVH!(caaavF?^OA=#UowE zlH@F#wj-(_g|bL$V$uXldjn!GfssZtt50eegGN&=ceyjg z&-Zp%0CeAbHFI6lh9qW$_;bHPXL%zu5T1A(7O6&9*gXt-UG5VBOj^-Fxj>HkUv;3v z#~&@tiZII(rhbU)M^hH2kLN0SI^787?qA>zKvU~9T#0sF=j`f~ZG;9(0ag)f%c6fL z$kOW0*1cja8h`;5a6Czy9bj{SfO!)=pJRaL#cMwD`$&)vRot>!_+y31xWgEit*ENn z;$pnw#9U(hhuL`#>ujRq`={dzX0c+i%8TG}I^6)!6Xm+tXGg0Sby#<3L}X;#HP*d% z?;Z^#s9Q!)NqFs2Vp$G^a!laUg*$~UsIp9{Li$W5S;g>&>cK58)+_I{O5AqLo$D#Z ze>UE~Z`bbPxbC&3`dhVMbxw6Jx{Ete?6U4HYpD`FB1nR$rsi5UlM7w-%1CK}`+6-2 zR;Xko{MYga9>SK(iGV)Hk?8pN5SA;fvB?kRHKLtiF7^S&{N8Nb##-2~V?c|A1?@h~ zNoEZS5LVNyGef#}5|-`Eg`E2uD~5*l%#PYKrPF)}NYpf8y82QuY_}SXW=F0fHf?YA zEl)f`_`Sq-r(7wDe<`vqVx>RZ3-rP40l{RT{JV7hN*qz9=f}#FY(Ppql_(j{F5Tsa z&I4SS0@^xh_ASx8eTTOCUZI^gO}~G zwaHCYqcd)JJheGWixGk}qF=W$hRd(zd}g_B+{US~ZFL&3_T%j` zI1h2h#pmLtor(UlA1yDftNEyAs+9s!CC|<8JBLfT_U*XryXl8_NqG8FvI=D}AzJx{ z&704Ssv_Imn4tG;QimptM&7vv-+Y$FTu0r9-vA1IW+YUtGIH6NTBmo{_Y(vttd)PHT|wu5SEhr&cuLLv$W^`j3DgV^XrDb!b)h{R=v6;sD zfJS*L(f-ISJ$`n{oNn_zTMLi=%aYB-V zcJK0mG@JKs4b@F>Nd+?rD!{ZPJUj3HB&8@l$O7>JrBJqef3aRBYQai1+q^rkh)iRMf3slaAWw^EP>kA6_gLWt+CtP5q&cMbR z*BTWa3d0mz8G8}9{)oJ4nxmKK>u6+F5GEK60Q^&|L`%X%P@gHwUvei(VoZSoPX3&*h`<01Dtwh zMuvlWbQCzAfqB@fNN^m@23=VaVmTAg3+u1^-X6I(^EH)09uDOB#D^c_$OaT>Xyki2 zo`9DlvG6{pJ<{Nx!5q1$q4#gL);%1tgpB~sxxspc+6YOBv4D>peC64KhmLLMQ>a7E$>))OsTk4brlw85%%&a_LK)m@dDVp_W%>Q*)y}3e9*iM^ zoCoT?7b^!7)x@~2!)hvAxAgMLVW%x-7&UVO^d-zaHY-;MqL{+}UjPuGbedRCa`U0_ z63t6Zk0E`cvSDk|Q_hFaBMXOrBMx&Bp?LP$HB9O5$Da~U;oCOr1j!*nh(2;kE;6_D2A#FlfZ z$gYbUU4uG{Ahr3i;;4AWrZba=$--C(hnB?3pB&FnSXbew35L*Lsn~AsEA3(v7KjEC zXcF)vIwdn5vmx;jU}0gYo?25Yt}=>VhS4+wg?h?Hd~Q*{+DQUFcR?Yw;f&C6HTR=W zf*pNo;x<;s1Q4wtKqSpfNaib3V(FcACa}=s<*3zpTr``Mo8e*=&a+jt8-@`9s99}g zLrx0c&1OEvpK)4$Rzr2{H1O%<2%NjVcH;3_(TmY$Lc0i@!mz4z+v&1T=*Va4AQz4e zxkU^1Xb(cW4?B{voyE2&}~wv`u=Tt<{!iJ_#q}DeRTS99Odh4 zb-_RvDPVdj)THH@52G`1z?Ge=-v4rM4P^g*wT7yy<6jn=iXT!9F)Dpo=b+{KK~S5fu6zjN(F$-KmPT)& z&m7-@%(8G|tRqIo`?Su%SSD7-p0Y+dN9=JPe~Zbo&R>>y%%EHc%iSdqHXQ2R%eH%6 zs#2R)v3&t%THC8gRAF60TxE=oGrQuO%=msEf%l&>RL&BPv3zEYxm7?3gH5eLK)bMJ zT)!_SYQ+r`qULjXPG;&KXtTmg8*#MmxL<%AVea~{WNQBa>U_5c>d^S&ZWw1aoOPtd z(gTlI$S@>K^i)hJZ@65>a_*#`i`U|93lLcgG`vs`5(BL2ZLOzIUwu?fBhZeH%sm@2 z9Wt>I-ETWsJAA8e2iHMp$oE5Y{Md=*UIFN!Q$|KcG7VT3*gZ93c?7s1JV<~-c?{RB zv)%6mh`d6pX#nPxBW|$vB2A`qg!E9OApRl>c(He%!KjRd{AqR2rGp;sW>4#T^n;+F zr79qE3KhE6Q}+3tMz|*M9B`_Q(KO#0*D?43hx3mO5zkB|NqqCIFm3Zz%*k!{>|~zA z{~R(s*$~z~i84J7lAGnInXHJ#DsEj-Z(&+tBwQVCn z__xQ|z#B*hd_FO1=pPN)XK#3?{rn#7js~T!=e>vf)kH8yP!=EfQJ#Np6~t~988IyI z$kvb+_@}-%ycE00xqC7wR(t$P<^0oglh=2Iir$CB4#e0!8X(-(Ak zz`FZ5;g5T?x@mgngSJ%AiduF5@DB+_>>~_kZ>31f&K%G(h2-aCun#T8iHY1RRcm#_ z-}RVun)&oeF?;0@^Ql?!#`_wzMrIT>>cZ8n%zSy$+?$}v_A-eqsjeLT`mpT`;go-( ztgCO;Gvl_hH%v^&>K4f2BcMe6j7j5>ychLilHBV@(*LWSYyWBL48vMrG8zPAK}8Fl z0 zI*eReIxRgS<@D_9w!dQ8FDE%C`I7U!d2iqIJQ$q)&G@amY+g)Qj$eXC zGLo!BS*_9*&S;s~wa0^z?kQ*_>`%UZY-(MAWXIL@3iLsK)GDxG4{&=qFELemzvd-E zn+d|6UR$*SD?~Mx^P2$4r&ZkEZpD-5%Hn=wg%t%v+S(*^AlAw2~yz>bg6-z$ff(5VeJTPcGO$`>EC1GBz$23|#*Q z8OqnC0!b9_i=#)?yc73`aXL3gJ+2FJBXyjy{h5xEp+i?#vfOYtM^oE?DD)C)J4(9Vq=%ob(1F*XZ)-YwIoKQ zILG~2E*a&%DXK$e8_zd(oaW)EQ>(~TE1}|O8640^h(se{R>>OquhQ`y4t?x`j$Uw7Zn5{Jsa;NE(gPk+k4a!+Poulk| z(W}7+1r3@FTITHhwECfQFaKW+s;%DrU3dR~{KpO9Pn|&Ql&b>h2Qk|9wh?HN2cp92 ILX(Am0X>54BE1ZQLxAY_vg;$?V;oX(lt{{G@3qJ`_O^!KmPq;Qw2}Cn0L9?Tam?khm;cmfj@-EE=vY<#k5o0f^m&*sM6pBI0EL{uRZtU4R5Z z`^X8`V)zNfJ8YaF;u6EP09P2E5Ad?jnUOMxUqvh^s*L``+lU9}BlcPZ2XpY-jyyG} zttleQMaed+noc9ry@UIIwng1*#M^^UkFzj+6yF25_LnMhmf?pGpH<}CbcnAmX|u%; z|7E#p3GpKUUAw%6atiT2#A8)^yd3Cssj4^8z$>TJnP(8+gLrkpJj%xIMEtceUz`ec zeo~wa<_{hK;NmJRuzL3+9o0iVC08s$)vACv7|KExH6jmbd4gn8^0FkllzkRT6fd|P0n zrwSVAXrbt(XrcJk;OyVRDf#{Yw^VBW$m{6QFhZvPBk<6w!tZERBfJcLd1$M3&)TehHS09YRtkN(Z(GDi^F2;vKBX;HY+Q*UEnTqUvgh@A9J5aYSek}G;8j! zqp=F+X_rylpF&#LEj%b>g+0Q~5^H*5N-?MwSLAe5T{?GXqX;9WfX;*4pXw6&V*io?m|<6;W0GAb4f<-njQ>$>Q>9MPr@ zwU=n+0I+*9IKpp9{{0u*d8K7@p9VPmCp-6AwmA>rtOL;dXv;Q$XXCx^0IWWyc=l3w zC&Xgk0yx!!!@*Zi8qcdA>-AfBZYACT*nGWS|NH%Vee(m<-vGFv{{#Oecnig2*2@3@ z061k>NoGw=04e|g00;m9hiL!=000010000Q0000000N)_00aO40096103e_P00aO4 z0096103ZMW0056pK*<0A1CmKZK~z`??N&QWTtO5*>pn1osB0sLSSc|Zkad%+Ni>a* zCKi%P1XHB6uuNmG$oAI$0>LJo1_^OvFvKFesEDLVrEJoOuDiPVzVY76%(##1B1z+c zVb08)$2W5x_ln%pe|~{L75|~0o}NTQL&Kx1tE=`vAP|BqNw|If=r1s z2dIei+~wtEwxy-z8>m1bo5})!<0OowAp8<@1BR{VYYh+z3G}xx-tX=0ZP|)9Ecf>I zK7;TF2)5I}U66Bu-*}DJhlhu0-KGH`7K=p+g~CTDytpgpt}}vw&94a8D}czm``1Bq zRu>l+aj?&8IcLnz&(9^F&#OV+$phE!joGpJG!zOY^g||=M@X6&7ECg4&t~vK(%RZ8 zlarH@OeTdl+uq)mwY4?L<#G}Zhiw&a4cP#IKE{TZ3kCojMuWj1pUP9xnPRah-QC@? zyu2)ZeSNaOzpswR$H!%AYD#8jXXWVVNN`JC&EuUgM7<3#MhGBK5k5iD@Z#d4baZsc z%*>2TOiZXTJv}XLZEdo&v?P&8q~ZnW71vTL0CX@vKQCirV`YuH0O@dUZce(ox@2Ktp<*@YplSl3ao%cZXh<2>mCNzbXjF!W zhm{UcNSy@@RwO>2Wj2uA57b{t;_LZ}B zs>veEh7aQf(8Y2rJDpfY%<9$ERT&u>(T^(f>+9=Er@~h;rz;w*%J~=MFO#uP1%hBZ z_Csu36<(o)OAhJ(hDHeRDgBNcf8nidqCxg~Kck#;$L0>cs@J9CI54hse0+R~PWA)B zZ!jL%AnIMyIYRpf;NPNW{M79j0C<4ks$clI_ypmHSaxBCen7RPn^cdE6_=EaP zA3EK9Yv(kPNQ6&MPoJP{VldE-In3wxLo7AO7T`RGIfLBU!KeJfkM6z9KYxM0tVD5F Tex8YH00000NkvXXu0mjfF1zl3 literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-32x32@2x.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-32x32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e66d934cd563683f2b7c1dde1c6cee9d78d938f2 GIT binary patch literal 3031 zcmV;|3n=u7P)BE1ZQLxAY_vg;$?V;oX(lt{{G@3qJ`_O^!KmPq;Qw2}Cn0L9?Tam?khm;cmfj@-EE=vY<#k5o0f^m&*sM6pBI0EL{uRZtU4R5Z z`^X8`V)zNfJ8YaF;u6EP09P2E5Ad?jnUOMxUqvh^s*L``+lU9}BlcPZ2XpY-jyyG} zttleQMaed+noc9ry@UIIwng1*#M^^UkFzj+6yF25_LnMhmf?pGpH<}CbcnAmX|u%; z|7E#p3GpKUUAw%6atiT2#A8)^yd3Cssj4^8z$>TJnP(8+gLrkpJj%xIMEtceUz`ec zeo~wa<_{hK;NmJRuzL3+9o0iVC08s$)vACv7|KExH6jmbd4gn8^0FkllzkRT6fd|P0n zrwSVAXrbt(XrcJk;OyVRDf#{Yw^VBW$m{6QFhZvPBk<6w!tZERBfJcLd1$M3&)TehHS09YRtkN(Z(GDi^F2;vKBX;HY+Q*UEnTqUvgh@A9J5aYSek}G;8j! zqp=F+X_rylpF&#LEj%b>g+0Q~5^H*5N-?MwSLAe5T{?GXqX;9WfX;*4pXw6&V*io?m|<6;W0GAb4f<-njQ>$>Q>9MPr@ zwU=n+0I+*9IKpp9{{0u*d8K7@p9VPmCp-6AwmA>rtOL;dXv;Q$XXCx^0IWWyc=l3w zC&Xgk0yx!!!@*Zi8qcdA>-AfBZYACT*nGWS|NH%Vee(m<-vGFv{{#Oecnig2*2@3@ z061k>NoGw=04e|g00;m9hiL!=000010000Q0000000N)_00aO40096106?Gv00aO4 z0096106+i$003o~V2S_$2S7AODkf)SN!qMP)O0FtyEA5wjkuI1ZkQFF$rw~C6U^s#Ykez zt7}c}J-z0hIrpA(&$&t3yJ0+&Ju`duUbAP<YEU%g0DQT zyuADibkvNLyB3T#(;K>FAsx)h%6emBV&VtX2{}4p0Z>*}_9fi@oNn%#DYKe9=vujX z^X7Db&=E?HkO08=104DFVmu9|heGG~0K(wpF!*moGom*+;%504P7S-bOyqSn3W> zI0T;7)3pU9B_$afHf-oa;^X$=uwW@Jlx(`9@2Ul9D)Tk#@1NJ@*L}gM)*A zt}Ni+iDpbykeFDAZ;r0qE}uru&dy3^W~S`ixl?xR*dYZ4%h-;Ok4txVw{&%N2_m;( z-WuT8?Z>2k7QikB-!=f;-X>A;NNHhVL5*yEeZ3q#dQ?hFOHD_Zv$IpCr>7&H6258nJv^)5tY0gLlg%2~;n90IiZs5qw$^Bht*Uf%bO^sBEVoiq zQzO`Is=xQ_*(2AlUzZanPN-?)LR`?=qV<66?CkA8j|U#d#5sTRj68h!P@X(_A_D^hYP9d(y(=>_GqP*fF2M;>ii?ZY=s$Y&NP*~733>nqf*!yF zQxIuGE*e^SN06YE47z?%QIQ-wcFZWca^;F#x^zjfHcM)1s%+f2Q6=Val9Q`fugb-X z7mfOi890E+{QSJZbCLl7+SN`A07fgKBQ6c1+haSbbbkE!vE00QQ&Lh=Oy@P-oIu>V zbxU~!8!>j&)zzth=)~}F+Gqg?r?FKXx^ZP?rBUV1ojYpeeL>0K2WD8tJwjVso59|_ zdv`=rz!0R#cme=}AKjbtm_!%u?d=WA5N?LBX)#7UFE3Ad4P|;ko&Z2aX$W1I#CQVjyv)#)1zVdHFs0$g_0bszRu{_W?A$kkFCJ+dmO4TNbJU7k< zfqJ5+-Dj~qD?R3{RKYs(-{lK@^=AGJb<@84<0;FCp@nAOay{FmPMPiF~BQDIMQa@V)9rP zZPEgOCC3vF;OTJx{{8B5ghyT`#su<$Q`}o6I7- zZ6TTl5y;lsq6Gl%`R{d(Mxj79moWYoFg7+8$aXbP+3dsaa*6uSb&6ggDG2t|b+Aun~b%b*!@csKkJI`|A_Wh`GS9uZ*puTbkeM*b`67Y^A1 zfC5XF_Xmg2=|TMYHbBThJ}gfPMAI-*uZDf4kT>hCmE~7y>Z_VhETc Z@DF3OtMKQwQ&j)}002ovPDHLkV1g?X!Ug~U literal 0 HcmV?d00001 diff --git a/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-512x512.png b/BLEUnlock/Images.xcassets/AppIcon.appiconset/mac-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..32eb06fcaaa191930a174df37ec5449f97f02fa1 GIT binary patch literal 20870 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 literal 0 HcmV?d00001 diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 07cf05ed..5e1ac220 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.1 + 1.10.2 CFBundleVersion - 721 + 736 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 030eef1b2e1e304ed72f2d685650f914fea9574a Mon Sep 17 00:00:00 2001 From: Skyearn Date: Mon, 7 Jun 2021 13:31:11 +0800 Subject: [PATCH 132/183] Change StatusBar icon to new app icon style. --- .../StatusBarConnected.imageset/app.pdf | Bin 1228 -> 58672 bytes .../unlock-off.pdf | Bin 1125 -> 59834 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf b/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf index 71185105ae7f63b9f2173a484cbaa692f06e1b0a..377bcfaa874e439e67db82e372c1ec7787ebd5a0 100644 GIT binary patch literal 58672 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

lrEsN@3)g@FJ;~spUI`@-I_%VCRsTLV;@i7Ll7G z73mQ^t(Y!K;MJ^!1AzaqG{*bO3-HO$V>F-#P+f(l6ahe(i4OyV1kB`yaB46)`f8B)x8sAG2*!s`hRuKoE;eM32Wtft!^a2nhkxp| zf2ocDhhG}~d!2lx4vZD1m0@IJnAQ~+Cse57=?<)niSSs_Dtx?w!WM~Y-n84*MvRV} z%*P363bK=!Y^1_raohwrU#g~o38h+Qx8lgS6c$Awqbh|C2a3TYTL>P9f$jmL|JUPg zfWxmwy;L4tDM|4n{9nXF1_1vDui^eu=m6kf=Kc>d0)TaO@ZX9b0Icb7 z5QG#&MSa-`t=Z=Mb|dsZ*>U%8a{qAq*uQfN0)Hd8fdKkwM4-|-312GxRFa{v$LsBS zrBNfcDa>}TuM8q`ywNm;(OwgP>ZksTLVt(Gb7^c=8wg?hOQaj5V;i&RdUVIk2(taB*oW|U+Lgq8bK|MKYSrV z=RaZMS9kRDLyPmbt;?Uf>*<|MXdj2;JR@CS4mxM{NwZg zsp}tARJXwYsjIFw{_*+$)b)=ls$1ay)Kym-|M>iW>iS0&)h+OM>Z)_L792&23UPyz zv;GsiwMTEklmE=F|6FhF*$mjfeJ?=@HqcW2K<9bgh)oP&)_cz2ghmrfBuC*;42X#Tg(oB zKzlI3Un4{Y2blk&BwGPu?foIVdX0JjCBlF92abi~5GVv1<&QZIg~LM!f}j+&Uhx+Q zeE*8dQG1q|uh)PxfM%@)oRY44<_7^-3>Jce&!0}h`HxbQEk=t?1db|e{69p1dH*SF ze<=_QLl-%~asp(~{TYFy*#2|VHj7H6aSR1L zO>cB)Y!C?1bZ96Vl|`+AYR_(?;n*+dvl&x|&AVd018;fe?WpEQ#^{CBgA;SuX79SG+x4{E1HEU zFE$ z)nfMBJHC$jf=(GQoSOC@3K2zQCKHdTHGw+jkO(mX5$D0kPlkbnLsEa_dNA^fec*>A zC{tr;gOLh2b;w{!?{HY!51dS(PCs`;A*NZa@o3cGq>oDv4xQ6U;EWyc7&W&I_~r5! z3;g`bQ2&SW5G5IuS5kU>$X5f(mTXm8Q*fiZ}IOgUQZw2o*_Mc8^ng9pKf4kCmrKo~Z% zzcP&6#~vv&VrY!mp^;I&`(Wb-Iw>TXnQ05BMO%b&9L3m=!%i3$9;1%tn44q!36Pcq zEmuhE+ZV>;M#2(>5|)V!XGW$N+2IzM-Yy>^N{|eNaD5{Cqi{BrQ4p7^VQKmdme6oQ zof6SJo-3urX`{^)IhrT7!$rOA3VJl19w#vh1_@%&mI-P7; zI8Fye_m#!jR0&qN874C(nI(gG1DYF$_Qi6|FnJO;6`p9vCEF86$Ycn!9N(XSRt)>0 z!PFU+{=u9Bei)dO--QLn7hq4}@I>eb4(^jdW3a#s{eun!x`|3-bvPBq&qj{L;UNF3 z3P=WtI10NV7*TUi#;40H0-n5PT`pLzx6E5G?_QCa#=n4iyFH-$|JiErMK8o6;* zd^LQ*!67TsfNtz#=2++oMUUc1=9ES65}uT9 zx>X*&>DQa752kjG_x50ApLa7K>}KFnCJe?DOYSX42*+gXJMnCRqvEHPqh>w6wqg65 zCVh*rGS0`FZt;Ri+m}rjYM7<#LHt#&@SW+Ntaxr}hpXAA`j$3gxbL)S+ThX4r;1Lm zT3_Xy?0v4$)$9|Y@QT5AeJ2`D*xqe?QgCUPEfr?sjUZ+YcXY{Rl)Ftaba11)pm0H( zBc%l!=O>)%9D5-*=G}?Sxm8ok|9IPVe8%~oPA#%NkJ_h7IHkU~B?jLpdPUo4Pgk3V zth+lQiu~wUhhGji9=o<};l-t-Ys))ZV8<^fJUO$yUazXDtIbRG(47WDOfwes!Zz;m zeC>+Tp0`-%rnb3Lnr)goG@aHWYwYr+ZxNoZ$ogd;wd>Cx8eVV3iUwKDyZ)Kq{*N-; zlG#POi^!L`zqH70@Eh!8_La2yv2Cg!^mJADIySsp@3$5~2g_zAUKl^?s%lIZ>%W_!D(&sx>fQ+Z8d?(PbC zsJu0@dw7%4&ES9b-}?3te-EMC(GSD6oy|LlKwsE8TaaAVJ0|An!0Bm4F~^hAb`KhS z@uO8Rm{rmI?)(XddmxV|W=*dCuy*!P(p&tnP6r!}*;;x0a?H!@b4j!A^_*VRa75S5 zi-MC2M)n?&S2X(4l>*f9C-n!9MRyA-&b1@om38j^ORt?#ZSx17i#=a~&z&%Qd+EJr zo#st47PMJ{YP=})K$8>MPx4tABOeq!MwFEIdEKz3nE9|tROQFYH$w+Dt~_)LmmhJA z!t5ASe2?6--eJ5S6S=3W&VLPHbsh!Z5rOId`!<6 z@>=sDllyjm$JwhNVuK#j`W$~z-RSkPk%u2$9#U_~AZ>#&AEHTbiVs5Td*UBn+}jx` zDQ+B8Rk7}hp}PC~So>J_W9(HK?WdSm&qF&j+CKX3>6;tg-=Fi6Xs4QjZaw6ktGB&! zV#_1WMeh6{sLEyzZs_*>_>2BVz2li4X4_n^&4) zL%Z|z*(EbqjNxcEmsY5^+)N*GQI^;3Pw3C&-laQm>GZr-BTwB(T;x4DqdtAhpwbGG z{7ox&!&}j}>aSjwv8r!TlcB+$_Z{XL)9zepGwOZk1;(^jC%4D)f(PVQJZ5i>$r_ag zdscD3L68|5^t@u-a(mj99#`l73H2?jcvGK0DYNtZac|!qtk1A^o?k!hW@Etr)rTiDN?QG5*tV#8-_xv{G4DTQSQC!Vs$Na^%`tW~-%1$A z9FiC`;?=nV`k{5#d7CYr*~e3E*K=m|>a=;a8|`TylDb#Xc=zI7jx8tY$z3R2k~;;> zzL+Ov6||GL-qB!b#Y5VJ>6b;*W?hXaxH_YLQz6G9nKA3?*@v52oju#Bq<7NBv-G$@ zCw7Lm2?~l%SUtAa5!lK*JL?UoKHDOq^zDQPo!z}pM!d^07IX?4&uhH*-P(2!{%qW` zS{PNay;+k?g*m1$DQflYLEZ0;E32~4+kN5c^@ryb=IVr}olDNa(??cIPe<*#WLf;M zXTsC7ZJ$6VE85=6%FcUwWl+rdva-p!L8huph2uAZ6iZQN*_{eg6*hfYRyJ?L<%`m7 z2~lt;qvD>yH3KuL*Qk9l?us7c>33WAx%qfn`ti)@X%`mG>|T)capcv8?ysR7|JL;7b)nGbh|J-aKZRIuc&=c52}PG9f1CSh{`PmDS--DuSDgnNj^Qd2K7g zpKoU&Zf{tcC%h8#lS+MAnr)dj5?=Urmfgok#Dq^Bxuc+S|A)(61D@h$?{Ctr`mk)n zy&*-P|8#r69RNIzc?|7lC zRcZ%pRYwFZX7|ta-gR!rXfWa-)O`l__HT=O>Hnmc(90{FW94bTP0X^wFX;NrYjd?E zb3)?JWjT^Z53nO!{8Gj_`(*jX7O0qmCtKRf-jitE6ltn!v>Ovw^t#up@KwaEvoAs} z&2kj19UD{GGh^59(XnX-^^aYi_0(Ks9J}^q#H}bw+15!M$<;~IvZ~sSs@m}M-L=p* zjspCY!aJN^#BSWGgPmGMmsMOnBY9?T-T6sIVa^$7z<{(N85K|GU&$R+$V*Ru(0VpA zEmGBYN(Lv*a3()1varJ&CPRhRj6oaHH_c5 zta3QvQD(*Py`u_l*4LIjY_oJh(W#_XsV4~!+t@p79Fy=Ull;20bFiuce>WUg@gO5< zRU`HEm|GQHA3l96_$6v<@5}oIY1H9W{RVB)?HLc55Z7jB*8eTO>FaBuqx5TAd7#}<&?7%J>I%|>#cTDoj?5Nx4J7V4x^-&iUWNm4hyR|IT zGxtnd+NqKC-`V<|>)Gn!v1dmbym}cw_SGM;6GNXprzBl&n$6ahi_Q+;F%R)qT|1Y(NE^4MIQ0F|fvFuk z(LE*ad(6#wxwI%K?`$Y~L%Q(r2WMqL`N6UMrmfply^u4Kb@xHC^j2Hhn4d1Ly01Jq zk2Qxg<4Rrw;o!};WBT29^L-Pi4X)Vu+Yo5~;&Jq*LEMT?2O7y7+mFHvVldbG4103MGPSF$(=p4$_Q4Zo&E7O5 zYq6~7=&=0_7WW+8?pk(mNKi$~w?R|3za6lpGcmYgde3?rw`cs`DCpFchxNxi)NN~8 z7+oe;*9*Q|i)59dFB*+6EyR{~4a1>H$`{u1dx9g>J= z&blUQG`M3T#NI0&*67B9%2k`oFOIpet5?Xqp|Q}BKBF~37qecAm^YgbFqh?A6Exkq zaAoIT*SzS+$j;1INb7YJ;xc!pncT0z_n6tZKC&Jcc z)8!pYJHnkP#Dz()Mz>h0=cmott*e*6yIGz%_f*KV5ic*d={^4Kp^WP*x``)g1S^_6 zlP+96WGlK$-}1;CQU;;4x_-+b!}KQGf3Ph4bXVMu7G4Ov{~e5fHJJE$;pLy}{+nfF z?fQ*wQ+R9V-r(Je$^WM0yXiW*X7yOBz}l4epYj3PS0scvr{&`&)l>%Y>&pVeBTCPfBF1D-S@s!NPc>K zbpDGj)O(8_%{sVHd0I4U!=Q(RdkLxhw(C~}2fz3w{IzRe*%SWSrW5=2RZK<9jg3D4 z?BbDhf9liSb2}FG$uTTJhKE(`Io);h-9>w!TtD$^o70xw*tPo)@|fkT zs@5MNpss%03m-htWZP^=Keh)_4L(R-P})io)%DKXm)nL_4>(9$eV*`pYivQ!nUlup z^ZV!a&q_RtuBysdcckxts+AvpZ|v9!jn+;+=gWLs*yrl+!w*i6&v|cN zWG~Jy9C___+xO-$Xw99e+Fr!p4@p85FGJ_cXP0feZhhNlzb|ok=)U`}wc#(|Z2UxX z$;OOn8*x9uefLvt4vnOFszS~!34Ky=UHS5Tqj<-ztSaT4o2$Dx^Y1^X`mldT1sfHC zoxM$>e6bdG^;hAAv(3(BIP0w#xHW9-$h7h)6|YA+k6g$cE`1yu+W6VvCuIYsZzGjV zjPH^i7PGBck5IZHazjOE)kN;RXIbZm6^h=hI{EnEvNOG7?o7Ho_sY`KkujDD>7|vq zH<6d~@S6)0pZy6fe%g8U$oV5P6jfZu^sc?&7pwb4P*=R3UYJLmym9XmW>weFgXAz> z&+!$n1RtK1Xl|ypk!h*)WJTQXi!&VER;=ROtj{S+UHkZapVxIdR~%`o-ns6Y4x*RnH*wONH<^D$bA!c6}8IF z-x;}wcHwn;o2Uh|!jMJJiq|YE%o!3FfAr1v{MCnVym(yvY_+a%9;{C~Db&|52G^_E zy=r4_9%<7`_@%Q_aCKETubdKUom$WZC7?-b64x5dyKb+R4!;{m; zDlz$qYbyGUNZZxnNZjHL_22g*ZEyc9c1*>lHt$bf$-rnm)vn?JbN022so1s#H}wYM zRZ}*R*Z$>_)s>TTrk#qw_f4PM=m_Jtqf@IYW9V(#hvj8uwLU`MBT6evbrtu_dA0Ls zWutt;LsRi<3uSw|x3G)~PZQq^&rJRJ%5FKTwxhUX1{HUjplVZi*0DA-_->C!uX$A7 zaoN+?)lt4?XTmMj{fgU9Z&6aD$|1)tN35*6nM39eFDS0Accg(Pb3?VExb^%qBg<$o zUz1<@UAR}>(%1O1(w*Mh*XVM8cQvuN`Er{(y_>HQcQ-5sX^ctV)@Ie%PO+uW1jyS5PMBber9JV!`X1+0F(>r`{jqn6YL>ek;c1jiFaN?0C9o+u~#SZLtDs)UfZ|3kZ3l;)=%*}iXFV;pA$YRD+(H)(R)Y~*CAMR@)Y?NaG0=U|4 zjkUyH=(}{*{W0?y>K&`-e-=~Q3R(c^=fN^suaf>b|G>uvq<>c2x)*~r z#*A4wz#X%K_%@a>8p=qUe1EL)#q;!5M;i1ydA-;8`}H0!fB1aEwETWQZ!Ia(w*hT3 z`F_<;piOi~DphaRYzPKX*B_rZ^tCvV)1+)qy(5i!TXs|nn)s%BX6`?p7BORZpW!oj z(MQG)?{nXmSGCDi+&$+N;%KFK+WkJa7p0XwaM%Cs{o9NilY91!e@m%OD{l#msR6n0 zxwklE=fc*nZzck3+eF6g$=J6VH&u)uoS`)33m(c;lS-}=iwC7fH=E7lMda+Bz-)bk z_j`WAnhL^*=wDa6r_OD^KSQ2#@>FuSv{LVHG^j(Lp&=O|M~S=+tNV~PXT-0`oH~2p z5?(pB7+-b!{k6(H-H*)7l8$>kZ(F0NS(*Je&B~%T_w}+s4YNZxliECfnLK3_Ysae4 zLiLIsn=?MF!WD|!BQw$*J)-7jbbCZD5ruDu4B;irK-i zjEzl?%%7EwEINO6!paNeX+(2Ol!-h`b7kq^AsP8m#qtmP2AvBX-O3kkaJFNW53K6s zgAdKr4Y&|0pE>+e{K6BX*4T>G3qQavZ`i0UPIk$sU-U}z-Qysc6>Y8-ThTZQD9F`^ppF!mp++Hy)egzNxBqP5}KDyhM-Wka&yYyI< zy_Lc#qg;_+Z6?1ZL_h7Inek9`_2@8rbWTc>2y)o7h{k0U%i19~e2bXTIfwGwI`<(i zgq8=bSVKeX9Sj^j#Hh*-(6^+92%)F+<__C^gN8g8Eubw$`zALSN$*2%jmk7~?ssZ)N!nfy<7B#l%CNB0ZdcHUR z)`OIX6Q3NqJa$|=SGPCN(~5h&QWU6&+?xl~JKu*-8fSLwm0AQ7-i=yxHj6TO#_o;h;eO8SkY zOE*@ZJmTnLTJsWm7JE5zV7o07sv606HA*T`%WT&MZamssb*iGUGSVA+Y|AnE(V%++ zP0F+(dF3Y5>Ye$J2&+gdo|R^A?*y}2)@g&+>Q05^30 zx)MzKE`5Ql*u1`F(Joidl*64iSIAYTXX2iAxoS8tl~utsE1uKOzP!U(&$X8vi_JW6 zU7fN$rYdLhM8mcAYbkHbvVs}}H5?u{_6Kv-I{S*XZ}c%})R#B<)z#HiRaKRhl@%2g zA3uI9FE1}ED=RH6Eh#Da@ZrPz_wS2~iwg@23knL}y?gie?b|nR-n@SO`qisfFJHcV z@#4kv=g;%=^PfI_`sB%z$B!RBdi3bw!-o$ZJoxj^KlAeP?%%(E@7}$;ckkZ0bLaN$ z+qt>9w{G3KdGqEUfBfvuDqq zK7IPssZ%FToH%~`__1Thva+&{9zA;G$dSW`4<9;o=)i#k`}gn9%*@=kZ{Oa%d-v?w zvuoF`ojZ5#*s){#_U+rYZQHta>uILrJbCiOi4!MGm@t0)__1Thrl+T;rKOD?J$mHGkv^Z#>-B=W zi(M|4)9G|L9I2_PR;$%wv6xIIqtOTsWrOnqI-L&OtEyJ3RVtNIsZ=NwBSwr!N=h0& ze0XAF;;>=EtlS-xW@$nLgL@XAIL?WS3C=dwvd_IrI<8rwi4u{QVvsf%9 zlgVH(Xfzs?N~KUJ;CMUU2iz0D{Pnj0B8WXO<+hzM|(G9Hh|;c!?i7K6cn zNk0mOLL!j}1OkMVLJ$-l9v&7J27|%+_U#)K6!g78`8T?1*D^J)`-2fRd(8gV`va<~ zoNpXv-q z{Wo>`g{_tDf3MCuf)JUaV68uf86t5YuKzkUBSa z@dApPH8m~sd5;}ZDFSpBYYkl;A?rx-?*aw*;0j!@`5la`-CF;ouE6hFeGr-co5#T2 zV&I1Lnhkd%6Kv<#lKRsTU@_}A7O=JjA-{UF#ug9mG6c88fj|Hd%n-PB?NbU80|`Nl zh1vmb{}u%GSBGN>UmRn<-h5G5z%Ig2AgrQQzkap# zMg)InEdzuDfh6H@P>9IHCwY zQnCMzJCOhRin?$Rt?JJR-~r+Nx3K{<5TOBKAzbj|{nMVzn1TSeeEQ2M%r0<_ol*zJKp4SPheKTtck zHV`|wCeS)K5J=q$2#!etyMr1aBe>ZQL{j)ACNdGoB$88aRCB>qa2g;zHjs)+3Lpmj z0zmbLf%psKk^-+BKzxS-unp?-YZUBe*X05v2L2l`^vSP_o=u$sqMj3hl88)z3F1FY z3Y!5CekH9xPpmOn{ZSbKT_CaHm|!H%|Mlsq20;u22$Tpz;t0XuvSmUr@Q=Z0Bv7Xx z<%?RtM!Uux5$x|x{wz!qa1s7seo!?U4KBJ5eAna%okopK0)p%TUr2uv5}m|W*g)h; zpv^=og}>Pm{v1M$UO+sK##oyaKn4*60d)i<_d7{Yf&aUv0Vx1akVXQj7@3 z!)vpE+Sv*(=sUGl6OwpVjah8*yUB>)FkCnsfj}d0C^Q%j&=?>oPzn}@fMbvt6pDbv z0&5G$;&E6M4xHx1B2j4I?cw-f0uEX8k_!$ZYta6#TEmi`DMWy^fH;^yZT`AGF@f_N zpFe!2=05I-S{ZOjbemrNrQHEMJ~;3d5HlRe5bR6VQhyTG zYdq;zBiQ=}-J5r9-mE#IX}$W0tHef>jmKIJ8a$?62tT-fZ+K9nkWpJNl{JTtYmVR_ zZyFLCJMpJmu?-unTSclL&mSAq=VwJwk6ZQXH-?{R)T!->1}VqJw_O!Hq1oLL6I$Ge zozQVuaKokpMl|WSaZFSEj=@I$7vp%-egk5ghFFg^ZS-?&YvG6n?aZ^swQIej zUc2FA#Wj|>H6L_%Oydatu|}lQ_B+NkA9OCZY4^or zTcgK}Z}(zR|E$br>-F@b_FKcp*Ath@vf`@+6`{xaym`>7Y0+y}7Wz{ElkK%vXFV9& zpz=ch+*69I`TaCSC#wrI*v!29=ZnIw>|S&^dw0)y*u?bPBX={G9nIq0F0MR-xwhUg zcP0N0!&jfh8}q0OljnIbH0@b#rW@_+|GZiCBuntEzEQ`k@hf%jvo!l>#+WYWBu5-I zEuCpOv|ise;X3;5z%rAib$`RsnK#hcL0$4kG#I!scx)H3HMSXL0I|WK9mg6D83A;J+4pu_TA1C&8M#dVaa&eH@5(vO$dRfp31@c?t+GZn zS+&xF?tYUYk2;k5qU^(?%)EDD%X%Gcd3Gk8vVcBi;KNqB8c>B8JS z_FHLX7v^o8P?n58Gitry=}Xl9%BM>&&B0Nab)??*Y|QCmlWMw`qn-$xPHj| zKO6VyJEC4ke%l5;cAOeX&l{41d$1!fyK0r?gyM0@5So{H zx~H?(Et|`0|KQ%T4HH)K=1t*DvS0_N=MS99K6k%QyF-&O9ill;M-`7+cJ%GS81_hm zw4#&t_R%cU?wEx~+)>v%AfH5yA6hVUf8L#jcdReARq`q>fOi=Y$dmh$(mSNFpFPc` zpDbv(?Rj}z+tI*~T4k83m#%lOhprJ`{d$y>dic6kwp{ixV@70?pl#1);7%8uU3{oZ zhqJwkUdvx^tDH4;&GW>Mz0Om{-QQdNJo^2HDkm=FfT9a#q&{clvI;LNI;IR&cEq%Q z?|FFBUR>toxYz0Cvb+w+AU%qYv9}mFImJTK;t8*|hHQhjq8t=L}yuK`-ySQn#V8|W_yD|LAvIj?3lzPWGK33k={xI2YI^YSK~y!@{6%%Sea^!FQ* z%9mBhK2(03q3gD9bcy*)wzf;jJ!{L0_eY+H8J$F`e4RAw=zYSgHbIvZ7VLh?mC)vS zMSHvd^rC~ldSzwlvNxWRKhg~LgNt;flU{qpOL+y*dk7QvYU869ohc7y=)0M{`PZLz z8NBww_MDjRp5TUE#jy?Bb@H_-7(elT*vX=NRKLV)HvRy4<>g(6w7OxWm-mj*RKO_xxkBZ{4zr4eQSx&C)(Ua3(T#V{Xx?fmvxct3q*kC4<+-K5$(h(YEi= zJY2U5ONWsD>Ju5iAIdG(S?>}z<`xdV9&N^+?Xhs;8_VXTX6*;KDp|8*cUF?jz${y8 zFSZcOw0F4w(Ui`9Hhfu#;&@dFxAD!A$_rWRProzm82J0g z_N^OuH}lfKtf>?3!qRUKCP$kVKDyhmVE1|J=w%nS6XT|2U?)chOPaMfa`tBFp;FGh zz16ELdnKKM($Y6o;3t+Z(3P${K7ad!iT6ily`IotH)2CfrRikG#dFJML_$G>a`W>_ zX63cxTpm1XZ{EU;%y;*Y6*F10)|Z#xJTmviqn@uHh5yl{+*2J}T3+(5V0%&OtZ^W9 zOW}QbWD5712)TqEGT?Tf{x7@{&m%(+IV;z6xXg>0%3xHjh+bf|pXhTWqeD#Z1Jd&S zC7Tb1mY2?Y-rYFp$k`IRWn)FxZEe?$n9%a-h$e$`){a6p-Ti7{)W@?wnXf1A?r_;# zjW4Nux#s4^W6db5fREfUrUmKeu`L~K$2Xe*uit)pu&>q9<-N0LUSD2>An%vu8%nmG z)9>TI9jd*Z{bn>QDm&hnrdeCHM_&AU-iHfN{`lBrTjNPfFW<-vxo0f6ky)DcV8!z- zF`P+;%#S@by@9d{&QV5hDH)zs*e$);yA2r|_17QwN;1MC@JupL$Uiq_IhM+H=ijQRc`*U?dYiYS#RAREX&3{KZ`2emN%yq!#D36bt>}` zem=z=ZJ zZd*=oFQ_b^foat>jagtQzMjc7@586;W1IK+QuYCXSG3C`9m;K=btdX#8)OdsW4$pi z%T`v-&(FpzA-{ckdx7Fkc+cZ~dtd2)GPC8)KHk>H^Y2x1_7PUToYV47Rlk-|9~Y*F z<5bDE{wW^uv88L?%zSgUe1s&dm9R#w%XjccDRVwFGZGmm#7v~mb1V||C7i0f1O_B&85j6ra2tUXK@&Y!+K zW9rJ}r7N}TGLG1h^_khBW!FP)t*iR!KF{2F?%hM4ed*7u_LWDy$Ng~^(}Mhnw~W}K zbvIw-+KSQ@OO9?$i`jAAe(P#cuhT~rT_n{W>BEXhjypTe+j{g*ZKDT2SXNcRzFyIP zQ?s)E?b=Lz(1As(m^^n=bxF7D2hzta>Hf!=-z_s&zMQnBQOlnd^;)*A*C1r$vG!&3 z1N(o47njm^nvkPF=QLSnHkf9;b@k(bybrerEMAf}zx<-@P}{Ic?#_$fcHfs?vTTbk zdzPYA%O^WlPhNJU;7J3;%tu+}_lH*P>s)*uYIKRd6SzpkzDk`v>fn=%JK0bFsOsnG zFll&7+NLsQdFbh4<{$f~v}Wg)MO@=%uH!pUP;O=*|T|9g}#9e9Wos1=Y{i{{Gg~cv197-MoZ? zwKsN8nzylfKw--}?|H}jcE`4i;;yVR%s=HjnpeKhoqDM(niZAzb~~tO^m26XBa~YvbzMIWt?YJxmEpPk;KFUc8ZhTvgHGLXh9zEHE8Rrd zvf%m8v>79NtUcqL-nC7X@5y?xYCLTdZEi9*dlTWwPL z3im)t{)}Dco$spOm8F+HOfQYZ*~k#i#@T$R{T=IcPR@#*tLyD6Ia$17!=RSEe46}Ug&mehOj>z2ajC5RftTCihgD7N zugi9gl>8iBY<(SeT;DT%=9_DGFW~c7ZAgb(iQ?D1xgzB>t4BWEhE+e@J$!wa#8Y?3 z+fn}yjzDq0akzL|M=3!S@$yi|6{6R8=2dF{tz6J6R~<8uwv}0UT$O&bs&~T|jTneT zv5XmezKo#8&gC;N@d2tZJYxsrOmOO$S&*a8>&GhNP+OVKA~CfZMlOV&Yr8Ui zcI;pcIQw%)>OXv*8_6ngiC}mi&w|2iRj(%_TEZnJ5bkw6K+J7r;%?x^k8P#tX zinq5cccMBH?Z1_wck#NFb@WT=J<&v34MViUFQs?GS5xTGi;+4ycCZZ(7qJZ=q1=gz zYe82XTYJ8Q?D;Y_P}b2&7zYIN$UIb&TNoarawjSSL^@ZMl4tIjGO`p$jE5e_m}SMW zs~@8Z!z;W7mC5jwCnPDYUJxt_Hxh>g$xRh5Y!z!mXP8Jw3?2<9Uk$?5IO{rQS;WQ- zH4}>k&7&iUtuUPpkfE+aFLrIDjKe@VBwUl$Anex4)hI<2iL_Hy~xJ-Z;iP(m>2$kV! znwCm~Vcg|9az{(MTYwPB$uwG8Q|BkzhE93GbXWbGH}q|T@CzF63QZ)H zWqv{9t>O}b4~+(4Cr0+$RR#n2?mDdxYQ>OT>YBj}N0cmM>pJxOg2p+dTZLFmLVU!M zp*AbQ@cE!DCc`4Upf+gekt9mP0@pjug2o>fp9rL*V!OKRR5lAEs&RHGx66w(EEsx^ zjA)t6ExWeY1&wRcL<=N88iWl=bZmA_f()Uz5mkmqLm2 zhtRW$LXDHi#m`p5&B9;~nWce;9vf?Tr5cvcYSJn0iQUpg&Ha;-&}25#F7c757%7gq zbT+L}B3-b+O!wo{JS~XF{uC2$uviQXx5Bjpl#&v-h^^Zo9B791;j6UTNdf zaM$Wws8c3IB2_aJ4R^6DVk7LaU0p9C3|mAn%q!M>ZX|X|nl9F^L(fbZayrwb!o{n8 zZ-gjDH4HCTJ)a92A2FcH$s%2e-3WEOt6pVftfSN7d%+C7^nGZlNEwH*LR^H6a!9y5 zwmL(PWlf_nk+uyd4G%R+5k*SVAV3bT;i}bH*P)kLF<$Y8LO>6O?VxzX+3KUvtHW;B zBFGTB9ytAk6fg3{p`fpbE3re5)B?HcR=Zlz_``xua=H?`Bp0BojyuW6BpGb(sznV9 zh=!ir7c{=W%`cz2H4+>1VWR0QIp^Wfs!#SpUE3eUp!&s+9LAJL~ zQUe2`;;K^eb9I4$r-$1EiCCs;x|qT~r+z5HoXeXQ0?6vtc)-=$4ph`U9?UIjuAfZK*;x z>;~01w7QHeMmMNklDHOCM@uKR;rH0$@B~VV*RWw5t+x$gDn$;WtaK#yZ0OzR0?a0A z^~QpvTGs8#>V~AY$ho@Wwf3!)5*wU5skN8;WHA>=CWXXsdeB&M^S)We5;T^)l%*_V zB!;SE=smtqn`gg8B^(aM#FiiB^X>`%)H{c*+3(BnhbF)8%=BX#zT+L}OL$GTZ)0vv zI}*PIZ^HD8vB~fa(=rX|Cx%~HmfPJ<|97;+f1D*%&yUNAPX`_Q!DTmN{?@zU5Ej(Bv6Ip=6Wc6MVm=Tb?!d zw`DsMuAV`-Yc@C6njga_)KhqC7rtLQH_O;>dvhG`Wf0~T_Vo1Kk>nYMKbNy~If3xu zloqYP}b395JY6#91C^9Qo?l> z*6zAtng#}vY<)wjj-0-_cwaZ9PVCJA9y4|#RmadHORiY1xw1tsNe1G9*#NUErUgtfdbHn%&p&PcPO2w`!e;gKG zlvka3%p{o!rU4YYgktH2ZLITy?VzKj)r3ehcIEVg4XrK>8PCZJhCu_fp%;S~=z8i{ zMsqWBLF^@`X#E`1piYG`X9i4Y!sSEoB{5K%;fk(ju&fRovUOeN>*|?!aDjR5!-Nx zNcNuZs^p=UfW-~Z;cP_BE_}43KXuir4aZyBNsG2CSn_&U`VLrctX|D#F;>)$r_ao)TrbNo$BHrXek<% zycQweF8O%-l*lS@dHJnOcO+sP{?Lp{4qHbh-+?HL*-LK8cMrD9HxjW8A6u%6A5qrP zNse3c(0e^F?#oeeUtPr778GX52|WwS;o>2f3P$mc6}1sjC-=MIu7(B=Yk%e(%Q7Dm=>maSAKNU*HC5>mH*U^HGwTJwEFivb$v5vq=~ui)Y7q+!8W=%gPz zFt)2ZEDS}ZfwVZWvxVO1Q+{hcS&7@O!QrrKVd{#e0#ulFy|^PV-K`-7nMyhlXpz}1 zNEB;qr%RZ$w*_`SE9(e*Y!}j`85?>N*vDZZG&w`84_Uqk#V&Nkv|hZhtGWTfh7wmHC^q;{bbHtVMBYR+P|>(s^CH3Jf= z>&A;A#>0{$#$^Zx##Q9);vSsH#ao%Bfh#(6itG9(#cwhr#c8q!6u($tCXO-|Oq{eG zAU<oy zFC{rKwkSPLn1)_4ml8ie);UL6|cu5fA zMG?KmbuCxbwOn1R0nuwCqSrXRN~sX5R;FW8q>fPwJ(P!XG)gDg8dY>NZ*iRdq-`H&Rt|O=EJ5R0O zZG8(eM!sdWi;-_x^I{~A@lKPD8lTp=Kmi#ZlmuyYZX_-%lu?mFl25z+&*F5Na~g!r zS|!rl0>eo5*sc~43=5x|>fA`|kz&^qh#R|{G`yr~5VmS{&LBhRxS=4?Y+)QxND>yv zu#v&?^pqD2W=38xom0AD*E-|St|FFG*e!L9`LIocg&1*>14q}3=mC|ek{+#} z_C{Gl?>>n}Q+D5Ttx1*9yrr`&gHRQwx~x<%)rG{Ew#-#+8HowK)eOBFW_lxrfoW)1 ze8h|BHSU>+UgL786>qUhupoucscP7Z;fC^{u>qPW>Ezp;%cOm*>nWAnCZxi_{BdW7C)(@WCTeGu>L zWtmroeylRD%uCeDbTjjECo26|k*)Jn<1LGm<6_jZ>Jn6;7o(ZNI(lv~Qpa2eRYM$X zSA!K1;wMD39i*ck9Z|y;oCnB&N)s(Gk(!B)jlx~TyE?Zqp!EtCMy#td1KR zLd6SXX(dTOhSo0CB{mXsRl6=nK^I}aR-wm`VGE|L5TzFl_A~7|T#MCe=6@dN2bW81`DKIZ$&$2f*hZ;A)AlB{ z>UozYx;3i8@U%qdWgx`c5vEMlygDzVdu)>_+g7I2k5%TCNkCQ^FxAT3kCh8qsbj{> zyc%W8f`nena)`3ZLg+PI+m&%>7$bFTHMmx020eVs5_cLgw&A76c|%32%uAO@<=pO- zqd8z2lP>|4xb_5btzaSrRg_(a9^<1ykRZb?B7R09O~oFkt#Q44uA~UPL>l4~jjzOZ z^{NqiteirpZX~6DOPDSp+6>m|3sb13;r&5_aA3tWDnYV&h7m%>cJ(48NqX!0~a_{VRmkEf{rTwkAj5fk#EB*{OP7gBu}xAbSQgx{8)tNQrYu*oGo-#GiXWz3d# z{cTh!dKB(~p!&MUe%ynF-ojn8F#a_6>AN$TE!9sl?6@ajS8iFd{aWT9~HmZ5JkM zVPT^D!Yo~!U1(myPi;109X2w)lrUgJOm;rd7GmBz+*FO2SM<^j#y>I4lMbjllB)yE zH=yc>+C1!SWc-V@YdtD%MYGm(99oZ?D-EbRl9f&C9}PI#uHj>>6=-s3c#-Ccu%Q#B zru719WUv!u=j91iN0P!+r1--6YD^xP&=iv=O4{(Frn4MoU9_2T5~_|Klg>H`RTU5t zs*aFO%SKZ8lJyX(j{cKwIuC_t$c%is)z4gVoEysFt$Rtp0qT{s~WQqk}imcy*GDAKHl z-YVM6EF23aoskvD3=&by9-Wr;akR-C;Y-GVBJZ=8YVAvwO%J{PNjIHqAe(YkY)A_2 zhTds&ALPp&MU?A6eB6#}8@h|DGi#P3kzO_~!tNXeB=k6j?v%1(dvr4LdUQ#TS>`-? zALHlI#}M=Aj(Z~WM;C95N9XISR#H$k9(v^Y3B68Xz~0Y8@2-U24dU=>(7i$S*H=8D zl*NoOFeuDX7Qtn`(xp_&11>Wt6)we9rMoIk2bGu5OQhy%B7z`}*>#+CZGT3#7jF>s zNM;b*lh9X2aS6RoPN7qE3>M9&TDA%+^g1L(uNBGeM0{R?MZJSgX3Qf##8id}MY^(z zaMo>c=2@2Y5gN-Rx2@6Zdqva~AsgR=^~(37n9=aY{~ULZMxfN9vvtt)zOx{{pPcUJ4Nhq)KcR`^Cgx#cOU7bVDfz6zRy3MZ+_<>9 z{9r!|qoq~tiD&JE=m#sSsF)DL!fdc{JPTUTp;kqyUYH@*1-BwPGpKYVY7M_BQ>eA{ zI!BWQvR&Rjy+kb7a%-4+#XDp+TH1$Y^+Z^=(b9$#&co-?MIMHXpCz17Ns{PLE9ikF zt)W-zP|^yT)f-D9TJdVTHO&9lnwch}7QDw-ev|GcADVWFrwU*mV_f8xt71cjP<2F4 zo7+REI*M#0hEpP<+}|B0hI$Zq2RAKl>>e03`oh->p z{ysjkn0bO#v&PH8x-`h_iKRV}8z%H(3kwvBp;S;QN0p@&DO!yW&Z=yXiiVd@7E?9q zGTY^OY4rGvO1^_|Mk8ia^3d}rmy6twTXI`fVfYtBSJY~yfQuX92ANu(ztkD=#<55l_W`G9*AD!8x&`Mk+CTgu?@Fds0x9hs|ur)Q@ptj zy51pOQAct^BQZ7pXow!*hMbL{14IG=9VQ(Jc(Z-^AO7R<7{_rOK_h4!LE{MWU?>XH z0dhDTj?iER>J8Pt)SWY6!g?s{5|U4Re1+zhBkGH>5DbcuPx_1`C8S zA|tXxOf;&~a?;ea9C|gj!R@c}qFcpw6!V@(m|7Kx73MkPBuuSf>)=@ti}WnS5qhB) zdZ8D3p@*2b4hzGl>XW=YRNHQ0ljyiGw31??QPIRiWAdTcg{QPcOKIl?MZ@oMHKjBT zEr~)mtjBVf>&J9y$b_D$?@-t8B$-Jl5sFj-w?R+Ix_ zvLjt;Do=j3Fy~N_u80SQYu-NrVs)jv!zsGh%yn<*`G(L7J;v=r&rXD1=!IVEg*maX z7GqLcx(sDiC`vQqu(X*SEzwF+-LfE(x$4XUeX6Yuw|Cca)cjyWF9QN)Hc?rCy6M#L z=o;Jtj;|=u?P_EOl{8AMaTqlpeUv2;mbIdZw7R%GLoM_~I(|98&A zgmL@24!zQc5_$`D!9t=~LeG?GK|p_TG+6fyVt1#?Xev-9(%-fP=0neIU!_u`2xna< zDoCr#Fc&7qCbho7;epL!Z${Q)OiF@Y)LPQw5_*q0^j0NCYK@gTdBtU7Z^j~;wSHA^ z=2fR+q}F<&fibI2)5`S5s#5Z?GWxs)Yu1!OGPzR2)UguT!fvgF)d8FNR*^!fBZ{T2 zL+_^?Ds|n;A&p~x$tKlW#o3Y4<|ayG8Sr$&G@Objwf?xbp$_*5(0i`-2-GuFdQL` ze}ETqHi9cT8^O?3=M%Y__ zcp?$DVWmr<>ZBEVr>T|cId*fBj63u)4j4+^=ulE)VOSC3&ct?77N->-7EWvoMFW;> zsN}N+%48Nwrx+Q5836`p00R>MFc=&fk%=a=={Cm)6aWKDJS0p;Oe``alnH~8I4leY zg^?J?Q6L6k6iGS77*jUT0HnCRyh`<$7`*=ATHWofN!eKl))=4N=%z0Zf#?nG^~z-6 zgLKmCAZGkhkAq+~-Xv&Ic9ylO_o!RQI<7qIfl%p@k{eU=N$;%crO7R9nnW+dpsELB z1YDW>cDrWJ@4J57J98;V@1-hTQ=Rd9=j5^8|aP0gjhASx6J6q zC995w#Yh~JE-1!7prdjdC$#s6*cqtSKf8=D>4F*A4D;F^x8I1q$c=ReKlf73PaWLredy-aRhDCkBVdcoHHAZswqj#J_(;q;#&yCL-n(9X!Bq8?IY|&CjN@&^eV|fZE{mk zJF(VI9(MlA((zBPqGwRGbG~;vbFQ8je5Q6a{hLaNAQD5Hi!tJrMR3+vu%haKt^6(& z-|P~oH+3|obt&A^PKuYRx*@*T(nxh*aG_T5jfa!9ekTq7rqxkjS0lZZL+ImfUC%-R zMt=xIacCG_F;dE2Z=Ctd25eC`_E^$0<@8>QfWqtu4xJMgJ+*70Ry{kb364C<5tRY< zw>^!VrHYNQsTnAQB&*`K*KqEUF|L)++e@@E##`(wfN%Mi* zf#MnRzi1KXNSJWLvNVr5ygou5f!!|j~Ly(MQlQ=P+ca?^I4cU~% z7%Ucb2vo5=ao7JnNP^?nL@i)c!=;#61z4GMGI5EOX4|T#8K#LP1J8E=M{^_q-!;U7 ziu<1fJ@&-^B?-UC^YN2#xFG=B)L-fY6^CEI4FvZ5z+=;HugYi=DcfzE%s~fPj60?_ z0z&7ijHb@8t8d4a${JBv@n?&tg;W6Jlkz1(N;lkDAS zavn6OD-56OM*t*;4q4cs%{jn&Oz_2|l=1&A670Bx-^fj_e^$xKO_QOi82k;;c_ zi6dcwINps`U64616v~uV%Rc6;Jfy;iVfs)s516Y6|=YELnboz%p>{2s?>E2wJ&`mZUTaBvJ1)BHTatZkX|^HSc6GAQLBr z^Ce_I>^*;4v(F^^DG+GyqK+;{BaR{7P%>$ z+{t2IXf9DXzj+PyGQ;QvHD{qKxJdfU9z8q+D}L8)Q*j~69p)&h45!M}jqK6#3JE+# z(~iQ&DdOc=!K3V8Q83L>pkl`=e+9BdF$+iGg=;J<%0WjV8{y43HcU|+#tc=DJnuX? zJd@=Nzg~o*2$02FT9Awc6p}1_85?u_%Bdnc{-^3>5i2b&Jv*MWU0N8YWxAv)_(G5qh*JZ1#qS=^p5QCmF5p1LfchXTz8*#SyWk-ragBtU9bz6$Y z+?73bu#jXz%QwP*eG06^0p#w~`fIO~Z3Aspr-kwlCXQ8-%n+sg+CXoN#++Ai(Z(TG`C#lQ^n3K1cn^=YKlrLfmxAw<2s z1)9x$!9J4e>|miSZlq7>d;v{L+v$QQ-`Z%acCRDZV_UARkqu3J|N7tDA;aOp^=5;3 znE$pQH%?HcbfzR|55PG6d(ae|KpjI^$}Bh7j7{yCM92?RFIc%Wz~8+se#ANMp2{## zy*AUz$kI&|lkp^@uuRnDZQY9+CYxBtwTs4h6vF02WcAO$Y+|r9_L9Z`Bb<`|$TAT-H=FR=$jZDVZrJa(QEruM)MFX3pi;y@`Ev{|Io+&rY8u_Y2@rB0I@&M+W|M7Lu^xD&dX{7etoG*@S^OMnSXA!LA;1JFp0 z#B>9D3m0gfHj$%&z<-gLDX4IBuT9GtJA`BKJON%Bx<{+QMetpwo@3d3m!NG^ zP;p*^VeFSd{7rLGVZs@ zHXsL?wVOpuWhhE=DeJo-;gNjGsz|wsLJ0o1GHC1?y_3&_S!)oKLnE!|3`R^kGA(lJ zCz6kB-5^Xpb}-NpM3HGYnjak(aAt$m(%7=YhDe%5crbbs>gl;csr33Gi6t8uhnr|X zez~Vm1VWXdT78`7&UqI924S-x%QWMTK!WT!gs<5^QM@ajDjLX108-xjam3p?iFVjZ zNE~DrYeLMNkusAS(qKg525;20#UB$h+zw;rZFPbRWvSVRM7Ueo5aJr9s0cX?`u>V$ z@5kt~!0|!6MHn4>mTs0h#jre7qyrG|L^Q#T{epULTeG>22ydmtbXLJ!ruZdsE?a>mkjn_k zuXs=S$8ox-q%@Y8C=wv$Dq&M2>tUMXv>*IU{hU30`e5<@WJ{O(@L|l9eun>Xx(`<` zow0ffB$t%;=9hN-ulMd$1hn54c%d<&c;4nsxR5Zi*;P#%JPpx8>iAn;0V5S{@=_#pCx$_8!D40MWoCVL9@a)_giSW2* zVa2*iswNyH2-8)7&v1p0t(5Uj3SrJIv-6{t+4U-%ut7jF43iPHY#fP+0dX{5KP4b0P6M%Hx1vPKZC~BjR^WWyiUeZ zEieRyEt^%!uc2S+HvTX0Y%CvPbW$VbJH+XrAcilCREBW|Ly-O|Y6Ex4FG7iM$O89v z0Ve;~_NRIMR^QuW5**j2bAj(aq9wBozum}{5H8I;qASHKeI>iWyX{*yUe6uY2* z27trgwvEPb*Hm08U;vWDZ|-K?sz_`@PNb)SMU@-jfc6{l zZJ~>_@)0{z>l)Qav`}B7@V&eiid~L%@j_1(ib+RdHqJKD_F%BDi4=GB!5(2_!_mmknBHE%P2 zb98Z(mVbmpa6yHbX_OV8Q2VFkNqo_JfBO4?jJ>c3?s<&NQ#bv(@&p?$L1{awe8_`~ zwrV_m#4O94h|-f%!d%7%%4h{+ZC8n%3ivj=SxIKe|+r{*NOsNl|aZn z5-E@O_e@so0Iit9%ECEf`?YhzJ363$f$+upTW(xGz!-k|jDgHn)M8v4QjA7%%uFnO zIxy6(G1=BiF*rY#Jt9l4neuPBPi{gl^S?njL0ubN;tZ-d)g;3p}?yz-|z? zRWLPmM0=aq_^g`6m1mx0g)vk-)oJX}G}DSkJoV}rx_;6*fK-|3!=USx60^Q0YPH$D z&~0)y?g4x*2_cHRxe3TQt2aHAwpy+YZKM-wj1NmXi`aaaN()N(O~t zN&K>Boo{AY7BX;rL{D|7pr}tl5}=i9=vx(V#{%H8Y4Of9wbv_x>duqc+`mROd->OV z95!p3`yEiK9+Nb~qb2CjU(T<5KnO8+L=qo$EBEns%vvU^Qrx0TH&J|mMP)*q9rSGk z=r8Yl?p(Z99GuAU#1p93{G3&`$D#W@bXS8f~Ca(Xf~cCTV4+S@zfFV zWq*VC@Q8!|8_|s?SL^Xa_=G-M0?;NTyyEeMz{`e(LhWMXNq!sp40|wdi=mr6H}pG^ z@Gi^h_2y+IsP@!0mz_pm1x<}YCtv0<)qoE?!II6KQ6Nq;tgv4UhcxLPhOxu^`sr11q{Z@$G|2PO&NC>R?i3q|3DwfVU7 zIR(*cb*4@*j`pSpW0@t1t!2V8C}2n00G8EgNruz8O0;zevX>>Fci}r)+v{yt_a%T@ zk=xL2^?t65PaBc=cO%Kbn4<>Ki?KwT(RaiQL72%62Y{-a6oqSn^^K-Y4WHCM?U&tJ zUuxcIlthH-J30E_D5HGL4%?i^yGyU~4)PZ&oO{9Ap~;^sxHK#Q%+$5)Z!jLE3sc?L z^zAuMAS+e}6GW%gM!U;T#eg9M!|>`RKPr%ruJ2m@=khl+b&Dr7kNG{a-m)16iTMT( zwFbWPsuaZVw?>V_kkD4*&!YrJEog0;EYlx$>Av`dE{|2wXx`KpZ%TN&aL{#SGHWb@ z>`Ea~Q%afM9>Q}>o58g+K5)!dBK;erQsWCgvBRw6Jx)={Y7c;i^0cPhIUs?+NSea*6)Q&B-b%Kw6FlrmNuRv~n>_!UjsItfI^yBVIPlzTu>Aq8GTP*^+^gvo3T znSfPup`xH?gDPQPLgslAEWvf0boa8%BA@&p$J-G8mpChLCh63w+02GzTWIA*2F;tP z?`707I1khK()ZDYg;stbMz(&O*2$Zot(^glH=LBU%t{C~zl6W&5PZyyx>ty$oZS4R zi0(Q$2^=G5o9Z};+-QxmHWD`(oSyMb&pZ_I8qo1#^o7WZ;l7ePl$U9zot28_r-5sT zh7sX`+m}Pjd&@lIO8)0H8*}M&46Tg*z5eP1-gsZZwKfyaokvA<{40m)iTopgCvp?M z%nC+iDC^&W>sNxC%ZklSb`F}E$R(RnowWEWgI&@c`5O4r%6EP8>JFkUZA*&cEi7n{LM}D@q&ze-jt1fCwV^#`0C%$bCF!} z1RjrnS(}@gB9sPA%pm;Rwlbtc|8_3B{rET8D*f0+|583)Xf5njbpjpwmqIOsRyy^+ z@ULMTr+@QM+TC&dy9RZcX@FjEx3~C!_3xfCgarKC$DOh(KC;HYS!4{0(7(<4_E*m_ z*}hXU^zW)Hl92Xz~7 z<>6+{$Gch?3Aqbq9u~T%3mr#Oz^@On1LWuZpBWgU5f7~UIf7qDK^yclLLreahX7kL z2R>{|Jq31Z;InQdrOkRnV1%MSrz|_wZ&{O`f$`o3Yd=HpvmeYl#Mvnn@eZJMuo<_p z1a(|;uvnU4|4vEe*jRviRL6(1MG~|*(8<-ydCT~VQo#1A#u&I=ZVL}$=!yscHyx+w zoOBAUox&S_*acg2Y)D(&vn*ObbSRiJ=gs#7v3PI9CQ$7CA5Xp~Yo9zXwy)VcPS`We zptlI!pGFBqf5Dd-;nL9TOuj6u_W#_}$W%8>tUAX)rfO}I@B;=}Je^o)TfWXTMd!mn zH0M7X#lSC$;n$|zA~GM&qF>=$5$TS(sDSo@|7DX_46uWlZ4OGWm!SvJ24h$2n;^>8PfvwZog&Gu#>I9 z6zW~Whz3RGsFiw0Gg(;oHrF7bYc2IQ@HhC@$hDoI-b;6?(?SuMV_ZZIQm7Y$=Ufk} zOzNG)R2gEYMb}!(7*m#O>=tZ9Wi%sV{b7$$MP%p$Y{dT72ia@5ShOw6!j)dj7S0Rm zltHoVWG8)U8rYF1Aiof-I|*C05Xqpm)uWydAf6O)WW+=!XbclS7c9ZMdS7(lG{6gx zv$f(6cCdx@sZ_^CJGJ!UVoP@sY#2$7h%Sx(kdyg6YnduN;B3J<$AVycuwYA$-+^pF z{3o~S)CHme99$3J`x00&Vsg=iPkNCuqx3ocW`KN*x7^(l_-(hcIRRE+GAQ0iEzRd_$CS|uOK zo6nRbSdfZqkCaOZ-&ru{X3c|16?vTP#RJ{l_!`K0B zZF|rv99xt_W|&Ao=EJfv&7KIwMt~=n?i-4f0wy~cW6NVvW`RO9@I5fcSs!5wsiH;f z6@wT-~TInV{xDhPyCV|e; z+lwgchis0`|5{C~?btTta7tDH^$gjgOdM$?i9Z*`94z0AW5V<|;0oFhZhbdyUd2Hs zVHV)#CLIiw@iLfno6%_ge#DpW{1yZ*f2nO2R7GGs8Be59|~$X3Aw)0hrNyE2K87z&hYXa%+Pu*aK8|?{Mr2(zwh=p%Oko*b=9FWEV1! zwavq{uupIg0a%cFJUgQnN_lrvDKs@>WsdfZC*MZh zGHPT`-d#@l(*u8S6z1p11p+a34d0hfLFl>GX-5afyFfU${kZGryNgG#*jEYoyt|MbeDSXMB=h}Jqu6bFX&3mr zi!6t}@$P2r3rPX(t`UOoZqu^>&XSvTHY>b1}&l=|6@NyvX`{QWEkOF{eHK^K>4>rbbMSMwt2fP}AOJoxT zVHtF-+05%w!0+#@=lO=4S1WV%vR(;qwcNZ~VM6$o!rWe+GzLTRQdtt9M^5QQn{pD$ zn{3K0Z%!o`87lpo&n&$N^-8CM-OtZrs+_=zQ?ce#zI|KEYA`IA*--XBHc|WZvUq>Z z9$%N6MHXihnv#IFCAkuw1r*&1KLj}T?M?-*TrBPSalEYf4A%{2czvBuZd zk-7q*(QjaSIj8FjvF>toYV;tF0ZWVdNn;543q_k&b9-HyRcZ84GwQczlp&oA5_wWm zNL*^lHRl}10o{@+(o7PY`)r8encvjL8zp7I54)QM!$stAewBNlSdh>Qrum9vZVUa4 zVBZzD5TU6dsAu!@lC#DFW`I=N#GLrV=PREbPEhvcuoZof+HuEv3!40P1#^yti<$oT+NzUR1+0$Tf$} z3hYkUB&mM%M*iL7dBZ*0X}eniY;eRsZWMs{p#m-d20Bh2DiDcRFn+5K;2T%Ohxd_; zvl_p<5qRX1>q)>&V1^CREXFOhSenU6F&;{)G}F6TZONEEMhc`pG7 zY$i0OjUK^GhwU5^oMo5=(Mfdx@2xE@i-ruDP%I3HKUEtCf>3;xVay@E>JuPcsE>;K ze}Ec2a=Zu~8Mq>r^AbEJ72=p;fkE+ltq7Z6Bk7irMl5Ei*k(f{^nbV>78^0m^rX1U z9OmuPUwc&?2N!qV@<}j?QH8G+r{GXv(2oUhvm#=EG;_B9KM=0jI=&~ch%bxkU)Ne* zFH%5Hk|Zc=cr}y)Kqu}91-pGUysQM(-^=5r%*&TzfZ%HaV7kDmy3>yYoMkkH@GW^n z_^#eu&v>|^FPHcZ`AC%pwuA+)!2=a?Qx~U=5MmM1ConYdD2wuFcAS;1sv7@l>MZ>f z*UWuyb`zA)RAcg4AX|u3zzmw&LUM4p$%x3^Eji!%BD=6`#{@3+QYMOq)l4vuBVPTu z{;kror0=3=CqSp<&7S6&R3_!HuXuUpM@l)o#&p?!=ozUW`9|Xu8)k=>*>wO^I~mX1bjxRFp0zY#L5sYztzWLGcm9bOn;nJ)yzRbeYI+owh%y4MSP|AW~)S6HHgc zQjg|%OEBH7-cWa6K~1H*M`bCwOg}Mc*S(P zn?k;URB@n!naBQ!w(nVhXb{SFLZT~iGSUc)w4idpK5-scthle3n7H@PGRoVLPnZ=n zJRO<}0B2Ca0NB3W7=ZPs;dm?KH?O0s71x$gCBv!=&+)VTXO$Y|05V}zogS-6{ngUr z|EHu=8JT1w!a0f0TN!Ryc~AzzZ`OTQq$^Rknf{11}YOnN=4PnKqf*Wo%&b%ro1@_cSbY1_%dV=7vk9sgPX|RH zgS=SlQ9|6aR=Ldd`-o~*7`gl!0S}ZgGU*X|+f4*3y0;-E) zkunA$T%+@AI%P5cK`gw>>JW@Ufvj@N?uvc9j68$jZ0lMe*u$&<@^a5_pV1`HEc2v{ zRG>2hh)E%h4`w1>hQE8{6;|kH%NkSLYzFbhBF4Nd|5ql7VHy7Q^CQ?p z4oK?IYd@AL%jjn7IK%oI9qA}16dlYLh<2al13@fbzJFW1S zmLQ*ZG3a9DpnIr~9`mc&yRuV;(Bfji`1L|knb=dufTmyOSp{6^XCAkhB*t|mVH z%W?zwWA*qud?FM5`Ac}unlu$%a+SB;wn|mg1=V|qL{%lzZ2{%rjlmz8IM0m@vpsJZ z@Rkcxzmf8@U_IZBNLtHc?)N2Lk*n|7Dk6Qs+`-aEQ#H(g4Xy>Q9GSuH8+M&~((SS5 z5sfWk-FjZRV$wY44zNqigj1~tKuW{fB5vm^CY&EpjJ)pbzL)tbY{Qg}4^Bf8?0UT$ zk6L?7E)q2|exUisx`N!zKO?N$ap7=N%8!l`mNQX=F1tzh3sehYY9z<2;01Ixpo_UXu+l`mjG&F+_P?zKS&c59|=woedBiHz7pzuKuD?lB3V-vpVxNvEIG%bFe+ zcrJDdP><6<6e!R6*Q?inwi9|f5_qK-e9Gt^+jdZ>%|K9q6T2>ODrh3qIZ+!tsl$Dp z1yEegx8R4t-Q5P4;Dft6!QI`R03i_EEjYn~yF+ky4Gtl=hTs~U+4=VEd$s%htG1`= zPS&STp$Z&t=x zdQj%|jH(Y+h>GsJqUSb>+kSG&PXc|m_HB!rq_fad+>YwEXk>YCYaFq`jNX+47Lb=+ zJSjynvwN62MiuIT_tL>rq_Mf0?8Mlt zV_=M1icdSPjy-jkd@4RWSnr`37d&|bEHO(5a$v;`W}FdUF3uVfwe?u#ZDWUj#tp3qUNwqX=RQPv8 z@b$U)-@h=F&H!d-qK*Gnw=oNeA8&I++9CIzeRJLd9(>Cx*^(e^$i4^v;xkGem9M@D zhZj^o-}UEsWW9X&=@E4|z>`IHKdP&pQ_I8#)=Qr(9bST@8n+xTs~z>-{J^Zx*Co7n zg>XEOPoF7%(Iws@9aA>-6m5lq>PeK1U&(a+u@-FKDj6bSjniCe)0Qw@!FBJw{zVJXCHNC9<8% z3EZ#M#dvJ}x@~Jaxu!S)5y6K_rNeSwv}n3c#lh zx*(FTGg7{_ViAU99D*bh0V3j@7`tH-Hy`u|gqt)P*X zc@Oezv&n1bpXInxjiXiq5eAtVBAvwKZbQ^we=>9gEvqj##2uTivW{Fg&1HqjAIM0- zh*6WCRe+@M9ZFP>Hah;mSr1KgVzQ>=E5xTNOUB6(34V)|n~QAyit!OR!z^QV(fdRA zcmojmHmw#Zwq8}Y$KokEb6rO4&MYH8ql7@lS{6kF&XG{kr)I)$-wj67;?ZX<*{G@& z!~@Vquw>==sDk?IXO-B>V;&{Yp(6`m-l;LEIU$E8mhYEqW zW(YgMU@O*{Ieb!As+pjfG!kA(@iOc7se za)OsLrpP_Or{eiy#NV0U|0o#QIqP|&@X_V`n|msoq7)2!ynnpqoM?Hm(n;W&uI*UU zS+~q}rO!{qgU1HVvdw#&+3_-I{z|fU{=AV31&8d3X*{6N%mTHKbUH@$8S44^QS!{N zy*CRmeGbLYt>Ekp=@i5)l~0!#AMW7lE*U!fv5mjdM9K$&Dz5>nv;@A^JwvzZ;$aB- zSRde`AVy$H?8^=8!%?K~7+75#)2}xEbA7YpwYMr8wC&@caQ_tbmU&ERozFW?Zr`(g zO!+-KO|Zj3i8p$UNTXFtT+}sbZrSjFt+bjxGxCLSFgI@}j8VttlR`bZc4=x+VG=#F=wtlz&O`UP_)u5rl-7d`51*brFemEGYPT8^B-|T@mfJK z`I%mnwjJwwAzbk;{0z3MvAGiF{tx^SsJU^8PBBl7eyeleb0d;_&P;##@A@^c?!9KS zs%;s)%ThP<_j~=T6qBS!JF=_B2=|^)XrKIt6-&|!_7BY&e+4w)sIx)&iIz}lyZb3~ ztLl5@MYI=$WGdlj4b;#Tp0fQ(!rBRT47x>E|ZqIBk zTfNd>`;Ac8k_)E2deM(h*yISdZGTzxUc?Q|Xkp_c7q4*kQ}1QDK3XTQd`iCP3F4V( zA4F{Gvl&mc)K>*J={me;h&u_G?}Dbk#`RB%-a9ShhnH%v{o2`u zWhg*{1g3)Har;(tkjhCJaXK6Kw5&c+TC*_wW~#D$F4EH9Tv^_^8LNg&X8QbTR6shX z<q@5C3(BWP>*)>gJhn`^R)^sjISE&X zFrxbh6IxVlX7I`Xz~IJgJ!60Vq%k%qmoe7%fsEvd%06e1l@+20@ym?IvP6+&QE(9A zs54W#E~s8KnZC?YEZNYG@0shok|#R3$iga~vc?UspQ}$}FM4pB{G1++@_IakkCWdA z_O3xn_#&ItZ}~aQoc22nT57QWqu&E2@rQ?OA0HXIQ^9Gajy}KEz zzPgSaf&;o9#_x{Ul&>o5>nMULbwg9xM?e_Qj@!tXMOFz4A)aeal{!SK`gHVfoPxCs znd_AVzm@fUC zL}({Q;Q$`%{I%%iIj7WF=LMAU*_IU4|7#FoDz&QLdKQd}i1;;Tgc^XSG>3|Vj5Yr* zLJo~_ILdvSV<>8$5IcC1XF+pcwm96a%>)Md+0Z%M&>^wd_jFp_4Q9K}^sU*C6nipw z7tbSesb%J-M&|G!?9%dhRQcMK7cs=%xmx1@t@QaY`KNA{9#iho)HU~$s{|BSRGRY; zrMRABaiz-Xf+vA~HNNPKgMz=AohT1qOwz2C9Mn1RXaB3l&kD5rdBXwq-Y7>lMyhDy zI^K{iuPwola6&Ntc+<~_t+>3Z@=x7d9@p#@P+66D7Mal z{opi$tM|)z5Q=?5_L*S~i6?@Nu{xaVkF-ibjY6nQRW`yVQKZk4#aqR~>sp$uND8mb zBMF#>JhwZ@7CD3ayN>ikd4Gk$z$7-k!}iVbOnL73TtoKUmX1KWNM`Qf&nDr$;PkJ2 zeb_A#aiROnBlAmHDC-UV=t#1<7)(MwsWWy3^`Su0SyJANo7yGp($fd5DWpYsw`*Qyw*(j5jBq&;DcbceONwN5{ zCZQ}h;{T!ScBz3Rk0WhMPu5WfriP;~wnSb}(U*#{p$6CNFShC|8`Q?xk7ilyqb%)RwzyC}84cdBTD@rOYf?kk!dof~!otY3Q&24eh11oaZ47LrAd z?k}Cjc2{d@&KBf9SZvC&M*pmN!Nxp2JCbrE$0XsHrS0s2jKaUG7O_a0 zq*eg)yfutkM2D&~rJPIUPy5nX5Ov@MRsF#?eTPxqnr%0lfHH2&=|)sf;qT1wC0Q9l zMr`z5%Y2&-TWtk?=iR7h+lIq$blpfWD4l%np7|Wd+ee3M>p*T#H&X^m6`iV4zbz(W zRoH$D5Au6G)Nl25DTM<$l6eD~-KCeJF0P*QQV?eb{h$GA(166Q{o=`=#-9z7JNxqHRLeC2ygH4H5 zZD&QEm0E5$twwU!M~)cf)Ko2v?9k_xpY1$=QQqh}UhyVm0omxodA;BWOq!r z5gF6E!zgmDJ*FNm2Q3kyG^sXb9Dsd4E-0vSYR5E7qcRV$@Fy%>-3GdVi{pChndERn z^(lFO6S{kcAOAZq->&>3JdH}Mk~h_~SrkK^X_AKQwtZo3WN=oWGO54@pQ|>mP&mPZ z;S$C|hG6yFUzsMsmAaoDKa4|dWVR{BTi}G+f58;nZ%ijvx-{cZoRY5HitG|+!?+YN zS#IyWw{uCBo){qdJZso@_ZZt1W@hACkapu#++Vvek~m8~&_xL(e{1N?;%61g>jru<91N^>tH3Q{%DFTa+JW-V~ ziqWghaA(?tn}!5lWQa6gsgLj6_`*Dn$6|lsHcWh``Uh@I-`%cekY3AMR)%d20{p;g z`=u^Y-g205vP4C{AubJOM$*#!i(7KSL-#F&O0)d%5t4QZH_MB-ZgoDuM(UJ{mY-hd zINJx*rozG4T`!K)rsUfXdzX1B`C6&U=8Z2sE%K{gst8k+N_hyGk6x9GO_<2}$(gIb zZYgUDF{coWN9?fM>Ex@Gl29~R&wP2?PORQ8kA{u7euenp+9&TaoO748`k?1gvUkeD z0w(!|WZoWcrxYD85-b)+#MayA1p`aG7MdI$0(+oNoO!kz9Gu#RdA$^MRk#<5Nc#5H zi!&mMMLw<_z!0&IG>E!br^5Q@_-_WmTPC6n27bcJcJxg8a`9BhkpCtSG7G27`-P7(tA*$Cg>pM%YupLCn!v$q)E3ai zr(OQ>Z&9*u;G`VV`2zZu0&T%oV{t$~ifDZ%f+?}=uf|`mO@nNMibO^!+LJSu1{^B} z_!r+qFqzq(HRz|ocLp(?N*;jSr_i`0Sf~Am!H8dEJIGAo=E{r1`ooV$R3MizU7^yrZr zT_0zO zeLGtoOCGzw*W*D+7Ox9f3k+{rCVdN|%F0ig&@JwAYyig!a5&2K^Q(X@nHHkkZKf(p!5T$e;!M62C(Y5wy%g++nEgh7Mh|Eg@W=FRmL4j2q2|2VG=m~o_AuY3sTHu-Ka{lOTUS{ex% z8A=Jd^AlP5(0$Kt(nZ8+F_b_Ky(8(y;4y{3A#-IGoWs;Ed(EP*14IEYU7_pR*h|J*;&nQ$BVmC)=Jj4oHlB`+A;k_q zy?%xgjW(Yx8lZ#%xQxlWo{NC3g+E^WWWStRmK@+=VJ*2yZiM9|C9fa-n^sTn{H}b3 zi6TxqB0@q0DH5Svhjyb{ohm2*3W}a;wwnV+OEae_(x=$3wO~<6W=+$pEZhi>Mqw9~ zmC4Dg!#4;xPIbOuj z23aQmZa!b!dkW^BbR>f$3hy_4I-%LzajQ2mZf>i-pkQj57_8`_)YMeywibQCp~$%O zx0_q{G7=Jxy%iOIsu_wjO@iX{G5%f|ftKoInb#bpI$YL#nMqLcEHC5}SbR&dBTP>|O) zzsq0*y0$iyYUQ%tHdi2pm@ye162e7>;Z8QX17tI5JL*31>iA#`1x(pxQD8*2u1$}P zLE%?NkN)T0LRqCyR+ZP$EDRh_n^{{yAsXr^W8L(F@^Z$kQl(VJxfbUJy{Q}jJlmo< zJr;r_`TaynvC;)5ARLl%CI>XGqKb=Y_0vCHkfWd+Lqxf`X;a3bAgtu^eUEl!1qCP& zi;0;zX72$4@fUUcZg8PkG9Lnu3PoH&86Y(}O6k@pc#N06T^+Sh-=W|$TX7MiqfgJx zCEahfoZ-LIS$ApO>;%H2*x1;_rQ6TuUlpj8qroDe6{z`odO|^3g_RCL02nwV ztl3Ylv-!aq6{k?(*N_D#DQPwYito}(x3;jzdS6zh&CJZ)Z&oiGPr|Ljmpyslk@uTF zK_UpyVAc-=fk0%V?%RDkySs&z$1^jkdB5f4FK*oRJFC~=}R*+iRkF)XmRYDR=e(@R4yoVOSWk4_YFD`M-UP=_2`~D zBVNx%RZ)@W?hvwmdiFFu7S`;<1WN2MGy+2s65boHxRWi|aWmpQL00l528Cyw*AU?0 zp|~#*-^(DVuOLvgd(egp6nl0HAw&sxUh}(vK%^qjqK7S@a4<+91qB63{9$(@7F7r) zrKpRG%NwNMOpQ6MrMda`xd&`DrnYi-^LYE{zxhd98#a`s+yWj(5d?z=pk)jJ44}vZ z0Du@8K}ISqSR4Qd1b~GBzyr|40WeS)9TETpfDs1(;Lre^M4=LRQH|ee04Czeh>hx0 zsw488XXj7I7lN?tWCVu(x6cu3EZPlQ!|rxv5k9Jk+nz_3KWM|z^yn(p24Yce>-;E5 zYa@halZSMG%tOJaX>767_Z=AA-4n^;l6;vM|~GpLntsC*O;i%(ro1}l%>m^jDkY+HO4)=e(e zg1u;#)^4rmFTisS620YfoYEk*B-=?%Vw)ZOS*7$>Hn_aqAbYS)^%HvCS zdL&4X*rHofz}W#9inzv=B2JV8g+j&0C?mz9M#8=-^MwZX8>xV_5xJDecEZc; z@fqHoBLm-h12qS78-Kd@A#PKaHAsj`idH!Fqr*$&&zbkh1qH3w)#N#nJXm2^(S#vV zWaV=ssKFKpGnn}Ix)<%JgCo`MBm@M3F56ftmNY%w8jcKJH!Y;)_U=iF z!$4R7*?4SUhq{Y9aViBDB3k07ZuX;V>pX@T zmyf|_TfFU^O^$+P{st20e$H(ft10-ahE)NsnyRtnXGG(At$X=)3A*Q{h)5>c55!1~ z+N~wW&>6Zmaf^Wn(Yjb*?smn&i!OW>tsuunF4R~{9U6Fm-a zO~^$FWcL5kDeo`}iWQPDfCtfL&I1;Ej$ra_lR1v1BMXdsjtO(RtvMZ(}J zBT(=dJQr!Bz3>r7t;R+rR}$BAbIoE?S&W6slFo8+aYPDyc%dQ7EaBtgAc&C96|a#J z0T9dMAe;C?OhN$#HQ8?aW4ic#J~?MZ{2xOJ&p?5cpu?n2L4?oI7$2!GD*lX;^r>|w z1Q!dirBQBPR95ke#|O8o(=EB4q?B-KEiBnQapdZM_0v#0d?OC_UU9@+)gUMs%*Wjx zGwJDU!j=+6HIw>*n4Hzu!N+8Pa7v#pbI2v8BIy6=4BR>|lAp{{GY~yI$GAFDdp>BL z%;!A2{+q?(J1jWk>h zb(l`xU(et&(lL<38oyr*L`VZj5wGArC=YKTpTs2&A0mC+aPk7G&k!zW1X0K;loN9j zr_$ZCW0{$3%uiF1iQ7IR@5hwlmaG`=`7&yGPPB=+WzrDf+d;uJ1fk4U}6j@kdl1L3BLsfLfr!524 zu!Bue1Dx) zZ=~}YFWG+xb|Bdu!P{@{k|Z&CA29hv4WjbQj^IZo!`>S1j3j~{s0819WVVN5?NQ%2 zj|d494`mK0oya-fV#$gjm?X=?W#c>By`i7gyO&Nv0;%cVzS<$QXK_eZ zR9sZi;H?B{%JVl1I3SuGa|)_vco1y8e=sbXGZH|>YfKPK7mNYa_TUH0WwNLQfg0e| zziZ=%p(7_^AQjtD4WRJWGD=S3A$f6QVx?de+Fb6S;@dYiy0r4Y?X_(*Hg+)<4H1wO zO7;Pw%5pUf9J}uonIKyRq_M+qkfWoq4W<%--v;kWyb!o9M>T=H^bAUBTwm3aX~l@~ zWFDD-{RC2&3SassCLHzx(c34`%7}hP7;f`(x}XZ=X7x#^SQ8uIV>U@+KcrtJq9rh1YhPI=I25b zz4=a;EJm>GnOuGf|JhtMM^$xAg2(1tM*6__{&-2wcPAJg#)_z&JUTS&CcolcWQp$B zykyEOZQQJavA{buH+dtxm-=>r*$t~Wb@5gLVYxoNqw~V`y;QGOpc77c;jh(TrB-ur zac3D8>`6Im-8>WZ=xBksPw$X_rtUo=OnpJ=!func^!Uk?Zg)q47a!V5o!N$*faa!R zZ*6r)WKVfE(gJzEPrxfI;TIP}Wb%=!B!3+0jz|JBN-hI2&eFhbH;HtgQ{Rytlg)o%h3NX*TWt_B9vnaXkcimB<*PrdGLNu*v->qjS&0 zgJoPp$I!i}PsUHh44z+P@yvOABYGFbnV%Or>FPRamc>r<44tgwmYmb>Vw+%#eCJQR z$T(}SG)Q}o)cx)6gboNr6f1_ox^Xp@j=e{7#NG1(d$-ZON8_Y))5#OUb!iW=6zT5s z`b;Gm@BMp!Vt;J>gmdlon|6N~1Em}_yh{%?EjhEVkgguV1q%;PFjy}yOr5)k#`C>T zhK8ubT(UTWM&Y6)sb-XP3W>yWF}D`#R|gd-GgjoVU2){%RXb%R3pKSPDxFmyw1o!1=)9%8wAPuIHkWkT&azj6yYdffR1!7~r9+Msol3=}@ z)G!yK0eIsH8wF2-Nd*v5Q{%i|R7$c%Q}G`vq6jg$e017mV6N|C22!w>judIra3e_* ze<|H>?aNb$eqQ0_s z#;jU}!90vDBzB*Yhl**7oz3Xd=Dy|FK{4A-HFE*7v943P9eYYl)At4)w6?>dVoI-_ zviMs>C)1Cf86Oudl-{MM*&J`>oK=}vi{XxgRs zG{o82xutBKfU~E}`6ImyCP=4VlnG9JT3PuZL++l|2$);xhd zC)XE$KkpHH*PP7vpBjdKZhmt9ah0+#3EpIJO{Xa8^8J<5F!}La+VY4Vxv5}NF5enc zS=ZN&`1TP4+qk7)Cwfes-Cl<|o~8Fio8$h6(X1Z~&!)F-h3%)YRsFytt0VjuQ+O-- zOr?=lwxbD=bxJYrYxl3 z+kR@wZGZowa-Xvs)nutizUIF->^tInpxbSDZB^5<`H87U@3W^Lo?M1#Q#0P)jNd)& ze!DR6*d|wbSPXfK?1AqWPAu3}~KO=@_oPajZEsj|bV?-e+${Y*M zoG3zqC=BMGTK^<*ETmvi30WtieBQ^;7H=i5hc?%KZ5=T0uC)eWIvHW~N8CuCoE+L7 zU1by2(}LR%>N(tM4=+SZh_yq~{RJ#(GyD-6>JMwK)s}c|pjYSFU?52nlGbIM8As6z z9urCbJdM^ABFs*=ApFL9(q;33CrrB^{j=soRXP>GXli6 zN~h+NA) zS+g%X9RQ3eNOctyL zo_bzRL!vGfWLgELwSJYuBA7V{x&9_H7sh!0TW0I_V5P#4WWPCpvjU4k>h}5}lh5kGb9-L!blA&I@%nTt30x-i1*g+A2+GyeOAh*G?ar4T=2 z|Ni%#hL4Yx7;vzMBb?@v-I7z;Gi1 z+sapfIR?#ugpHD{3s9awY4aFC^N$hWm#K^&>9g2o*-|MKa_@GouK<6=rgb_Ct9F(~ z=J|f|)<-+r`7H4=w<9Mw%H-o2IXd+_ey>4ZD;C}ALf_o-TR(5gvuXdlL?SXSQXeE+?)UayxYNBT6aJ2aS6^Za=mhC%8Fjf!M{ z^2hA~>LgB$V@{6T?|dcS&?w$F^W@4>!|B-abt}T^U;b1Xum*5R<_O5`QqgfS{S3DB z+Jmdg^htgu4oX&k)7grU=J*wG81|_*MS-4k_dCrhdClb0z@0COj2G-7?lfW!Eqk&+D z;DOiJ6&1aRT~pWcT&s+4l={D`{LUs275898oKla)g?kZ^Bye~_;kD_HI8(xzZ-e$T z?eb8OmTBA#MCEX%Y=5bETN_;>&t{^7V59&1>=?49m_gL>+;%s94k6$_WzWJ3|8OfvQT*SOlWx0sG0W!?K_2R`a9Rb+KWy^cUOczTt|`gu-C?e z)aMnp%WhZ9Y1e@`m_nyPLhZ1)3ExCI-L3o*%>auJ{n7GG*Wz(zv@OwORXcPyhuVkX zOif%s<3HiyspxSNs~5~QGYF#G(@_pfKym}XFGqzW9^I?IF74iMMXW7X^vj=cVKy7X z`3v=ofKjmohvgH8S{3caWNKa-jea2#V*?u9S*Xf%pfp;x$)%Uj%o3oyGEt~%*M}Ns(^#ppAy2xrRBRqoePo7+x#*~XK zK)^gszK|I^{9)BH3|9V{nXe-*48xp>S6QR2+4N?7CNu7mNRqLp>Azp7@)D6i%J@1Q ztPerPw1K{+vnZd8GQ3USZz zv6%1UeSF(qMB9QaiwiD+Rf?uj=odhrBe39{Y6|yV|8m*Xn(R-10i?iN;`{3u{&)8F z1jXY=g?ZpQcU>}+Z%UQZ;$RzM@2K(QRs8jI8E&QQu8vO!roS0fxBrs7Z5DAA&aYAw z^}isczTRa{Gr~Y_r4{Z@X-0>u%VEHiuwFzbPdiXQ9a+(6NWWxd>_p6xP-wC3nGv#9 zLvty?kuy8C!%y@bAWzsD_Pq4CDmaFh>Hi^Pu{pXrG$*g|^Usfi$X%xwQa6&J@r%YV zZCtqQhEN(c9Ux;k}OE0D@zi+SQP2OZlJqMefa-y%c78SUePV`lI)T3WYytXZm`OIz1E#xLOm29h{P+;Z~H@r?LPUQ{qtwufZsVmzhB`r zx32-c54vNo$Cs}dFfgIn#?0;vak|?L&8qApr)V`2`RK}+=;GhSNIDGBxlrl&P_zaH z=g=6Qi#G9*aezHxszVR$siBPDfD*z15r(zY3)#&uS9pKcPGqnPt#&?De2(P!V#AQ$ z=GaX1v90IFtpEZ3*6C49`F8t=Uw#<_ekPcyZ4FO74MkdrXKu^i+}w;+0VB0cG>=pS zd;Inb6{e;y7fwm|CeW^f-f0(InxvE!_v^Clp0Y<|>e&U~sNSSUngz>Xqm#)m{;L?P z`(V%7GzwbbKCOd0UFi*Vje1_#FJCk^Fwm`Cte}5~w>EeFC+BM6fX)Zv0R8iB$P3MA zfO!5n8vl18*FVC4N`-~lrJ$|6>|I@?&AqG{r3JY-xHvg@IruoioZMiZcO3i-92^Wx z|5eM?gHZz7xiv_^$>{@Bow=8*2Z)P@jRR!FsA6tu@8ad^X=lX5#IE9M_5bfq&BN8| zgQfL<_opT;3v#lz@G$rI2;zkH$HBy|W$)!=%_ylXqwV74YU#)%D*E4p_w}&0K}X>L zaq&Tq|C2zxJUn0?&_5;rCvieg@}C59`5zf4FXz8xoIKn-;D5>ftJQxU|GPgfFqnty zU-h`a-2WKzpY%UEc=-5u|J@%a4-W_Tzv^-F2yp+qEf<)d8|tV3r#~(Z4lbxG|6duH z5B%@3a6%m>@UMEDyxd$I{~_by;~I23m)Vf#_ji$~1*=j|uQ#-EN?h>L{nN#= z`UO*-co(z_zdRx0>#p&*Qn)7NQr(jIQ)*v+cY8m7W?sFgmienM3Jp8e7HCY@yb?KU z@-^;`Fb;{is}?wmc`iOUsk_lcVKeW;z3xxB_HJg*arkn=^Lfvv^^p&*&bs4dbn8T~ z<*g;+dxD?dOP`RnYV!)_l2ECOO!c)QURS-pzrDMRQ{4G`^Of7o;WZ0p@61@3q^*6b z?Nvs>Qy%sGRkEvoXX~4`vlc%59;z9AxocDA$0%FY!z-3@Omv-H`GR++f_DGTf?T!B zvm`#w)O~%>@JeCV<0Go~3^lFfB%Z#sj-F`yWZJEm)A!su;vK)e5f&>FU7YCtT)3Lc z(a8U^WOcxn^~;meW=_6j_qpK4o&J7N!!;d=n;yi?=@GaZ#B@s3@?X&LqYfr>`}Q^l zJU{*CiPCGUn@{GQE`1VgUb{S8BmSlGU*<{CQ@3)Z=A}S`8pr}g5-4UsG%#+AO^rbE zAf7Qi3c-=)T2bO2Tmpz5Jqd zO--;x&cG~Fl9`|90*V+77b_zJLrVh_BSS+&Q!`UjZ39bn0|QMYJI#^oggDwcu_Q4k zKOJluC}N7CQGtlSilWpsE&~M%E(17F02*#=s*t7t6EimjMhOTg1D$Auy_-*q=J(d}`;YHRCO@ks%PT5EOc50jt9bh>r?b zEjD*tAR$@}x&p_6&*5Y!Ix14cV@`-BhcluSJYf{o?t-K6vB(%L5{Zr?kYZ7IEDl8) z7==cnkr0vyp>Z)NLL3eoheSrzzM>Obflu)+ojy)XW7IYWz9mE(Jsw9K1o{2`SU)D# z?lM3q5{U#M(GVIP14_iWgEo&k5My)qtO0q4LxbH~m&xHV*=6z^F- z5ZwoQS4-_D0MUK0&GR=f->Y#AI6y|6I`knXHDtYco~&FSR|8qyEZn56xu#=ay^42#Ucr zH&7{{ZS^Y>^PgerZ{K9Gc)?gzd+e_ItJf(IQOEikchng!A8{RIfJzwX{rCXt9}n2g zAL7o#U(Niife1{H%tqp@18nnWUybnkHM z)cp&tz-0m^Ol_&B>m(RS)Z*~y7(G!3x~|c}F(eHRjzNQ_^=KH0N5Q0bP1FPPm!`zP z@|wa1glbW{-H8sD-L8)V+JpY>owN^KuBj1jsHQ+YTJmWl z;4jtxz74+-wk3jz8x@1Z#~=x*NK_n>7>C6mz&jH8uB(w!e-Gle>pgz83nm+YsY$Hy z6xbh3UKAP^i;U{WQ)^8&kKJwT{|@%!dVi0vHL7g}SeFRBtN1SA&sE}~u5*2U-2IV^G`hgm`m@5B->UWXXAC+I6W98YEPot;joyr>hNR;MI5aU zSs%I`F7GjUEbzy2hzNmf@qC=4!7iR%8MM88SMhENFUW+F>@%4}KELhmwE7NHuz`8k^c=sc&F?%wr(8I-(0p=HI>N+B$$A;gw{Z-=qU( z4~@e|)UQs`hhEjx2siYuk`Xo;jGm8c)u#PZwOak<2N zCW|Cy6-R6!Q^*E7Swg0;$z&Qt5Ru(drqQQkl0s@G$7U7WCW5Nc~Quq zeE3Bt_vslR4%m$eT5uHMo(kTWhgJLi< zDIz6@>NhYc1_PHywx>}2_LK-9jY8(p$o}x3h(^W=>13jSM&YG0D56w)z^`Ej+*($E zsDJHto;lY5+xKDxendMa!m&>|9Wk z3L2o14I(N?0~`TBHXaZbPYk-kV2jxSk&MoyqvZB@GDh=snvpIGihiWvaj2yr^rvxeU z@MS3!R}c#c=@CGbAQh=n$Qc&392L+hEF`%KZFH!u9=Ar0^C>Z6l+LEZ!U_jL&q5*e z7y;d=RI^NMYl;aQprQvOfOZYb&)~7dSP7rX zka0{}A3emOiD?F-S|Sa~O-cfnO2G&ve3(Nw1awlf-w+|0ldfVgO**rVA;l1#f{;IG za6|x+2w}*`k(fQaR4Gm%K(* z|CIJ0eT@Kq+{S-|r-%T4)aU;UPZb#(@0h`hUXNLr0F!ZfG*StgJ5f&s`c<0 zD5fMOK*XYzNGDcn6gy>}AdKP&iDZEhCy4-xX?Qs+J(bGgrBVnumBM4w%gr*JDm8%N zI|O!^iZv>QOuWq=bUWB=gj}LbL8o{Fvija4fc0h6O|!G)Bn{FhB5}MPIZBR|pxj(O zjUyD`>|_d>Ws>L&6mP1>a1aToL!$jljVTH;T z0c4><5{$r45fIH3A(|ufYkX)nn@bOPNN$82JZMTssQ7e`PK|`IjF5wbwF#I~4NHks z$tf6_MM>w;kb+dTi5?8dK8c6*@!yvog2#rnf<^=r z>qR0MXuiNH);N7WDS|2HXxK)q#VptGpkN3}afO5kCDUm|A{`W(NyV-u8&4qyk0w2y5M5IDwn#h`tPv@rwaW0-I5F&<%e4mz2 zjPQ`B#S+LYl}f15lWbIKz|2Bh%xFhl-L6kAe@!z$xCkH;p|cTfdJD#Bb)tzj2i}-s z_wWTmt%45ujTRCX1vHMpB1;1d3z1GYo6S~-0LKZ^v^JYLJ(%uPSP3Dq5>}7|j0hly zfKD|L9h8)S*hQ3@T~Y$u&VguDgM~zbQ=t^O2?0zX$z_Mr{iYP8$AZxS*MP0}1cP+F zS{FhIkvg6gMR$Zu#t2|7Z4L#_PIRmJI2%%cN2bcz6s??(Hi}Vvfy73#im4))YuBNK z=_WfD%Yb;apv);W`$8%(L<%J62X$;cOKNI0{KhZS1&Y)?8KhH!o&(Gf7AP=G9+ zMa9zTcy6>%nqrr$^^n1Y)MHp2u^ML~aa0^b?MxQ|teNOTF33;D8DW{-p<^KxAu-d! zA;?u8p;y5IX~5S@@ww9j9-xB^cRI&L_E~JFsc-;f=?2DX#A5Lj0j+Oq7O2lkOe_t5raCO$V`+WVY)FD>C`xJPBFmYcg9tA>m9 zPGLm=>$-Eq5O7c=5Yg-bvyY#eE`*#ygP&nxSrJU40qxWJ08i;a6L?mc6DT)2;D8Wz zCpsvV(-En7LAjQRFaRCVi99&rcuA#W=x?#c%}s6Xdox(r%eEQfp9YU4y=R#kyC1N$SGDX zH-B!vx{ApkX229syLMeN+XIm?)^gal!w!?LlPzUz;MQfSP_9-d)#A!mBj9J|${my=SxkQ0M~F$kAJtLGvK z-V_;>ZZU}|Vmb#SAX|7!D<XX7EPpwI2?*A(x>Q*cGxEIXo7f%13YY= zk*&ddG)|jSY)E(D(!I|s!QTR3EnG=!0f2~3MyY{bfiOlHkkMi@oHPzr?i z!s&WqI>80GtpThI>j#P}r4fTZgv(~fKx_maB5>R=1Lg@JJcA7JT#yro=R-lE*M|sd zSVl!U%@^U}|NDS^J`^(15Gu7(!WSb|CbEPQ5fZ0lO%XB8!zrE>s5Kv;P9!zLFJ%lFu5FZ2{;h~0w3IGF&h0!{%&CHT>Q8I@% zRqZ75(g_*|*rstC5QY#x(yWguQGyjuU1Rx9!?A!7)(9X&1s0}MU^`VJ6;>ycGx<)9 zk>i3@Hilm7cSZaib9&hA_&^|;tMft1ztR=~{AVfwfqq&j>}`<%fn>gq3%O;$mC&+6 zLSSP^dOZQvn4RSKPTDny#(BLRwJ04*bBD6dq!s>?zfd6DBzsA4g{j+};0jy(f zLP8*VJ%j{mL<0&&B>w~dL!iuGSRVmoC<3Uk_bP;}e7zcn3t5S7IWnLS>u5@`kM3fn zb9F>@P$CdO4x%qr?vogaT7v{FrUg>TA)q5@KmjLUfOzT}R|F{x*?@?=Dihj-#|F|R z9IKF-LcpTZ%_5yoibYsa0*@H06v^oZL_lk;@f3VCJwh9vql0pn z;|@V2k;h}kdZY*qhD!}ZMuy&oLa1vNk8~mnXM@#9tJH}@sY&LD3UCAjyWSx)Xbp0# z)|#TEgCT3tPqTC+>cGD9K-$HR?S8K73c zn+2Obw6I!%7)qrOVVC3l5|s$&GqZp@R%b-ln5?=ZIzsY)YO*3c0oKX@#(Ms$@uTEDgOfanHF}x@@HeIEoyKy3!8G^}9k{@r=iKHAE z8TLr@xKy)&8R?-{te1cdt%%*M(JmqxD7wYvumOqx8xx7-x@Iu~_-`h^Mk|2b2Z{xy zvVbAb1V5hWKPXlN5NH`al&jM*%Bb9*s}p9J}Xj&oH&nC(O z4h@&DeeabK`Cu6W1_V^AWRV^!fpq`YcKN9o4TSzQ+Xc*41n`~PSL+Y~9sx%AX@F4# zh8C=hDZ~(t;nqum*jX5>K2?ZhxL6jGL`6q2G;X|+LJioFW~o`KO|?V-`H%_A1iMeb z&E$eeX1j~S`x`$K0k#6QVTa4Aw{yi1m#z&8om_W-bAug?Ir6 z78#z;$OyUCgcdr|p|Ig`xB*MRAp0c469IfTQ%CF&Fg#=-&<{Ti7#^^e``GX>Bp9#8 zgtkdwv;}6uJfa<^M5hQGES;P|rJ0Q82w)0i)gV;V6nbh%;YxAZ$uhB%0Ujy}3}jL& zO^sv%V2cXES;9*WG#;2bN`Q)o@J=P#L%~2PAqY5weyYQRvemBjxF!Y)LP-N2HO+^R z*&wS7Lh-$B&`Yfd_jnr`B2d>afXhBfNI z0zBLv5`(isR!PtzC!*Cs8QU(>iM%E%4GJN{B(Pj2oRg6QDT9092s=P(<+d?C%8;lhU0ei%_Z1s@Q5OHeJp4)Tj*>N0za?e76D3 zW-)4Jg8F+UMyKOhE`dkklncyqDGAaA=@caw<~x`eg3Uw|!4W_lIv_%zAg~vdg2oaJ z2&v78qB}u|uRx?=+9_0~TxjyJu_8ZOW1yqOL}c4PomHcXmP)Tj3j+Peb1;m{jLM` zj@Lp92%+Mut#EyDM;}Lz*2WOkhJsOpYFqvK5vHfM5Dzd6oI(26a!S}@vHSnE7{vmP zYSmw=CiUYEzh^fjWuV`>M-aX6(2)>#Dh;%gW5VkZ<%q1@KLugyAvPp zTli?xsz+0{K3JOlaNqbyU+d<7kYr8$aDP5PgpW!jf=I+arTn?l{{^r9h8^*B&e^<+ zJR6OhyJ1Thd#!GIL0_|U{L4P!miq&KycJB%3{*E)UIF@3Ix9c8b2ykTf2s^ zItSSzlB-Dt$K=2XJ?Q-ry^n?Zo}2&W4VVoi0Klj4 zE%PCLoeF{Po49+3j^mcGlL)~+SC_qM(7+k*a?lf-r zItQxVVs}ZvbqR3ziv&;^z6l~+3PkBKB_6Pn02$11M&vq(@ZAkAyH)~w21CHFgV^DS zernBs?4jYj4_8I-sK}_uH4zYximJK$0aRBAH0iL`6YiW9phoV1NCYn^veJw~VIwR{ zb(ySxOK#+jfw0^@To(~$bgkGJCW{AlK_E`t1JhuwT?a$8H%MR!gb%kwu%=j2^JcU| zeLgBjey{oOcZbBE`1L$}@}v%_@h_*X-x{^!nQct<|C;=D6iIlC^D-p|Rmcgie(O!;|PVdo-`_th~(`6PqiQ_%f(9AV|T z$zw0)Jb%2*aIQ^k<&Cp121OTqx6YhhmNujRsymAUXF`|OuG)MH-z>XVD>x?6v0?}3 z($V7kPm(EWUWVpm|MLFLRC5=v`@QS$w9f;sC6u|x(tN*BX0In5ZQpvsq4vReL=$q` zR{I(?>2cw!Q!g5Rd$a8q{ko9lG*$3Q$l8}}Rh-R`mJ z8J(NI>A9fct)5(G%hr62J{Q~i=sXLpS)a#=%?6wnGW(vZ9D9CuUZswlS9xcZvde&d zBRX&ID`)JUI&{Q0XL8QxrSC4tXen%!@9jQFXI-;u)VAcj6)BVS53hL1`0{g0+K{kk zT<#Z5rYjoyPmRJnn|b?DRZC=p?vJl6`Ku|2=vA$TP)r`T5Oz`RR)^gd9yr80;{YsC zA{tnuMuQ=;gvw?UakZLMN2`WHtjOsZa52d{1tXy#dH5I(Ncx~?#6j{nU0N>(8SW{v z#tutTd0E1Q0vcLNDctV^F_s34K<r>5Q}i(I)EPK?4#n!|g)U;2x4;{gY7r+2DD-*D<^m9p&l)4IF?R zJb)Cd24_>yeKaxDp6+;s1;=0|4~|dh(GxH0@1;=`HnuC4k!TmI2sBG?9#=UeHc6Mr zv$ewa7NPA*Jzvb|)e9lu$0LS{r5r02$&OFAaAWNXlUp@NqLdDX2tDKbU)% zIdIQ`QU*b6(4bm@C~HQFKG8-~VFjsfq@;&i%}iu6Q=}Hr08tVa7e{6H!E*Yhq0{4G z4~I=nC%^=okf%uy;3Wj9fEC|LC?L3ouwz-?J}^?(haMl>x4)h)c0m@z0QtcF&SA6x zY7;Kr>*Xq92?i*!mm0QQ6Wv8Fs*P@35u_x0}= ztTB_LWvx2UnIVt(O==3#|1vl7w$C%2- zHy+#a_VhirM5i}o2{NA;UfnCz&o1G$6>pf$L-x56ouS+vyKfb7M%A7}II)#wAl! zuVu|z=L_kC&eL^=1Sfxf{QJ}e{lDMe!}3HwZT@K}tEz*rQU0uXd$q$yUoLObdGC}P z>-$gi>pFNPf_1CQ@(2?yI9p+J1suX4${O7H%WcLy7`$Rw*~H@9r*UtGnamSswTbVc z?pi$2mcHPn^8VX(SIT17{dC!RdrBu+uq)@x2|xSxHWnd${6Ji>^u`Y;@#w6bhab-K zRJ30SS z{CRC!G`{=vfx6?fFV69f^f=!1+?m6Dkre~4hYmLxzq!k}w5Ye8H&ocj7aOp1`J+nC zV*G82p}lMUg+=q)?0;LhcCPYhr{t4YlU^QPf3<2#*`+kN+IvoSxmEjqQBa{h--cEq8x%KN`>ZrHtQ$_m?J z6O`9@kahZk?)YY%AOE=YZMQ3&<5Sw?y**={GB}g*S@xJ878jrb-=G_nmg`ra*f*@< z(xr{FTYYou(N~vB35#aE+V+ZimjB&nR~!F~IC3T@qfv63>f7CX6`>AIt~dPovj%%h zXAC_#Zss}d=+2hai_D`ja|+FGUw#>CiKKrIYYqlmDo;(F*{bJg)YlK1CR7~xa?TJ} z!G@kcY9AcUZ=Q60YxEt>l@Z^@HXqdzd8^OHf_=j6q%H^E4B2!ne=iDqa^ozKv9w20 z(t-ZdGF~MeO3T9>eorjXIrEh&gn>(ZDg-E)9yWy3sF7JAM0I_g@m) zKI(rw`9uZr>iA)s-`;rGan3|bVVgymW()f6X@2<3{YRXv5w~C6LzR^EeBR{qRQ8?b z36Tbk4r*?EgON*N|sH#|%W3K-8b+UVm{~rFFg3&(d+2g+Lnr$_bzE_b%{1YGBzixmANfEqmVGlg- zuKzl8a)c*t%$x~#@(#{V8ewSO&En|VoBEWUay)DjtabPH^-8Zm(ynC>rV45 z8PASvP8LM)lGQO4zFtiiA4hm#}?&YpC1n$xOko8(2GANp3kxj((6^>^k?3#xZM$iAHP z`c0NYd1z+!3TEhgO9$H(fjf%(F|U z&pdbR&brpej&&^Qk+$|2GiAWxU;4Ib&>&H{Vodk_h-G=dH0)P>?6bJH1>@$KjXHUsDDHD)TR>cjoZ#r&b_mMl3{1sitF|U8o^YXo^nTK{I zPCYq)#BRyXh9Q zgjrVM9iz(lc|x`WdD75xPMdQjyT%Xwx-?gM_cngSXWy0bj@|!Z?Pr*zy+=NGm%gSj zx~Ma>=NT6#EbV@yd(pGFE61KhpPuO{{BcZDWw)%YerU>%uiD$Obz*77x!~2v8eOOiR0qYFg z$3a%q`B}Rf{VZ$o{CwY$rXO1ep+nGmt#94SHu5WLFBD9E^@T^#dD~#bo~;aQ_QU|; zp!B<03D-_^z`c0YQ}?Pcdqaz>8%z5JX8)d%adbqZm#*H&yR|-b@ZtW(&z{Q0JiC-U zq3^@Tw6wEL1I&h72k$?&ThpgY*1aL4&lD{h`h;|_!&a{P%!rmD`+Uz6Q%u+U-z^N3 zb^RmIX?S((t-maJk}-Beao^Vm`a3&xWCluJcb%R4bn&YO`N#TV*JO%+edDbxEZaM# z_taJEs^{}YaIW7r%CEFljBbBw`AyBB zHgqCKRZQ#FaP8)-Kbkf;nscYo=sSi@Es7FLRl0^zIaPDf7n|yC8BEPthjO@IwjWUl zk81eyZ0yMK>3bi~md=?xQ=HxOR{63t{o+%@>2ZmapNjDmYQsd%b6zt4a+^8py5P7) z*M#(sW>lp%8aR>X4=v=T$CWUv_Hr$*tX^ zZwyX`_V*kGH#n93T*AKGs-LYi_q?dZFY}jm`f25p4y-e~vgT6)h*8BeQlB@x@k4H0 z&&-zV_jYclJMrsRiymKpJ8bFq+nRJFZCo-<)!}Uiq!)uaIT6wH3deb3>i63W4Igc5 znV)*~X!O+KPtUgLF|J@=)`g{AQYXTqrOhA8=dT#F5!<;}S^Pygi}bd-(dP}!)0%It zXMOqZ2F>~wUMM2i>8ROsdcW`r`?{a99<5!!Fg0*qUXm8*Fod zeI2jW<_1eX;41%Ld+1m!_PvK*d++9_I_38+SIaM4?s}}yPZ35NWHVc7a=T4Uo0Y!q z^{#arW46PdA9k)0_fgFq@a>MLYH9oD2j)KMOuw<vgK8)_meQ^%6A|K_P4s$n==n?hqMFtQs=#G zElv0)ui)vXA=Ul%GFF@*{qY69u-lA@V@;3xTp#NH5Q#Z>PR_y>({E zmT%dAtavqciOMri{_C+n?mT|PFnxxwjC^{-(mbn?funGVpDO#xslQJyw+}zQzgNGi zWpDmy=J^F0rJr;>w5y<~=ea+I?VTpeeQjIdE`C%r;{3I?uWd2V%DgH1?&LrAN&Bjw z_MNMmRl4bdqoC*R(9mIhciw!ik9~sV5+~S7)@DszOK6V_-AuncIGz!xiax%m@BNAk zny0Uu$~;@Mt2EzVUeVe6=;rOJH@mk~a4~WCS(~JqCqE+2{UknltmW}6Z^NbiH^z(^ zkx@3e;`s>g{*$|g$?qliZT4{B{nCEZHc?6@$U2{iN!rx1YhR{0eoaN+stNo#53^4U zDU!Tce&pWXCBOGb%A0t0cFyA8;*;#-Gv8KTy^KDaPh4L#^x-XN;e$>qM$8?NrLN+8 zrhU^Ld8)d19DV8YX+`Fj~-hpRB)AmSqN0; z`%MEf-&l(svePXG6#bZ&yX~r}%bH)a8zn6M3H@8?hI4bSmMuD5@mOE1*=b8!-7hU| zYPZv>QR8PWfwdM6ROx4co!EM_h9O*$ytVq@TN(>D&3p%2-XalMQk*T(Wc$~-wsyd9t~Yt(R+Bt)^__-7OrXZx;tg_ zR}Yg%SFCIE`bbU|P9LcD75Dpo=jTZkn^qF0Ttq!2=b>h2HU7*Vp{8g1 z_^Zjqt$uL%GrNSE^0y(9(3YgkO>LHsF*Xb}5r37-DQ>oE;L5MS`%>DZ+%B`em{9oq z@)>XA15<8}_Do;7^igZp*|mMowcGMw^^cu~|Fn?`9Xxqs;;_*>;5MO#GE?VJ!%JEG z<(-CiHPfJj>$ArX8~ulk0v+62)V;XT&zX~6zcLKJRo1n*(Lgr@inj5APa}4hOe!x( zx}KeRJELksd1c@01=+W&s!YiMU;{vjD?@>zRYM;&RrDU5``H%!W;%3GHD|`K(evg5 zeB9+nG(ob9AyOobu3~yR?r*s6`$x!7V{y}5J0QTU7bkV=B`cs+XOw*ol&LYb=y9+(`j`1%Jij~?Xxlm}VSCoj z6@)3N#DQ5F>m$(}g?3`e1#|_%j1)*K=@mR`da~pxug|l=WG%mAj_Q>c2=(hA$>oU3-1La{IUYXJpIA z-kY<$a5EOS`Vmdb6BRl=>AqE5p+@ zVRlxRyVMd%EEu%*lgIlG#fADnxI;OKn-?WKS~0R>Rum#@ZHxVLXP!a7I&p6NvXj)Q zWLr{#l{yp7Sv+u1)}w@C)tj9Ij`tnaIuvX6e#t58U)3>$9K6fW?_^)qjA5r`^AC?) z=_=OEe}g!?X05*1=u=HQS^oH!g4}J&Jva$HG50fHu>=3DX?@X@n@q@{-FI^2dHKpU z+g|SZRh}00JLXa0wWU*KO9j~bS&vFpqq=P9k!4)gx$E*X8)>{!T8`>mOVt%I_CY&% z`W?x+14G=2x#`X0s4)-Ynw8S*KMuMWTEI@s-S?=icPHv(-?9cvS29rBMy`zZOmk(c z7Yep#%pKKaX;bd2DY7>|&Y8RAA^NQUM&q9!^(!4X@NDIZj{cR~?xCT(vjo_ut>U=L z>^p9j{=~H%I)+!6YlRjJ7(;czAi7I z2NgHEr>ik>S1<3;{qj0?;#iw!hukh2|8nGlW7)JxBSzwyiwahr+CRqD{p;P!8n5?H zjeT+CQr?>x%^&PL9XIxL(xTBD{V5ZcHhH*=xogC#Wz36dr!THJvftC$y7DRXF!^kJ z|1UR;uWG8=+BB_1r*NI`zxF^6?a_*&%J^XN!3_sh2O8YyZ`EWB$}gL-@@tQ_1(@4k zez)secGpEoBQx8*5?sA{EN{bQUh;wy`2G)dNv8r#>Y-H69Lj~FnK$5e`Ccsa#n2x) z_uTui`K?qb3+O8cZT~t;HDqJa^eg$>3GP;*q|Zbt_<4lEb61t%GPjxv6~(sIpTFAb z>z4j&$MqE|?Qb&(4?3SS@0r4>VB6G>na7^y@mBNQB?pst?YW>!-<(vHJ86RX{8vBH z3QDsZG;YvjSjw1seAPM+IcsZ+=$%F4=$ii+~`^0KnB($dnmZ{L=b zl)QQK=Jo5>#l^)%MMZ^$g)d*eEGQ^=@#4kv=g*%#d-n9{(TLUxPJY5US8g{YuBz`y?W)!mCKheU%GVZ zk3asnc=6)-^XGGOb8~WX&YnGc=FFMXr%#_edGh%2ijvhUF`0(LFhYlS) zcrZIV`@n$%`}gnv_19nb?c29!&z{}8ckkM@Yv<0LJ9g~YzJ2@Fty_Qj<(Dm6wrt+K zdDEs%8#iwJ`RAWEY}l}V{rYw5)~#K;cFmeKt5&UAxpL)-6)To6U%qtd(j`llEMB~L z(V|5Q7cN||V8Q(P^XJW*H)qbA*|TSV|NZx~X3d&0W5)F9)2B_FHg)RM@4owP%9JUS zCr_R-Bg%PN&o1 zaMV1U6RWt~o|)oL^vwOT!V`0%u}v|+=B4IMgk$dDl_l}f2p zDijL2TrQKzq*7^WYN|vc5sSqlkw_>M3IqZ^pU>m*xLhuW!(p@8EEbEwV9@Dw8jVJ! zQYjR0aThr`IT<`{O-M+HkB=WTXi!{S9JpbgNF)*n1Uw!OZW#eiKL&$AqtPf73Iw=9 z5EL658xs?QKp=Yc>eZk@gTE?N_=#EVTBc^ZKMGaz<4#RyAY@r9o5!RR5ao zeDIS1;C@c<02kbnAATJ5;W=*j>D0$a=$gk4e?mh46{+_5^t%OCq+S?mFeEddya=C$AY)qitu|M$N@0Kx8%`maG)cyC` ztV0NyEs4^HL#iPP58{U}nY2Jua#Z+Ae}S+zMZ^`V3u<#Bo8ng=hSs12@>%=@SQ zkX9Y3XLr=pt3zZRDn2cEK?p7e2M^<;(6tZy-?1z5y_O$jcKA9*aN#2O-HDnI05Tf{ z7}Qn`r=!4PHmodQZ42U$O*Ysi12?>ayE8#(0tm5)Tt)dV1&xEmAmBsi0l$eLiTo@? zIA$OU{UP;3$q$r88m{fnyC0oE66!o_>fJ*qiHYb%3%zw%xd>zi3N8aeqKb zED{tVvx#XRcQ7t;3v?utVRywv^&{I1UU!t(1nzQn>7uwW+&`*ce^50weDf#B|8Q|< zEQp;A=R^Pj^?)^?Ksam!0;kZU?(Lp>fBIw;xcoR=Ms4$fyWBx|dtCVEGl&?FMhWx9 zWHW&1inyp43?cGILPe2KAd_VR!6E-xf>wK-dN>LMcDp0-Bks7UaOikcZD@E@P3U)2 zB(%FVpdOb79udNTP;luf2=EA#O=hFeX%w%?qT_>0GGV|1d?Xc<7D0>{2Y?!mP6-z# zq($}}K-h@~bRD!8W-EB4UAGq?G4ik9;QK!SQ-_PiPa*M$TjKiKeyt?5#@Q>aMSBIMvAt%ciq*9k&cW5ZEBH9uAm zS4#sSjp;J!K1d$G6Qd$;5i(-|J>eG$MM5J9!K;fN?KFrl%;e@m$m zLO1{fJkYL{08nBo??c~^$XK2PP(iopKyZmQ90JxlZ0tUcN{g7WkIf7QjUggGIAAr3 zpaS7nHXX1BX<_r%ZwFM_K#|&@@yU)oSMtuaJ=nQH<2jLp=EVfgZA)k(?H%4*V! zXGzrFWz|`YUscVpd>?b-n@dePA05v~cSMaD->=`#fjJ#5)2{!p=gL^7PTa8B#4Ak^ zuN!x3^>wRet$HL!HH%w2xQ1ZbBjNJztQ3A=W18p0%ACDZZ_=S{!{+#N4V!ip-frCg>lIC!wjek8;_DR+8XqUOvP2zjEk+)0h>tq_8M1Ao z7Kv@gHi~U~c#VmPnLOqTj&O9V&dKBnoz5jUZ?!wQMOq`7kleI$i!qHc=f*U1ASspS z@0H*WVV@uOf01WKj&9Zc>E$O$-&HpW98LJOq58m$7Yim{)hP~tUWOWJUY>Tf*An8h z=IWHI6)!VKE;{pkm%nJ_saMZ_a=-fd*iXF6S#NGWJHNWXz5qWe+qrT33>z3t>q9;eq z#qn}>ZC!1;+AJ?icJe{<&Xo^r3jL`Elj4@0**@xW+SvG=(ko}!vy7beY_1RA&PC;{+*D!)oa9&t>;(Yg>C~bWNlqK^rp+Y>5t+y zXZ9?V-E_64Tv~3UVm(vb!z8hF}_1>;n z_|-VmcUuxpy(FBeRELR%_#?3PO7xMX+Emm7l@o~_V1E=t;u4YXWvU1T|lV%Va?2$ z#|n?p%KDe~`toMw6waek?363IxB4SR3s%lPzUo=RSHGAQnA;b0XPG&>9+%rnk8I!N z`^~Oi{k%L6U#`76;xZ#@@Z!f*P`ew5!WGIVXdSuv>y!OWiwVw3blpWPqkGmV(3f&AyzRf(n zX9>z#fy&?X2SEG5)M}5py5}O+`F(GaZcr;8Kef?ZN6_hS&aW)N2iwmZnNegY8j;6{H$%`O{!kY3~MVN zScn-TLaY0)s@lPOQ!2P4%DUu^&pA-CWLt`m{9u5v+|NIIe1I?iyPCHoI$_Xcl+!4`%A`WmK_*hmY3aa)PDDs?hO_mxoL4s zSte+I;Gvn+?_%HcRSD&Vgd1Q>zw*)p50vw=b>6GB*sMEgWo4_5&WPKTL2iF@;m+Ih zCnZ{!9YUY1_@?s6nC6t_J`lIx(_V2bza7tEx5C9)&1qd?PW`0|CzJ9$6u-> z{kWoP)bVKC)w?A#64U3L$Sh_hl}`F0y1Z)fPY=JLjumcuSgOgW+*B#B9Vk#T#vFdW z`cM>WWVRjuHop(;?6L7l8~ocI=iJ&oPPpy9a`mX|mKlQves&{2HT$bx!e?dkqQ+>- zCtM$yx3BZ*`JGmN`>@ZiC3#f~&+f<5509-ZSo-;K2qfkaBi2RsPHqY>O*`c{P<0sr4k^OvpAH(pq%u4H#tW(FA zOpk{e47mCzzhq|qm%OtBNAAd9oVDxa4Rpl}?#$I?WtaEQesZ_l^SiN^nwJHtli!w= zye!=O$~ki^NZn9$lQ}ZPk6NokFQT2_&OCe3eSFoc$@jQ}dM$O#C}Y#aMQvWUZJE?J z{`{c0!a1`?{4{gK=Jg|1&a7T|_E4V-J+re$P#;`&Oo(dQQ#ZQVpy5-x-Je5V2+w?c zHEFeD$&1BH9cRmr?I}p>w4c$GAJwS!fT*!ehqOK1LbrU}*W9S_U(OodB-ecI9=8<7 zy;$CHaha?MeZQT4Zri+2eq4{rXE%36Jp#5S>+0I1jU7k5-nGBzv$Ae&7XL8jr#|iP z4E{c2C-La6x7oLsKHiYTn`quu-gVszD7)|xchrWGVcA7pGF!e}^W$UFg?ru84lIco zm$d6@<=EltA9h&{9}qMenUiP*(HRK|gAe4~>QZ?LPn?m)ZN;vv`XBiaGM>w0jofGMLn=PobH1c3aaaZ##ZhCY$+I*5q}}i=2*4TswuCJEN;C zD=IC)VhcU$z`P9)ui1awTv%B)9oPDsjFdvN=i)A5#?C-!CqH9n+&v4u>?)<_fq8GX z_0E_+;!Stm%9(E(H@N&Np}c?Y5;J1Kizxv zw*sWKi!;>UMYv>8|q{*u?6Vau;MxM{USl;dNx`&VYvwOg0 zytt$8v3ZLR73fK7xoSL zl1%NHq1(1#tG0K>-rlm}a?;6}`HLRsJt=8SO_i+MhqZC_o1^Et z;c5W`Mpys?M8JRuDzHTUQ7&#N3_bo)Y?}<<_cwjpo8!PD95(bm%D25X3U)$1O8sTI zU5FOLy9>;B4%@%`&DFE!$FK&La@f$r&EEL(fiTDMUw!W_f7=WCw(108%36JmDH{YUxt+sZTCsL|-M<7RU#qcWe1_)&g*!>2Ix8sgXZ|MSu6 z_cnt|pS`omtoTvtS;NcJe-wF!?fR!+5)K=N9xqY#*C1`cir~i-uB5f|ycEf*PDUg5|@FTunL;5Q9@kFf-{q zz}Lw>U?lkg+H7Cfp~u3hq?M79;?9Ux z2~n2QLyHuY<@6XNip4`GMwI3B(4n{MbO(`A)3sc6?C2)DPPcS&?nD()isC#wxsF1l zC{FL}Q=OjGFKfp_Ix>EFxADEP_Xy&jcswOF8)$_6_$Hr^FYfTdOMox;(g|2 z(D=qip1$)`h2cZb482rpf>X!PleJM62&rR?D;Z=-eYxoE<2acyhIXIVR*(4#+l&M zF|#N~pV#Cn<4{|f&MGmr8dffZo@=`@eRk|%4LJLAN9sR(o*T(3u!&%JAJ2l~Y*nu( zBU;8*#Of%rlO09&MEh^1t(ie&m{I+Pp?G`Cawn=I(f*qmdKa%-Sx3K=-V;rv)i6vu z{8D;1d^Lq0y%?#ZV+Y&ta1q<^5z3vYxE6HPv9;$*$eu4_BV`?(gmFADWQJ5vZee(g z%AKeT5D8sXOrE)C%E(e2F&=sxbD9;$t|mtnhF5qEDwE+UPe@W)y&zZ=ZX^x~k_%C| zuvM%Lp6ut8Xg(cDY=sGJfDCmVda-LOWgG^|A>p31 z24S~Wu0|=MNI*IzwyQ&rq4qgTRRgW-ovLDThAaV@D;9>L<~pf5GR}lLTBQ>9V^m>y zL|w9kBz6YDI(kD^fz1Snk%(=0i%=P!rfI1(7{+a`BX_j4y9M}=oJ^yoHFbWXZRnU6 zOvkmSO6YCU8$?q2MckQnQcW`JAaVy{cZwC}Cw&H)_qe;UcEjg*k%ldO39?;&;p1(z z`gLro7IDTB*V<$Qs&%(_?*r7+tG>BGk z!HGDiU2@;)#wCi>@`k=`5Pm`9U7?9UWSL*kc&oUCAgR$H?8L}^yUJhy-(9EmL9G~) zOI>HSFrpvw#fljd(vi)BtWIkjFy? zW3#Nfv5G>EK}UjdO-r>HM>$e?yBrLy>LK)OqEO=`a&ffPaI-L&LuP5Dn^Q9E}cy)lz=Q)V5Xb=G*9c{u|LJc8!Q$B!>w@b z06|CzT*TIG5RNp%`ta2O4LuF6mFX$m#{@}0#K&rNiF`O6Sk&j{^E*OW4G;exF}gkEXm(s0)bU8qwgMFOH`CK~QyS;R)zW4pRuL>RV+ zV3=2|8Qn;1vpij_U5DNYVaSk~CKWDT^?M^kajIc>x#}5R(D;Y}RZv#xO6*3c<6ZSC zBV!$%7T*hJ=t16xma3F-80*7D*eHjD%VR4v^jOw33KNjF;iTcAMk%6z&@>2;gKM~I zh1PZGWmb$=yrmBi$*>(1k3d^}6nb^o?OFsGLe~RBJ|V@cd~qo0E8y9(xbzT4ZVZ~l-b|eV6EYi}7G_m2hs>Ic z#e-rqQP~#j=$8ftOiLu%SXE38U+xANwJ=M!i`FkPoRC>6b#9%3C3<*8!N8uVnVof!;i-&} zSgMXRel|oxZ`Q;f3=TPn14MF4E+@$LHcDz>z*JmSOpdND5AgJGBSHR%IXPeG|ay`d9(R(HD~*5csIx+YxQdc5$WYod&mOovU> zyFwFMoF-#&IZYtP(>8oSV`g`$f17YX)KL;sV9L!tBja&q zF76BzyJR-(#uVKW6HR}>H7=)>#-TM;>4x2;8i!Vwk;UmIwapUOrs`KJRA8UE1Z_nn!3Ov87)1APgv$@Xo`t!YQ%x8P5hela!~zF}IX0rH9ASC-{=x6{Ah z`pe(N)i0?2e3oYr+JA1YZ?jpRK@{HFrSR77>D#FLmCf~x?Ynp;b4E4>j^`14+rRxW zT)SlsT!U}<(>OGF#!x8PWcUQ%Z|Rn2&HZiJ&V;LH5bm1I&9&yo@G12a-r9xlm(I;H z_S@bZ$9oxsxrIF)^6p6T48xxbTDqJ-_;5=08*cm6@Ely9?wZAP8&|&|+b?mx?H$21 z?WlJSOVe+CqTzaG{}u)+7~ugm;6Viju!9m*K!gR*K!gEwAcO;KK!gp5AOr-&Afk)h z+MG)i4bC4a>*zA*A+l|bg}PuV;W`Uzcik{e14CK1z5$|+oW8nvUpJ&q?9BllGj;-^ zj-gkYq6yQOJoI#Ddtz}F*KJ#^!m|J_cIt?je9kgt`?|hMsLA0Lz4W-%*l>>1vL>rr zJ3QKCNTP_lz&}G-OCJixHHTp6-4HZ~Ov!lP%%`-`MQ-5E#Nz77U$7aGvTex>+y#T7 z6EID#HdoF{H*8Clid|LyI4ry%pn=)Yi$RQZJ#{SOxtX~j_L5VyevWC7DQZB6uh>vzcVKCbH!Clj zX1n}naWTv$6;Vesl&Ygiq22xnrRw-7Uv3{mq35W$Cbx9>%+P5$h9-OIN!8Kfk)_<^ zsN$;9A%^S@PfX@y*QvKUik<~#V{TG)bSPhLFLe{exo%Q*!_tgSP!jeedtyC3^EgO!n11*<@OK*nDLZPf~!32MwQBpp$BoL>X?phhevGK z;+%w_m{vn z)_DB|NDde3Kr!!OHDDUp3BJ7Sq~X+$h2do{&cc~0oXH<0T+^Oc!=_MD08Uow+- zmdxZ0&PUd{N@k*DCYK|uqmzl)hCf8I7kyVI54{8|Zg>u7BWiXb*^d6yRV$OzIPK{( zR*fr}kR+YPhG)8vGn1?6z@4@?E4 zc*ly`h^UkM-Edb!gNL<0bB^Vl)Z`N#Zk7qXn}VvLpT|R_jGI&)M;)Wq*re*{_zxoN z{oE`?&hOkrah3?2$#tnz9aWo+>=KaAXln4Yu)<&(lXvrlF$*2z=!*8=%+X79ugU9# z&H6%G`i6y(b(>`?nF$gs>#n5Ktsgjz*OAtI z-_T-!#(9*gBke1AcsglVFcv~-atFqCb%%was5FokCw8{b8-2=e%_u8z+ch{GRxO0M z;;8@?XI(Gu2uyctfPhRTod~qZ>=q=7HMY|wPTJc7JD-(xggv$kY0``hy$S5&un?M{ zA=Za1--BXTx?)JK7hHo6js{_C z##W|hT7ScUd%9W7at#BotGO5rUz9p)qS&p`IL4iP2}gV#J3R6bNKM)X{8n zfICU;LMLq2P1)6)#cbCQ7i-syN2snFFNPQoOO6B0mA8v~a3U9PWtIl6=+G&y z>z@?A$&3`I$sSPrVu6`>%2+V*(sqFO%z^dctV`s>L$9aW1c-eoXOMZxK^mS0IXpZxI!4@-Tw!>O zND(PDky7I?m8ujeq)6c)#EW=K5aLY{y~cGdSJky#U8@1nYa^o9IK4`#5UW6brZ$8>vT&eH&L9sPPcga*~xW0-O_*(aUO2{#(7{gGNJ_o%C^gMN|~+R z8?W9cQXTtgWAhuEt=@G6J$>h?^}DTaLB`0ptadT-Eo)wk zt-Qb_V?xBpq3PIFF!uvx1FB)7mYmOZwsMFhjb=cYn85__cB_2l8kE+-8) zX&QvBTA?$@5ISxtNHkj*M--BT1u|?TaY&m|bsRnB1%sKD7fj~_*|2M!acEZ&%PH)Z zy2gCirolptxX6L0>qYc{Oq5BF)=zt*tf6`3BC0Uy&86UBZh%#Xjpv2i|94(orqrJh^}k7DuzMo7&T9lI!2AuF>3TG z)kvjOoK~h|PI~38+&MMk>_4YaDwGN{^4usla#fQU2~n04BvC9L`V3K)^8`s0i$^3; zET@O|BuErXh_alacaJERPO6(IcAehIbtZ~Vj?pO!Pw(Uq2#TU(;wFll?eZJjh{9AC z-#0dItCo9X>mV}pHk)piuI__)XD`dVGBml$yfQCQE7Q-+%bln+xhh-er^Z_rFUQ5G zWz{CALN7)$g?04YVx*3_PO64D*scaEBE(OKXuC&8ksVRP790{}KtU2MFabFe9UFza zh7*Ui&(7v#@KGLwaElX^E*}R;ovdsqs>zlp>{usZ=nLQsb&eLx_MO-bReZwVk)E%%g2( z+E%7xbc`COS1!GBQ>2bjbLbc~A9^Fs{@g4dBnD^CsX67UD3xQx*$tfix$5{281Kd` zXdEMw3mRuMVDCR0joippO)8Vhq$*<@j`4)v>EZXua&~g4ZlXBPPOc+*x}{T66sM{s^ey3+t@a-myh}W@-dIV-Ds+d553J}rMh_U zCAeDRYY9&EF3nBcCOCDxl-`{Lr+VI{iEfRmFgz{Mc^L@tcEl-DHLuRg=pNf-%(j*3 zG`Y&WGKt431EyM;n_Rh%l{#k5%&TFxtVigzEQctoEQDUewOtv9hA~pdR)cG0W)SIH zmblZ1u?;Vg=q(kgGA~^MBIkCm9L)jKn0yJy#I>h~YXuV!P(|5w=rKMTLSM4Q1nePIgKG`v4(5Du)E zMkPo#&oDm9*sfk67$yN_S$gkL6dlJAMNt$*z4x4R6h%My-~$v2<-PylgAdkqJl%qfCX-1ICX-3;9rfOy-h1>s z&))lFGU)*TI4ZAq2xQVf%6q@Ik*KUZ3o{9}0`)pv1Ae+EnV zt?9X{kADrDOqV7--#GiXX3Uy){cV&fdKB(~p!&MUe%ynF-ojn8F#a_E8#tfM==NCu^1BDUcRGlgj&^v$q7WEn>5 zRAOg{xYf8$7!jLGEzZ;Rwu_UsusBhEahfj9E<7*cr#2h04qF*tN*J&qCPN=+3o-8< zZmLGiD|%@MqFL)X4z0({m4;Lu%gUzp zWCM=2Yxo#z1)3ZhUZlAqYzRfEka__%GS~^TbMus{BS~>8QhZ^3H6~;xG{xnK0%`bB z(^(F)F51jEDOE?0fy_E7RTU3Xs*aCN%SKZ8lJ!ukj{XDLbRG)Pk6HP0#~1N&d+5ed zJb0~QD`_1?%0i4I^kOn6ZR@MX8Q*q!#+owF)Z~l=1A(%fH{0bOy4U0+Z3FWUoXX_# z7l{q7+M0A+n2-DJS`}=xwCD_cNP24W3Wn7B%BGE2S`2OXiW^=vYxt5w!+%H(uWB}x z)xto5ET9icsc85M%VE}86lvB&ZxwB37LEl2nU$5t>=9AS$WF`pIND^6@FinFl{eZ= zwe}^;riWhtKsKFg9vgF2Y)T64h8}8j$@1lnBFc3jK5oag4c*1noi)pmfLt~%!tNZ! zBlI|i?i90PBYPQnkzLYb);W(}a{Q2e3^8PP+!L9TUA!^K&evDXq@Zd%^h)#-dY!_6 zy`P8PT?xJ0!{ODSd!y{HuXsW!%Nb)}P@JPIg3WrROQ{qRHZv#{HpNw?yDCiwnU~N@ zK+e@f1U($H>p<(;{)}ud-XQ3a%pSHUrLT0dWT-eA{LdTN@vR&Rjy+kb7a%-4+#rtD6T3XVwdLpdbXlX;r4n=$} zCSIe}A#nh)w<-;pi5rYs?@?Wxp{Kje=_*QCZtOz`qgJ&)%>UP#nI@waJn}QYN%xX3 zg>;Fh3Sb^%T;!UoVpE1vbxcv4+e4{3ifkkXR3f6>-X)Qbw^2|`WI<`%^CK^^Yh*-x z!C_RB(;URxU85&e$MK$!J@gu$R2|J5gX|c`1JhbxH5LU`V~Er#QXL)8PmMp;R3}nu z95o@{&WNNYrz@*+E7MDsb@Vb3+i;V0o1Ed}Z8Rv$DRV$=vKRMYTbcgoF5>Ma3in8DG7iJe zkpZDo2{l9~J^KeE_)f>pD|%fY%d$n1%wJ&_wG^kNGO6pNu$P!NtX zORG|}8cEK|Y?O+Imrs^cHR>|k<#}mDenuwWJvgHgGctMTd6dgV?#DH`t*S8m>!ER9 z4GkW4T;0%XP+b05Uptp~l^T^?87Qwz4?PEkB8oIFq8B=4Ia_8~mKYMzYkZU9>@PAl zW+JxXb}LmMuyj>nv~r3!*Gbnqq$}!JZfGT@#>s|=1UKbu1RWld2k0>AJiwdn&HwO8 zk{riz96=*!96{p<@?a>6(*bfg9FEX)ylhWuwG6P;Xf#xi6WtgdO(yePvcU9BFnq0NtKATYnklUL~rMs zm@HT}3WDIyMGbtFZ4n$^g<6YaUB+hPt|C7AywOMVUy^%Ftn0lqEXYtL}T)y z*o8xAiI&pN3yOx{=4wi59NH3;yD&Q6jcb{jiQ;7j8v45GOm*K}_b!uNLPVDj+N3f#II_Pk>lm>F#ifE;e)B8+yJW z^g@qu`_Qu!p%;3g*Lq=2EUd+xl$I_-SrrQN%s4Df{xdiM<(%Xx92wy_r{?iji9Dg$Bl~I!!Cn8>@=R$I9sQ7OdG*2Fc_~4O7QT zWedBt7FGvr=37MyrH&|;x(>aca;VgGD~B|W`6ZiFYZYjFVm}Nv5u4bMFwIQ_iDkgk z4byNcp49qNp;yb{%?v#YP8~sB&PK3Bk{_TN!wvBDkUT*0;%I>D+8{xb3PghSV>W^w zoH~LZhWP+Lw2lNB5)4O3<4N#R&PH%8XCoMT6NgX(gxtQaRjUMPUDPh46_K!|>&3Rf zeCBmSrU*4}9rj?2gAw-DNlzrgHmr21RGqX!4>h$iJ;!d)l5vM##sNdA8(mszEDS3` z+?m)8gvDvahlLXxOVd{4T1NqvY^c<;70P55N~ahZff)e?XaExv05BLF8j*=6v*|R) z2NVDUOgtn^MocO)B$Nq*kvJ?22ZfOs$59{#VH8O@#TZjI&;Xmby}Ziwni!<+;9A}7 zjY;2F0n)gi-TJ044{_)X?e)rJ;DdD5=KyB>LQjHV7TqRjO;(n*s^^H?j5V%2>`_qY zm5w`8^Fg1i>Y>FwY??wZ!(gZf*+)p3`*gc@zwf(#)a!aho@O_>bu(B$Z?S&s{bNPY z>ziu2552LH+l}do!=%_ebT=8&$0e(Z1&a}$Nw){if1o3{jT5ftm)IDn`oCR9xP24R z_ob9(hRP`lr0K39FpvjqSD6Q+8dTj`&6#~-7rPWawkU12@D8%@g zHyAF<{GAN^O^XS&u9g8S-^|CMb3LmBIMyQ|3Z!A6GZK6DU}4N3+qcza?0%%@TGA^% z0*Xoy9EB$=lxx=#r+S{K5}dIq$HF1lM?Z}+QmtsVLO=>3nrkalYMe~}a!ji5@FrMA zTBeiQfTloge7=KYk(?C8j`QE(6Ho_k2SoFWccG7>x@94r%11^$w?srRV(|RO__7L_ z`GvZ&1&2F4D_~*+Og0945CK^w^EB=gscnlK{I1NcIM^0Bf*2b};ZUCK_=>vq=rfkY zK!DiiV%Q*Id0LW@su8Ba6RzB;UvrVN7=z_N9fF%VPXznFM@(|`nrIar)$EEHe}Kh| zPEM+lb+N6WH^bCw$pG^XUYn%s5>I%biod|$X(jh7vlr;yK%nke*X=SWm z3posZCOO#PMDSpQN|JAq^52pOYW(EETB4{}02S}%RTr(r%TzL1TbhqJDnC*|vKKuJ z$;0?hl|%1A52bs1BsI74fzlB8Gazz`nM&%7OKYys`_{rDZ6=Jx+ZiG99&Mv-l$oF5 z<5P}Z+On}WZI>r8L6=48P&{jP2HRccTqmnI=FFlL*HlXs_&2gemP1`VJEj3h-sQqJ zyuAtmwTe=0je%cSfV2~c#zt(Z{}-z-GaKnwaN?->JO+#nl^Tg;0W3PqNO~C~YeXcE zVF|62PfMU8a)~6tuRrWn_d^l5XPBWN`T2LfIS^ zu7i6cADfEw5Hd_mylF>O>YIr48Aifai%bht})lCctM zmOyBWzz2s=@*p%Y_+m5TeQVwkWN;%V>9a$ibG5AfJaF*wH-F-DBR}^sV@G0=Lb>J; zS~V)sy`DwjA%V{ue!jfO>%f^0@XP6&wdMcCjxitrjuOAuirvXhesW94`PwmzKwv*s zM;2t|aXPSF+Ikjn*}O_yHO6siGz;|8OYSa8FEm;rUtF&Ybtu8;c*I%EO>lGijKw|N zqRfx}ZF{+JRUKvnsXS0+f*kg!?h16BvWkvkjZ=(O)5@dVXHk}Rlv%MgDSr`ZQOJs; zz=7)ti!zX-4Bz981ivR`sOgc%3|5aE&v|kZY0=nue z=h&vLWa&K)5Er=?_0l!zsyH%6EyN&; zOazwi!QoSuivn2KFP0BxmEO7$J#V$7x-I8h=AS0^kp(yQHHpvKCbr+k{UyZs~KF`rl0tJZfj)6@s_3Qw^JfeXL+7zu;o zQ1F}1zQ%Vpe4v`$d>-k+k6TJ;Q7=nmScNlhic%3ZM+6ZgOBDXs{nq~h1RCMLHSJx) zni?@sUeT6@>Ea>e>OEbbit_ArN(k9>Z;_={Uog^0HS1YuP#WpYb-p1dr6IE5dz417 zQoA;jtgtPQ(8vZUzVG_)mB{ed;CjtL9M6CEm>VBdDGgLKw0~fZ{uF4MSs+ZoFuiyl zY$mq&(Fl1G*LJjd4)D6%_Qbh5I%TnARP>u^8Mt(#bTTgU7Ze?JN3DC&!(_XyzzA=py`f0FBk5r$+b+&)SM_WYd*hN5hezkoOb8`MqC0_2 za@9zjw6?Xe*eNQ{6$;iGS`I8DzvT-fhMx)?IaSFEi51t%88X@J@xoDM!Jk*o3vMCr z=Y)x@Q+dbpaCZ}oC(*|AV}A$GtYcdODH5l=Ik4uqEPJcN3zQ14ML9Cc=uA(dkpQp^ z!5y8h+McN}8*J>1CK>iR5KRM=Myt->b#5+z?E78{eeW zvAf_i(H<UnWT%V7L0AT!N8Adp5I zj4)IhC_ZC^4Z%Q}?) zPa}TdlP~rC%lv2{muZ#W^@~vP8>Li4pE{D?xJiBW3k!ZXxN>v)ZThkO{8A2BV7qh; z^p1$=RZ55!@ObF2!DZyF+eN#*AFpL@1dW~YTjrip=EU~Mhm9es4MR@0Qk&7gv^nL-ar z3Jap2nD)cO4Zz*hn$Ys0!Q8(@NU&rs#R`iAKg2==8@h*J6(Bb06l7n?1(at*5hjA1 z(AiX*lZnog&!pf6yBu+{FzY(exQ!jdxzB0))Dh+&W`n&zJk^;~#~epFU?`|OC-Uc z^n*Q$p24f5eGMQhRe5{zBftQYGZcg@ISiw#IMsMZf|6Zj&>gOzv6TYoq%hEVWOjaQ z8JL2tfX>1H=a^R-W4TLH0K|g22(QT?l)k z(YgCF`)$O(7n66SkjlkBa8Ftaru$AL&{nGBlmqBi_q|TO4o2y21H#A2^qWJ7LTr%# z5`~1u?Ht;mykxnAhSYvq>5GXpEdtjo^upGS%b@d{gec~)&kRL?XoFokXP2^uriP~6 z;=|NV08F*lmUQ@G{WEHA^@yPV^K}XwRpUcY&DfAq9xXkjZsq?xUv{$Fd*quqBd~p{34VHhb(b#7m)IQaepe-ofEztAI`YJ-xk4~3zkYqKqK*l zGSf~uF*ku_<$W%?g<^|-kuwaV(8W}%E{hB5EjS}y;e4F|yXkQUcU^^3v~bg#ce?6We5v$1=YXJB~vYt(%F+4&_#r~eW`Z0tfC>US1=lWn|l z=X1ryxwuI?fE&1^Z!I+dNreG^?17ITK5R=1NoK0Fcq6#uCVE^0rb zq21h{8a*R#3t=>&;|bV(ib~Rl@`s_GW%P%E+}mQa*YBSVnh3_b=c`LkF)nG^v8P1s zK1sWx<|9A+p?|68BTpZoNT_x_pF?{m6rpeUJEZb|H*WklTIv;SFM}sesZW~K|3~Xd zRHb9?QdtvQXDb3_EUj;Z$!a;U*ZKL68O5rx#VEMak`dg(j}nF9t&3sHBOvdUNLtTM4Q*wD2Pw;tML=O=GS2gn@rbLgLHp`vcks z#IR&Lpx&U!HNNTI-oLhB5qfT~U?rSHaWBi63(wkS;#T~yiSfirpp0%|)>$gasNln9 zm#|a4LiRd|P5sP;|Hs$MaIGTIRe6Kl_?43M{&KS771D~MbXho7wqLpv-lYfhFI~O> zbIVQr0mjs-&nT7Iij|Dkl49gFj+q^$PY0fLjY)K?6octw8CY3z*(v`zKjbDfeg0R3 z6DYK?TFapFR858qLR%>Wn>M-8UE6|u`n-;N#J77E6eOnV<1uUGVHvPkL=yZ{(7a2u zqk%W1N!l_4DD7MjS5qwFHg5=y3S0`}WCbr%N36GrkI%@kxE1CF6%ZPVCsK`7k!BLq zh^KWOBf3wD1Q5%seHg5dDls|KT&p&vv%O8!==V_4CM#DXp4N#9{$}1U8WB$z0j;8}OFK>>`SJD(V;OK4V!bKnXU zaEvH6DZC01r_(#Pl;Zp`_Pkel?{8|^nIyLz9mHu5yRu|n*#)*^GRj3I&j)%~zAf4$ z%R-txuJ^>am3KqHj%7eYPrR!zw{m?$xFmArS?3$GEQJglAJ9)7EGX(*l!R#H8n~?r zv9SS0Hg&u+UG4Q!Qa$q{wzsQMVqgAXA1BS4W?u)es>dYF=4ja+`b+qi4-i6}9g*Uq z-t|7tJtnmz`V_0cvL#v{!1(zPM-_d62>r#G&jp41ih&n7IPo+^fIsI|U53)$=bCdp z(9BxuwnJ5e5WJ&dfTXy^FijJUC)PhN$vLN&n)!A zG0}Jcu~)zNmIIUqFa*Yi5_zMr!)-pU`HTVJSUE%E_(yxw!5HR}q*O9t5gM=yZ2(DX zu$058tP&f##Q4jw&KvOEgzRl;SH=@SDUh3Jr+B}EYNrjM=DU$Gz!+bHoW$6Jo5>Yo zek#n|hJ!$;+!TfTko88hMh#QqpZd%0Z6=jBwUj8%{GBZNUzAaP$b@Ci#=lFUdJXFr z62`s2%E8F*72G5&g?;M!dp8&k(nUkvboy3pCy*_rgE_)eYNwsbPhl{Sf@i#%ouBm8 zNtZd6?>TuJn7W-Ow8i`mS#P`zgJFF`kJpf0U$!ZUuhw{W$?i4ma#ygN}73X1rB?dfNQYcM-^%W~Y+1yID&Iums$w`=f zgH1M?ev`-Q)Rb`)Fsudsvn^}SfCNNiEuJ3YqPup69+THu<{B` z+BywH#@h_e8p7^LVv>AhfxD1lGBlV1%6= znKz8g+67|>HNS+v=n!1Y1${4vrJTzAq-gCrIYk^JSM%w3kDRKHvN#eh8Dx_2sg_w3 zaX!%P!^VZktKmMEI~1I0sgae6YNyFdh>Q{K#qXOb%XDRs<4)S=HJF}oUJS9E{Sg9K{qjz!<|P(RQyYa>52R!fG2VjzRn6pWQglu0qe&kZl0CRO*IY*vm)n~4!yZa zOw22XN!En-P-S~FaBZEn(JWJnVHE9E#Ad~#FbUnP@><||4iyuLtl6)}x} zC)t~u%Et@id|sCgyq%PJ6!7ccuE#}Ec5&n1 zvZ(ZvlKutRcmd_G|HcXE>)(R45Rk9IKf}NFICcFS#L^B3<6k;xWTqMPg2#Ga*3`e1 zD1$=a-w*7REAfFc{zV~Uh?D*mt1rWPo73&%!l-}q$bvg^h;@KAGL(s0gQ|hOENI$Lz>Op|*b2BJ~_*;Ir`Q3HN$I2Doq;pit7n1;wwOp^*A zFHA2}m}0m(IF#lfc)iMc;O-kc5g%tTnitR@u@t3rJO-k9UA(AFL{_3m5w>FKDj*aO zN3qIH%AJh9wY}y5z6bXFpJ{EUOJSA;YwaT$$41MaVHa%69M~REZ;hkn7qi9i!U~qA zVB|Osy#j;*sh93jke3lBQ1A9zP7Qn28pJ@oYlvu2WDXapcQljtb#FusG`ikWZ}q>y zaYnAa4C=jcr>YeSm~sqf(3mD8_9AwzY#1*^xu!0`Mk^VeBR&-N zVvdLm{DF-YaO=0c2F%0SR(rTIZrNS)0%RHE$4)M-PlNz_|D^6On0co!Q!RKgXsq?{ zQf`>B*Iuqa_c2nWl5$+he-SuB9boiB9`XaF1312EkvSInPKxa2E+Ny;cU z$KMDcPDRCDmzSG0&@JhTXB8UT-M^NUf_OU{m^z9Lx@xP8Scum+#mMXKu32W!G0&e2 zBDOfiCA*Xl@q5L^SQ4s>w&V+sm7rD?kMf3^GVR|Q5Vncym%^DjrRpVHDBwu^xs$PZ4OOos zX~3-ca;aDE&^!y|6@WDEfYhF$i|fYP(W-ZqPNi2b>j1B08OE%7hffE!^uhimF#oHa z8JqCLe#?}v-lQkpjf5{rd*yC)d@aIq)eC(ik@PnT3*r6Nmv1%tclAzqhJUSk+d<~H z?yMHQ>LtYiEHSD{SMS`AqG(jTZk%Dz0ZnOpyDRW-2?EbBK0pSy>^Pc*4aL*ITaxZ$ z6e$f4IvCi*V-ec|X$>4aFzbjuLOE0Iso2gftH?-Fm7p&6&3`W_P9_MhEh;Nk&FwrA z?$nWsq;EIZ@Tu{ZWtX(-COf!ME#M6VorBYhAl8r5oQ?fmO)Bl&Y>2>#tU$UM(#I3A zQ6(S$L=Yn^J+qGq?SI5Yt}$qRcig|l&n9j!#>OTM8j9mGn&39SXx@Irm+$-(1SWUs z>nsq_x>y)bq!GnU0`p8b(5X{F?&aG%rXJ8&AHuv^Dxqe`WmrL&(d<{anw0^5{6#`* z!z|biPL`XQfcz4%)<-J$e_9WVoOlFzbbH!7b7l?OBaGd@)&CYk1 zhhRZlCFOp1ku3SeyYVXWo$Z&{cYSFg@b7LJhq&=>UbQc$1q53ILf_q`X90~PpU(0V zy}R18(B_o-nGTZ#W4&dBn4^7bZz;}>!bj`-#MoFhMTWO=IY^I!D=;J zz8c|#@FT-qnw~TQZ}L)E5|D?SAQHB63aU35>04e)K`>${`_dKVEZ3WumNc^~6 z2%FO3@lP((%fr zRf%S-+^hsG$T zYC(Nrs^>f}I}QUt$}MZdvP3)JUimd%{A9^BG>y%(?z>6e-;9G(J)jkk-~*ixFIoJ( zS1{$N0=B+EdNFRPS6ujGO@m_aLmAq&Bv$5bOWD{FJhI#QAmgtkC;pkxMfWihGIDg2 z9E|E0Mv=>CnV9g8k4(*~i_n#jD`YvZpfO(|hEweC6zlmE zVf!_*Zy8I(s-TK(H$=kz5AR`k5YyyOiodMGyglV%L&b6Mkj^WXgrpd#u$p2N4s{Ir zTN?6JL;%n>XZimFP&Gq$bV(Lyy1e_>P~?S36p%1U!taGwo~8igcsdVwsQN_a|WI?C{JceGudjY@#jvx#7}WXvu{l|bqPw9m^KXL36aXPLBU@{3eKKD zAGsbR$)R2(Iu_V5)nczmiF09{m1A>cw;vDR>RK-DUlex;RMfuNQ#|9|q)6%&g9m;D zlmqcGrPz;Gq4Xo!)Hve9#C*Yy8~}ECD5xdfk>9_VuJoK#>DmOD!x4^ck>E8bAM`QZ zr1NBcwpB#*ZWOjp(ok>4vQtaL$9nHZVw!m<;; zj}xUkY7K$KbZheLi7FsyDLL>u0omxLnC`_UEoG4BJk7(*+8^2NdkTvKgbH6sU?&cx zHP|CP%{jnJoSPMEP85?f(D+%C^8V`+?gR~p4ly#oGOl16Y`-N2BKy-=xs@Tc*R*Sm zX3M}(nN@~8ah9QEr3Ny9>O-hbXl7D^Zs~#lN#CiAG;Tyh;*up*Mu;n@mm%+)@xY2` zC+aSgxo}2haJ@;biq7xArC~2?)oZc9s+k>wnb{a(tz$Cn@aS#n;N>~@o+gJ)I#@Y z)$#eBl8fcBKae;IE0ZV>7AN?EXl3x;Z--?qEKs*G2|aUXSE4K_Igv=*B+1}MCRSYQ z5s|0+VdrP8YI2sSWb&)qdtum>0^WhlzC0=X!YmwSz4AFn61R|jbPG>1Ym6z1D%Z3f zg8Xx;*5EN5)So^%_fI!piKKCDch@`ld7708xCBC|z#6^U*iy<# z&=SNTRSTIs+arodsUwKl%F6Ci77*8;T>i$oD$t3|>^*z7P^#cWaQ z@;v1$`fva(Wz)dT2+ehkJc2&*WCuG3@5XA<^qDWmTPaoC2;Ey460W10h{M^Oc*^0$ z#EF?*q5B1&VSDB~PV!Uvw`996K8x>^J^BDtyXxWn2N<}AzD?U!0@GaLGGQ5x9?j_2 zT-Nj3E|$p=hLWvbWjE4ub~UPbVuQ{$NryU$_fODwcd84tzUb}14jP!}WlK+WVojNFF5KY-yT3=VgbQ}!l_WykYLAq(04CsQW|=**sb1vx zc#GsYBaK^z9Qczj1y}E9rIB<;IEu3y`T;pBB)@y{*t05Zt0jcobK$oynBgkIcWb>1 zJnLQEy4cS?8%bq|Rdm`nE7Q(irM&7a&D6!74ld^OR$kA1w$_Y6IoIt&pkT9Aj-xwk zdj_^GbyzGP8Gc z%<2hGGzwqyoMXoL7mgHNmNG#&HSE0_zhZU(3^B$VEI{wd7Q;}c$X)7hn ze#L#D$EnGF1{@9U9xPY1_p;RXmMSs43@UH|hZK0z>P!}e1ykjvZ!f<30m_26rlLL{ zPgk#d1d@rq`JW702<_2Oj}kD6o+rr(m#-=lSA|f@2E4DtY1@7r+wuon!%dBpC&;Fs z?>t{cZ+wbvjGV(Mux?`hvpY#ax)uAJ64@v)bv<5$B~UT&C7|N{9pdg}zHH(pM5|W3 zi;*RobAQpWJQVuQ@?tY@PbBtF#uj>;KRFNlzA37zTs5R}6C4@}ERHjX3<(TKM$|Bx@QAwaZ*}L3i>Lq=(M(`0*E$qi1oRFh(B5n;8QvlXp zb-2UG0WF6}-^0@c1)gjUK!Bk)#SbAZJI#EuE)}hrJgZgRNf{63d`h9>T)KF<5o`VQfmv6BXC5)IDN{d<(KrMuDY{2JI9n#nuHHu103 zoH|;{*$u2YDx*t~Dm2HeOW;`laT%p%vj_XbIpqAFk7Pq)Oen8YkFjL>Yqnh=!;L{S zR)3gq{92VzAhw9ZnA%IkUS*C?$mm9io8XXOXPAq5=L@sH!0Ml^Rl%5+jY5Sbv2`Ao zEZ*h_jXb8`eyro7E-qqw`XeWmsD7%$STO;%{63N(Q^B*t?L9g6^q-0q5D#K855rh2 zyqpOM3AVW2uwf1V!irg0g}Sp&!PGXgp4TnodMX)A^X`D}_a05{6*P&n(U~(7ncC|B z6~kKNm{(V77PTK;z8vz{NQjPGquWy2lH5dHkgcgKm&tU&yj-uNoT=qhCU&`iY{FUhOM#ESaNuZj z`P_O0oh@Xa8%wsPk2%xxW2eB;!w!=W%*w#xg9HXWg`~Ji+)+GeOYIhse}JrYID5u= zZZ{=9^Lw%au3GH+Lfypg&0ln*(eGOC>u*5X_HJP+=Q?iqC)Y!v=OFErFr8c_bUwro z@=0I2M>^EJezzdLAM18qGu;Jz{8E&o!ZG(#6D#g82BFrRg|?x}&f_zpEp|&%$VqGd zH^r}KzyWt|^5oUCz{iO~?k2W=U7eML>2EiRs%-K|j7Uqoh`Q^h0<13KnJY6E!knn0 z_{D0+&MFn!ut~`~6u)C~Y8M2&JZSD3J|NrBRf-}~!KZxLI5*>DpTZ?97ZoFlF0-{G z!`2i3HMWCgBSHD36ne+TPdX`BA#1yHNI5=?>0pTTDDgD7O#8VDQRabfIn2Srp^&S} zH@Tsg7$U}hN6JJ)&Do4vyN_f7M=Kj-BT+@*bV~BBG@zyZV!fyLmziI|GZH@)mH(|( zEBDX%SDE64S#I&`1?#U_IN~sL?!zkVdsDq<3=X5b0mP~LgsAVbnt zK(N{y|Ex5aw==11Si|KrCO9ik@N8urWU7gX;VxX^g{vOU9p1LG-ZR8gH^5>ct~T{ zQxRh9#e!3$UO4Olrk;Swd4RkXEpj=W2%z6J`06ebYezO)LK}&7O_#gn3rxiF?&mtA z^X2&mdn>y8toDSe`aeNW0Tr+5Vpj65NxP|f-cZrGO@lnToglAkC5vyLRml=AP;Pzs zqqqLTk)+|gtMbJ%c}j=H{J>{l?4VtidoIu>6={JOCIWE>uJ+_uuc zv$b6no*(!%TmH2oX?q&6%Q+m+OYkPpEf&IS<&gb~nCVRRx|#5aOC=V#*pPQs%4Q;? z_`8$=)TPNZrp;D6w3dW*e7=}l8Jhml4hZ8d0@oZrM{EyI^eB`xrG%^3M@YOHR~w^C zHQD7-Ks^W6aKgF&D;^G~EvqA+Cx(46JO8ehqaKp1jQk`kmYe^gjO8y!abmP=-|LTaim``qwb8?4|?nK?a zc|~=@)AN|RN~KwJO&@5OE_PUQov^psYoxC4&?2UrrL90GIP;a+$iw=n+N$d+;4GsBc>l!u2KR{Q}=4O4R0@q`B|;}*cQMNBOWq@oV2?D2@*jONC| z7!{)%=uw=8wI@lVrzjS{Gv<6*v@Bl!oD}L*lqErH|Bd*Xah5vzyRGl27U<$l<=0id zA!$gJyG_tf&-dpEbh;CikCZH5+E_{)VU zLa5HGrK|z27-J4&1G+8k3|Z^#5qks~FQWb=`O!jhSocwuQBmcUF&V|cPj}}zaIPX_ zAQLR> zoMA)SEBdZOERA2t@_!qspGyy$$Sj`gheLmzPhmWAUT5l4eY^yP9%(*$2c5*Rb>1*I z&klMF<+(xbxPLfvyDQGxz?n;fY)!H)N9@;vXj%Q210kYm$i@yo_?BE~JaH&Y-sp}! zUq+}VQ)a)gN?U9e-h4C^Qp!b5FaIU2CNaVrH;bbr-gcRN-E%;$?%H#;jeliJia~PI zqg!O-lN{tqG8p}(1+{9d@05&y#_LjSdG{$d4e#eF*MnBkWJ(RNQ`Ny<&eY7IFEpI; z5~u4H!uqXWZ)?XLDvD9q=(Og^Z)}*AtT{#c%uYzy&70W+ayM2LdDw|s*fp{Yq3ZpC z61Bw}?(vSu+Ku9Xl;ohGYBeP>1+fFuY#NdYcNeBC@I5-6Ri0?CxqX?p>O8UXGoRpveIhd8J1MvDV{6bYB$MKV6vblmWUOjhAu6;lhz_2`_IS1H>DX7b<`A%Fng< zAnQK_EqKlGH+d7b>*`n69PWwP-j`zt)ENyhpBDOCzU1P)&0+B<+x_h@VEDD7yoG5>VI#6l zF{bU#@_h|v^XTG2M`zgtLFA0h;Z6CvWeT=!z6i9wFURA zRiiFVLk3LO_AJVwg?$`0dL#g^2X{mAqtN&}4;2w)x;FW-xXK8y2tvUlbK%St_)6^i zN=~OhK{Xl`C@t-x$T7?{_SLF@D~PWAPTp*<6FB~S9^dx~^|%~&FD|@_(YoP!V7#}& z^AH;+0);Dh4uDhPd~agIxw<|7%Q+6C1YCM6x#o(os@1{g+E1e=kY?+%uV@zRswn5^ zcN)T?m2mr#E=!Pa&B@%x{^O_Fg7wa9;8rwZlMccUS9R?qzgASC-2M7qG-B9PO1YYi3m7=nz#P^eg6EgS5 zjv&~^^6w^#_4&X>dwe*qwcV5*O=o%av6ahPSd~R7u$CrNoH|A^1rH6peW-*7-Sr74 za%7IG8&CB)`cve7hZjwwluaZdSGw}wxrB(CpTnUXY!3P$s)8v^=Ux|9N9s8PBnrB; z+54xk{RxJilV^%y>+v3n(jY%*%{0EsmI`YOKM=cr|J}iyt&!Yx#r&XSx=P1#M8FKP z-TXW2)ES>cL|nb{HXqGZ`72W$@+$ELfkpl~&guJ)le@w1i!+-TJCx*5#Vba<1j&^mar`nWW66D49DsAydsp&I^N6rp|L6rR!~Y zcVQa;Rk}*~?0Z2}XJmuvw@*8XGhJW{SDU(tiHAYqE7wZbF)Umwj1O!K%CyU}^p9oq zzEExRdxW++m;*?t$IH=lYm_iO%-?v#(12UTx;{xs>D^f|hlAjt#~f*bXE@^Dy%{@t z!^*FEmIsA&ofAUm?6vTJ^J`&W>dSeCTdSMiPBl`?8|{JPeS@tH#a0pJEA+LyL#z+L z%8-CWiSbu&j}MBEr0@`~lIv1mueG@Guu=qfRf+d~GBI-OCy3*dZ(L(G9M5VkwT_(8 zSv+C{w$w6RbX!}Q@;FXeUr~7FeRB@F&R+&sib*YrBKmD` z=<|@Ag!VFJpCta7mN}aq!_}MnWk7f$eBjHj9&u%0`4%!EM}2S6oB3lx@I9+P%(IwH z)cj}pn-hdY8^D z!-hfmKTaD1W}Mm9D_%j}rr!*wy-aYZrIArkpsb*Ke~}fh?gtLjE+UY{P%^oYF}q$o z9#c3x3bhp*I+2uX#1~4BQgvue9q*3&t=fh#Zymy zvUSJSB?kmJI7@DlTVXj#$(yIZ=C$*C|7%}iqUc{8(c$5O6e&=w!*}C)-5Mwc3JRcV zv73jDlV;9Rq|dP5XvL#52^=S_}#d zd~0iL{J!O^TG71ZAX7U3b8)e>+93)R|Kjh=`1olz&yVy5i!DKKN&Hm1kMVq;@Zo&qnth4Rax#468|IT(0Y9cCQ` zg*a%UOrXMdm6eS7OLA1yGl(cRH*Ll^6o-{Qe&GIHSwR7c#bRP+j^BTTKmtWSd^5aMEL#XiK!bv= zpa_sUU8QVmR6NEj->!~&Xzoz)nXSOY7#P#@^Jx#;t-tYEbT^&bwmV@FP;G2%0s{lh zh_dbHimr>*E79Q)(Tml6Jv^W|t&(boPyh@(GWJ}6%Un^IX4N?q`!!?%f<9h{Kp|fG z+13^o`KlE)I?T+>{bmiaNhI7VdC~ymz7`t&bPlNgpDBWgZGU&tgxYeh{Rv^Iu9A zm$YZZ90~a4rpX;{i2+a4+-H_cmTY35s|X;}JB7*M?c-|WYgAJ#E@ZTSq8$Q!0vvb? zsT*qV;wAEA$k1xAs{-s^o27lmB(u;rQ>mUK$q;4p;F%L#rQ)^E2()WLteLaRU__Ev zP2yPB4(8Fj3g8ar&9G33z zr8MC9gTU(jp(!;bY=?mKxsGd7Jp>@3R1(R@zlcsnowV!hw!Cb}zEh=(3hi$w>y->v z=yi*GYz*k|#dKst<^$=xbGuZ_&w`#S^}gC@aLs(tJQHCExd@BDjY$+_#BlO?)%mJA z!BW{X;&v6!>oSPy9ARYPFn{IAhPvVJXrI5ck|Am$DI(9ojNz>D7vE(xMV3hLFHyo` z;+utcwUmSqh+4rSRdcBS`Fs@yo9nu<6o#Vthi0f~%Z}g_BeM2OFsFFXaVuMLF-!!` zUy-qHm}#_a%nQ@icMq3TQc}?)Fh7DLV39OOQ2JmshO#W)eXtWx^#qEQQC=hgi1P6Z z>DAbZ_h2y}Z8ASlL3C`m;R%MD=Rr)I^kKWu%CZ2V)hW4ieawY&WXX9-o0_1_f5PH$-dTW7Dcz-@87s*q6h7YNz{YI&%lHVN?8=`~*O33PGtofTfLCA02dGUvU?>rxS!Zb`4(g zGMArUwhn)3?#HQA2#=rY?ZtvK=;c z1YtY=_^gwR{)L;xiV#j|rmG4qN^I6MF^qV4mvyJ44qx1P6_Ih>nSoTUfPuB+%M7>t z<-i^Bh?{i?TqkJ43gUI@6r^}x(pTu$yM?bq@2vWxFU(bMia%>ayc0+KqSU;e$_9?B zY)q1&=+i{x3ue4o)pnYDgut^t;i7!4f+ab0fIwFiA@6DD8N#U&@fRsQqbs0X}KVt#B!#B^QGC^x65lwH682&lzz`nEXJsBTo;Tf^#u-4{h^K z^y=_k^}8JkRt4D7h`{b+!!^pW_p8lPr?sE%X-2uO#P%2|Tsy)dg|IUWDW5$c!c0z6 zt%E3qJM_ZVV}+tE4uD~OI1_|6T+GyOZwLk~v$#*6)?MKs^kUEZ%hL*4p~05-F`r2O z2z=*E?WZTFrOm-;)L8=XDviAs6~$N}kVXmNXVb;Cb4VO*&!b3-yw`sZ>yJYhW;Fx8bde znFt?Eo8EXcB9&_+=~#xSo8t7~-%o^d{3%Q=Zh7D(%Hpbr%?`Hch36_Yu2i8up2T{o ztlW$I#aRXf%Wp=F^SA<`zXU(Pi8ZZrn9}P-5So+R<62_F`Dk9`#68m zc6Rl*T5~$#8>7sR1eu~Il!X??%%G9(cBOX7$Eqb$4qHN`T&M!#tVfL7*{_TEkX~~; zY?^4SZDc`?bgV8@d5OR&Hkf6Hza~Kv_wyEH~UWEP~#YP>EC$C%vCfNNX%XOrKCEMX7%D zn%I2;z(4PfQi+oyA6vh=Ulhw%?9^h^JX@Dy&>C%Y$fTGH8wLPK6)?;G;^aiz7bC3m znfm}qS@@&J2UNW+rn1aH#f*aQo2&Sb@2BoPJ&34I;V5DGV4GpoIzj?p;ZE7#<~?Vx zLZo~UAiZ$OA^buelaL(ebx6vbQ{Qh0@0W^4jzy6Hh>#cD_45Pnf(Cb8z}WS?)Ezun zpQz#8Gt!C=0?J<8$s+{6{~cvH+v8}Fxrs0dQC7%TzX4xP@ANk@J8}-uX-p&tIrr3A zqZ#1~A=~v&X|BF|6AA8@<2l$`KR(2(x8CTuryN)t+L=*v@iWS(<*<8~VVcgKg#4?o zl9!23grync1wW~A4?o9ehp8kc63(ybxAj0B9eA zPSwZz*mNpzkAbi-KC5sc!N}Z>UopP^?Uub>kPvn;?d=yADmJrW z2u(wTb#e$h!^d%i-{ksx{MVjvmL-gPbx&rnCv!SHq1d@%uw&shv*3lQ^rOc7K5si4 z;OSMUD`L{T(Kx1|O*QR~L{U7Z%@H7?3}*ma*SB5A@5NIGnTseI7%Iky&IlY`iG`sN&41Md8e~#Ic@%3SFm?T)^0Oc^hQP+Sl`2 zaExKsH}T=imPm^Dh;^d`^}r4$Jp*m;j+!sy4zM3+IocV|RGXr7H2B`h5uGjA(zcLd+z(Z3XcH3JUQ1ouYtR0M{TF~5V@LSj#j3D@YB#B1tQcHE_%)zWrGan^K z0H0}Y^=qt$5}5QftB48#RL|m?#>GiCSFgI5JAGRmF(mKJjPfQfJRB{X?WsX zx)H#1>Wr{T>uS9;IGF7`Xd~~Vg(F*0(SC%lh9e+~iO@kxaMa(|A%#G;URu?&e#N0Z)HQC6Ac zdoE*@fthn1qkdY~k?@)2+eI zTwOl|NzaB1#1}ziMDkTHW_8BietM~~?AsoZQ_mdvcCJ(Fh3|}^5Yu zzKDy|k<(^LpV%^?I+eqFU4WzT(ka%U!M2MWJe(%i_jy8&OK?XNJ$=IG>J? zpd`~r4@IWpFAj>I-Z5!7zSmyN(JrMnKWd?E4pQM|$g|bD`NFS%qb{ei7o>|hND6rZMe`Q5aK1Bc7C3PC#7O zK%L?f-F#Jd8u^Ba;9%GFVTPrRC3t1ccp5|Q9l}OyS-;cs zRZ8B{fJMQ}@&ahD`4m_)8?|^p+|CfP65ne6oEOu+=#slZ`DPLpu{+ISy{hr5N^u7~ zny^QsVQPZqXI{{4Wm-7-HjNCG>frgCBO;z+R!EDdfS)9@)t#q95dZs;fHq?#&oU!b zj|C49*2J2MN>lL+$(nEdcNq>uz(}kOZUlt zCML>r53ISwp(bK;_B{9RU6iMHTQW4e#7uPALr$v2JX)oM`Jw3KR@C~cWip?y81hMe zw?)vvPfCDRZj@P>U;MOsq-!wfzIaF(c=PbGQvJ-~(6;Wk2H`GlkL0`G{P2Kez-LaZ zDjPo^?2S~Y+cTr&-}o4&Nhyw%5aRix;OAf@P{FFP=nTn4fz*6Pu!x}?5De3=N2jWE z#DDu&ab>*GU075iZw(;8QEo}0Bx_7h**fiEmgvRZ(pTK&100|XEyDT}rnvS1k!i7e z{emvYXPMDNJveqH{$5K9KkZYZ)3oe1molfb_OQcKZ|H+ zX+%VE9#|r;xQIP;b;j&Vu;iM6{9gmrUXlsk;ce48Tc&LRHFVtyG(UaQ_8yIebK^a} z%zn&1n&}ki$Dr*{F*Sj06Wy(fvD)$X0%ZiCAfYP<`*a4Jh~&;5!rj0%dmRQMZ=tFa{QY}=-qe7SJ71Ax3=3qoU^cO1tPoT(@m$j~GqxSL zZ1;kK_bFGazL5|7Liyy}LYUq!zxc^-{5r(3a7uIGc&Q1wsR=Rm{ye!|OV5Q~e@lHh z`rP}S=KIIgb=Aq=T}+OTy31pHfY%w|k5>g?71iqVR8TAGbPn7{n)lSKUy&8pysahj zR|&vLeo3T<>Qi}qqsBmsw?&4WmbCq@k$vj;eb31EF8yDdd#8?lr>Q!R3cerhJ9cx} zcGorh1buC=)^AkPZCv3Trfe5T%xd3!tSlN2su8|kAHi-&5ycQmb@A95kqv&VQs^UYpP=TA>7dcZ zuf(BlSS*4)m=43JRWHCeceW{P<&l|Uwh2rxo%j*JJy&pG$>%zK64@Qq><$bVbiXjI=0h;arz&8KqhcKGN?6iU{wPA#NQ!kLoq@$9t*vHS zD6ht@x6OuvaEbC85tiZz<)^kmdrYzeI^N~=x6DLEMzDMbQoc;t3)fGiv;pi0qf$Z| zPJHeI>QCCV$v^M;vE8nVeGp_MFw-oyS603(64f^>PUDNa@k+L-V(i_)S))*v;HXiE zRmO@~U_|_0m=s71x(A({Ew)m^7sp88Ivs7QuM3@|<)p#;xQ;L#4mnsO7`ct<ikYH7x#APnJQ911UVe#H;+)=SWB`M$;A@Y5#0(7o%g z*qEQLUNONi5gRhb3Z=~4URX_+=6+GkJELB8srYbn$;MXUr+|7@;`2l}?Br6T310R^f zhb2ne!?gm|lTq^9#Z^XIWN7vu9myoYs1vELM!vPyam7TFuKL+oyfe>vYe7VX=89N1 zsIYo|Y=p&bx(ih{ef&uuQDFa*SI-CJ1pb4oH{yk!XaafuIhy?A_Ky+#kMaM&?4{hT z%{}d1oT0dSYes27Feezq$;-(H;sWz>^0RRAGjMV;Fme2smy0{21hl?2P{GmB%fr*% z+|$J!21fR;sVt-8?C4_o0mLLK`rp0xb+@*`K;;C2`Jl)DDL`Hx9xfi>Ki~YH z0)me6p8|CL9~+1l^e-ETmlwqSFWdjF^}mn*9S_XS%kytPFgG8V|6g@^_yxfK_Jg*; z3r)a3<$wAD;sbI1+YihG;)dq!|M3Gu6Cv<#8xJSfzw3hdxw-#iY<#@j|E>$-4(;FZpmWdjf5n3?Prl!B-Oa2WDZ007!4b|I+Xy-PsQ0sV3SQsw28iWDex zQ<1U8yOq*MkmA@bzeRg)-#EoP2R+s6n|(#!ezcBnmT!z?xu%9Z}IXM>%FE|5!hz!?tD2}vlOaJ zYW`z;QZZY&JhkASpId26tK&aq_NNc#oM`hgv$bI8qwo`IPQukhjZPuOP{Y%$ApV>S9{+OIkK`C&+0sbQF5z5Gp zNi&+QNac(s>t0TzOfH0Gwtc(D7&;o-GfVNix7SMhHub5wafmb{a{21eY0PX7y zK|p(iAOt$VYJNR}0wsi>#$sEcGA z>%GIeRPvUt69X#FpOo&6=N=|yCOEJpIPfTPAneE8s`m?`A_LaIS_}-(cOVo}OKD6l zMuUnF>Ja^^K~{nV1zI3G?qwv(l7k4Nca$0#*2-{<$!IbfnUX@rn$Iu=2{B6&Vn&m; z3rOgagPJ!YjCvr&p@0zuwo?q?DJDLEh2u;dXa_OO!eKhd08$(?V~eAh$%1J-V#l!m zu`~bcuY|>n9eK-}Fvl?rDT3Gq742`(AYO$yWg>=GmJuL@M?0ogYL@}wUK3MAf`YUP NdEQLvbk!G})L&QcP9p#S From 65ac86f85299964ffafb6f2eb2b855c061e8578c Mon Sep 17 00:00:00 2001 From: Skyearn Date: Mon, 7 Jun 2021 21:41:26 +0800 Subject: [PATCH 133/183] Fix StatusBar icon size. --- .../StatusBarConnected.imageset/app.pdf | Bin 58672 -> 59781 bytes .../unlock-off.pdf | Bin 59834 -> 60498 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf b/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf index 377bcfaa874e439e67db82e372c1ec7787ebd5a0..402ef2fa3af9fb1a8243463f1a2e9d21172a8621 100644 GIT binary patch delta 36392 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 delta 34910 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

-dV?X3i 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 diff --git a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf index 5af36fb0c22d185eb0dda35ee984b8f3cb273aa9..84fa5898c6284a965c293c1f351b6bcd35706f36 100644 GIT binary patch delta 40102 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�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 E7X~J>k^lez delta 38928 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 Date: Wed, 9 Jun 2021 20:43:31 +0800 Subject: [PATCH 134/183] Fix Status Bar icon color --- .../StatusBarConnected.imageset/app.pdf | Bin 59781 -> 59599 bytes .../unlock-off.pdf | Bin 60498 -> 60334 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf b/BLEUnlock/Images.xcassets/StatusBarConnected.imageset/app.pdf index 402ef2fa3af9fb1a8243463f1a2e9d21172a8621..b2559a50a47252e6aa332887656a39cbe192d5f0 100644 GIT binary patch delta 36771 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 VR9Ik;G?qXF2-4cxR*n?u{{je7S=s;q delta 37047 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 diff --git a/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf b/BLEUnlock/Images.xcassets/StatusBarDisconnected.imageset/unlock-off.pdf index 84fa5898c6284a965c293c1f351b6bcd35706f36..1d30b3a2f3cde0f6402dd0a606db7c32336dc7cb 100644 GIT binary patch delta 37895 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%0D6Wjow*uQ9G>IUF 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

n2YsW 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

3GdBp0# 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 Date: Sun, 16 May 2021 16:01:47 +0900 Subject: [PATCH 135/183] Changed timing of manual unlock detection --- BLEUnlock/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 9661171e..10756a3e 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -331,7 +331,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } @objc func onUnlock() { - if Date().timeIntervalSince1970 >= unlockedAt + 2 { + if Date().timeIntervalSince1970 >= unlockedAt + 5 { runScript("intruded") self.playNowPlaying() } From 3f6b3fb0ce0020b4edba3224dfb90935e6a8ed1d Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 7 Jun 2021 10:10:43 +0900 Subject: [PATCH 136/183] Changed timing of manual unlock detection (again) --- BLEUnlock/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 10756a3e..01805919 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -331,7 +331,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa } @objc func onUnlock() { - if Date().timeIntervalSince1970 >= unlockedAt + 5 { + if Date().timeIntervalSince1970 >= unlockedAt + 10 { runScript("intruded") self.playNowPlaying() } From ef58efcb9ee1517f1be5485bad36469158652df5 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 7 Jun 2021 10:29:12 +0900 Subject: [PATCH 137/183] Update READMEs --- README.ja.md | 8 +++++++- README.md | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.ja.md b/README.ja.md index 3761242a..fb696715 100644 --- a/README.ja.md +++ b/README.ja.md @@ -103,6 +103,9 @@ Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取 デバイスを識別するには、Macから遠ざけたり近づけたりして、信号強度(dB値)がそれに応じて変化するかどうかを確認してください。 +もしリストに何も表示されない場合、下記のBluetoothモジュールのリセットを試してください。 + + ### アンロックされない *システム環境設定* > *セキュリティとプライバシー* > *アクセシビリティ* でBLEUnlockがオンになっているか確認してください。すでにオンになっている場合、一度オフにしてもう一度オンにしてみてください。 @@ -115,7 +118,9 @@ Apple製以外のBLEデバイスでは、BLEUnlockはデバイスの名前を取 ### Bluetoothキーボード、マウス、インターネット共有その他Bluetoothがおかしくなった -*パッシブモード*をオンにしてください。 +メニューバーもしくはコントロールセンターにあるBluetoothアイコンをShift+Option+クリックし、表示される*Bluetoothモジュールのリセット*をしてみてください。 + +それでも問題が繰り返し起こる場合、*パッシブモード*をオンにしてください。 ## MACアドレスについて @@ -218,6 +223,7 @@ do shell script "/usr/local/bin/ffmpeg -f avfoundation -r 30 -i 0 -frames:v 1 -y - wenmin-wu: 最小RSSIと移動平均 - stephengroat: CI - joeyhoer: Homebrew Cask +- Skyearn: Big Surスタイルのアイコン アイコンはmaterialdesignicons.comからダウンロードしたSVGファイルをもとにしています。これらはGoogleによってデザインされApache License version 2.0でライセンスされています。 diff --git a/README.md b/README.md index 98275b65..6d8341bd 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ If your BLE device is not from Apple, BLEUnlock may not able to find the device If that is the case, your device is displayed as a UUID (long hexadecimal numbers and hyphens). To identify the device, try moving the device closer to or farther away from the Mac and see if the RSSI (dB value) changes accordingly. +If you don't see *any* device in the list, try resetting the Bluetooth module as described below. + ### It fails to unlock Make sure BLEUnlock is turned on in *System Preferences* > *Security & Privacy* > *Privacy* > *Accessibility*. @@ -96,7 +98,9 @@ Or try *Passive Mode*. ### My Bluetooth keyboard, mouse, Personal Hotspot, or whatever Bluetooth, went nuts! -Turn on *Passive Mode*! +Firstly, Shift + Option + Click the Bluetooth icon in the menubar or Control Center, then click *Reset the Bluetooth module*. + +If the problem persists, turn on *Passive Mode*. ## Notes on MAC address @@ -201,6 +205,7 @@ If you like this app, I'd appreciate it if you could make a donation via [Buy Me - wenmin-wu: Minimum RSSI and moving average - stephengroat: CI - joeyhoer: Homebrew Cask +- Skyearn: Big Sur style icon Icons are based on SVGs downloaded from materialdesignicons.com. They are originally designed by Google LLC and licensed under Apache License version 2.0. From 7d51bbbbd0a3176a7f225f6a84753fc395043f00 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 29 Jul 2021 18:29:11 +0900 Subject: [PATCH 138/183] Beginning of support for Monterey --- BLEUnlock/Info.plist | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 5e1ac220..cdca4ef0 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,15 +17,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.2 + 1.11-alpha CFBundleVersion - 736 + 742 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) - LSUIElement - NSBluetoothAlwaysUsageDescription BLEUnlock uses Bluetooth to detect devices. NSHumanReadableCopyright From 09d59eb04b544e640678aa451bbba0f536737b4a Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 12 Aug 2021 19:55:09 +0900 Subject: [PATCH 139/183] Try to fix crash on Device.description (#47) --- BLEUnlock/BLE.swift | 18 ++++++++++-------- BLEUnlock/Info.plist | 4 ++-- release | 4 ++++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index ba298345..20d27acd 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -65,14 +65,16 @@ class Device: NSObject { } // iBeacon if let adv = advData { - var iBeaconPrefix : [uint16] = [0x004c, 0x01502] - if adv[0...3] == Data(bytes: &iBeaconPrefix, count: 4) { - let major = uint16(adv[20]) << 8 | uint16(adv[21]) - let minor = uint16(adv[22]) << 8 | uint16(adv[23]) - let tx = Int8(bitPattern: adv[24]) - let distance = pow(10, Double(Int(tx) - rssi)/20.0) - let d = String(format:"%.1f", distance) - return "iBeacon [\(major), \(minor)] \(d)m" + if adv.count >= 25 { + var iBeaconPrefix : [uint16] = [0x004c, 0x01502] + if adv[0...3] == Data(bytes: &iBeaconPrefix, count: 4) { + let major = uint16(adv[20]) << 8 | uint16(adv[21]) + let minor = uint16(adv[22]) << 8 | uint16(adv[23]) + let tx = Int8(bitPattern: adv[24]) + let distance = pow(10, Double(Int(tx) - rssi)/20.0) + let d = String(format:"%.1f", distance) + return "iBeacon [\(major), \(minor)] \(d)m" + } } } if let name = blName { diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 5e1ac220..d5ae9db7 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.2 + 1.10.3-rc4 CFBundleVersion - 736 + 740 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/release b/release index 6465c953..d73a86b5 100755 --- a/release +++ b/release @@ -66,3 +66,7 @@ notarize $RELEASEDIR/$APPNAME.app version=$(extract_info $RELEASEDIR/$APPNAME.app CFBundleShortVersionString) ditto -c -k --keepParent $RELEASEDIR/$APPNAME.app $RELEASEDIR/$APPNAME-$version.zip + +v=$version-$(extract_info $RELEASEDIR/$APPNAME.app CFBundleVersion) +mkdir -p archives/$v +cp -a "$RELEASEDIR/$APPNAME.xcarchive" archives/$v From b5927859508959efe01cedff43dc75092059b8e2 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 12 Aug 2021 20:01:41 +0900 Subject: [PATCH 140/183] 1.10.3-rc4 --- BLEUnlock/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index d5ae9db7..98420c09 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.10.3-rc4 CFBundleVersion - 740 + 741 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 491ce57219b00749135e0ba5cead199fb4c921d4 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 29 Aug 2021 15:49:22 +0900 Subject: [PATCH 141/183] 1.10.3 --- BLEUnlock/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 98420c09..52c4cf88 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.3-rc4 + 1.10.3 CFBundleVersion 741 LSApplicationCategoryType From 4f9b125ae86c94a4bab5d158197a790e35c9f455 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 29 Aug 2021 23:28:35 +0900 Subject: [PATCH 142/183] Add support for Monterey BLE device information --- BLEUnlock.xcodeproj/project.pbxproj | 8 ++- BLEUnlock/BLE.swift | 11 +++- BLEUnlock/Info.plist | 2 +- BLEUnlock/LEDeviceInfo.swift | 90 +++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 BLEUnlock/LEDeviceInfo.swift diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 65bb8e80..b1f55638 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 3D4EE4152279E94B00AF9E93 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D4EE4142279E94B00AF9E93 /* MainMenu.xib */; }; 3D600A37226CC1A40068FB7B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D600A35226CC1A40068FB7B /* Localizable.strings */; }; 3D600A5A22701E740068FB7B /* Launcher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3D600A4022701A5A0068FB7B /* Launcher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 3D6070E226DBC68E00EE4E48 /* LEDeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6070E126DBC68E00EE4E48 /* LEDeviceInfo.swift */; }; 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */; }; 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D95379322784D550017D8B9 /* AboutBox.swift */; }; 3D953797227853E20017D8B9 /* AboutBox.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3D953799227853E20017D8B9 /* AboutBox.xib */; }; @@ -50,6 +51,7 @@ 3D600A4022701A5A0068FB7B /* Launcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Launcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3D600A4922701A5C0068FB7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3D600A4A22701A5C0068FB7B /* Launcher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Launcher.entitlements; sourceTree = ""; }; + 3D6070E126DBC68E00EE4E48 /* LEDeviceInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LEDeviceInfo.swift; sourceTree = ""; }; 3D79FD7C226C335100A373C0 /* appleDeviceNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = appleDeviceNames.swift; sourceTree = ""; }; 3D95379322784D550017D8B9 /* AboutBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutBox.swift; sourceTree = ""; }; 3D953798227853E20017D8B9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutBox.xib; sourceTree = ""; }; @@ -129,6 +131,7 @@ 3D953799227853E20017D8B9 /* AboutBox.xib */, 3D600A35226CC1A40068FB7B /* Localizable.strings */, 3DD4B665226C1DF200451B7B /* BLE.swift */, + 3D6070E126DBC68E00EE4E48 /* LEDeviceInfo.swift */, 3DD4B653226C1C3200451B7B /* AppDelegate.swift */, 3DC0BC6922728FF1001145EC /* Info.plist */, 3DCAAA7C22ADE9830011A2DA /* checkUpdate.swift */, @@ -312,6 +315,7 @@ 3DD4B664226C1CF000451B7B /* lowlevel.c in Sources */, 3DCAAA7D22ADE9830011A2DA /* checkUpdate.swift in Sources */, 3D95379522784D550017D8B9 /* AboutBox.swift in Sources */, + 3D6070E226DBC68E00EE4E48 /* LEDeviceInfo.swift in Sources */, 3D79FD7D226C335100A373C0 /* appleDeviceNames.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -354,7 +358,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; @@ -521,7 +525,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = BLEUnlock/BLEUnlock.entitlements; - CODE_SIGN_IDENTITY = ""; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 42LGPQYC7M; diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index ba298345..50343d96 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -34,12 +34,21 @@ class Device: NSObject { override var description: String { get { + if macAddr == nil || blName == nil { + if let info = getLEDeviceInfoFromUUID(uuid.description) { + blName = info.name + macAddr = info.macAddr + } + } if macAddr == nil { macAddr = getMACFromUUID(uuid.description) } if let mac = macAddr { - blName = getNameFromMAC(mac) + if blName == nil { + blName = getNameFromMAC(mac) + } if let name = blName { + // If it's just "iPhone" or "iPad", there's a chance we can get the model name in the following code if name != "iPhone" && name != "iPad" { return name } diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index cdca4ef0..202c6242 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.11-alpha CFBundleVersion - 742 + 757 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/LEDeviceInfo.swift b/BLEUnlock/LEDeviceInfo.swift new file mode 100644 index 00000000..adc1e697 --- /dev/null +++ b/BLEUnlock/LEDeviceInfo.swift @@ -0,0 +1,90 @@ +// Resolve MAC address and device name of BLE device from SQLite database at /Library/Bluetooth introduced in Monterey. + +import SQLite3 + +private var inited = false +private var db_paired: OpaquePointer? +private var db_other: OpaquePointer? + +private func connect() { + if inited { return } + + if sqlite3_open("/Library/Bluetooth/com.apple.MobileBluetooth.ledevices.paired.db", &db_paired) == SQLITE_OK { + print("paired.db open success") + } else { + db_paired = nil + } + + if sqlite3_open("/Library/Bluetooth/com.apple.MobileBluetooth.ledevices.other.db", &db_other) == SQLITE_OK { + print("other.db open success") + } else { + db_other = nil + } + + inited = true +} + +struct LEDeviceInfo { + var name: String? + var macAddr: String? +} + +private func getStringFromRow(stmt: OpaquePointer?, index: Int32) -> String? { + if sqlite3_column_type(stmt, index) != SQLITE_TEXT { return nil } + let s = String(cString: sqlite3_column_text(stmt, index)) + let trimmed = s.trimmingCharacters(in: .whitespaces) + if trimmed == "" { return nil } + return trimmed +} + +private func getPairedDeviceFromUUID(_ uuid: String) -> LEDeviceInfo? { + guard let db = db_paired else { return nil } + var stmt: OpaquePointer? + if sqlite3_prepare(db, "SELECT Name, Address, ResolvedAddress FROM PairedDevices where Uuid='\(uuid)'", -1, &stmt, nil) != SQLITE_OK { + print("failed to prepare") + return nil + } + if sqlite3_step(stmt) != SQLITE_ROW { + return nil + } + let name = getStringFromRow(stmt: stmt, index: 0) + let address = getStringFromRow(stmt: stmt, index: 1) + let resolvedAddress = getStringFromRow(stmt: stmt, index: 2) + var mac: String? = nil + if let addr = resolvedAddress ?? address { + // It's like "Public XX:XX:..." or "Random XX:XX:...", so split by space and take the second one + let parts = addr.split(separator: " ") + if parts.count > 1 { + mac = String(parts[1]) + } + } + return LEDeviceInfo(name: name, macAddr: mac) +} + +private func getOtherDeviceFromUUID(_ uuid: String) -> LEDeviceInfo? { + guard let db = db_other else { return nil } + var stmt: OpaquePointer? + if sqlite3_prepare(db, "SELECT Name, Address FROM OtherDevices where Uuid='\(uuid)'", -1, &stmt, nil) != SQLITE_OK { + print("failed to prepare") + return nil + } + if sqlite3_step(stmt) != SQLITE_ROW { + return nil + } + let name = getStringFromRow(stmt: stmt, index: 0) + let address = getStringFromRow(stmt: stmt, index: 1) + var mac: String? = nil + if let addr = address { + // It's like "Public XX:XX:..." or "Random XX:XX:...", so split by space and take the second one + let parts = addr.split(separator: " ") + if parts.count > 1 { + mac = String(parts[1]) + } + } + return LEDeviceInfo(name: name, macAddr: mac) +} + +func getLEDeviceInfoFromUUID(_ uuid: String) -> LEDeviceInfo? { + connect() + return getPairedDeviceFromUUID(uuid) ?? getOtherDeviceFromUUID(uuid); +} From 3fcc4d273e741f6ebd88cbf468e3175bc009b82f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 29 Aug 2021 23:29:40 +0900 Subject: [PATCH 143/183] 1.11-alpha2 --- BLEUnlock/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 202c6242..bff0436f 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.11-alpha + 1.11-alpha2 CFBundleVersion 757 LSApplicationCategoryType From 31aa2604ee19d334e7391890ef686f2c7ecb8772 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 30 Aug 2021 20:56:59 +0900 Subject: [PATCH 144/183] Add archives to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 209d67aa..a62ff656 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ fastlane/report.xml fastlane/screenshots .DS_Store + +/archives From adc9fc1fef3742d0f280847f52d2e4a290961778 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 30 Aug 2021 21:22:59 +0900 Subject: [PATCH 145/183] Hide dock icon once app is launched --- BLEUnlock/AppDelegate.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 01805919..3852fa18 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -30,7 +30,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa var unlockedAt = 0.0 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(.prohibited) + } + func menuWillOpen(_ menu: NSMenu) { if menu == deviceMenu { ble.startScanning() From ef7858bccfadf8c9cde426f330e1fa75e6c93b91 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 30 Aug 2021 21:30:28 +0900 Subject: [PATCH 146/183] Skip device names with only whitespaces --- BLEUnlock/BLE.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/BLE.swift b/BLEUnlock/BLE.swift index c5212a31..b37fc88b 100644 --- a/BLEUnlock/BLE.swift +++ b/BLEUnlock/BLE.swift @@ -18,7 +18,12 @@ func getNameFromMAC(_ mac: String) -> String? { guard let plist = NSDictionary(contentsOfFile: "/Library/Preferences/com.apple.Bluetooth.plist") else { return nil } guard let devcache = plist["DeviceCache"] as? NSDictionary else { return nil } guard let device = devcache[mac] as? NSDictionary else { return nil } - return device["Name"] as? String + if let name = device["Name"] as? String { + let trimmed = name.trimmingCharacters(in: .whitespaces) + if trimmed == "" { return nil } + return trimmed + } + return nil } class Device: NSObject { From fa241897ed5ce87a3496a544feeb9da1beb1728b Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Mon, 30 Aug 2021 21:37:46 +0900 Subject: [PATCH 147/183] 1.11-rc --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index bff0436f..8c23a135 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.11-alpha2 + 1.11-rc CFBundleVersion - 757 + 763 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 713cf74762a576edced6c75fd54b70ff11b7a3ee Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 4 Sep 2021 15:03:21 +0900 Subject: [PATCH 148/183] Fixed bug that modal disappears on first launch Closes #53. --- BLEUnlock/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 3852fa18..a1aa4e47 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -35,7 +35,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa // 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(.prohibited) + NSApp.setActivationPolicy(.accessory) } func menuWillOpen(_ menu: NSMenu) { From f047375affcf7f28b5830f0c4fa376ca2933770f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 4 Sep 2021 15:06:16 +0900 Subject: [PATCH 149/183] 1.11-rc2 --- BLEUnlock/Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index 8c23a135..89bdb4d2 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.11-rc + 1.11-rc2 CFBundleVersion - 763 + 764 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From a294faae804d47db0e2e5a04cd13e9b84f523d5f Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Sep 2021 17:20:38 +0900 Subject: [PATCH 150/183] 1.11 --- BLEUnlock/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index beb56065..d539024b 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10.3 + 1.11 CFBundleVersion 764 LSApplicationCategoryType From 29b4efa84195801df8aac2f347f9f933faf6e535 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Thu, 30 Sep 2021 21:40:49 +0900 Subject: [PATCH 151/183] Update Apple device names from Device Support Update --- BLEUnlock/appleDeviceNames.swift | 12 ++++++++++ apple-device-names | 40 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/BLEUnlock/appleDeviceNames.swift b/BLEUnlock/appleDeviceNames.swift index f60522cb..04d38235 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/apple-device-names b/apple-device-names index 12237d5c..c2d1db9a 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(): From 2c4188b51e8b8a68a9aa42063ecbb04814e8ed7e Mon Sep 17 00:00:00 2001 From: Klaus-J <95517970+cyberclaus@users.noreply.github.com> Date: Tue, 7 Dec 2021 17:17:25 +0100 Subject: [PATCH 152/183] Add files via upload --- BLEUnlock/da.lproj/AboutBox.strings | 15 +++++++++++ BLEUnlock/da.lproj/Localizable.strings | 36 +++++++++++++++++++++++++ BLEUnlock/de.lproj/AboutBox.strings | 4 +++ BLEUnlock/de.lproj/Localizable.strings | 37 ++++++++++++++++++++++++++ BLEUnlock/nb.lproj/AboutBox.strings | 15 +++++++++++ BLEUnlock/nb.lproj/Localizable.strings | 36 +++++++++++++++++++++++++ BLEUnlock/sv.lproj/AboutBox.strings | 16 +++++++++++ BLEUnlock/sv.lproj/Localizable.strings | 37 ++++++++++++++++++++++++++ 8 files changed, 196 insertions(+) create mode 100644 BLEUnlock/da.lproj/AboutBox.strings create mode 100644 BLEUnlock/da.lproj/Localizable.strings create mode 100644 BLEUnlock/de.lproj/AboutBox.strings create mode 100644 BLEUnlock/de.lproj/Localizable.strings create mode 100644 BLEUnlock/nb.lproj/AboutBox.strings create mode 100644 BLEUnlock/nb.lproj/Localizable.strings create mode 100644 BLEUnlock/sv.lproj/AboutBox.strings create mode 100644 BLEUnlock/sv.lproj/Localizable.strings diff --git a/BLEUnlock/da.lproj/AboutBox.strings b/BLEUnlock/da.lproj/AboutBox.strings new file mode 100644 index 00000000..63cc51bc --- /dev/null +++ b/BLEUnlock/da.lproj/AboutBox.strings @@ -0,0 +1,15 @@ + +/* Class = "NSTextFieldCell"; title = "Copyright © 2019-2021 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ +"GN4-8z-o8I.title" = "Copyright © 2019-2021 Takeshi Sone"; + +/* 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 00000000..8b008b03 --- /dev/null +++ b/BLEUnlock/da.lproj/Localizable.strings @@ -0,0 +1,36 @@ +"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…"; +"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"; diff --git a/BLEUnlock/de.lproj/AboutBox.strings b/BLEUnlock/de.lproj/AboutBox.strings new file mode 100644 index 00000000..515dfefc --- /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 00000000..9e9904a7 --- /dev/null +++ b/BLEUnlock/de.lproj/Localizable.strings @@ -0,0 +1,37 @@ +"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"; +"display_off" = "Display ausschalten"; +"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…"; +"timeout" = "Kein-Signal Zeitablauf"; +"unlock_rssi" = "RSSI entsperren"; +"use_screensaver_to_lock" = "Den Bildschirmschoner verwenden zum Sperren"; +"wake_on_proximity" = "Aufwachen bei Annäherung"; diff --git a/BLEUnlock/nb.lproj/AboutBox.strings b/BLEUnlock/nb.lproj/AboutBox.strings new file mode 100644 index 00000000..a1e7bdd1 --- /dev/null +++ b/BLEUnlock/nb.lproj/AboutBox.strings @@ -0,0 +1,15 @@ + +/* Class = "NSTextFieldCell"; title = "Copyright © 2019-2021 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ +"GN4-8z-o8I.title" = "Copyright © 2019-2021 Takeshi Sone"; + +/* 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 00000000..941e080a --- /dev/null +++ b/BLEUnlock/nb.lproj/Localizable.strings @@ -0,0 +1,36 @@ +"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…"; +"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"; diff --git a/BLEUnlock/sv.lproj/AboutBox.strings b/BLEUnlock/sv.lproj/AboutBox.strings new file mode 100644 index 00000000..64c3d13c --- /dev/null +++ b/BLEUnlock/sv.lproj/AboutBox.strings @@ -0,0 +1,16 @@ + +/* Class = "NSTextFieldCell"; title = "Copyright © 2019-2021 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ +"GN4-8z-o8I.title" = "Copyright © 2019-2021 Takeshi Sone"; + +/* 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 00000000..2016e0bc --- /dev/null +++ b/BLEUnlock/sv.lproj/Localizable.strings @@ -0,0 +1,37 @@ +"about" = "Om BLEUnlock"; +"bluetooth_power_warn" = "Bluetooth är avstängt. För att BLEUnlock ska fungera måste du slå på den."; +"cancel" = "Avbryta"; +"closer" = "⬆Närmare"; +"device" = "Enhet"; +"device_not_set" = "Enhet inte valts"; +"disabled" = "Inaktivera"; +"enter_password" = "Ange lösenord för att låsa upp skärmen."; +"enter_rssi_threshold" = "Ange minsta RSSI värde"; +"enter_rssi_threshold_info" = "Enheter vars RSSI-värde är lägre än detta värde kommer att ignoreras vid skanning."; +"farther" = "⬇Längre bort"; +"launch_at_login" = "Starta vid inloggning"; +"lock_delay" = "Fördröjning till låsning"; +"lock_now" = "Låsa skärmen nu"; +"lock_rssi" = "Lås RSSI"; +"minute" = "minut"; +"minutes" = "minuter"; +"not_detected" = "Signalen har inte upptäckts"; +"notification_device_away" = "Enhet är borta"; +"notification_locked" = "Den här datorn har låsts av BLEUnlock"; +"notification_lost_signal" = "Signalen är förlorad"; +"notification_update_available" = "Updateting finns tillgänglig."; +"ok" = "OK"; +"passive_mode" = "Passivt läge"; +"password_info" = "Ditt lösenord lagras säkert i nyckelknippan."; +"password_not_set" = "Lösenordet har inte angetts."; +"pause_now_playing" = "Pausa \"Spela nu\" när den är låst"; +"quit" = "Avsluta BLEUnlock"; +"scanning" = "Skanning…"; +"seconds" = "seconder"; +"set_password" = "Ange lösenord…"; +"set_rssi_threshold" = "Ange lägsta RSSI…"; +"timeout" = "Ingen-signal tidsgräns"; +"unlock_rssi" = "Lås upp RSSI"; +"use_screensaver_to_lock" = "Använd skärmsläckare för att låsa"; +"wake_on_proximity" = "Väckning om in närheten"; + From 200976a4013525d04c97ea9dd5da2a73d55a5227 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Dec 2021 18:34:47 +0900 Subject: [PATCH 153/183] Support for sleep display on lock (WIP) --- BLEUnlock/AppDelegate.swift | 21 +++++++++++---------- BLEUnlock/Info.plist | 2 +- BLEUnlock/lowlevel.c | 11 +++++++---- BLEUnlock/lowlevel.h | 3 ++- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index a1aa4e47..5059dc32 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -209,12 +209,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 +243,8 @@ 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") - } + notifyUser(reason) + runScript(reason) } manualLock = false } @@ -537,7 +538,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa guard !isScreenLocked() else { return } manualLock = true pauseNowPlaying() - _ = lockOrSaveScreen() + lockOrSaveScreen() } @objc func showAboutBox() { diff --git a/BLEUnlock/Info.plist b/BLEUnlock/Info.plist index d539024b..218b641e 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.11 CFBundleVersion - 764 + 781 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/lowlevel.c b/BLEUnlock/lowlevel.c index 2f70ccb0..add5e5b0 100644 --- a/BLEUnlock/lowlevel.c +++ b/BLEUnlock/lowlevel.c @@ -1,5 +1,6 @@ #include "lowlevel.h" #include +#include 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 eec0625a..af1127e7 100644 --- a/BLEUnlock/lowlevel.h +++ b/BLEUnlock/lowlevel.h @@ -2,7 +2,8 @@ #define lowlevel_h #include +void sleepDisplay(void); void wakeDisplay(void); -bool lockScreen(void); +int SACLockScreenImmediate(void); #endif /* lowlevel_h */ From f3b8b81ebd5da95438c120b9996bd7d879bf59d9 Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sun, 12 Dec 2021 18:50:22 +0900 Subject: [PATCH 154/183] Add localizations into project --- BLEUnlock.xcodeproj/project.pbxproj | 20 +++++ .../xcshareddata/xcschemes/BLEUnlock.xcscheme | 78 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index b1f55638..62a1d1da 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 = ""; }; 3D2FCF07226C99CB007A06E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 3D43D0112765FCCB00D15991 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/AboutBox.strings; sourceTree = ""; }; + 3D43D0122765FCCB00D15991 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + 3D43D0132765FCFD00D15991 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/AboutBox.strings; sourceTree = ""; }; + 3D43D0142765FCFD00D15991 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + 3D43D0152765FD1600D15991 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/AboutBox.strings; sourceTree = ""; }; + 3D43D0162765FD1600D15991 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; + 3D43D0172765FD2800D15991 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/AboutBox.strings; sourceTree = ""; }; + 3D43D0182765FD2800D15991 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; 3D4EE40F2279E55A00AF9E93 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 3D4EE4102279E55A00AF9E93 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 3D4EE4122279E56900AF9E93 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -242,6 +250,10 @@ Base, ja, "zh-Hans", + de, + da, + nb, + sv, ); mainGroup = 3DD4B647226C1C3200451B7B; productRefGroup = 3DD4B651226C1C3200451B7B /* Products */; @@ -329,6 +341,10 @@ 3D600A36226CC1A40068FB7B /* Base */, 3D600A38226CC1AA0068FB7B /* ja */, B1EFF91622E010F50010DB0A /* zh-Hans */, + 3D43D0122765FCCB00D15991 /* de */, + 3D43D0142765FCFD00D15991 /* da */, + 3D43D0162765FD1600D15991 /* nb */, + 3D43D0182765FD2800D15991 /* sv */, ); name = Localizable.strings; sourceTree = ""; @@ -339,6 +355,10 @@ 3D953798227853E20017D8B9 /* Base */, 3D95379B227853EC0017D8B9 /* ja */, B1EFF91422E010F50010DB0A /* zh-Hans */, + 3D43D0112765FCCB00D15991 /* de */, + 3D43D0132765FCFD00D15991 /* da */, + 3D43D0152765FD1600D15991 /* nb */, + 3D43D0172765FD2800D15991 /* sv */, ); name = AboutBox.xib; sourceTree = ""; diff --git a/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme b/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme new file mode 100644 index 00000000..0227bea0 --- /dev/null +++ b/BLEUnlock.xcodeproj/xcshareddata/xcschemes/BLEUnlock.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 45ff65b416705de2d2fb167a0a4169557d056eb7 Mon Sep 17 00:00:00 2001 From: alonewolfx2 Date: Thu, 23 Dec 2021 23:41:40 +0300 Subject: [PATCH 155/183] Turkish language added --- BLEUnlock.xcodeproj/project.pbxproj | 5 ++++ BLEUnlock/tr.lproj/AboutBox.strings | 15 +++++++++++ BLEUnlock/tr.lproj/Localizable.strings | 36 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 BLEUnlock/tr.lproj/AboutBox.strings create mode 100644 BLEUnlock/tr.lproj/Localizable.strings diff --git a/BLEUnlock.xcodeproj/project.pbxproj b/BLEUnlock.xcodeproj/project.pbxproj index 62a1d1da..c7befb8b 100644 --- a/BLEUnlock.xcodeproj/project.pbxproj +++ b/BLEUnlock.xcodeproj/project.pbxproj @@ -77,6 +77,8 @@ 3DD4B668226C1E3700451B7B /* login.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = login.framework; path = ../../../../System/Library/PrivateFrameworks/login.framework; sourceTree = ""; }; B1EFF91422E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutBox.strings"; sourceTree = ""; }; B1EFF91622E010F50010DB0A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + F749D28C277515C600C78C28 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AboutBox.strings; sourceTree = ""; }; + F749D28D277515C600C78C28 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -254,6 +256,7 @@ da, nb, sv, + tr, ); mainGroup = 3DD4B647226C1C3200451B7B; productRefGroup = 3DD4B651226C1C3200451B7B /* Products */; @@ -345,6 +348,7 @@ 3D43D0142765FCFD00D15991 /* da */, 3D43D0162765FD1600D15991 /* nb */, 3D43D0182765FD2800D15991 /* sv */, + F749D28D277515C600C78C28 /* tr */, ); name = Localizable.strings; sourceTree = ""; @@ -359,6 +363,7 @@ 3D43D0132765FCFD00D15991 /* da */, 3D43D0152765FD1600D15991 /* nb */, 3D43D0172765FD2800D15991 /* sv */, + F749D28C277515C600C78C28 /* tr */, ); name = AboutBox.xib; sourceTree = ""; diff --git a/BLEUnlock/tr.lproj/AboutBox.strings b/BLEUnlock/tr.lproj/AboutBox.strings new file mode 100644 index 00000000..b4cbe5a0 --- /dev/null +++ b/BLEUnlock/tr.lproj/AboutBox.strings @@ -0,0 +1,15 @@ + +/* Class = "NSTextFieldCell"; title = "Copyright © 2019-2021 Takeshi Sone"; ObjectID = "GN4-8z-o8I"; */ +"GN4-8z-o8I.title" = "Copyright © 2019-2021 Takeshi Sone"; + +/* 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 00000000..71c46244 --- /dev/null +++ b/BLEUnlock/tr.lproj/Localizable.strings @@ -0,0 +1,36 @@ +"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"; +"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ığı"; From 47ea556b10c04eb953ac8e6654f65e0f28746d24 Mon Sep 17 00:00:00 2001 From: Wern Jie Lim Date: Wed, 5 Jan 2022 16:11:16 +0800 Subject: [PATCH 156/183] add 'wake without unlocking' functionality --- BLEUnlock/AppDelegate.swift | 15 ++++++++++++++- BLEUnlock/Base.lproj/Localizable.strings | 1 + BLEUnlock/Info.plist | 2 +- BLEUnlock/da.lproj/Localizable.strings | 1 + BLEUnlock/de.lproj/Localizable.strings | 1 + BLEUnlock/ja.lproj/Localizable.strings | 1 + BLEUnlock/nb.lproj/Localizable.strings | 1 + BLEUnlock/sv.lproj/Localizable.strings | 2 +- BLEUnlock/tr.lproj/Localizable.strings | 1 + BLEUnlock/zh-Hans.lproj/Localizable.strings | 1 + README.md | 1 + 11 files changed, 24 insertions(+), 3 deletions(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index a1aa4e47..3cb8e7f1 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -297,6 +297,8 @@ 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 } @@ -533,6 +535,12 @@ 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 @@ -605,6 +613,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 @@ -691,7 +704,7 @@ 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() diff --git a/BLEUnlock/Base.lproj/Localizable.strings b/BLEUnlock/Base.lproj/Localizable.strings index 0d16039e..58c7c83f 100644 --- a/BLEUnlock/Base.lproj/Localizable.strings +++ b/BLEUnlock/Base.lproj/Localizable.strings @@ -34,3 +34,4 @@ "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 d539024b..c3a4885a 100644 --- a/BLEUnlock/Info.plist +++ b/BLEUnlock/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.11 CFBundleVersion - 764 + 766 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/BLEUnlock/da.lproj/Localizable.strings b/BLEUnlock/da.lproj/Localizable.strings index 8b008b03..fddcd28c 100644 --- a/BLEUnlock/da.lproj/Localizable.strings +++ b/BLEUnlock/da.lproj/Localizable.strings @@ -34,3 +34,4 @@ "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/Localizable.strings b/BLEUnlock/de.lproj/Localizable.strings index 9e9904a7..0b3340ed 100644 --- a/BLEUnlock/de.lproj/Localizable.strings +++ b/BLEUnlock/de.lproj/Localizable.strings @@ -35,3 +35,4 @@ "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 9a4f6cf4..c00312a9 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -34,3 +34,4 @@ "unlock_rssi" = "アンロック信号強度"; "use_screensaver_to_lock" = "スクリーンセーバーでロック"; "wake_on_proximity" = "画面スリープから復帰"; +"wake_without_unlocking" = "ロックを解除せずに目を覚ます"; diff --git a/BLEUnlock/nb.lproj/Localizable.strings b/BLEUnlock/nb.lproj/Localizable.strings index 941e080a..2b1957a1 100644 --- a/BLEUnlock/nb.lproj/Localizable.strings +++ b/BLEUnlock/nb.lproj/Localizable.strings @@ -34,3 +34,4 @@ "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/Localizable.strings b/BLEUnlock/sv.lproj/Localizable.strings index 2016e0bc..ea07c22d 100644 --- a/BLEUnlock/sv.lproj/Localizable.strings +++ b/BLEUnlock/sv.lproj/Localizable.strings @@ -34,4 +34,4 @@ "unlock_rssi" = "Lås upp RSSI"; "use_screensaver_to_lock" = "Använd skärmsläckare för att låsa"; "wake_on_proximity" = "Väckning om in närheten"; - +"wake_without_unlocking" = "Vakna utan att låsa upp"; diff --git a/BLEUnlock/tr.lproj/Localizable.strings b/BLEUnlock/tr.lproj/Localizable.strings index 71c46244..d746607f 100644 --- a/BLEUnlock/tr.lproj/Localizable.strings +++ b/BLEUnlock/tr.lproj/Localizable.strings @@ -34,3 +34,4 @@ "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 ae958fa9..ca1f7189 100644 --- a/BLEUnlock/zh-Hans.lproj/Localizable.strings +++ b/BLEUnlock/zh-Hans.lproj/Localizable.strings @@ -34,3 +34,4 @@ "unlock_rssi" = "解锁 RSSI"; "use_screensaver_to_lock" = "用屏保来锁定它"; "wake_on_proximity" = "靠近唤醒"; +"wake_without_unlocking" = "唤醒时不需解锁"; diff --git a/README.md b/README.md index 6d8341bd..19802d72 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ 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. Set Password... | If you changed your login password, use this. From db98c7233a92f0b9f32a44a6280cd7192db7c43e Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 8 Jan 2022 17:30:28 +0900 Subject: [PATCH 157/183] Fix Japanese --- BLEUnlock/ja.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BLEUnlock/ja.lproj/Localizable.strings b/BLEUnlock/ja.lproj/Localizable.strings index c00312a9..230b39d5 100644 --- a/BLEUnlock/ja.lproj/Localizable.strings +++ b/BLEUnlock/ja.lproj/Localizable.strings @@ -34,4 +34,4 @@ "unlock_rssi" = "アンロック信号強度"; "use_screensaver_to_lock" = "スクリーンセーバーでロック"; "wake_on_proximity" = "画面スリープから復帰"; -"wake_without_unlocking" = "ロックを解除せずに目を覚ます"; +"wake_without_unlocking" = "アンロックせずに画面復帰"; From c6c3ce1354e79f297f422babae84794c889f5927 Mon Sep 17 00:00:00 2001 From: Klaus-J <95517970+cyberclaus@users.noreply.github.com> Date: Thu, 6 Jan 2022 08:28:36 +0100 Subject: [PATCH 158/183] Add files via upload --- BLEUnlock/de.lproj/Localizable.strings | 1 - 1 file changed, 1 deletion(-) diff --git a/BLEUnlock/de.lproj/Localizable.strings b/BLEUnlock/de.lproj/Localizable.strings index 0b3340ed..ed7b26e8 100644 --- a/BLEUnlock/de.lproj/Localizable.strings +++ b/BLEUnlock/de.lproj/Localizable.strings @@ -12,7 +12,6 @@ "launch_at_login" = "Beim Login starten"; "lock_delay" = "Sperrverzögerung"; "lock_now" = "Jetzt sperren"; -"display_off" = "Display ausschalten"; "lock_rssi" = "RSSI Sperren"; "minute" = "Minute"; "minutes" = "Minuten"; From d353c71fb1046c00752d830e0bcfc1df06add6ac Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 8 Jan 2022 17:42:23 +0900 Subject: [PATCH 159/183] Don't run "intruded" script if unlock is disabled --- BLEUnlock/AppDelegate.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BLEUnlock/AppDelegate.swift b/BLEUnlock/AppDelegate.swift index 3cb8e7f1..e8463d2e 100644 --- a/BLEUnlock/AppDelegate.swift +++ b/BLEUnlock/AppDelegate.swift @@ -341,7 +341,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSMenuItemVa @objc func onUnlock() { if Date().timeIntervalSince1970 >= unlockedAt + 10 { - runScript("intruded") + if ble.unlockRSSI != ble.UNLOCK_DISABLED && !prefs.bool(forKey: "wakeWithoutUnlocking") { + runScript("intruded") + } self.playNowPlaying() } manualLock = false From 00310ce12d0906d020ce730cd226b5d114ee7bbc Mon Sep 17 00:00:00 2001 From: Takeshi Sone Date: Sat, 8 Jan 2022 18:19:08 +0900 Subject: [PATCH 160/183] New year --- BLEUnlock/Base.lproj/AboutBox.xib | 20 ++++++++++---------- BLEUnlock/Info.plist | 4 ++-- BLEUnlock/da.lproj/AboutBox.strings | 4 ---- BLEUnlock/nb.lproj/AboutBox.strings | 3 --- BLEUnlock/sv.lproj/AboutBox.strings | 3 --- BLEUnlock/tr.lproj/AboutBox.strings | 3 --- 6 files changed, 12 insertions(+), 25 deletions(-) diff --git a/BLEUnlock/Base.lproj/AboutBox.xib b/BLEUnlock/Base.lproj/AboutBox.xib index 4c4cbe86..3434e16b 100644 --- a/BLEUnlock/Base.lproj/AboutBox.xib +++ b/BLEUnlock/Base.lproj/AboutBox.xib @@ -1,8 +1,8 @@ - + - + @@ -17,7 +17,7 @@ - + @@ -36,16 +36,16 @@ - + - + - + @@ -53,7 +53,7 @@ - + @@ -404,12 +404,12 @@ Included icons are derived from SVGs downloaded from materialdesignicons.com. Th - +