From 0fa8b1dc40f58ad61c958b6493ee2dbcf3d6f5df Mon Sep 17 00:00:00 2001 From: b3b Date: Sat, 14 Aug 2021 23:20:40 +0300 Subject: [PATCH] add public decorators to request BLE adapter and permissions #27 --- able/__init__.py | 12 +++++++-- able/android/dispatcher.py | 54 +++++++++++++++++++++++++------------- able/dispatcher.py | 15 +++++++++++ docs/api.rst | 7 +++++ 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/able/__init__.py b/able/__init__.py index da76d55..b0ce668 100644 --- a/able/__init__.py +++ b/able/__init__.py @@ -23,7 +23,11 @@ class WriteType(IntEnum): if platform == 'android': - from able.android.dispatcher import BluetoothDispatcher + from able.android.dispatcher import ( + BluetoothDispatcher, + require_bluetooth_enabled, + require_runtime_permissions, + ) else: # mock android and PyJNIus modules usage @@ -43,7 +47,11 @@ def __call__(self, *args, **kwargs): jnius.autoclass = mocked_autoclass() sys.modules['jnius'] = jnius - from able.dispatcher import BluetoothDispatcherBase + from able.dispatcher import ( + BluetoothDispatcherBase, + require_bluetooth_enabled, + require_runtime_permissions, + ) class BluetoothDispatcher(BluetoothDispatcherBase): """Bluetooth Low Energy interface diff --git a/able/android/dispatcher.py b/able/android/dispatcher.py index 0b889a7..7c088be 100644 --- a/able/android/dispatcher.py +++ b/able/android/dispatcher.py @@ -27,7 +27,6 @@ def require_bluetooth_enabled(method): - """Decorator to execute `BluetoothDispatcher` method when bluetooth adapter becomes ready.""" @wraps(method) def wrapper(self, *args, **kwargs): @@ -41,6 +40,21 @@ def wrapper(self, *args, **kwargs): return wrapper +def require_runtime_permissions(method): + + @wraps(method) + def wrapper(self, *args, **kwargs): + self._run_on_runtime_permissions = partial(method, self, *args, **kwargs) + if self._is_service_context or self._check_runtime_permissions(): + self._run_on_runtime_permissions() + self._run_on_runtime_permissions = None + else: + Logger.debug("Request runtime permissions") + self._request_runtime_permissions() + + return wrapper + + class BluetoothDispatcher(BluetoothDispatcherBase): @property @@ -74,11 +88,9 @@ def _request_runtime_permissions(self): self.on_runtime_permissions) @require_bluetooth_enabled + @require_runtime_permissions def start_scan(self): - if self._is_service_context or self._check_runtime_permissions(): - self._ble.startScan(self.enable_ble_code) - else: - self._request_runtime_permissions() + self._ble.startScan(self.enable_ble_code) def stop_scan(self): self._ble.stopScan() @@ -117,23 +129,29 @@ def _start_advertising(self, advertiser): def _set_name(self, value): self.adapter.setName(value) - def on_runtime_permissions(self, permissions, grant_results): - if permissions and all(grant_results): - self.start_scan() - else: - Logger.error( - 'Permissions necessary to obtain scan results are not granted' - ) - self.dispatch('on_scan_started', False) - def on_activity_result(self, requestCode, resultCode, intent): if requestCode == self.enable_ble_code: self.on_bluetooth_enabled(resultCode == Activity.RESULT_OK) def on_bluetooth_enabled(self, enabled): + callback = self._run_on_bluetooth_enabled + self._run_on_bluetooth_enabled = None if enabled: - if self._run_on_bluetooth_enabled: - self._run_on_bluetooth_enabled() - elif self._run_on_bluetooth_enabled: - if self._run_on_bluetooth_enabled.func == self.start_scan: + if callback: + callback() + elif callback: + if callback.func == self.start_scan: + self.dispatch('on_scan_started', False) + + def on_runtime_permissions(self, permissions, grant_results): + callback = self._run_on_runtime_permissions + self._run_on_runtime_permissions = None + if permissions and all(grant_results): + if callback: + callback() + else: + Logger.error( + 'Permissions necessary to obtain scan results are not granted' + ) + if callback.func == self.start_scan: self.dispatch('on_scan_started', False) diff --git a/able/dispatcher.py b/able/dispatcher.py index 9e2dd1e..12af34c 100644 --- a/able/dispatcher.py +++ b/able/dispatcher.py @@ -19,6 +19,21 @@ def __getattr__(self, name): raise Exception(self.msg) +def require_bluetooth_enabled(method): + """Decorator to start a system activity that allows the user + to turn on Bluetooth, if Bluetooth is not enabled. + Calls `BluetoothDispatcher` method when bluetooth adapter becomes ready. + """ + return method + + +def require_runtime_permissions(method): + """Decorator to ask for runtime permission to access location. + Calls `BluetoothDispatcher` method when permission is granted. + """ + return method + + class BluetoothDispatcherBase(EventDispatcher): __events__ = ( 'on_device', 'on_scan_started', 'on_scan_completed', 'on_services', diff --git a/docs/api.rst b/docs/api.rst index 29a3607..0512c0b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -41,6 +41,13 @@ BluetoothDispatcher on_rssi_updated, on_mtu_changed, +Decorators +"""""""""" + +.. autofunction:: require_bluetooth_enabled +.. autofunction:: require_runtime_permissions + + Advertisement """""""""""""