Cleanup unneeded dependencies

This commit is contained in:
Mark Qvist 2025-11-27 13:57:16 +01:00
parent c727abe1ef
commit 65e08a9d41
30 changed files with 6 additions and 1857 deletions

View file

@ -40,8 +40,10 @@ preparewheel:
pyclean . pyclean .
$(MAKE) -C sbapp cleanrns $(MAKE) -C sbapp cleanrns
build_wheel: compile_wheel:
python3 setup.py sdist bdist_wheel python3 setup.py bdist_wheel
build_wheel: remove_symlinks compile_wheel create_symlinks
build_win_exe: build_win_exe:
python -m PyInstaller sideband.spec --noconfirm python -m PyInstaller sideband.spec --noconfirm

View file

@ -5,7 +5,7 @@ Plyer
''' '''
__all__ = ( __all__ = (
'accelerometer', 'audio', 'barometer', 'battery', 'bluetooth', 'accelerometer', 'barometer', 'battery', 'bluetooth',
'brightness', 'call', 'camera', 'compass', 'cpu', 'email', 'filechooser', 'brightness', 'call', 'camera', 'compass', 'cpu', 'email', 'filechooser',
'flash', 'gps', 'gravity', 'gyroscope', 'humidity', 'irblaster', 'flash', 'gps', 'gravity', 'gyroscope', 'humidity', 'irblaster',
'keystore', 'light', 'notification', 'orientation', 'processors', 'keystore', 'light', 'notification', 'orientation', 'processors',
@ -29,9 +29,6 @@ accelerometer = Proxy('accelerometer', facades.Accelerometer)
#: Keyring proxy to :class::`plyer.facades.Keystore` #: Keyring proxy to :class::`plyer.facades.Keystore`
keystore = Proxy('keystore', 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 to :class:`plyer.facades.Barometer`
barometer = Proxy('barometer', facades.Barometer) barometer = Proxy('barometer', facades.Barometer)

View file

@ -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', 'Compass', 'Email', 'FileChooser', 'GPS', 'Gravity', 'Gyroscope',
'IrBlaster', 'Light', 'Orientation', 'Notification', 'Proximity', 'IrBlaster', 'Light', 'Orientation', 'Notification', 'Proximity',
'Sms', 'TTS', 'UniqueID', 'Vibrator', 'Wifi', 'Flash', 'CPU', 'Sms', 'TTS', 'UniqueID', 'Vibrator', 'Wifi', 'Flash', 'CPU',
@ -17,7 +17,6 @@ __all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera',
import RNS import RNS
if RNS.vendor.platformutils.is_android(): if RNS.vendor.platformutils.is_android():
from plyer.facades.accelerometer import Accelerometer from plyer.facades.accelerometer import Accelerometer
from plyer.facades.audio import Audio
from plyer.facades.barometer import Barometer from plyer.facades.barometer import Barometer
from plyer.facades.battery import Battery from plyer.facades.battery import Battery
from plyer.facades.call import Call from plyer.facades.call import Call
@ -53,7 +52,6 @@ if RNS.vendor.platformutils.is_android():
from plyer.facades.devicename import DeviceName from plyer.facades.devicename import DeviceName
else: else:
from sbapp.plyer.facades.accelerometer import Accelerometer 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.barometer import Barometer
from sbapp.plyer.facades.battery import Battery from sbapp.plyer.facades.battery import Battery
from sbapp.plyer.facades.call import Call from sbapp.plyer.facades.call import Call

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()