diff --git a/Tiny Wi-Fi Analyzer.spec b/Tiny Wi-Fi Analyzer.spec index 2f86f01..6a6ac13 100644 --- a/Tiny Wi-Fi Analyzer.spec +++ b/Tiny Wi-Fi Analyzer.spec @@ -51,5 +51,6 @@ app = BUNDLE( "CFBundleShortVersionString": "0.2.0", "CFBundleVersion": "0.2.0", "NSHighResolutionCapable": True, + "NSLocationWhenInUseUsageDescription": "On macOS 14 Sonoma and Later, Location Services permission is required to get Wi-Fi SSIDs.", }, ) diff --git a/poetry.lock b/poetry.lock index 72de778..e7e6ad1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -225,6 +225,23 @@ files = [ [package.dependencies] pyobjc-core = ">=10.0" +[[package]] +name = "pyobjc-framework-corelocation" +version = "10.0" +description = "Wrappers for the framework CoreLocation on macOS" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyobjc-framework-CoreLocation-10.0.tar.gz", hash = "sha256:d84001ab5ef58441514bd92ed9b2fd4225faf0241d2a09ab503592fbc6a6066d"}, + {file = "pyobjc_framework_CoreLocation-10.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:0a9d535f00a0369d493f49bd898e68d5ce7227ce161a3f0df0d9e6668e396a77"}, + {file = "pyobjc_framework_CoreLocation-10.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cf39e4f092d7a94a8ad516bda2603872fd0952aeac0bb0143e7ec2e2244a172d"}, + {file = "pyobjc_framework_CoreLocation-10.0-cp36-abi3-macosx_11_0_universal2.whl", hash = "sha256:4ec1c23b92285f7f33bdc86dc4e6cbccb8788ceca6ea6205f420859ed172abee"}, +] + +[package.dependencies] +pyobjc-core = ">=10.0" +pyobjc-framework-Cocoa = ">=10.0" + [[package]] name = "pyobjc-framework-corewlan" version = "10.0" @@ -346,4 +363,4 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar [metadata] lock-version = "2.0" python-versions = "<3.12,>=3.8" -content-hash = "4f5458b44532f0ef0225ff1ed15a097385e29849982eb69abf949d15a0eb3c4a" +content-hash = "7d9f24176b3d22761553bc67f235f2d4109583a0c7c6e1ae57d3ac5b1bb7b13d" diff --git a/pyproject.toml b/pyproject.toml index 1d51aa4..44a5e0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ pyobjc-core = "~10.0" pyobjc-framework-cocoa = "~10.0" pyobjc-framework-CoreWLAN = "~10.0" pyobjc-framework-WebKit = "~10.0" +pyobjc-framework-corelocation = "^10.0" [tool.poetry.dev-dependencies] pyinstaller = "^5.13" diff --git a/tiny_wifi_analyzer/__main__.py b/tiny_wifi_analyzer/__main__.py index 77a9abc..8cfff0d 100644 --- a/tiny_wifi_analyzer/__main__.py +++ b/tiny_wifi_analyzer/__main__.py @@ -2,12 +2,20 @@ import logging import os.path import queue +import stat import threading from time import sleep # NOTE: https://github.com/r0x0r/pywebview/issues/496 -from objc import super, nil, registerMetaDataForSelector # pylint: disable=unused-import # noqa F401 - +from objc import ( + super, + nil, + registerMetaDataForSelector, +) # pylint: disable=unused-import # noqa F401 + +import AppKit +import Foundation +import CoreLocation import CoreWLAN import webview @@ -32,8 +40,10 @@ def __init__(self, channel): self.channel_width = channel.channelWidth() def __repr__(self): - return " [channel_band={}, channel_number={}, channel_width={}]".format( - self.channel_band, self.channel_number, self.channel_width + return ( + " [channel_band={}, channel_number={}, channel_width={}]".format( + self.channel_band, self.channel_number, self.channel_width + ) ) @@ -133,8 +143,59 @@ def on_closing(): is_closing = True +class LocationManagerDelegate(AppKit.NSObject): + def locationManagerDidChangeAuthorization_(self, manager): + status = manager.authorizationStatus() + if status in [ + CoreLocation.kCLAuthorizationStatusDenied, + CoreLocation.kCLAuthorizationStatusNotDetermined, + ]: + self.show_dialog() + + def show_dialog(self): + alert = AppKit.NSAlert.alloc().init() + alert.setMessageText_(f"Location Services are disabled") + alert.setInformativeText_( + "On macOS 14 Sonoma and Later, Location Services permission is required to get Wi-Fi SSIDs.\n" + + "Please enable Location Services in System Preferences > Security & Privacy > Privacy > Location Services." + ) + alert.addButtonWithTitle_("Open Preferences") + alert.addButtonWithTitle_("OK") + response = alert.runModal() + if response == AppKit.NSAlertFirstButtonReturn: + AppKit.NSWorkspace.sharedWorkspace().openURL_( + Foundation.NSURL.URLWithString_( + "x-apple.systempreferences:com.apple.preference.security?Privacy_LocationServices" + ) + ) + AppKit.NSApplication.sharedApplication().terminate_(None) + + +def request_location_permission(): + location_manager = CoreLocation.CLLocationManager.alloc().init().retain() + delegate = LocationManagerDelegate.alloc().init().retain() + location_manager.setDelegate_(delegate) + # location_manager.delegate() + # location_manager.startUpdatingLocation() + location_manager.requestWhenInUseAuthorization() + for i in range(100): + status = location_manager.authorizationStatus() + if not status == 0: + break + sleep(0.01) + + def main(): - index_html = os.path.join(os.path.dirname(__file__), 'view/index.html') + # NOTE: Sonoma (macOS 11) and later requires location permission to read Wi-Fi SSIDs. + os_version = AppKit.NSAppKitVersionNumber + # print(os_version, AppKit.NSAppKitVersionNumber13_1) + if os_version > AppKit.NSAppKitVersionNumber13_1: + request_location_permission() + + # Bring the app to the foreground + AppKit.NSApplication.sharedApplication().activateIgnoringOtherApps_(True) + + index_html = os.path.join(os.path.dirname(__file__), "view/index.html") window = webview.create_window("Tiny Wi-Fi Analyzer", index_html) window.events.closing += on_closing webview.start(startup, window, debug=True)