From 65e08a9d41ed4d0a4e222d4afbe678a47cb901ca Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 27 Nov 2025 13:57:16 +0100 Subject: [PATCH] Cleanup unneeded dependencies --- Makefile | 6 +- sbapp/plyer/__init__.py | 5 +- sbapp/plyer/facades/__init__.py | 4 +- sbapp/plyer/facades/audio.py | 104 ----- sbapp/plyer/platforms/android/audio.py | 108 ----- sbapp/plyer/platforms/ios/__init__.py | 0 sbapp/plyer/platforms/ios/accelerometer.py | 34 -- sbapp/plyer/platforms/ios/barometer.py | 31 -- sbapp/plyer/platforms/ios/battery.py | 47 -- sbapp/plyer/platforms/ios/brightness.py | 27 -- sbapp/plyer/platforms/ios/call.py | 29 -- sbapp/plyer/platforms/ios/camera.py | 52 --- sbapp/plyer/platforms/ios/compass.py | 43 -- sbapp/plyer/platforms/ios/email.py | 52 --- sbapp/plyer/platforms/ios/filechooser.py | 81 ---- sbapp/plyer/platforms/ios/flash.py | 50 --- sbapp/plyer/platforms/ios/gps.py | 80 ---- sbapp/plyer/platforms/ios/gravity.py | 31 -- sbapp/plyer/platforms/ios/gyroscope.py | 55 --- sbapp/plyer/platforms/ios/keystore.py | 23 - sbapp/plyer/platforms/ios/maps.py | 78 ---- sbapp/plyer/platforms/ios/sms.py | 43 -- .../plyer/platforms/ios/spatialorientation.py | 31 -- sbapp/plyer/platforms/ios/storagepath.py | 62 --- sbapp/plyer/platforms/ios/tts.py | 37 -- sbapp/plyer/platforms/ios/uniqueid.py | 27 -- sbapp/plyer/platforms/ios/vibrator.py | 43 -- sbapp/plyer/platforms/linux/audio.py | 139 ------ sbapp/plyer/platforms/macosx/audio.py | 128 ------ sbapp/plyer/platforms/win/audio.py | 413 ------------------ 30 files changed, 6 insertions(+), 1857 deletions(-) delete mode 100644 sbapp/plyer/facades/audio.py delete mode 100644 sbapp/plyer/platforms/android/audio.py delete mode 100644 sbapp/plyer/platforms/ios/__init__.py delete mode 100644 sbapp/plyer/platforms/ios/accelerometer.py delete mode 100644 sbapp/plyer/platforms/ios/barometer.py delete mode 100644 sbapp/plyer/platforms/ios/battery.py delete mode 100644 sbapp/plyer/platforms/ios/brightness.py delete mode 100644 sbapp/plyer/platforms/ios/call.py delete mode 100644 sbapp/plyer/platforms/ios/camera.py delete mode 100644 sbapp/plyer/platforms/ios/compass.py delete mode 100644 sbapp/plyer/platforms/ios/email.py delete mode 100644 sbapp/plyer/platforms/ios/filechooser.py delete mode 100644 sbapp/plyer/platforms/ios/flash.py delete mode 100644 sbapp/plyer/platforms/ios/gps.py delete mode 100644 sbapp/plyer/platforms/ios/gravity.py delete mode 100644 sbapp/plyer/platforms/ios/gyroscope.py delete mode 100644 sbapp/plyer/platforms/ios/keystore.py delete mode 100644 sbapp/plyer/platforms/ios/maps.py delete mode 100644 sbapp/plyer/platforms/ios/sms.py delete mode 100644 sbapp/plyer/platforms/ios/spatialorientation.py delete mode 100644 sbapp/plyer/platforms/ios/storagepath.py delete mode 100644 sbapp/plyer/platforms/ios/tts.py delete mode 100644 sbapp/plyer/platforms/ios/uniqueid.py delete mode 100644 sbapp/plyer/platforms/ios/vibrator.py delete mode 100644 sbapp/plyer/platforms/linux/audio.py delete mode 100644 sbapp/plyer/platforms/macosx/audio.py delete mode 100644 sbapp/plyer/platforms/win/audio.py diff --git a/Makefile b/Makefile index b74a4b5..7b08d25 100644 --- a/Makefile +++ b/Makefile @@ -40,8 +40,10 @@ preparewheel: pyclean . $(MAKE) -C sbapp cleanrns -build_wheel: - python3 setup.py sdist bdist_wheel +compile_wheel: + python3 setup.py bdist_wheel + +build_wheel: remove_symlinks compile_wheel create_symlinks build_win_exe: python -m PyInstaller sideband.spec --noconfirm diff --git a/sbapp/plyer/__init__.py b/sbapp/plyer/__init__.py index 9bfb190..9859034 100644 --- a/sbapp/plyer/__init__.py +++ b/sbapp/plyer/__init__.py @@ -5,7 +5,7 @@ Plyer ''' __all__ = ( - 'accelerometer', 'audio', 'barometer', 'battery', 'bluetooth', + 'accelerometer', 'barometer', 'battery', 'bluetooth', 'brightness', 'call', 'camera', 'compass', 'cpu', 'email', 'filechooser', 'flash', 'gps', 'gravity', 'gyroscope', 'humidity', 'irblaster', 'keystore', 'light', 'notification', 'orientation', 'processors', @@ -29,9 +29,6 @@ accelerometer = Proxy('accelerometer', facades.Accelerometer) #: Keyring proxy to :class::`plyer.facades.Keystore` keystore = Proxy('keystore', facades.Keystore) -#: Audio proxy to :class:`plyer.facades.Audio` -audio = Proxy('audio', facades.Audio) - #: Barometer proxy to :class:`plyer.facades.Barometer` barometer = Proxy('barometer', facades.Barometer) diff --git a/sbapp/plyer/facades/__init__.py b/sbapp/plyer/facades/__init__.py index 81a0f63..88f3985 100644 --- a/sbapp/plyer/facades/__init__.py +++ b/sbapp/plyer/facades/__init__.py @@ -6,7 +6,7 @@ Interface of all the features available. ''' -__all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera', +__all__ = ('Accelerometer', 'Barometer', 'Battery', 'Call', 'Camera', 'Compass', 'Email', 'FileChooser', 'GPS', 'Gravity', 'Gyroscope', 'IrBlaster', 'Light', 'Orientation', 'Notification', 'Proximity', 'Sms', 'TTS', 'UniqueID', 'Vibrator', 'Wifi', 'Flash', 'CPU', @@ -17,7 +17,6 @@ __all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera', import RNS if RNS.vendor.platformutils.is_android(): from plyer.facades.accelerometer import Accelerometer - from plyer.facades.audio import Audio from plyer.facades.barometer import Barometer from plyer.facades.battery import Battery from plyer.facades.call import Call @@ -53,7 +52,6 @@ if RNS.vendor.platformutils.is_android(): from plyer.facades.devicename import DeviceName else: from sbapp.plyer.facades.accelerometer import Accelerometer - from sbapp.plyer.facades.audio import Audio from sbapp.plyer.facades.barometer import Barometer from sbapp.plyer.facades.battery import Battery from sbapp.plyer.facades.call import Call diff --git a/sbapp/plyer/facades/audio.py b/sbapp/plyer/facades/audio.py deleted file mode 100644 index 0394037..0000000 --- a/sbapp/plyer/facades/audio.py +++ /dev/null @@ -1,104 +0,0 @@ -''' -Audio -===== - -The :class:`Audio` is used for recording audio. - -Default path for recording is set in platform implementation. - -.. note:: - On Android the `RECORD_AUDIO`, `WAKE_LOCK` permissions are needed. - -Simple Examples ---------------- - -To get the file path:: - - >>> audio.file_path - '/sdcard/testrecorder.3gp' - -To set the file path:: - - >>> import os - >>> current_list = os.listdir('.') - ['/sdcard/testrecorder.3gp', '/sdcard/testrecorder1.3gp', - '/sdcard/testrecorder2.3gp', '/sdcard/testrecorder3.3gp'] - >>> file_path = current_list[2] - >>> audio.file_path = file_path - -To start recording:: - - >>> from plyer import audio - >>> audio.start() - -To stop recording:: - - >>> audio.stop() - -To play recording:: - - >>> audio.play() - -Supported Platforms -------------------- -Android, Windows, macOS - -''' - - -class Audio: - ''' - Audio facade. - ''' - - state = 'ready' - _file_path = '' - - def __init__(self, file_path=None): - super().__init__() - self._file_path = file_path or self._file_path - - def start(self): - ''' - Start record. - ''' - self._start() - self.state = 'recording' - - def stop(self): - ''' - Stop record. - ''' - self._stop() - self.state = 'ready' - - def play(self): - ''' - Play current recording. - ''' - self._play() - self.state = 'playing' - - @property - def file_path(self): - return self._file_path - - @file_path.setter - def file_path(self, location): - ''' - Location of the recording. - ''' - assert isinstance(location, str), 'Location must be string or unicode' - self._file_path = location - - # private - - def _start(self): - raise IOError("JUICE") - raise NotImplementedError() - - def _stop(self): - raise NotImplementedError() - - def _play(self): - raise NotImplementedError() diff --git a/sbapp/plyer/platforms/android/audio.py b/sbapp/plyer/platforms/android/audio.py deleted file mode 100644 index 33a0a0d..0000000 --- a/sbapp/plyer/platforms/android/audio.py +++ /dev/null @@ -1,108 +0,0 @@ -import time -import threading -from jnius import autoclass - -from plyer.facades.audio import Audio - -# Recorder Classes -MediaRecorder = autoclass('android.media.MediaRecorder') -AudioSource = autoclass('android.media.MediaRecorder$AudioSource') -OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') -AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder') - -# Player Classes -MediaPlayer = autoclass('android.media.MediaPlayer') - - -class AndroidAudio(Audio): - '''Audio for android. - - For recording audio we use MediaRecorder Android class. - For playing audio we use MediaPlayer Android class. - ''' - - def __init__(self, file_path=None): - default_path = None - super().__init__(file_path or default_path) - - self._recorder = None - self._player = None - self._check_thread = None - self._finished_callback = None - self._format = "opus" - self.is_playing = False - - def _check_playback(self): - while self._player and self._player.isPlaying(): - time.sleep(0.25) - - self.is_playing = False - - if self._finished_callback and callable(self._finished_callback): - self._check_thread = None - self._finished_callback(self) - - - def _start(self): - self._recorder = MediaRecorder() - if self._format == "aac": - self._recorder.setAudioSource(AudioSource.DEFAULT) - self._recorder.setAudioSamplingRate(48000) - self._recorder.setAudioEncodingBitRate(64000) - self._recorder.setAudioChannels(1) - self._recorder.setOutputFormat(OutputFormat.MPEG_4) - self._recorder.setAudioEncoder(AudioEncoder.AAC) - - else: - self._recorder.setAudioSource(AudioSource.DEFAULT) - self._recorder.setAudioSamplingRate(48000) - self._recorder.setAudioEncodingBitRate(12000) - self._recorder.setAudioChannels(1) - self._recorder.setOutputFormat(OutputFormat.OGG) - self._recorder.setAudioEncoder(AudioEncoder.OPUS) - - self._recorder.setOutputFile(self.file_path) - - self._recorder.prepare() - self._recorder.start() - - def _stop(self): - if self._recorder: - try: - self._recorder.stop() - self._recorder.release() - except Exception as e: - print("Could not stop recording: "+str(e)) - - self._recorder = None - - if self._player: - try: - self._player.stop() - self._player.release() - except Exception as e: - print("Could not stop playback: "+str(e)) - - self._player = None - - self.is_playing = False - - def _play(self): - self._player = MediaPlayer() - self._player.setDataSource(self.file_path) - self._player.prepare() - self._player.start() - self.is_playing = True - - self._check_thread = threading.Thread(target=self._check_playback, daemon=True) - self._check_thread.start() - - def reload(self): - self._stop() - - def playing(self): - return self.is_playing - - -def instance(): - return AndroidAudio() diff --git a/sbapp/plyer/platforms/ios/__init__.py b/sbapp/plyer/platforms/ios/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/sbapp/plyer/platforms/ios/accelerometer.py b/sbapp/plyer/platforms/ios/accelerometer.py deleted file mode 100644 index 0f61f2b..0000000 --- a/sbapp/plyer/platforms/ios/accelerometer.py +++ /dev/null @@ -1,34 +0,0 @@ -''' -iOS accelerometer ------------------ - -Taken from: http://pyobjus.readthedocs.org/en/latest/pyobjus_ios.html \ - #accessing-accelerometer -''' - -from sbapp.plyer.facades import Accelerometer -from pyobjus import autoclass - - -class IosAccelerometer(Accelerometer): - - def __init__(self): - super().__init__() - self.bridge = autoclass('bridge').alloc().init() - self.bridge.motionManager.setAccelerometerUpdateInterval_(0.1) - - def _enable(self): - self.bridge.startAccelerometer() - - def _disable(self): - self.bridge.stopAccelerometer() - - def _get_acceleration(self): - return ( - self.bridge.ac_x, - self.bridge.ac_y, - self.bridge.ac_z) - - -def instance(): - return IosAccelerometer() diff --git a/sbapp/plyer/platforms/ios/barometer.py b/sbapp/plyer/platforms/ios/barometer.py deleted file mode 100644 index 9ec8d6e..0000000 --- a/sbapp/plyer/platforms/ios/barometer.py +++ /dev/null @@ -1,31 +0,0 @@ -''' -iOS Barometer -------------- -''' - -from sbapp.plyer.facades import Barometer -from pyobjus import autoclass - - -class iOSBarometer(Barometer): - - def __init__(self): - super().__init__() - self.bridge = autoclass('bridge').alloc().init() - - def _enable(self): - self.bridge.startRelativeAltitude() - - def _disable(self): - self.bridge.stopRelativeAltitude() - - def _get_pressure(self): - ''' - 1 kPa = 10 hPa - ''' - return ( - self.bridge.pressure * 10) - - -def instance(): - return iOSBarometer() diff --git a/sbapp/plyer/platforms/ios/battery.py b/sbapp/plyer/platforms/ios/battery.py deleted file mode 100644 index d7fa5d8..0000000 --- a/sbapp/plyer/platforms/ios/battery.py +++ /dev/null @@ -1,47 +0,0 @@ -''' -Module of iOS API for plyer.battery. -''' - -from pyobjus import autoclass -from pyobjus.dylib_manager import load_framework -from sbapp.plyer.facades import Battery - -load_framework('/System/Library/Frameworks/UIKit.framework') -UIDevice = autoclass('UIDevice') - - -class IOSBattery(Battery): - ''' - Implementation of iOS battery API. - ''' - - def __init__(self): - super().__init__() - self.device = UIDevice.currentDevice() - - def _get_state(self): - status = {"isCharging": None, "percentage": None} - - if not self.device.batteryMonitoringEnabled: - self.device.setBatteryMonitoringEnabled_(True) - - if self.device.batteryState == 0: - is_charging = None - elif self.device.batteryState == 2: - is_charging = True - else: - is_charging = False - - percentage = self.device.batteryLevel * 100. - - status['isCharging'] = is_charging - status['percentage'] = percentage - - return status - - -def instance(): - ''' - Instance for facade proxy. - ''' - return IOSBattery() diff --git a/sbapp/plyer/platforms/ios/brightness.py b/sbapp/plyer/platforms/ios/brightness.py deleted file mode 100644 index f4a70f4..0000000 --- a/sbapp/plyer/platforms/ios/brightness.py +++ /dev/null @@ -1,27 +0,0 @@ -''' -iOS Brightness --------------- -''' - -from pyobjus import autoclass -from sbapp.plyer.facades import Brightness -from pyobjus.dylib_manager import load_framework - -load_framework('/System/Library/Frameworks/UIKit.framework') -UIScreen = autoclass('UIScreen') - - -class iOSBrightness(Brightness): - - def __init__(self): - self.screen = UIScreen.mainScreen() - - def _current_level(self): - return self.screen.brightness * 100 - - def set_level(self, level): - self.screen.brightness = level / 100 - - -def instance(): - return iOSBrightness() diff --git a/sbapp/plyer/platforms/ios/call.py b/sbapp/plyer/platforms/ios/call.py deleted file mode 100644 index 9999d6a..0000000 --- a/sbapp/plyer/platforms/ios/call.py +++ /dev/null @@ -1,29 +0,0 @@ -''' -IOS Call ----------- -''' - -from sbapp.plyer.facades import Call -from pyobjus import autoclass, objc_str - -NSURL = autoclass('NSURL') -NSString = autoclass('NSString') -UIApplication = autoclass('UIApplication') - - -class IOSCall(Call): - - def _makecall(self, **kwargs): - tel = kwargs.get('tel') - url = "tel://" + tel - nsurl = NSURL.alloc().initWithString_(objc_str(url)) - - UIApplication.sharedApplication().openURL_(nsurl) - - def _dialcall(self, **kwargs): - pass - # Not possible, Access not provided by iPhone SDK - - -def instance(): - return IOSCall() diff --git a/sbapp/plyer/platforms/ios/camera.py b/sbapp/plyer/platforms/ios/camera.py deleted file mode 100644 index a61bd96..0000000 --- a/sbapp/plyer/platforms/ios/camera.py +++ /dev/null @@ -1,52 +0,0 @@ -from os import remove -from sbapp.plyer.facades import Camera - -from sbapp.plyer.utils import reify - - -class iOSCamera(Camera): - - @reify - def photos(self): - # pyPhotoLibrary is a ios recipe/module that - # interacts with the gallery and the camera on ios. - from photolibrary import PhotosLibrary - return PhotosLibrary() - - def _take_picture(self, on_complete, filename=None): - assert on_complete is not None - self.on_complete = on_complete - self.filename = filename - photos = self.photos - - if not photos.isCameraAvailable(): - # no camera hardware - return False - - photos.bind(on_image_captured=self.capture_callback) - self._capture_filename = filename - photos.capture_image(filename) - return True - - def capture_callback(self, photolibrary): - # Image was chosen - - # unbind - self.photos.unbind(on_image_captured=self.capture_callback) - - if self.on_complete(self.filename): - self._remove(self.filename) - - def _take_video(self, on_complete, filename=None): - assert on_complete is not None - raise NotImplementedError - - def _remove(self, fn): - try: - remove(fn) - except OSError: - print('Could not remove photo!') - - -def instance(): - return iOSCamera() diff --git a/sbapp/plyer/platforms/ios/compass.py b/sbapp/plyer/platforms/ios/compass.py deleted file mode 100644 index a5c865f..0000000 --- a/sbapp/plyer/platforms/ios/compass.py +++ /dev/null @@ -1,43 +0,0 @@ -''' -iOS Compass ------------ -''' - -from sbapp.plyer.facades import Compass -from pyobjus import autoclass - - -class IosCompass(Compass): - - def __init__(self): - super().__init__() - self.bridge = autoclass('bridge').alloc().init() - self.bridge.motionManager.setMagnetometerUpdateInterval_(0.1) - self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) - - def _enable(self): - self.bridge.startMagnetometer() - self.bridge.startDeviceMotionWithReferenceFrame() - - def _disable(self): - self.bridge.stopMagnetometer() - self.bridge.stopDeviceMotion() - - def _get_orientation(self): - return ( - self.bridge.mf_x, - self.bridge.mf_y, - self.bridge.mf_z) - - def _get_field_uncalib(self): - return ( - self.bridge.mg_x, - self.bridge.mg_y, - self.bridge.mg_z, - self.bridge.mg_x - self.bridge.mf_x, - self.bridge.mg_y - self.bridge.mf_y, - self.bridge.mg_z - self.bridge.mf_z) - - -def instance(): - return IosCompass() diff --git a/sbapp/plyer/platforms/ios/email.py b/sbapp/plyer/platforms/ios/email.py deleted file mode 100644 index ec54860..0000000 --- a/sbapp/plyer/platforms/ios/email.py +++ /dev/null @@ -1,52 +0,0 @@ -''' -Module of iOS API for plyer.email. -''' - -try: - from urllib.parse import quote -except ImportError: - from urllib import quote - -from sbapp.plyer.facades import Email -from pyobjus import autoclass, objc_str -from pyobjus.dylib_manager import load_framework - -load_framework('/System/Library/Frameworks/UIKit.framework') - -NSURL = autoclass('NSURL') -NSString = autoclass('NSString') -UIApplication = autoclass('UIApplication') - - -class IOSEmail(Email): - ''' - Implementation of iOS battery API. - ''' - - def _send(self, **kwargs): - recipient = kwargs.get('recipient') - subject = kwargs.get('subject') - text = kwargs.get('text') - - uri = "mailto:" - if recipient: - uri += str(recipient) - if subject: - uri += "?" if "?" not in uri else "&" - uri += "subject=" - uri += quote(str(subject)) - if text: - uri += "?" if "?" not in uri else "&" - uri += "body=" - uri += quote(str(text)) - - nsurl = NSURL.alloc().initWithString_(objc_str(uri)) - - UIApplication.sharedApplication().openURL_(nsurl) - - -def instance(): - ''' - Instance for facade proxy. - ''' - return IOSEmail() diff --git a/sbapp/plyer/platforms/ios/filechooser.py b/sbapp/plyer/platforms/ios/filechooser.py deleted file mode 100644 index 0320d6c..0000000 --- a/sbapp/plyer/platforms/ios/filechooser.py +++ /dev/null @@ -1,81 +0,0 @@ -''' -IOS file chooser --------------------- - -This module houses the iOS implementation of the plyer FileChooser. - -.. versionadded:: 1.4.4 -''' - -from sbapp.plyer.facades import FileChooser -from pyobjus import autoclass, protocol -from pyobjus.dylib_manager import load_framework - - -load_framework('/System/Library/Frameworks/Photos.framework') - - -class IOSFileChooser(FileChooser): - ''' - FileChooser implementation for IOS using - the built-in file browser via UIImagePickerController. - - .. versionadded:: 1.4.0 - ''' - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._on_selection = None - - def _file_selection_dialog(self, *args, **kwargs): - """ - Function called when action is required, A "mode" parameter specifies - which and is one of "open", "save" or "dir". - """ - self._on_selection = kwargs["on_selection"] - if kwargs["mode"] == "open": - self._open() - else: - raise NotImplementedError() - - def _get_picker(self): - """ - Return an instantiated and configured UIImagePickerController. - """ - picker = autoclass("UIImagePickerController") - po = picker.alloc().init() - po.sourceType = 0 - po.delegate = self - return po - - def _open(self): - """ - Launch the native iOS file browser. Upon selection, the - `imagePickerController_didFinishPickingMediaWithInfo_` delegate is - called where we close the file browser and handle the result. - """ - picker = self._get_picker() - UIApplication = autoclass('UIApplication') - vc = UIApplication.sharedApplication().keyWindow.rootViewController() - vc.presentViewController_animated_completion_(picker, True, None) - - @protocol('UIImagePickerControllerDelegate') - def imagePickerController_didFinishPickingMediaWithInfo_( - self, image_picker, frozen_dict): - """ - Delegate which handles the result of the image selection process. - """ - image_picker.dismissViewControllerAnimated_completion_(True, None) - - # Note: We need to call this Objective C class as there is currently - # no way to call a non-class function via pyobjus. And here, - # we have to use the `UIImagePNGRepresentation` to get the png - # representation. For this, please ensure you are using an - # appropriate version of kivy-ios. - native_image_picker = autoclass("NativeImagePicker").alloc().init() - path = native_image_picker.writeToPNG_(frozen_dict) - self._on_selection([path.UTF8String()]) - - -def instance(): - return IOSFileChooser() diff --git a/sbapp/plyer/platforms/ios/flash.py b/sbapp/plyer/platforms/ios/flash.py deleted file mode 100644 index 17f59f1..0000000 --- a/sbapp/plyer/platforms/ios/flash.py +++ /dev/null @@ -1,50 +0,0 @@ -# coding=utf-8 -""" -Flash ------ -""" -from sbapp.plyer.facades import Flash -from pyobjus import autoclass - -NSString = autoclass("NSString") -AVCaptureDevice = autoclass("AVCaptureDevice") -AVMediaTypeVideo = NSString.alloc().initWithUTF8String_("vide") -AVCaptureTorchModeOff = 0 -AVCaptureTorchModeOn = 1 - - -class IosFlash(Flash): - _camera = None - - def _on(self): - if self._camera is None: - self._camera_open() - if not self._camera: - return - self._camera.lockForConfiguration_(None) - try: - self._camera.setTorchMode(AVCaptureTorchModeOn) - finally: - self._camera.unlockForConfiguration() - - def _off(self): - if not self._camera: - return - self._camera.lockForConfiguration_(None) - try: - self._camera.setTorchMode(AVCaptureTorchModeOff) - finally: - self._camera.unlockForConfiguration() - - def _release(self): - pass - - def _camera_open(self): - device = AVCaptureDevice.defaultDeviceWithMediaType_(AVMediaTypeVideo) - if not device: - return - self._camera = device - - -def instance(): - return IosFlash() diff --git a/sbapp/plyer/platforms/ios/gps.py b/sbapp/plyer/platforms/ios/gps.py deleted file mode 100644 index d7ee0a7..0000000 --- a/sbapp/plyer/platforms/ios/gps.py +++ /dev/null @@ -1,80 +0,0 @@ -''' -iOS GPS ------------ -''' - -from pyobjus import autoclass, protocol -from pyobjus.dylib_manager import load_framework -from sbapp.plyer.facades import GPS - -load_framework('/System/Library/Frameworks/CoreLocation.framework') -CLLocationManager = autoclass('CLLocationManager') - - -class IosGPS(GPS): - def _configure(self): - if not hasattr(self, '_location_manager'): - self._location_manager = CLLocationManager.alloc().init() - - def _start(self, **kwargs): - self._location_manager.delegate = self - - self._location_manager.requestWhenInUseAuthorization() - # NSLocationWhenInUseUsageDescription key must exist in Info.plist - # file. When the authorization prompt is displayed your app goes - # into pause mode and if your app doesn't support background mode - # it will crash. - self._location_manager.startUpdatingLocation() - - def _stop(self): - self._location_manager.stopUpdatingLocation() - - @protocol('CLLocationManagerDelegate') - def locationManager_didChangeAuthorizationStatus_(self, manager, status): - if self.on_status: - s_status = '' - provider_status = '' - provider = 'standard-ios-provider' - if status == 0: - provider_status = 'provider-disabled' - s_status = 'notDetermined' - elif status == 1: - provider_status = 'provider-enabled' - s_status = 'restricted' - elif status == 2: - provider_status = 'provider-disabled' - s_status = 'denied' - elif status == 3: - provider_status = 'provider-enabled' - s_status = 'authorizedAlways' - elif status == 4: - provider_status = 'provider-enabled' - s_status = 'authorizedWhenInUse' - self.on_status(provider_status, '{}: {}'.format( - provider, s_status)) - - @protocol('CLLocationManagerDelegate') - def locationManager_didUpdateLocations_(self, manager, locations): - location = manager.location - - description = location.description.UTF8String() - split_description = description.split('<')[-1].split('>')[0].split(',') - - lat, lon = [float(coord) for coord in split_description] - acc = float(description.split(' +/- ')[-1].split('m ')[0]) - - speed = location.speed - altitude = location.altitude - course = location.course - - self.on_location( - lat=lat, - lon=lon, - speed=speed, - bearing=course, - altitude=altitude, - accuracy=acc) - - -def instance(): - return IosGPS() diff --git a/sbapp/plyer/platforms/ios/gravity.py b/sbapp/plyer/platforms/ios/gravity.py deleted file mode 100644 index 9452bda..0000000 --- a/sbapp/plyer/platforms/ios/gravity.py +++ /dev/null @@ -1,31 +0,0 @@ -''' -iOS Gravity ------------ - -''' - -from sbapp.plyer.facades import Gravity -from pyobjus import autoclass - - -class iOSGravity(Gravity): - - def __init__(self): - self.bridge = autoclass('bridge').alloc().init() - self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) - - def _enable(self): - self.bridge.startDeviceMotion() - - def _disable(self): - self.bridge.stopDeviceMotion() - - def _get_gravity(self): - return ( - self.bridge.g_x, - self.bridge.g_y, - self.bridge.g_z) - - -def instance(): - return iOSGravity() diff --git a/sbapp/plyer/platforms/ios/gyroscope.py b/sbapp/plyer/platforms/ios/gyroscope.py deleted file mode 100644 index 367e1bf..0000000 --- a/sbapp/plyer/platforms/ios/gyroscope.py +++ /dev/null @@ -1,55 +0,0 @@ -''' -iOS Gyroscope ---------------------- -''' - -from sbapp.plyer.facades import Gyroscope -from pyobjus import autoclass - -from pyobjus.dylib_manager import load_framework - -load_framework('/System/Library/Frameworks/UIKit.framework') -UIDevice = autoclass('UIDevice') - -device = UIDevice.currentDevice() - - -class IosGyroscope(Gyroscope): - - def __init__(self): - super().__init__() - self.bridge = autoclass('bridge').alloc().init() - - if int(device.systemVersion.UTF8String().split('.')[0]) <= 4: - self.bridge.motionManager.setGyroscopeUpdateInterval_(0.1) - else: - self.bridge.motionManager.setGyroUpdateInterval_(0.1) - - self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) - - def _enable(self): - self.bridge.startGyroscope() - self.bridge.startDeviceMotion() - - def _disable(self): - self.bridge.stopGyroscope() - self.bridge.stopDeviceMotion() - - def _get_orientation(self): - return ( - self.bridge.rotation_rate_x, - self.bridge.rotation_rate_y, - self.bridge.rotation_rate_z) - - def _get_rotation_uncalib(self): - return ( - self.bridge.gy_x, - self.bridge.gy_y, - self.bridge.gy_z, - self.bridge.gy_x - self.bridge.rotation_rate_x, - self.bridge.gy_y - self.bridge.rotation_rate_y, - self.bridge.gy_z - self.bridge.rotation_rate_z) - - -def instance(): - return IosGyroscope() diff --git a/sbapp/plyer/platforms/ios/keystore.py b/sbapp/plyer/platforms/ios/keystore.py deleted file mode 100644 index a18cde2..0000000 --- a/sbapp/plyer/platforms/ios/keystore.py +++ /dev/null @@ -1,23 +0,0 @@ -from sbapp.plyer.facades import Keystore -from pyobjus import autoclass, objc_str - -NSUserDefaults = autoclass('NSUserDefaults') - - -class IosKeystore(Keystore): - - def _set_key(self, servicename, key, value, **kwargs): - NSUserDefaults.standardUserDefaults().setObject_forKey_( - objc_str(value), objc_str(key)) - - def _get_key(self, servicename, key, **kwargs): - ret = NSUserDefaults.standardUserDefaults().stringForKey_( - objc_str(key)) - if ret is not None: - return ret.UTF8String() - else: - return ret - - -def instance(): - return IosKeystore() diff --git a/sbapp/plyer/platforms/ios/maps.py b/sbapp/plyer/platforms/ios/maps.py deleted file mode 100644 index dac01d5..0000000 --- a/sbapp/plyer/platforms/ios/maps.py +++ /dev/null @@ -1,78 +0,0 @@ -''' -Module of iOS API for plyer.maps. -''' - -import webbrowser -from sbapp.plyer.facades import Maps -from urllib.parse import quote_plus - - -class iOSMaps(Maps): - ''' - Implementation of iOS Maps API. - ''' - - def _open_by_address(self, address, **kwargs): - ''' - :param address: An address string that geolocation can understand. - ''' - - address = quote_plus(address, safe=',') - maps_address = 'http://maps.apple.com/?address=' + address - - webbrowser.open(maps_address) - - def _open_by_lat_long(self, latitude, longitude, **kwargs): - ''' - Open a coordinate span denoting a latitudinal delta and a - longitudinal delta (similar to MKCoordinateSpan) - - :param name: (optional), will set the name of the dropped pin - ''' - - name = kwargs.get("name", "Selected Location") - maps_address = 'http://maps.apple.com/?ll={},{}&q={}'.format( - latitude, longitude, name) - - webbrowser.open(maps_address) - - def _search(self, query, **kwargs): - ''' - :param query: A string that describes the search object (ex. "Pizza") - - :param latitude: (optional), narrow down query within area, - MUST BE USED WITH LONGITUDE - - :param longitude: (optional), narrow down query within area, - MUST BE USED WITH LATITUDE - ''' - - latitude = kwargs.get('latitude') - longitude = kwargs.get('longitude') - - query = quote_plus(query, safe=',') - maps_address = 'http://maps.apple.com/?q=' + query - - if latitude is not None and longitude is not None: - maps_address += '&sll={},{}'.format(latitude, longitude) - - webbrowser.open(maps_address) - - def _route(self, saddr, daddr, **kwargs): - ''' - :param saddr: can be given as 'address' or 'lat,long' - :param daddr: can be given as 'address' or 'lat,long' - ''' - saddr = quote_plus(saddr, safe=',') - daddr = quote_plus(daddr, safe=',') - - maps_address = 'http://maps.apple.com/?saddr={}&daddr={}'.format( - saddr, daddr) - webbrowser.open(maps_address) - - -def instance(): - ''' - Instance for facade proxy. - ''' - return iOSMaps() diff --git a/sbapp/plyer/platforms/ios/sms.py b/sbapp/plyer/platforms/ios/sms.py deleted file mode 100644 index fc6b1b4..0000000 --- a/sbapp/plyer/platforms/ios/sms.py +++ /dev/null @@ -1,43 +0,0 @@ -''' -IOS Sms ----------- -''' - -from sbapp.plyer.facades import Sms -from pyobjus import autoclass, objc_str -from pyobjus.dylib_manager import load_framework - -NSURL = autoclass('NSURL') -NSString = autoclass('NSString') -UIApplication = autoclass('UIApplication') -load_framework('/System/Library/Frameworks/MessageUI.framework') - - -class IOSSms(Sms): - - def _send(self, **kwargs): - ''' - This method provides sending messages to recipients. - - Expects 2 parameters in kwargs: - - recipient: String type - - message: String type - - Opens a message interface with recipient and message information. - ''' - recipient = kwargs.get('recipient') - message = kwargs.get('message') - url = "sms:" - if recipient: - # Apple has not supported multiple recipients yet. - url += str(recipient) - if message: - # Apple has to supported it yet. - pass - - nsurl = NSURL.alloc().initWithString_(objc_str(url)) - UIApplication.sharedApplication().openURL_(nsurl) - - -def instance(): - return IOSSms() diff --git a/sbapp/plyer/platforms/ios/spatialorientation.py b/sbapp/plyer/platforms/ios/spatialorientation.py deleted file mode 100644 index 7c6d9cc..0000000 --- a/sbapp/plyer/platforms/ios/spatialorientation.py +++ /dev/null @@ -1,31 +0,0 @@ -''' -iOS Spatial Orientation ------------------------ - -''' - -from sbapp.plyer.facades import SpatialOrientation -from pyobjus import autoclass - - -class iOSSpatialOrientation(SpatialOrientation): - - def __init__(self): - self.bridge = autoclass('bridge').alloc().init() - self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) - - def _enable_listener(self): - self.bridge.startDeviceMotion() - - def _disable_listener(self): - self.bridge.stopDeviceMotion() - - def _get_orientation(self): - return ( - self.bridge.sp_yaw, - self.bridge.sp_pitch, - self.bridge.sp_roll) - - -def instance(): - return iOSSpatialOrientation() diff --git a/sbapp/plyer/platforms/ios/storagepath.py b/sbapp/plyer/platforms/ios/storagepath.py deleted file mode 100644 index c69a083..0000000 --- a/sbapp/plyer/platforms/ios/storagepath.py +++ /dev/null @@ -1,62 +0,0 @@ -''' -iOS Storage Path --------------------- -''' - -from sbapp.plyer.facades import StoragePath -from pyobjus import autoclass -import os - -NSFileManager = autoclass('NSFileManager') - -# Directory constants (NSSearchPathDirectory enumeration) -NSApplicationDirectory = 1 -NSDocumentDirectory = 9 -NSDownloadsDirectory = 15 -NSMoviesDirectory = 17 -NSMusicDirectory = 18 -NSPicturesDirectory = 19 - - -class iOSStoragePath(StoragePath): - - def __init__(self): - self.defaultManager = NSFileManager.defaultManager() - - def _get_home_dir(self): - return os.path.expanduser('~/') - - def _get_external_storage_dir(self): - return 'This feature is not implemented for this platform.' - - def _get_root_dir(self): - return 'This feature is not implemented for this platform.' - - def _get_documents_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSDocumentDirectory, 1).firstObject().absoluteString.UTF8String() - - def _get_downloads_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSDownloadsDirectory, 1).firstObject().absoluteString.UTF8String() - - def _get_videos_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSMoviesDirectory, 1).firstObject().absoluteString.UTF8String() - - def _get_music_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSMusicDirectory, 1).firstObject().absoluteString.UTF8String() - - def _get_pictures_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSPicturesDirectory, 1).firstObject().absoluteString.UTF8String() - - def _get_application_dir(self): - return self.defaultManager.URLsForDirectory_inDomains_( - NSApplicationDirectory, 1).firstObject().absoluteString.\ - UTF8String() - - -def instance(): - return iOSStoragePath() diff --git a/sbapp/plyer/platforms/ios/tts.py b/sbapp/plyer/platforms/ios/tts.py deleted file mode 100644 index 510399d..0000000 --- a/sbapp/plyer/platforms/ios/tts.py +++ /dev/null @@ -1,37 +0,0 @@ -from pyobjus import autoclass, objc_str -from pyobjus.dylib_manager import load_framework - -from sbapp.plyer.facades import TTS - -load_framework('/System/Library/Frameworks/AVFoundation.framework') -AVSpeechUtterance = autoclass('AVSpeechUtterance') -AVSpeechSynthesizer = autoclass('AVSpeechSynthesizer') -AVSpeechSynthesisVoice = autoclass('AVSpeechSynthesisVoice') - - -class iOSTextToSpeech(TTS): - def __init__(self): - super().__init__() - self.synth = AVSpeechSynthesizer.alloc().init() - self.voice = None - - def _set_locale(self, locale="en-US"): - self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_( - objc_str(locale) - ) - - def _speak(self, **kwargs): - message = kwargs.get('message') - - if not self.voice: - self._set_locale() - - utterance = \ - AVSpeechUtterance.speechUtteranceWithString_(objc_str(message)) - - utterance.voice = self.voice - self.synth.speakUtterance_(utterance) - - -def instance(): - return iOSTextToSpeech() diff --git a/sbapp/plyer/platforms/ios/uniqueid.py b/sbapp/plyer/platforms/ios/uniqueid.py deleted file mode 100644 index e9fa815..0000000 --- a/sbapp/plyer/platforms/ios/uniqueid.py +++ /dev/null @@ -1,27 +0,0 @@ -''' -Module of iOS API for plyer.uniqueid. -''' - -from pyobjus import autoclass -from pyobjus.dylib_manager import load_framework -from sbapp.plyer.facades import UniqueID - -load_framework('/System/Library/Frameworks/UIKit.framework') -UIDevice = autoclass('UIDevice') - - -class IOSUniqueID(UniqueID): - ''' - Implementation of iOS uniqueid API. - ''' - - def _get_uid(self): - uuid = UIDevice.currentDevice().identifierForVendor.UUIDString() - return uuid.UTF8String() - - -def instance(): - ''' - Instance for facade proxy. - ''' - return IOSUniqueID() diff --git a/sbapp/plyer/platforms/ios/vibrator.py b/sbapp/plyer/platforms/ios/vibrator.py deleted file mode 100644 index 05c12c5..0000000 --- a/sbapp/plyer/platforms/ios/vibrator.py +++ /dev/null @@ -1,43 +0,0 @@ -'''Implementation Vibrator for iOS. - -Install: Add AudioToolbox framework to your application. -''' - -import ctypes -from sbapp.plyer.facades import Vibrator - - -class IosVibrator(Vibrator): - '''iOS Vibrator class. - - iOS doesn't support any feature. - All time, pattern, repetition are ignored. - ''' - - def __init__(self): - super().__init__() - try: - self._func = ctypes.CDLL(None).AudioServicesPlaySystemSound - except AttributeError: - self._func = None - - def _vibrate(self, time=None, **kwargs): - # kSystemSoundID_Vibrate is 0x00000FFF - self._func(0xFFF) - - def _pattern(self, pattern=None, repeat=None, **kwargs): - self._vibrate() - - def _exists(self, **kwargs): - return self._func is not None - - def _cancel(self, **kwargs): - pass - - -def instance(): - '''Returns Vibrator - - :return: instance of class IosVibrator - ''' - return IosVibrator() diff --git a/sbapp/plyer/platforms/linux/audio.py b/sbapp/plyer/platforms/linux/audio.py deleted file mode 100644 index 091e9e5..0000000 --- a/sbapp/plyer/platforms/linux/audio.py +++ /dev/null @@ -1,139 +0,0 @@ -import time -import threading -import RNS -import io -from sbapp.plyer.facades.audio import Audio -from ffpyplayer.player import MediaPlayer -from sbapp.pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter -import pyaudio - -class LinuxAudio(Audio): - - def __init__(self, file_path=None): - default_path = None - super().__init__(file_path or default_path) - - self._recorder = None - self._player = None - self._check_thread = None - self._finished_callback = None - self._loaded_path = None - self.sound = None - self.pa = None - self.is_playing = False - self.recorder = None - self.should_record = False - - def _check_playback(self): - run = True - while run and self.sound != None and not self.sound.get_pause(): - time.sleep(0.25) - if self.duration: - pts = self.sound.get_pts() - if pts > self.duration: - run = False - - self.is_playing = False - - if self._finished_callback and callable(self._finished_callback): - self._check_thread = None - self._finished_callback(self) - - def _record_job(self): - samples_per_second = self.default_rate; - bytes_per_sample = 2; frame_duration_ms = 20 - opus_buffered_encoder = OpusBufferedEncoder() - opus_buffered_encoder.set_application("voip") - opus_buffered_encoder.set_sampling_frequency(samples_per_second) - opus_buffered_encoder.set_channels(1) - opus_buffered_encoder.set_frame_size(frame_duration_ms) - ogg_opus_writer = OggOpusWriter(self._file_path, opus_buffered_encoder) - - frame_duration = frame_duration_ms/1000 - frame_size = int(frame_duration * samples_per_second) - bytes_per_frame = frame_size*bytes_per_sample - - read_bytes = 0 - pcm_buf = b"" - should_continue = True - while self.should_record and self.recorder: - samples_available = self.recorder.get_read_available() - bytes_available = samples_available*bytes_per_sample - if bytes_available > 0: - read_req = bytes_per_frame - len(pcm_buf) - read_n = min(bytes_available, read_req) - read_s = read_n//bytes_per_sample - rb = self.recorder.read(read_s); read_bytes += len(rb) - pcm_buf += rb - - if len(pcm_buf) == bytes_per_frame: - ogg_opus_writer.write(memoryview(bytearray(pcm_buf))) - # RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame)) - pcm_buf = b"" - - # Finish up anything left in buffer - time.sleep(frame_duration) - samples_available = self.recorder.get_read_available() - bytes_available = samples_available*bytes_per_sample - if bytes_available > 0: - read_req = bytes_per_frame - len(pcm_buf) - read_n = min(bytes_available, read_req) - read_s = read_n//bytes_per_sample - rb = self.recorder.read(read_s); read_bytes += len(rb) - pcm_buf += rb - - if len(pcm_buf) == bytes_per_frame: - ogg_opus_writer.write(memoryview(bytearray(pcm_buf))) - # RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame)) - pcm_buf = b"" - - ogg_opus_writer.close() - if self.recorder: - self.recorder.close() - - def _start(self): - self.should_record = True - if self.pa == None: - self.pa = pyaudio.PyAudio() - self.default_input_device = self.pa.get_default_input_device_info() - self.default_rate = 48000 - # self.default_rate = int(self.default_input_device["defaultSampleRate"]) - if self.recorder: - self.recorder.close() - self.recorder = None - self.recorder = self.pa.open(self.default_rate, 1, pyaudio.paInt16, input=True) - threading.Thread(target=self._record_job, daemon=True).start() - - def _stop(self): - if self.should_record == True: - self.should_record = False - - elif self.sound != None: - self.sound.set_pause(True) - self.sound.seek(0, relative=False) - self.is_playing = False - - def _play(self): - self.sound = MediaPlayer(self._file_path) - self.metadata = self.sound.get_metadata() - self.duration = self.metadata["duration"] - if self.duration == None: - time.sleep(0.15) - self.metadata = self.sound.get_metadata() - self.duration = self.metadata["duration"] - - self._loaded_path = self._file_path - self.is_playing = True - - self._check_thread = threading.Thread(target=self._check_playback, daemon=True) - self._check_thread.start() - - def reload(self): - self._loaded_path = None - - def playing(self): - return self.is_playing - - -def instance(): - return LinuxAudio() diff --git a/sbapp/plyer/platforms/macosx/audio.py b/sbapp/plyer/platforms/macosx/audio.py deleted file mode 100644 index 1f2069e..0000000 --- a/sbapp/plyer/platforms/macosx/audio.py +++ /dev/null @@ -1,128 +0,0 @@ -from os.path import join - -from pyobjus import autoclass -from pyobjus.dylib_manager import INCLUDE, load_framework - -from sbapp.plyer.facades import Audio -from sbapp.plyer.platforms.macosx.storagepath import OSXStoragePath - -import threading - -load_framework(INCLUDE.Foundation) -load_framework(INCLUDE.AVFoundation) - -AVAudioPlayer = autoclass("AVAudioPlayer") -AVAudioRecorder = autoclass("AVAudioRecorder") -AVAudioFormat = autoclass("AVAudioFormat") -NSString = autoclass('NSString') -NSURL = autoclass('NSURL') -NSError = autoclass('NSError').alloc() - - -class OSXAudio(Audio): - def __init__(self, file_path=None): - default_path = None - super().__init__(file_path or default_path) - - self._recorder = None - self._player = None - self._current_file = None - - self._check_thread = None - self._finished_callback = None - self._loaded_path = None - self.is_playing = False - self.sound = None - self.pa = None - self.is_playing = False - self.recorder = None - self.should_record = False - - def _check_playback(self): - while self._player and self._player.isPlaying: - time.sleep(0.25) - - if self._finished_callback and callable(self._finished_callback): - self._check_thread = None - self._finished_callback(self) - - def _start(self): - # Conversion of Python file path string to Objective-C NSString - file_path_NSString = NSString.alloc() - file_path_NSString = file_path_NSString.initWithUTF8String_( - self._file_path - ) - - # Definition of Objective-C NSURL object for the output record file - # specified by NSString file path - file_NSURL = NSURL.alloc() - file_NSURL = file_NSURL.initWithString_(file_path_NSString) - - # Internal audio file format specification - af = AVAudioFormat.alloc() - af = af.initWithCommonFormat_sampleRate_channels_interleaved_( - 1, 44100.0, 1, True - ) - - # Audio recorder instance initialization with specified file NSURL - # and audio file format - self._recorder = AVAudioRecorder.alloc() - self._recorder = self._recorder.initWithURL_format_error_( - file_NSURL, af, NSError - ) - - if not self._recorder: - raise Exception(NSError.code, NSError.domain) - - self._recorder.record() - - # Setting the currently recorded file as current file - # for using it as a parameter in audio player - self._current_file = file_NSURL - - def _stop(self): - if self._recorder: - self._recorder.stop() - self._recorder = None - - if self._player: - self._player.stop() - self._player = None - - def _play(self): - # Conversion of Python file path string to Objective-C NSString - file_path_NSString = NSString.alloc() - file_path_NSString = file_path_NSString.initWithUTF8String_( - self._file_path - ) - - # Definition of Objective-C NSURL object for the output record file - # specified by NSString file path - file_NSURL = NSURL.alloc() - file_NSURL = file_NSURL.initWithString_(file_path_NSString) - self._current_file = file_NSURL - - # Audio player instance initialization with the file NSURL - # of the last recorded audio file - self._player = AVAudioPlayer.alloc() - self._player = self._player.initWithContentsOfURL_error_( - self._current_file, NSError - ) - - if not self._player: - raise Exception(NSError.code, NSError.domain) - - self._player.play() - - self._check_thread = threading.Thread(target=self._check_playback, daemon=True) - self._check_thread.start() - - def reload(self): - self._loaded_path = None - - def playing(self): - return self.is_playing - - -def instance(): - return OSXAudio() diff --git a/sbapp/plyer/platforms/win/audio.py b/sbapp/plyer/platforms/win/audio.py deleted file mode 100644 index c74d97d..0000000 --- a/sbapp/plyer/platforms/win/audio.py +++ /dev/null @@ -1,413 +0,0 @@ -''' -Documentation: -http://docs.microsoft.com/en-us/windows/desktop/Multimedia - -.. versionadded:: 1.4.0 -''' - -from os.path import join - -from ctypes import windll -from ctypes import ( - sizeof, c_void_p, c_ulonglong, c_ulong, - c_wchar_p, byref, Structure, create_string_buffer -) -from ctypes.wintypes import DWORD, UINT - -from sbapp.plyer.facades import Audio -from sbapp.plyer.platforms.win.storagepath import WinStoragePath - -# DWORD_PTR i.e. ULONG_PTR, 32/64bit -ULONG_PTR = c_ulonglong if sizeof(c_void_p) == 8 else c_ulong - -# device specific symbols -MCI_OPEN = 0x803 -MCI_OPEN_TYPE = 0x2000 -MCI_OPEN_ELEMENT = 512 -MCI_RECORD = 0x80F -MCI_STOP = 0x808 -MCI_SAVE = 0x813 -MCI_PLAY = 0x806 -MCI_CLOSE = 0x804 - -# recorder specific symbols -MCI_FROM = 4 -MCI_TO = 8 -MCI_WAIT = 2 -MCI_SAVE_FILE = 256 - - -class MCI_OPEN_PARMS(Structure): - ''' - Struct for MCI_OPEN message parameters. - - .. versionadded:: 1.4.0 - ''' - - _fields_ = [ - ('mciOpenParms', ULONG_PTR), - ('wDeviceID', UINT), - ('lpstrDeviceType', c_wchar_p), - ('lpstrElementName', c_wchar_p), - ('lpstrAlias', c_wchar_p) - ] - - -class MCI_RECORD_PARMS(Structure): - ''' - Struct for MCI_RECORD message parameters. - - http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-record-parms - - .. versionadded:: 1.4.0 - ''' - - _fields_ = [ - ('dwCallback', ULONG_PTR), - ('dwFrom', DWORD), - ('dwTo', DWORD) - ] - - -class MCI_SAVE_PARMS(Structure): - ''' - Struct for MCI_SAVE message parameters. - - http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-save-parms - - .. versionadded:: 1.4.0 - ''' - - _fields_ = [ - ('dwCallback', ULONG_PTR), - ('lpfilename', c_wchar_p) - ] - - -class MCI_PLAY_PARMS(Structure): - ''' - Struct for MCI_PLAY message parameters. - - http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-play-parms - - .. versionadded:: 1.4.0 - ''' - - _fields_ = [ - ('dwCallback', ULONG_PTR), - ('dwFrom', DWORD), - ('dwTo', DWORD) - ] - - -def send_command(device, msg, flags, params): - ''' - Generic mciSendCommandW() wrapper with error handler. - All parameters are required as for mciSendCommandW(). - In case of no `params` passed, use `None`, that value - won't be dereferenced. - - .. versionadded:: 1.4.0 - ''' - - multimedia = windll.winmm - send_command_w = multimedia.mciSendCommandW - get_error = multimedia.mciGetErrorStringW - - # error text buffer - # by API specification 128 is max, however the API sometimes - # kind of does not respect the documented bounds and returns - # more characters than buffer length...?! - error_len = 128 - - # big enough to prevent API accidentally segfaulting - error_text = create_string_buffer(error_len * 2) - - # open a recording device with a new file - error_code = send_command_w( - device, # device ID - msg, - flags, - - # reference to parameters structure or original value - # in case of params=False/0/None/... - byref(params) if params else params - ) - - # handle error messages if any - if error_code: - # device did not open, raise an exception - get_error(error_code, byref(error_text), error_len) - error_text = error_text.raw.replace(b'\x00', b'').decode('utf-8') - - # either it can close already open device or it will fail because - # the device is in non-closable state, but the end result is the same - # and it makes no sense to parse MCI_CLOSE's error in this case - send_command_w(device, MCI_CLOSE, 0, None) - raise Exception(error_code, error_text) - - # return params struct because some commands write into it - # to pass some values out of the local function scope - return params - - -class WinRecorder: - ''' - Generic wrapper for MCI_RECORD handling the filenames and device closing - in the same approach like it is used for other platforms. - - .. versionadded:: 1.4.0 - ''' - - def __init__(self, device, filename): - self._device = device - self._filename = filename - - @property - def device(self): - ''' - Public property returning device ID. - - .. versionadded:: 1.4.0 - ''' - return self._device - - @property - def filename(self): - ''' - Public property returning filename for current recording. - - .. versionadded:: 1.4.0 - ''' - return self._filename - - def record(self): - ''' - Start recording a WAV sound. - - .. versionadded:: 1.4.0 - ''' - send_command( - device=self.device, - msg=MCI_RECORD, - flags=0, - params=None - ) - - def stop(self): - ''' - Stop recording and save the data to a file path - self.filename. Wait until the file is written. - Close the device afterwards. - - .. versionadded:: 1.4.0 - ''' - - # stop the recording first - send_command( - device=self.device, - msg=MCI_STOP, - flags=MCI_WAIT, - params=None - ) - - # choose filename for the WAV file - save_params = MCI_SAVE_PARMS() - save_params.lpfilename = self.filename - - # save the sound data to a file and wait - # until it ends writing to the file - send_command( - device=self.device, - msg=MCI_SAVE, - flags=MCI_SAVE_FILE | MCI_WAIT, - params=save_params - ) - - # close the recording device - send_command( - device=self.device, - msg=MCI_CLOSE, - flags=0, - params=None - ) - - -class WinPlayer: - ''' - Generic wrapper for MCI_PLAY handling the device closing. - - .. versionadded:: 1.4.0 - ''' - - def __init__(self, device): - self._device = device - - @property - def device(self): - ''' - Public property returning device ID. - - .. versionadded:: 1.4.0 - ''' - return self._device - - def play(self): - ''' - Start playing a WAV sound. - - .. versionadded:: 1.4.0 - ''' - play_params = MCI_PLAY_PARMS() - play_params.dwFrom = 0 - - send_command( - device=self.device, - msg=MCI_PLAY, - flags=MCI_FROM, - params=play_params - ) - - def stop(self): - ''' - Stop playing a WAV sound and close the device. - - .. versionadded:: 1.4.0 - ''' - send_command( - device=self.device, - msg=MCI_STOP, - flags=MCI_WAIT, - params=None - ) - - # close the playing device - send_command( - device=self.device, - msg=MCI_CLOSE, - flags=0, - params=None - ) - - -class WinAudio(Audio): - ''' - Windows implementation of audio recording and audio playing. - - .. versionadded:: 1.4.0 - ''' - - def __init__(self, file_path=None): - # default path unless specified otherwise - default_path = join( - WinStoragePath().get_music_dir(), - 'audio.wav' - ) - super().__init__(file_path or default_path) - - self._recorder = None - self._player = None - self._current_file = None - self._check_thread = None - self._finished_callback = None - self._loaded_path = None - self.is_playing = False - self.sound = None - self.pa = None - self.is_playing = False - self.recorder = None - self.should_record = False - - def _start(self): - ''' - Start recording a WAV sound in the background asynchronously. - - .. versionadded:: 1.4.0 - ''' - - # clean everything before recording in case - # there is a different device open - self._stop() - - # create structure and set device parameters - open_params = MCI_OPEN_PARMS() - open_params.lpstrDeviceType = 'waveaudio' - open_params.lpstrElementName = '' - - # open a new device for recording - open_params = send_command( - device=0, # device ID before opening - msg=MCI_OPEN, - - # empty filename in lpstrElementName - # device type in lpstrDeviceType - flags=MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, - params=open_params - ) - - # get recorder with device id and path for saving - self._recorder = WinRecorder( - device=open_params.wDeviceID, - filename=self._file_path - ) - self._recorder.record() - - # Setting the currently recorded file as current file - # for using it as a parameter in audio player - self._current_file = self._recorder.filename - - def _stop(self): - ''' - Stop recording or playing of a WAV sound. - - .. versionadded:: 1.4.0 - ''' - - if self._recorder: - self._recorder.stop() - self._recorder = None - - if self._player: - self._player.stop() - self._player = None - - def _play(self): - ''' - Play a WAV sound from a file. Prioritize latest recorded file before - default file path from WinAudio. - - .. versionadded:: 1.4.0 - ''' - - # create structure and set device parameters - open_params = MCI_OPEN_PARMS() - open_params.lpstrDeviceType = 'waveaudio' - open_params.lpstrElementName = self._current_file or self._file_path - - # open a new device for playing - open_params = send_command( - device=0, # device ID before opening - msg=MCI_OPEN, - - # existing filename in lpstrElementName - # device type in lpstrDeviceType - flags=MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, - params=open_params - ) - - # get recorder with device id and path for saving - self._player = WinPlayer(device=open_params.wDeviceID) - self._player.play() - - def reload(self): - self._loaded_path = None - - def playing(self): - return self.is_playing - - -def instance(): - ''' - Instance for facade proxy. - ''' - return WinAudio()