diff --git a/README.rst b/README.rst index 4ba6cbbdf..9e77544fd 100755 --- a/README.rst +++ b/README.rst @@ -36,6 +36,7 @@ Gyroscope X X Battery X X X X X Native file chooser X X X Orientation X +Proximity X Audio recording X Flash X X Wifi X X X diff --git a/examples/proximity/buildozer.spec b/examples/proximity/buildozer.spec new file mode 100644 index 000000000..9d1cf20af --- /dev/null +++ b/examples/proximity/buildozer.spec @@ -0,0 +1,233 @@ +[app] + +# (str) Title of your application +title = Plyer Proximity Example + +# (str) Package name +package.name = plyer.proximity + +# (str) Package domain (needed for android/ios packaging) +package.domain = org.test + +# (str) Source code where the main.py live +source.dir = . + +# (list) Source files to include (let empty to include all the files) +source.include_exts = py,png,jpg,kv,atlas + +# (list) List of inclusions using pattern matching +#source.include_patterns = assets/*,images/*.png + +# (list) Source files to exclude (let empty to not exclude anything) +#source.exclude_exts = spec + +# (list) List of directory to exclude (let empty to not exclude anything) +#source.exclude_dirs = tests, bin + +# (list) List of exclusions using pattern matching +#source.exclude_patterns = license,images/*/*.jpg + +# (str) Application versioning (method 1) +version = 0.1 + +# (str) Application versioning (method 2) +# version.regex = __version__ = ['"](.*)['"] +# version.filename = %(source.dir)s/main.py + +# (list) Application requirements +# comma seperated e.g. requirements = sqlite3,kivy +requirements = kivy, plyer + +# (str) Custom source folders for requirements +# Sets custom source for any requirements with recipes +#requirements.source.plyer = + +# (list) Garden requirements +#garden_requirements = + +# (str) Presplash of the application +#presplash.filename = %(source.dir)s/data/presplash.png + +# (str) Icon of the application +#icon.filename = %(source.dir)s/data/icon.png + +# (str) Supported orientation (one of landscape, portrait or all) +orientation = portrait + +# (list) List of service to declare +#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY + +# +# OSX Specific +# + +# +# author = © Copyright Info + +# +# Android specific +# + +# (bool) Indicate if the application should be fullscreen or not +fullscreen = 0 + +# (list) Permissions +#android.permissions = INTERNET + +# (int) Android API to use +#android.api = 19 + +# (int) Minimum API required +#android.minapi = 9 + +# (int) Android SDK version to use +#android.sdk = 20 + +# (str) Android NDK version to use +#android.ndk = 9c + +# (bool) Use --private data storage (True) or --dir public storage (False) +#android.private_storage = True + +# (str) Android NDK directory (if empty, it will be automatically downloaded.) +#android.ndk_path = + +# (str) Android SDK directory (if empty, it will be automatically downloaded.) +#android.sdk_path = + +# (str) ANT directory (if empty, it will be automatically downloaded.) +#android.ant_path = + +# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) +#android.p4a_dir = + +# (str) The directory in which python-for-android should look for your own build recipes (if any) +#p4a.local_recipes = + +# (list) python-for-android whitelist +#android.p4a_whitelist = + +# (bool) If True, then skip trying to update the Android sdk +# This can be useful to avoid excess Internet downloads or save time +# when an update is due and you just want to test/build your package +# android.skip_update = False + +# (str) Bootstrap to use for android builds (android_new only) +# android.bootstrap = sdl2 + +# (str) Android entry point, default is ok for Kivy-based app +#android.entrypoint = org.renpy.android.PythonActivity + +# (list) List of Java .jar files to add to the libs so that pyjnius can access +# their classes. Don't add jars that you do not need, since extra jars can slow +# down the build process. Allows wildcards matching, for example: +# OUYA-ODK/libs/*.jar +#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar + +# (list) List of Java files to add to the android project (can be java or a +# directory containing the files) +#android.add_src = + +# (str) python-for-android branch to use, if not master, useful to try +# not yet merged features. +#android.branch = master + +# (str) OUYA Console category. Should be one of GAME or APP +# If you leave this blank, OUYA support will not be enabled +#android.ouya.category = GAME + +# (str) Filename of OUYA Console icon. It must be a 732x412 png image. +#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png + +# (str) XML file to include as an intent filters in tag +#android.manifest.intent_filters = + +# (list) Android additionnal libraries to copy into libs/armeabi +#android.add_libs_armeabi = libs/android/*.so +#android.add_libs_armeabi_v7a = libs/android-v7/*.so +#android.add_libs_x86 = libs/android-x86/*.so +#android.add_libs_mips = libs/android-mips/*.so + +# (bool) Indicate whether the screen should stay on +# Don't forget to add the WAKE_LOCK permission if you set this to True +#android.wakelock = False + +# (list) Android application meta-data to set (key=value format) +#android.meta_data = + +# (list) Android library project to add (will be added in the +# project.properties automatically.) +#android.library_references = + +# (str) Android logcat filters to use +#android.logcat_filters = *:S python:D + +# (bool) Copy library instead of making a libpymodules.so +#android.copy_libs = 1 + +# +# iOS specific +# + +# (str) Path to a custom kivy-ios folder +#ios.kivy_ios_dir = ../kivy-ios + +# (str) Name of the certificate to use for signing the debug version +# Get a list of available identities: buildozer ios list_identities +#ios.codesign.debug = "iPhone Developer: ()" + +# (str) Name of the certificate to use for signing the release version +#ios.codesign.release = %(ios.codesign.debug)s + + +[buildozer] + +# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) +log_level = 2 + +# (int) Display warning if buildozer is run as root (0 = False, 1 = True) +warn_on_root = 1 + +# (str) Path to build artifact storage, absolute or relative to spec file +# build_dir = ./.buildozer + +# (str) Path to build output (i.e. .apk, .ipa) storage +# bin_dir = ./bin + +# ----------------------------------------------------------------------------- +# List as sections +# +# You can define all the "list" as [section:key]. +# Each line will be considered as a option to the list. +# Let's take [app] / source.exclude_patterns. +# Instead of doing: +# +#[app] +#source.exclude_patterns = license,data/audio/*.wav,data/images/original/* +# +# This can be translated into: +# +#[app:source.exclude_patterns] +#license +#data/audio/*.wav +#data/images/original/* +# + + +# ----------------------------------------------------------------------------- +# Profiles +# +# You can extend section / key with a profile +# For example, you want to deploy a demo version of your application without +# HD content. You could first change the title to add "(demo)" in the name +# and extend the excluded directories to remove the HD content. +# +#[app@demo] +#title = My Application (demo) +# +#[app:source.exclude_patterns@demo] +#images/hd/* +# +# Then, invoke the command line with the "demo" profile: +# +#buildozer --profile demo android debug diff --git a/examples/proximity/main.py b/examples/proximity/main.py new file mode 100644 index 000000000..1761a5437 --- /dev/null +++ b/examples/proximity/main.py @@ -0,0 +1,77 @@ +from kivy.app import App +from kivy.clock import Clock +from kivy.lang import Builder +from kivy.properties import BooleanProperty +from kivy.properties import ObjectProperty +from kivy.uix.boxlayout import BoxLayout + + +Builder.load_string(''' +#:import proximity plyer.proximity +: + proximity: proximity + orientation: 'vertical' + padding: '50dp' + spacing: '20dp' + + BoxLayout: + orientation: 'horizontal' + size_hint_y: 0.3 + Button: + id: button_enable + text: 'Enable' + disabled: False + on_release: + root.enable() + button_disable.disabled = not button_disable.disabled + button_enable.disabled = not button_enable.disabled + Button: + id: button_disable + text: 'Disable' + disabled: True + on_release: + root.disable() + button_disable.disabled = not button_disable.disabled + button_enable.disabled = not button_enable.disabled + + Label: + text: 'Does Proximity Sensor detect something?' + Label: + text: 'Yes' if root.is_near else 'No' + + Widget: + Label: + text: 'Cover with your hand' + Label: + text: 'a top part of phone to see result.' +''') + + +class ProximityInterface(BoxLayout): + '''Root Widget.''' + + proximity = ObjectProperty() + is_near = BooleanProperty(False) + + def enable(self): + self.proximity.enable() + Clock.schedule_interval(self.get_proxime, 1 / 20.) + + def disable(self): + self.proximity.disable() + Clock.unschedule(self.get_proxime) + + def get_proxime(self, dt): + self.is_near = self.proximity.proximity + + +class ProximityApp(App): + + def build(self): + return ProximityInterface() + + def on_pause(self): + return True + +if __name__ == "__main__": + ProximityApp().run() diff --git a/plyer/__init__.py b/plyer/__init__.py index 3c8aeb5f5..7e5c8862b 100644 --- a/plyer/__init__.py +++ b/plyer/__init__.py @@ -6,8 +6,8 @@ __all__ = ('accelerometer', 'audio', 'battery', 'call', 'camera', 'compass', 'email', 'filechooser', 'flash', 'gps', 'gyroscope', 'irblaster', - 'orientation', 'notification', 'sms', 'tts', 'uniqueid', 'vibrator', - 'wifi') + 'orientation', 'notification', 'proximity', 'sms', 'tts', 'uniqueid', + 'vibrator', 'wifi') __version__ = '1.2.5dev' @@ -54,6 +54,9 @@ #: Notification proxy to :class:`plyer.facades.Notification` notification = Proxy('notification', facades.Notification) +#: Proximity proxy to :class:`plyer.facades.Proximity` +proximity = Proxy('proximity', facades.Proximity) + #: Sms proxy to :class:`plyer.facades.Sms` sms = Proxy('sms', facades.Sms) diff --git a/plyer/facades/__init__.py b/plyer/facades/__init__.py index a726c1687..99e57f64b 100644 --- a/plyer/facades/__init__.py +++ b/plyer/facades/__init__.py @@ -8,8 +8,8 @@ __all__ = ('Accelerometer', 'Audio', 'Battery', 'Call', 'Camera', 'Compass', 'Email', 'FileChooser', 'GPS', 'Gyroscope', 'IrBlaster', - 'Orientation', 'Notification', 'Sms', 'TTS', 'UniqueID', 'Vibrator', - 'Wifi', 'Flash') + 'Orientation', 'Notification', 'Proximity', 'Sms', 'TTS', 'UniqueID', + 'Vibrator', 'Wifi', 'Flash') from plyer.facades.accelerometer import Accelerometer from plyer.facades.audio import Audio @@ -22,6 +22,7 @@ from plyer.facades.gps import GPS from plyer.facades.gyroscope import Gyroscope from plyer.facades.irblaster import IrBlaster +from plyer.facades.proximity import Proximity from plyer.facades.orientation import Orientation from plyer.facades.notification import Notification from plyer.facades.sms import Sms diff --git a/plyer/facades/proximity.py b/plyer/facades/proximity.py new file mode 100644 index 000000000..eb0d32750 --- /dev/null +++ b/plyer/facades/proximity.py @@ -0,0 +1,42 @@ +class Proximity(object): + '''Proximity facade. + + The proximity sensor is commonly used to determine distance whether + phone is close to your head. Commonly is used when you have a call + and you stick your phone with your head. Then screen of phone turns off. + + Use method `enable` to turn on proximity sensor and method `disable` for + turn off. + + To check if some object (or your head) is near sensor check values from + property `proximity`. It returns `True` when object is close. + + .. versionadded:: 1.2.5 + ''' + + @property + def proximity(self): + '''Return True or False depending if there is an object or not. + + :return: True if there is an object. Otherwise False. + ''' + return self._get_proximity() + + def _enable(self, **kwargs): + raise NotImplementedError() + + def enable(self): + '''Enable the proximity sensor. + ''' + self._enable() + + def _disable(self, **kwargs): + raise NotImplementedError() + + def disable(self): + '''Disable the proximity sensor. + ''' + self._disable() + + def _get_proximity(self): + raise NotImplementedError() diff --git a/plyer/platforms/android/proximity.py b/plyer/platforms/android/proximity.py new file mode 100644 index 000000000..37c0ba7cd --- /dev/null +++ b/plyer/platforms/android/proximity.py @@ -0,0 +1,67 @@ +from jnius import autoclass +from jnius import cast +from jnius import java_method +from jnius import PythonJavaClass + +from plyer.platforms.android import activity +from plyer.facades import Proximity + +ActivityInfo = autoclass('android.content.pm.ActivityInfo') +Context = autoclass('android.content.Context') +Sensor = autoclass('android.hardware.Sensor') +SensorManager = autoclass('android.hardware.SensorManager') + + +class ProximitySensorListener(PythonJavaClass): + __javainterfaces__ = ['android/hardware/SensorEventListener'] + + def __init__(self): + super(ProximitySensorListener, self).__init__() + service = activity.getSystemService(Context.SENSOR_SERVICE) + self.SensorManager = cast('android.hardware.SensorManager', service) + + self.sensor = self.SensorManager.getDefaultSensor( + Sensor.TYPE_PROXIMITY) + self.value = None + + def enable(self): + self.SensorManager.registerListener(self, self.sensor, + SensorManager.SENSOR_DELAY_NORMAL) + + def disable(self): + self.SensorManager.unregisterListener(self, self.sensor) + + @java_method('(Landroid/hardware/SensorEvent;)V') + def onSensorChanged(self, event): + self.value = event.values[0] + + @java_method('(Landroid/hardware/Sensor;I)V') + def onAccuracyChanged(self, sensor, accuracy): + pass + + +class AndroidProximity(Proximity): + + listener = None + + def _enable(self, **kwargs): + if not self.listener: + self.listener = ProximitySensorListener() + self.listener.enable() + + def _disable(self, **kwargs): + if self.listener: + self.listener.disable() + delattr(self, 'listener') + + def _get_proximity(self): + if self.listener: + value = self.listener.value + # value is 0.0 when proxime sensor is covered. In other case + # value is 5.0 because in smartphone, optical proximity sensors + # are used. + return value < 5.0 + + +def instance(): + return AndroidProximity()