├── .gitattributes ├── .github └── workflows │ ├── ci_osx.yml │ ├── ci_ubuntu.yml │ ├── ci_windows.yml │ ├── no-response.yml │ ├── plyer_deploy.yml │ └── support.yml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTACT.md ├── CONTRIBUTING.md ├── FAQ.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── ci ├── ci_osx.sh ├── ci_ubuntu.sh ├── ci_windows.ps1 ├── docker │ ├── Dockerfile.archlinux.py3 │ ├── Dockerfile.bionic.py3 │ ├── Dockerfile.bionic.style │ └── Dockerfile.fedora28.py3 ├── docker_build.sh ├── docker_run.sh ├── entrypoint.sh ├── github_checkout_branch.sh ├── osx_fix_cellar.sh ├── osx_get_pipvirtualenv.sh ├── osx_get_py3.sh ├── osx_run_tests.sh ├── win_install.bat ├── win_set_python.bat ├── win_style.bat └── win_tests.bat ├── devrequirements.txt ├── docs ├── Makefile ├── requirements.txt └── source │ ├── api.rst │ ├── conf.py │ ├── contact.rst │ ├── contribute.rst │ ├── faq.rst │ └── index.rst ├── examples ├── README ├── accelerometer │ ├── basic │ │ ├── README │ │ ├── accelerometertest.kv │ │ ├── buildozer.spec │ │ └── main.py │ └── using_graph │ │ ├── README │ │ ├── Screenshot_Android_4.3.png │ │ ├── accelerometerdemo.kv │ │ ├── buildozer.spec │ │ ├── libs │ │ └── garden │ │ │ └── garden.graph │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ └── graph.png │ │ └── main.py ├── audio │ ├── buildozer.spec │ └── main.py ├── barometer │ ├── buildozer.spec │ └── main.py ├── battery │ ├── buildozer.spec │ └── main.py ├── bluetooth │ ├── buildozer.spec │ └── main.py ├── brightness │ ├── buildozer.spec │ └── main.py ├── call │ ├── buildozer.spec │ └── main.py ├── camera │ └── basic │ │ ├── README │ │ ├── buildozer.spec │ │ ├── camerademo.kv │ │ └── main.py ├── compass │ ├── README │ ├── buildozer.spec │ └── main.py ├── email │ ├── buildozer.spec │ └── main.py ├── filechooser │ └── main.py ├── flash │ ├── buildozer.spec │ └── main.py ├── gps │ ├── README │ ├── buildozer.spec │ └── main.py ├── gravity │ ├── buildozer.spec │ └── main.py ├── gyroscope │ ├── buildozer.spec │ └── main.py ├── humidity │ ├── buildozer.spec │ └── main.py ├── light │ ├── buildozer.spec │ └── main.py ├── maps │ └── main.py ├── notification │ ├── buildozer.spec │ ├── main.py │ ├── notificationdemo.kv │ ├── plyer-icon.ico │ └── plyer-icon.png ├── orientation │ ├── buildozer.spec │ └── main.py ├── proximity │ ├── buildozer.spec │ └── main.py ├── screenshot │ └── main.py ├── sms │ ├── buildozer.spec │ └── main.py ├── spatialorientation │ ├── buildozer.spec │ └── main.py ├── speech2text │ ├── buildozer.spec │ └── main.py ├── storagepath │ ├── buildozer.spec │ └── main.py ├── temperature │ ├── buildozer.spec │ └── main.py ├── text2speech │ ├── buildozer.spec │ ├── main.py │ └── text2speechdemo.kv ├── uniqueid │ ├── buildozer.spec │ └── main.py ├── vibrator │ ├── buildozer.spec │ └── main.py ├── voip │ ├── README.MD │ ├── buildozer.spec │ ├── echoServer.py │ ├── main.py │ └── node VOIP server.js └── wifi │ └── main.py ├── plyer ├── __init__.py ├── facades │ ├── __init__.py │ ├── accelerometer.py │ ├── audio.py │ ├── barometer.py │ ├── battery.py │ ├── bluetooth.py │ ├── brightness.py │ ├── call.py │ ├── camera.py │ ├── compass.py │ ├── cpu.py │ ├── devicename.py │ ├── email.py │ ├── filechooser.py │ ├── flash.py │ ├── gps.py │ ├── gravity.py │ ├── gyroscope.py │ ├── humidity.py │ ├── irblaster.py │ ├── keystore.py │ ├── light.py │ ├── maps.py │ ├── notification.py │ ├── orientation.py │ ├── processors.py │ ├── proximity.py │ ├── screenshot.py │ ├── sms.py │ ├── spatialorientation.py │ ├── storagepath.py │ ├── stt.py │ ├── temperature.py │ ├── tts.py │ ├── uniqueid.py │ ├── vibrator.py │ ├── voip.py │ └── wifi.py ├── platforms │ ├── __init__.py │ ├── android │ │ ├── __init__.py │ │ ├── accelerometer.py │ │ ├── audio.py │ │ ├── barometer.py │ │ ├── battery.py │ │ ├── bluetooth.py │ │ ├── brightness.py │ │ ├── call.py │ │ ├── camera.py │ │ ├── compass.py │ │ ├── devicename.py │ │ ├── email.py │ │ ├── filechooser.py │ │ ├── flash.py │ │ ├── gps.py │ │ ├── gravity.py │ │ ├── gyroscope.py │ │ ├── humidity.py │ │ ├── irblaster.py │ │ ├── keystore.py │ │ ├── light.py │ │ ├── notification.py │ │ ├── orientation.py │ │ ├── proximity.py │ │ ├── sms.py │ │ ├── spatialorientation.py │ │ ├── storagepath.py │ │ ├── stt.py │ │ ├── temperature.py │ │ ├── tts.py │ │ ├── uniqueid.py │ │ ├── vibrator.py │ │ └── voip.py │ ├── ios │ │ ├── __init__.py │ │ ├── accelerometer.py │ │ ├── barometer.py │ │ ├── battery.py │ │ ├── brightness.py │ │ ├── call.py │ │ ├── camera.py │ │ ├── compass.py │ │ ├── email.py │ │ ├── filechooser.py │ │ ├── flash.py │ │ ├── frameworks │ │ │ ├── Voip.framework │ │ │ │ ├── Headers │ │ │ │ │ ├── Voip-Swift.h │ │ │ │ │ └── Voip.h │ │ │ │ ├── Info.plist │ │ │ │ ├── Modules │ │ │ │ │ ├── Voip.swiftmodule │ │ │ │ │ │ ├── arm64-apple-ios.abi.json │ │ │ │ │ │ ├── arm64-apple-ios.private.swiftinterface │ │ │ │ │ │ ├── arm64-apple-ios.swiftdoc │ │ │ │ │ │ ├── arm64-apple-ios.swiftinterface │ │ │ │ │ │ └── arm64-apple-ios.swiftmodule │ │ │ │ │ └── module.modulemap │ │ │ │ ├── Voip │ │ │ │ └── _CodeSignature │ │ │ │ │ └── CodeResources.xml │ │ │ ├── __init__.py │ │ │ ├── simulator │ │ │ │ ├── Voip.framework │ │ │ │ │ ├── Headers │ │ │ │ │ │ ├── Voip-Swift.h │ │ │ │ │ │ └── Voip.h │ │ │ │ │ ├── Info.plist │ │ │ │ │ ├── Modules │ │ │ │ │ │ ├── Voip.swiftmodule │ │ │ │ │ │ │ ├── Project │ │ │ │ │ │ │ │ └── arm64-apple-ios-simulator.swiftsourceinfo │ │ │ │ │ │ │ ├── arm64-apple-ios-simulator.abi.json │ │ │ │ │ │ │ ├── arm64-apple-ios-simulator.swiftdoc │ │ │ │ │ │ │ └── arm64-apple-ios-simulator.swiftmodule │ │ │ │ │ │ └── module.modulemap │ │ │ │ │ ├── Voip │ │ │ │ │ └── _CodeSignature │ │ │ │ │ │ └── CodeResources.xml │ │ │ │ └── __init__.py │ │ │ └── utils.py │ │ ├── gps.py │ │ ├── gravity.py │ │ ├── gyroscope.py │ │ ├── keystore.py │ │ ├── maps.py │ │ ├── sms.py │ │ ├── spatialorientation.py │ │ ├── storagepath.py │ │ ├── tts.py │ │ ├── uniqueid.py │ │ ├── vibrator.py │ │ └── voip.py │ ├── linux │ │ ├── __init__.py │ │ ├── accelerometer.py │ │ ├── battery.py │ │ ├── brightness.py │ │ ├── cpu.py │ │ ├── devicename.py │ │ ├── email.py │ │ ├── filechooser.py │ │ ├── keystore.py │ │ ├── notification.py │ │ ├── orientation.py │ │ ├── processors.py │ │ ├── screenshot.py │ │ ├── storagepath.py │ │ ├── tts.py │ │ ├── uniqueid.py │ │ └── wifi.py │ ├── macosx │ │ ├── __init__.py │ │ ├── accelerometer.py │ │ ├── audio.py │ │ ├── battery.py │ │ ├── bluetooth.py │ │ ├── cpu.py │ │ ├── devicename.py │ │ ├── email.py │ │ ├── filechooser.py │ │ ├── keystore.py │ │ ├── libs │ │ │ ├── __init__.py │ │ │ ├── osx_motion_sensor.py │ │ │ └── osx_paths.py │ │ ├── maps.py │ │ ├── notification.py │ │ ├── screenshot.py │ │ ├── sms.py │ │ ├── storagepath.py │ │ ├── tts.py │ │ ├── uniqueid.py │ │ └── wifi.py │ └── win │ │ ├── __init__.py │ │ ├── audio.py │ │ ├── battery.py │ │ ├── cpu.py │ │ ├── devicename.py │ │ ├── email.py │ │ ├── filechooser.py │ │ ├── keystore.py │ │ ├── libs │ │ ├── __init__.py │ │ ├── balloontip.py │ │ ├── batterystatus.py │ │ ├── wifi_defs.py │ │ └── win_api_defs.py │ │ ├── notification.py │ │ ├── screenshot.py │ │ ├── storagepath.py │ │ ├── tts.py │ │ ├── uniqueid.py │ │ └── wifi.py ├── tests │ ├── __init__.py │ ├── common.py │ ├── images │ │ └── kivy32.ico │ ├── test_audio.py │ ├── test_battery.py │ ├── test_bluetooth.py │ ├── test_cpu.py │ ├── test_devicename.py │ ├── test_email.py │ ├── test_facade.py │ ├── test_notification.py │ ├── test_screenshot.py │ ├── test_storagepath.py │ ├── test_uniqueid.py │ └── test_utils.py ├── tools │ └── pep8checker │ │ ├── pep8.py │ │ ├── pep8base.html │ │ ├── pep8kivy.py │ │ └── pre-commit.githook └── utils.py └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/ci_osx.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration with OSX 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | 8 | runs-on: macos-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 3.12 13 | uses: actions/setup-python@v4 14 | with: 15 | python-version: 3.12 16 | 17 | - name: Install dependencies 18 | run: | 19 | source ci/ci_osx.sh 20 | dependencies 21 | 22 | - name: Tests 23 | run: | 24 | source ci/ci_osx.sh 25 | tests 26 | -------------------------------------------------------------------------------- /.github/workflows/ci_ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration with Ubuntu 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | style: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python 3.x 14 | uses: actions/setup-python@v1 15 | with: 16 | python-version: 3.x 17 | 18 | - name: Install Dependencies 19 | run: | 20 | source ci/ci_ubuntu.sh 21 | style_dependencies 22 | 23 | - name: Style 24 | run: | 25 | source ci/ci_ubuntu.sh 26 | style 27 | 28 | tests: 29 | 30 | runs-on: ubuntu-latest 31 | 32 | steps: 33 | - uses: actions/checkout@v2 34 | - name: Set up Python 3.x 35 | uses: actions/setup-python@v1 36 | with: 37 | python-version: 3.x 38 | 39 | - name: Install dependencies 40 | run: | 41 | source ci/ci_ubuntu.sh 42 | dependencies 43 | 44 | - name: Tests 45 | run: | 46 | source ci/ci_ubuntu.sh 47 | tests 48 | 49 | - name: Upload Coverage 50 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 51 | env: 52 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 53 | run: | 54 | source ci/ci_ubuntu.sh 55 | upload_coverage 56 | -------------------------------------------------------------------------------- /.github/workflows/ci_windows.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration with Windows 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 3.x 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: 3.x 16 | 17 | - name: Install dependencies 18 | run: | 19 | . .\ci\ci_windows.ps1 20 | Dependencies 21 | 22 | - name: Tests 23 | run: | 24 | . .\ci\ci_windows.ps1 25 | Tests 26 | -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | name: No Response 2 | 3 | # Both `issue_comment` and `scheduled` event types are required for this Action 4 | # to work properly. 5 | on: 6 | issue_comment: 7 | types: [created] 8 | schedule: 9 | # Schedule for five minutes after the hour, every hour 10 | - cron: '5 * * * *' 11 | 12 | jobs: 13 | noResponse: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb 17 | # This commit hash targets release v0.5.0 of lee-dohm/no-response. 18 | # Targeting a commit hash instead of a tag has been done for security reasons. 19 | # Please be aware that the commit hash specifically targets the "Automatic compilation" 20 | # done by `github-actions[bot]` as the `no-response` Action needs to be compiled. 21 | with: 22 | token: ${{ github.token }} 23 | daysUntilClose: 42 24 | responseRequiredLabel: 'awaiting-reply' 25 | closeComment: > 26 | This issue has been automatically closed because there has been no response 27 | to our request for more information from the original author. With only the 28 | information that is currently in the issue, we don't have the means 29 | to take action. Please reach out if you have or find the answers we need so 30 | that we can investigate further. 31 | -------------------------------------------------------------------------------- /.github/workflows/plyer_deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to PyPI 2 | 3 | on: 4 | create 5 | 6 | jobs: 7 | deploy: 8 | if: startsWith(github.ref, 'refs/tags/') 9 | 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python 14 | uses: actions/setup-python@v1 15 | with: 16 | python-version: '3.x' 17 | 18 | - name: Install dependencies 19 | run: | 20 | source ci/ci_ubuntu.sh 21 | deployment_dependencies 22 | 23 | - name: Build Package 24 | run: | 25 | source ci/ci_ubuntu.sh 26 | build 27 | 28 | - name: Create artifacts 29 | uses: actions/upload-artifact@v1 30 | with: 31 | name: plyer_artifact 32 | path: dist 33 | 34 | - name: Upload to GitHub Releases 35 | uses: softprops/action-gh-release@v0.1.14 36 | with: 37 | files: dist/* 38 | draft: true 39 | 40 | - name: Publish to PyPI 41 | env: 42 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 43 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 44 | 45 | run: | 46 | source ci/ci_ubuntu.sh 47 | deploy 48 | -------------------------------------------------------------------------------- /.github/workflows/support.yml: -------------------------------------------------------------------------------- 1 | name: 'Support Requests' 2 | 3 | on: 4 | issues: 5 | types: [labeled, unlabeled, reopened] 6 | 7 | permissions: 8 | issues: write 9 | 10 | jobs: 11 | action: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: dessant/support-requests@v4 15 | with: 16 | github-token: ${{ github.token }} 17 | support-label: 'support' 18 | issue-comment: > 19 | 👋 We use the issue tracker exclusively for bug reports and feature requests. 20 | However, this issue appears to be a support request. Please use our 21 | [support channels](https://github.com/kivy/plyer/blob/master/CONTACT.md) 22 | to get help with the project. 23 | 24 | Let us know if this comment was made in error, and we'll be happy 25 | to reopen the issue. 26 | close-issue: true 27 | lock-issue: false 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | .*.swp 4 | .*.swo 5 | build 6 | docs/build/ 7 | dist 8 | *.egg-info 9 | venv 10 | 11 | *.komodoproject 12 | .komodotools 13 | 14 | # PyDev 15 | .project 16 | .pydevproject 17 | .settings/ 18 | 19 | bin 20 | .buildozer 21 | .idea 22 | 23 | # VS Code 24 | .vscode 25 | 26 | # Mac 27 | *.DS_Store 28 | 29 | # Coverage 30 | .coverage 31 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3" 10 | 11 | python: 12 | install: 13 | - requirements: docs/requirements.txt 14 | 15 | sphinx: 16 | configuration: docs/source/conf.py -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | In the interest of fostering an open and welcoming community, we as 2 | contributors and maintainers need to ensure participation in our project and 3 | our sister projects is a harassment-free and positive experience for everyone. 4 | It is vital that all interaction is conducted in a manner conveying respect, 5 | open-mindedness and gratitude. 6 | 7 | Please consult the [latest Kivy Code of Conduct](https://github.com/kivy/kivy/blob/master/CODE_OF_CONDUCT.md). 8 | 9 | -------------------------------------------------------------------------------- /CONTACT.md: -------------------------------------------------------------------------------- 1 | .. _contact: 2 | 3 | Contact Us 4 | ========== 5 | 6 | If you are looking to contact the Kivy Team (who are responsible for managing the 7 | Plyer project), including looking for support, please see our 8 | `latest contact details `_. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Plyer is part of the [Kivy](https://kivy.org) ecosystem - a large group of 4 | products used by many thousands of developers for free, but it 5 | is built entirely by the contributions of volunteers. We welcome (and rely on) 6 | users who want to give back to the community by contributing to the project. 7 | 8 | Contributions can come in many forms. See the latest 9 | [Contribution Guidelines](https://github.com/kivy/kivy/blob/master/CONTRIBUTING.md) 10 | for how you can help us. 11 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ for Plyer 2 | 3 | ## Introduction 4 | 5 | Plyer is a platform-independent Python API for accessing hardware features 6 | of various platforms (Android, iOS, macOS, Linux and Windows). 7 | 8 | Plyer is managed by the [Kivy Team](https://kivy.org/about.html). It is suitable for 9 | use with Kivy apps, but can be used independently. 10 | 11 | ## No questions yet 12 | 13 | No Frequently Asked Questions have been identified yet. Please contribute some. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2010-2023 Kivy Team and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *README.rst *LICENSE 2 | include *CHANGELOG.md 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON = python 2 | CHECKSCRIPT = plyer/tools/pep8checker/pep8kivy.py 3 | PLYER_DIR = plyer/ 4 | 5 | build: 6 | $(PYTHON) setup.py build 7 | 8 | force: 9 | $(PYTHON) setup.py build -f 10 | 11 | debug: 12 | $(PYTHON) setup.py build -f -g 13 | 14 | pdf: 15 | $(MAKE) -C docs latex && make -C docs/build/latex all-pdf 16 | 17 | html: 18 | env USE_EMBEDSIGNATURE=1 $(MAKE) force 19 | $(MAKE) -C docs html 20 | 21 | style: 22 | $(PYTHON) $(CHECKSCRIPT) . 23 | 24 | stylereport: 25 | $(PYTHON) $(CHECKSCRIPT) -html $(PLYER_DIR) 26 | 27 | hook: 28 | # Install pre-commit git hook to check your changes for styleguide 29 | # consistency. 30 | cp plyer/tools/pep8checker/pre-commit.githook .git/hooks/pre-commit 31 | chmod +x .git/hooks/pre-commit 32 | 33 | install: 34 | python setup.py install 35 | 36 | clean: 37 | -rm -rf docs/build 38 | -rm -rf build 39 | -find plyer -iname '*.so' -exec rm {} \; 40 | -find plyer -iname '*.pyc' -exec rm {} \; 41 | -find plyer -iname '*.pyo' -exec rm {} \; 42 | 43 | distclean: clean 44 | -git clean -dxf -e debian 45 | -------------------------------------------------------------------------------- /ci/ci_osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dependencies() 4 | { 5 | python -m pip install --upgrade pip 6 | 7 | pip install --upgrade -r devrequirements.txt 8 | pip install https://github.com/kivy/pyobjus/zipball/master 9 | } 10 | 11 | tests() 12 | { 13 | pip install --editable . 14 | coverage run \ 15 | --source ./plyer \ 16 | -m unittest discover \ 17 | --start-directory ./plyer/tests \ 18 | --top-level-directory . \ 19 | --failfast 20 | coverage report -m 21 | } 22 | -------------------------------------------------------------------------------- /ci/ci_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | dependencies() 5 | { 6 | # install default packages 7 | sudo apt-get update && \ 8 | sudo apt-get -y --force-yes install \ 9 | build-essential \ 10 | openjdk-8-jdk \ 11 | lshw \ 12 | wget \ 13 | git \ 14 | && apt-get -y autoremove \ 15 | && apt-get -y clean 16 | 17 | # generate user folder locations (Pictures, Downloads, ...) 18 | xdg-user-dirs-update 19 | 20 | # install PIP 21 | python -V 22 | python -m pip install --upgrade pip 23 | 24 | # install dev packages 25 | python -m pip install \ 26 | --upgrade \ 27 | --requirement devrequirements.txt 28 | python -m pip install pyjnius 29 | 30 | python -m pip install . 31 | } 32 | 33 | deployment_dependencies() 34 | { 35 | python -m pip install --upgrade pip 36 | 37 | pip install setuptools wheel twine 38 | } 39 | 40 | style_dependencies() 41 | { 42 | python -m pip install --upgrade pip 43 | 44 | pip install flake8 45 | } 46 | 47 | style() 48 | { 49 | python -m flake8 . --show-source 50 | } 51 | 52 | tests() 53 | { 54 | # tests and coverage for plyer package 55 | python -m coverage run \ 56 | --source plyer \ 57 | -m unittest discover \ 58 | --start-directory plyer/tests \ 59 | --top-level-directory . \ 60 | --failfast 61 | 62 | coverage report -m 63 | } 64 | 65 | upload_coverage() 66 | { 67 | python -m coveralls 68 | } 69 | 70 | build() 71 | { 72 | python setup.py sdist 73 | python setup.py bdist_wheel --universal 74 | } 75 | 76 | deploy() 77 | { 78 | # deploy to PyPI 79 | python -m twine upload dist/* 80 | } 81 | -------------------------------------------------------------------------------- /ci/ci_windows.ps1: -------------------------------------------------------------------------------- 1 | function Dependencies { 2 | python -m pip install --requirement devrequirements.txt 3 | python -m pip install . 4 | echo Plyer version is 5 | python -c "import plyer;print(plyer.__version__)" 6 | } 7 | 8 | function Tests { 9 | $current_directory = (pwd).Path 10 | 11 | python -m coverage run ` 12 | --source "$current_directory\plyer" ` 13 | -m unittest discover ` 14 | --start-directory "$current_directory\plyer\tests" ` 15 | --top-level-directory "$current_directory" ` 16 | --failfast 17 | 18 | python -m coverage report -m 19 | } 20 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile.archlinux.py3: -------------------------------------------------------------------------------- 1 | FROM base/archlinux 2 | 3 | ENV APP_DIR=/app 4 | RUN mkdir $APP_DIR 5 | WORKDIR $APP_DIR 6 | 7 | # install default packages 8 | RUN pacman -Fy && \ 9 | pacman -Sy && \ 10 | yes |pacman -Sy \ 11 | gcc \ 12 | extra/python \ 13 | jdk-openjdk \ 14 | lshw \ 15 | wget \ 16 | apparmor \ 17 | xdg-user-dirs \ 18 | && yes |pacman -Rns $(pacman -Qtdq) ||true \ 19 | && yes |pacman -Sc 20 | 21 | # generate user folder locations (Pictures, Downloads, ...) 22 | RUN xdg-user-dirs-update 23 | 24 | # install PIP 25 | RUN wget https://bootstrap.pypa.io/get-pip.py -O get-pip3.py 26 | RUN python -V && \ 27 | python get-pip3.py && \ 28 | rm get-pip3.py && \ 29 | python -m pip install --upgrade pip 30 | 31 | # install dev packages 32 | COPY devrequirements.txt . 33 | RUN python -m pip install \ 34 | --upgrade \ 35 | --requirement devrequirements.txt 36 | RUN python -m pip install pyjnius 37 | 38 | COPY . $APP_DIR 39 | COPY ./ci/entrypoint.sh $APP_DIR 40 | ENV PYTHON=/usr/bin/python 41 | ENTRYPOINT ["/app/entrypoint.sh", "env"] 42 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile.bionic.py3: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic-20180821 2 | 3 | ENV APP_DIR=/app 4 | RUN mkdir $APP_DIR 5 | WORKDIR $APP_DIR 6 | 7 | # install default packages 8 | RUN apt-get update && \ 9 | apt-get -y --force-yes install \ 10 | build-essential \ 11 | python3-setuptools \ 12 | python3.6-dev \ 13 | openjdk-8-jdk \ 14 | lshw \ 15 | wget \ 16 | git \ 17 | && apt-get -y autoremove \ 18 | && apt-get -y clean 19 | 20 | # generate user folder locations (Pictures, Downloads, ...) 21 | RUN xdg-user-dirs-update 22 | 23 | # install PIP 24 | RUN wget https://bootstrap.pypa.io/get-pip.py -O get-pip3.py 25 | RUN python3.6 -V && \ 26 | python3.6 get-pip3.py && \ 27 | rm get-pip3.py && \ 28 | python3.6 -m pip install --upgrade pip 29 | 30 | # install dev packages 31 | COPY devrequirements.txt . 32 | RUN python3.6 -m pip install \ 33 | --upgrade \ 34 | --requirement devrequirements.txt 35 | RUN python3.6 -m pip install pyjnius 36 | 37 | COPY . $APP_DIR 38 | COPY ./ci/entrypoint.sh $APP_DIR 39 | RUN python3.6 -m pip install . 40 | ENTRYPOINT ["/app/entrypoint.sh", "py3"] 41 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile.bionic.style: -------------------------------------------------------------------------------- 1 | FROM plyer:py3 2 | ENTRYPOINT ["/app/entrypoint.sh", "py3", "style"] 3 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile.fedora28.py3: -------------------------------------------------------------------------------- 1 | FROM fedora:28 2 | 3 | ENV APP_DIR=/app 4 | RUN mkdir $APP_DIR 5 | WORKDIR $APP_DIR 6 | 7 | # install default packages 8 | # redhat-rpm-config: https://stackoverflow.com/a/34641068/5994041 9 | RUN yum -y install \ 10 | gcc \ 11 | python3-devel \ 12 | java-1.8.0-openjdk \ 13 | java-1.8.0-openjdk-devel \ 14 | lshw \ 15 | wget \ 16 | xdg-user-dirs \ 17 | redhat-rpm-config \ 18 | && yum -y autoremove \ 19 | && yum clean all 20 | 21 | # generate user folder locations (Pictures, Downloads, ...) 22 | RUN xdg-user-dirs-update 23 | 24 | # install PIP 25 | RUN python3.6 -V && \ 26 | python3.6 -m pip install --upgrade pip 27 | 28 | # install dev packages 29 | COPY devrequirements.txt . 30 | RUN python3.6 -m pip install \ 31 | --upgrade \ 32 | --requirement devrequirements.txt 33 | RUN python3.6 -m pip install pyjnius 34 | 35 | COPY . $APP_DIR 36 | COPY ./ci/entrypoint.sh $APP_DIR 37 | RUN python3.6 -m pip install . 38 | ENTRYPOINT ["/app/entrypoint.sh", "py3"] 39 | -------------------------------------------------------------------------------- /ci/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # build docker images from Travis matrix 4 | if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DOCK" = "1" ] 5 | then 6 | docker build \ 7 | --tag plyer:py3 \ 8 | --file ci/docker/Dockerfile.$IMAGE.py3 \ 9 | "$(pwd)" 10 | 11 | # style image that inherits layers from Python 3 image 12 | if [ "$RUN" = "style" ] 13 | then 14 | docker build \ 15 | --tag plyer:style \ 16 | --file ci/docker/Dockerfile.$IMAGE.style \ 17 | "$(pwd)" 18 | fi 19 | fi 20 | -------------------------------------------------------------------------------- /ci/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$DOCK" = "1" ] 4 | then 5 | 6 | if [ "$RUN" = "unit" ] 7 | then 8 | 9 | # running coverage report (COVERALLS=1) 10 | # and even deploy to PyPI (PLYER_DEPLOY=1) if asked for 11 | if [ "$COVERALLS" = "1" ] 12 | then 13 | docker run \ 14 | --interactive \ 15 | --tty \ 16 | --env COVERALLS_REPO_TOKEN=$COVERALLS_REPO_TOKEN \ 17 | --env COVERALLS_SERVICE_NAME=travis-ci \ 18 | --env TRAVIS_JOB_ID=$TRAVIS_JOB_ID \ 19 | --env TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \ 20 | --env PLYER_DEPLOY=${PLYER_DEPLOY:-"0"} \ 21 | --env TWINE_REPOSITORY=$TWINE_REPOSITORY \ 22 | --env TWINE_REPOSITORY_URL=$TWINE_REPOSITORY_URL \ 23 | --env TWINE_USERNAME=$TWINE_USERNAME \ 24 | --env TWINE_PASSWORD=$TWINE_PASSWORD \ 25 | plyer:py3 26 | 27 | # ordinary tests run 28 | else 29 | docker run \ 30 | --interactive \ 31 | --tty \ 32 | plyer:py3 33 | fi 34 | 35 | elif [ "$RUN" = "style" ] 36 | then 37 | docker run \ 38 | --interactive \ 39 | --tty \ 40 | plyer:style 41 | fi 42 | fi 43 | -------------------------------------------------------------------------------- /ci/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -vex 3 | 4 | if [ "$1" = "py3" ] || [ "$1" = "pep8" ] 5 | then 6 | PYTHON=$(which python3.6) 7 | elif [ "$1" = "env" ] 8 | then 9 | PYTHON=$PYTHON 10 | else 11 | exit 1 12 | fi 13 | 14 | $PYTHON -V 15 | 16 | # pep8 check 17 | if [ "$2" = "style" ] 18 | then 19 | $PYTHON -m pycodestyle "$(pwd)" \ 20 | --exclude=pep8.py,utils.py \ 21 | --ignore=E402,W503 22 | touch "$(pwd)/__init__.py" 23 | $PYTHON -m pylint \ 24 | --jobs=0 \ 25 | --disable=C0103,C0111,C0123,C0200,C0325,C0411,C0412,C1801,E0203,E0401 \ 26 | --disable=E0602,E0611,E0711,E1003,E1101,E1102,R0201,R0205,R0801,R0903 \ 27 | --disable=R0912,R0914,R1702,R1705,R1710,R1711,R1714,W0101,W0109 \ 28 | --disable=W0201,W0212,W0221,W0223,W0401 \ 29 | --disable=W0613,W0614 \ 30 | "$(pwd)" 31 | exit 0 32 | fi 33 | 34 | # tests and coverage for plyer package 35 | $PYTHON -m coverage run \ 36 | --source $APP_DIR/plyer \ 37 | -m unittest discover \ 38 | --start-directory $APP_DIR/plyer/tests \ 39 | --top-level-directory $APP_DIR \ 40 | --failfast 41 | coverage report -m 42 | 43 | # submit coverage report from tests to coveralls.io 44 | # requires: REPO_TOKEN, SERVICE_NAME, JOB_ID, PULL_REQUEST 45 | coveralls || true 46 | 47 | # deploy to PyPI if set in CI with PLYER_DEPLOY variable 48 | if [ "$PLYER_DEPLOY" = "1" ]; then 49 | $PYTHON setup.py sdist bdist_wheel 50 | $PYTHON -m twine upload dist/* 51 | fi 52 | -------------------------------------------------------------------------------- /ci/github_checkout_branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # use real branch name instead of detached HEAD 4 | # unless the job is created for a GitHub Pull Request 5 | if [ "$TRAVIS_PULL_REQUEST_BRANCH" = "" ] 6 | then 7 | git checkout $TRAVIS_BRANCH 8 | fi 9 | -------------------------------------------------------------------------------- /ci/osx_fix_cellar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # uninstall old GNUpg, install new one and add Brew 4 | # 'Cellar' folder to the path (contains binaries) 5 | if [ "$TRAVIS_OS_NAME" = "osx" ] 6 | then 7 | brew uninstall gnupg 8 | brew install gnupg2 9 | sudo ln -sv /usr/local/Cellar/gnupg /usr/local/Cellar/gpg || true 10 | sudo ln -sv /usr/local/Cellar/gnupg /usr/local/Cellar/gpg2 || true 11 | export PATH=$PATH:/usr/local/Cellar 12 | fi 13 | -------------------------------------------------------------------------------- /ci/osx_get_pipvirtualenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # manual download of get-pip.py on OSX because TLS1.2+ is required 4 | # source: pyfound.blogspot.com/2017/01/time-to-upgrade-your-python-tls-v12.html 5 | # install pip, virtualenv and plyer deps to virtualenv 6 | if [ "$TRAVIS_OS_NAME" = "osx" ] 7 | then 8 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 9 | 10 | if [ "$PY" = "3" ] 11 | then 12 | sudo python3 get-pip.py 13 | else 14 | sudo python get-pip.py 15 | fi 16 | 17 | if [ "$PY" = "3" ] 18 | then 19 | pip3 install --user virtualenv 20 | python3 -m virtualenv env 21 | else 22 | pip install --user virtualenv 23 | python -m virtualenv env 24 | fi 25 | 26 | # activate virtualenv 27 | source env/bin/activate 28 | 29 | # install requirements from PyPI 30 | pip install --upgrade --requirement devrequirements.txt 31 | 32 | # install PyOBJus from source (master branch) 33 | pip install https://github.com/kivy/pyobjus/zipball/master 34 | fi 35 | -------------------------------------------------------------------------------- /ci/osx_get_py3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # get Py3 because it's not present in any OSX image on Travis 4 | if [ "$TRAVIS_OS_NAME" = "osx" ] 5 | then 6 | pyftp=https://www.python.org/ftp/python/3.5.4/ 7 | py3pkg=python-3.5.4rc1-macosx10.6.pkg 8 | 9 | if [ "$PY" = "3" ] 10 | then 11 | curl -O -L $pyftp$py3pkg 12 | sudo installer -package $py3pkg -target / 13 | fi 14 | fi 15 | -------------------------------------------------------------------------------- /ci/osx_run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$TRAVIS_OS_NAME" = "osx" ] 4 | then 5 | source env/bin/activate 6 | pip install --editable . 7 | coverage run \ 8 | --source ./plyer \ 9 | -m unittest discover \ 10 | --start-directory ./plyer/tests \ 11 | --top-level-directory . \ 12 | --failfast 13 | coverage report -m 14 | fi 15 | -------------------------------------------------------------------------------- /ci/win_install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | %PYTHON% -m pip install --requirement devrequirements.txt 3 | %PYTHON% -m pip install . 4 | echo Plyer version is 5 | %PYTHON% -c "import plyer;print(plyer.__version__)" 6 | -------------------------------------------------------------------------------- /ci/win_set_python.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set PYTHON=C:\Python36\python.exe 3 | :: cd to Plyer folder and set PYTHONPATH 4 | cd C:\projects\app 5 | set PYTHONPATH=%PYTHONPATH%;%cd% 6 | -------------------------------------------------------------------------------- /ci/win_style.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if "%STYLE%"=="1" ( 3 | %PYTHON% -m pycodestyle "%cd%" ^ 4 | --exclude=pep8.py,utils.py ^ 5 | --ignore=E402,W503 ^ 6 | && echo off > "%cd%\__init__.py" && echo on ^ 7 | && %PYTHON% -m pylint ^ 8 | --disable=C0103,C0111,C0123,C0200,C0325,C0411,C0412,C1801,E0203 ^ 9 | --disable=E0401,E0602,E0611,E0711,E1003,E1101,E1102,R0201,R0205 ^ 10 | --disable=R0205,R0801,R0903,R0912,R0914,R1702,R1705,R1710,R1711 ^ 11 | --disable=R1714,W0101,W0109,W0150,W0201,W0212,W0221,W0223,W0401 ^ 12 | --disable=W0511,W0601,W0603,W0610,W0611,W0612,W0613,W0614,W0622 ^ 13 | --disable=W0702,W0703 "%cd%" 14 | ) 15 | -------------------------------------------------------------------------------- /ci/win_tests.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if not "%STYLE%"=="1" ( 3 | %PYTHON% -m coverage run ^ 4 | --source %cd%\plyer ^ 5 | -m unittest discover ^ 6 | --start-directory %cd%\plyer\tests ^ 7 | --top-level-directory %cd% ^ 8 | --failfast ^ 9 | && %PYTHON% -m coverage report -m 10 | ) 11 | -------------------------------------------------------------------------------- /devrequirements.txt: -------------------------------------------------------------------------------- 1 | coverage 2 | coveralls 3 | cython 4 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx~=7.2.6 2 | furo==2023.9.10 -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _api: 3 | 4 | API 5 | === 6 | 7 | .. automodule:: plyer 8 | :members: 9 | 10 | .. automodule:: plyer.facades 11 | :members: 12 | -------------------------------------------------------------------------------- /docs/source/contact.rst: -------------------------------------------------------------------------------- 1 | .. _contact: 2 | 3 | Contact Us 4 | ========== 5 | 6 | If you are looking to contact the Kivy Team (who are responsible for managing the 7 | Plyer project), including looking for support, please see our 8 | `latest contact details `_. -------------------------------------------------------------------------------- /docs/source/contribute.rst: -------------------------------------------------------------------------------- 1 | .. _contribute: 2 | 3 | Contribution Guidelines 4 | ======================= 5 | 6 | 7 | Plyer is part of the `Kivy `_ ecosystem - a large group of 8 | products used by many thousands of developers for free, but it 9 | is built entirely by the contributions of volunteers. We welcome (and rely on) 10 | users who want to give back to the community by contributing to the project. 11 | 12 | Contributions can come in many forms. See the latest 13 | [Contribution Guidelines](https://github.com/kivy/plyer/blob/master/CONTRIBUTING.md) 14 | for general guidelines of how you can help us. -------------------------------------------------------------------------------- /docs/source/faq.rst: -------------------------------------------------------------------------------- 1 | .. _faq: 2 | 3 | FAQ 4 | === 5 | 6 | Plyer has an `online FAQ `_. It contains the answers to 7 | questions that repeatedly come up. 8 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to Plyer 2 | ================ 3 | 4 | Plyer is a platform-independent Python API for accessing hardware features 5 | of various platforms (Android, iOS, macOS, Linux and Windows). 6 | 7 | Plyer is managed by the `Kivy Team ` 8 | and is suitable for use with Kivy apps. 9 | 10 | Plyer is released and distributed under the terms of the MIT license. Our 11 | `latest license `_ 12 | is available. 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | api 18 | contact 19 | contribute 20 | faq 21 | 22 | Indices and tables 23 | ================== 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | 29 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | Each facade has its own set of examples here. 2 | For each facade, there is a simple example which shows basic usage of the facade and its options. 3 | This simple example typically requires Kivy to run and buildozer to compile for 4 | Android. 5 | Requirements for run these examples on android devices: kivy,plyer,android 6 | These requirements should be rewrite in the buildozer.spec of example before run the example. 7 | There may also be more advanced examples for a facade. Requirements 8 | for the advanced examples may require a different framework (QT, Tkinter, etc) 9 | or additional Python modules. 10 | -------------------------------------------------------------------------------- /examples/accelerometer/basic/README: -------------------------------------------------------------------------------- 1 | Basic acccelerometer example. 2 | 3 | 4 | Python-for-android compilation: 5 | 6 | ./distribute.sh -m 'kivy plyer' 7 | cd dist/default 8 | ./build.py --package org.test.accelsimpleexample --name "Kivy Basic Accelerometer" \ 9 | --dir /path/to/plyer/examples/accelerometer/basic --version 1.0 \ 10 | debug installd 11 | 12 | 13 | Buildozer: 14 | 15 | cd /path/to/plyer/examples/accelerometer/basic 16 | # edit the buildozer.spec if required, then 17 | buildozer android debug deploy run 18 | -------------------------------------------------------------------------------- /examples/accelerometer/basic/accelerometertest.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | : 3 | BoxLayout: 4 | orientation: 'vertical' 5 | 6 | Label: 7 | id: x_label 8 | text: 'X: ' 9 | 10 | Label: 11 | id: y_label 12 | text: 'Y: ' 13 | 14 | Label: 15 | id: z_label 16 | text: 'Z: ' 17 | 18 | Label: 19 | id: accel_status 20 | text: '' 21 | 22 | BoxLayout: 23 | size_hint_y: None 24 | height: '48dp' 25 | padding: '4dp' 26 | 27 | ToggleButton: 28 | id: toggle_button 29 | text: 'Start accelerometer' 30 | on_press: root.do_toggle() -------------------------------------------------------------------------------- /examples/accelerometer/basic/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Basic accelerometer example. 3 | ''' 4 | 5 | from kivy.app import App 6 | from kivy.uix.boxlayout import BoxLayout 7 | from kivy.clock import Clock 8 | 9 | from plyer import accelerometer 10 | 11 | 12 | class AccelerometerTest(BoxLayout): 13 | def __init__(self): 14 | super().__init__() 15 | self.sensorEnabled = False 16 | 17 | def do_toggle(self): 18 | try: 19 | if not self.sensorEnabled: 20 | accelerometer.enable() 21 | Clock.schedule_interval(self.get_acceleration, 1 / 20.) 22 | 23 | self.sensorEnabled = True 24 | self.ids.toggle_button.text = "Stop Accelerometer" 25 | else: 26 | accelerometer.disable() 27 | Clock.unschedule(self.get_acceleration) 28 | 29 | self.sensorEnabled = False 30 | self.ids.toggle_button.text = "Start Accelerometer" 31 | except NotImplementedError: 32 | import traceback 33 | traceback.print_exc() 34 | status = "Accelerometer is not implemented for your platform" 35 | self.ids.accel_status.text = status 36 | 37 | def get_acceleration(self, dt): 38 | val = accelerometer.acceleration[:3] 39 | 40 | if not val == (None, None, None): 41 | self.ids.x_label.text = "X: " + str(val[0]) 42 | self.ids.y_label.text = "Y: " + str(val[1]) 43 | self.ids.z_label.text = "Z: " + str(val[2]) 44 | 45 | 46 | class AccelerometerTestApp(App): 47 | def build(self): 48 | return AccelerometerTest() 49 | 50 | def on_pause(self): 51 | return True 52 | 53 | 54 | if __name__ == '__main__': 55 | AccelerometerTestApp().run() 56 | -------------------------------------------------------------------------------- /examples/accelerometer/using_graph/README: -------------------------------------------------------------------------------- 1 | Python-for-android compilation: 2 | 3 | ./distribute.sh -m 'kivy plyer' 4 | cd dist/default 5 | ./build.py --package org.test.accelexample --name "Kivy Accelerometer" \ 6 | --dir /path/to/plyer/examples/accelerometer/using_graph --version 1.0 \ 7 | debug installd 8 | 9 | 10 | Buildozer: 11 | 12 | cd /path/to/plyer/examples/accelerometer/using_graph 13 | # edit the buildozer.spec if required, then 14 | buildozer android debug deploy run 15 | 16 | 17 | This example uses Garden Graph widget by matham. Available at https://github.com/kivy-garden/garden.graph. The required files are present it ./libs/garden/garden.graph. If you need to update these files, use: 18 | cd /path/to/plyer/examples/accelerometer/using_graph 19 | /path/to/kivy_instalation/kivy/tools/garden install --app graph 20 | -------------------------------------------------------------------------------- /examples/accelerometer/using_graph/Screenshot_Android_4.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/examples/accelerometer/using_graph/Screenshot_Android_4.3.png -------------------------------------------------------------------------------- /examples/accelerometer/using_graph/accelerometerdemo.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | : 3 | BoxLayout: 4 | orientation: 'vertical' 5 | padding: 20 6 | 7 | Graph: 8 | size_hint_y: 0.8 9 | id: graph_plot 10 | xlabel:'Time' 11 | ylabel:'Value' 12 | y_grid_label: True 13 | x_grid_label: True 14 | padding: 5 15 | xmin:0 16 | xmax:100 17 | ymin:-15 18 | ymax:20 19 | 20 | Button: 21 | id: toggle_button 22 | text: 'Start Accelerometer' 23 | size_hint_y: 0.2 24 | on_press: root.do_toggle() 25 | 26 | : 27 | size_hint: .7, .4 28 | title: "Error" 29 | 30 | BoxLayout: 31 | orientation: 'vertical' 32 | padding: 10 33 | spacing: 20 34 | 35 | Label: 36 | size_hint_y: 0.4 37 | text: "This feature has not yet been implemented on this platform." 38 | Button: 39 | text: 'Dismiss' 40 | size_hint_y: 0.4 41 | on_press: root.dismiss() 42 | -------------------------------------------------------------------------------- /examples/accelerometer/using_graph/libs/garden/garden.graph/README.md: -------------------------------------------------------------------------------- 1 | Graph 2 | ====== 3 | 4 | The `Graph` widget is a widget for displaying plots. It supports 5 | drawing multiple plot with different colors on the Graph. It also supports 6 | a title, ticks, labeled ticks, grids and a log or linear representation on 7 | both the x and y axis, independently. 8 | 9 | To display a plot. First create a graph which will function as a "canvas" for 10 | the plots. Then create plot objects e.g. MeshLinePlot and add them to the 11 | graph. 12 | 13 | To create a graph with x-axis between 0-100, y-axis between -1 to 1, x and y 14 | labels of and X and Y, respectively, x major and minor ticks every 25, 5 units, 15 | respectively, y major ticks every 1 units, full x and y grids and with 16 | a red line plot containing a sin wave on this range:: 17 | 18 | from kivy.garden.graph import Graph, MeshLinePlot 19 | graph = Graph(xlabel='X', ylabel='Y', x_ticks_minor=5, 20 | x_ticks_major=25, y_ticks_major=1, 21 | y_grid_label=True, x_grid_label=True, padding=5, 22 | x_grid=True, y_grid=True, xmin=-0, xmax=100, ymin=-1, ymax=1) 23 | plot = MeshLinePlot(color=[1, 0, 0, 1]) 24 | plot.points = [(x, sin(x / 10.)) for x in xrange(0, 101)] 25 | graph.add_plot(plot) 26 | 27 | The `MeshLinePlot` plot is a particular plot which draws a set of points using 28 | a mesh object. The points are given as a list of tuples, with each tuple 29 | being a (x, y) coordinate in the graph's units. 30 | 31 | You can create different types of plots other than `MeshLinePlot` by inheriting 32 | from the `Plot` class and implementing the required functions. The `Graph` object 33 | provides a "canvas" to which a Plot's instructions are added. The plot object 34 | is responsible for updating these instructions to show within the bounding 35 | box of the graph the proper plot. The Graph notifies the Plot when it needs 36 | to be redrawn due to changes. See the `MeshLinePlot` class for how it is done. 37 | -------------------------------------------------------------------------------- /examples/accelerometer/using_graph/libs/garden/garden.graph/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/examples/accelerometer/using_graph/libs/garden/garden.graph/graph.png -------------------------------------------------------------------------------- /examples/barometer/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.properties import NumericProperty 5 | from kivy.properties import ObjectProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | 9 | Builder.load_string(''' 10 | #:import barometer plyer.barometer 11 | : 12 | barometer: barometer 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '20dp' 16 | 17 | BoxLayout: 18 | orientation: 'horizontal' 19 | size_hint_y: 0.3 20 | Button: 21 | id: button_enable 22 | text: 'Enable' 23 | disabled: False 24 | on_release: 25 | root.enable() 26 | button_disable.disabled = not button_disable.disabled 27 | button_enable.disabled = not button_enable.disabled 28 | Button: 29 | id: button_disable 30 | text: 'Disable' 31 | disabled: True 32 | on_release: 33 | root.disable() 34 | button_disable.disabled = not button_disable.disabled 35 | button_enable.disabled = not button_enable.disabled 36 | 37 | Label: 38 | text: 'Current pressure:' + str(root.pressure) + ' hPa.' 39 | 40 | ''') 41 | 42 | 43 | class BarometerInterface(BoxLayout): 44 | '''Root Widget.''' 45 | 46 | barometer = ObjectProperty() 47 | pressure = NumericProperty() 48 | 49 | def enable(self): 50 | self.barometer.enable() 51 | Clock.schedule_interval(self.get_pressure, 1 / 20.) 52 | 53 | def disable(self): 54 | self.barometer.disable() 55 | Clock.unschedule(self.get_pressure) 56 | 57 | def get_pressure(self, dt): 58 | self.pressure = self.barometer.pressure or self.pressure 59 | 60 | 61 | class BarometerApp(App): 62 | 63 | def build(self): 64 | return BarometerInterface() 65 | 66 | def on_pause(self): 67 | return True 68 | 69 | 70 | if __name__ == "__main__": 71 | BarometerApp().run() 72 | -------------------------------------------------------------------------------- /examples/battery/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.lang import Builder 4 | from kivy.properties import ObjectProperty 5 | from plyer import battery 6 | 7 | Builder.load_string(''' 8 | : 9 | lbl1: lbl1 10 | lbl2: lbl2 11 | FloatLayout: 12 | 13 | Button: 14 | size_hint_y: None 15 | pos_hint: {'y': .5} 16 | text: "Battery Status" 17 | on_press: root.get_status() 18 | BoxLayout: 19 | size_hint_y: None 20 | pos_hint: {'y': .1} 21 | Label: 22 | text: "Is Charging?" 23 | Label: 24 | id: lbl1 25 | text: 26 | Label: 27 | text: "Percentage" 28 | Label: 29 | id: lbl2 30 | text: 31 | 32 | ''') 33 | 34 | 35 | class BatteryInterface(BoxLayout): 36 | lbl1 = ObjectProperty() 37 | lbl2 = ObjectProperty() 38 | 39 | def get_status(self, *args): 40 | self.lbl1.text = str(battery.status['isCharging']) 41 | self.lbl2.text = str(battery.status['percentage']) + "%" 42 | 43 | 44 | class BatteryApp(App): 45 | 46 | def build(self): 47 | return BatteryInterface() 48 | 49 | def on_pause(self): 50 | return True 51 | 52 | 53 | if __name__ == "__main__": 54 | app = BatteryApp() 55 | app.run() 56 | -------------------------------------------------------------------------------- /examples/bluetooth/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.lang import Builder 3 | from kivy.properties import ObjectProperty 4 | from kivy.properties import StringProperty 5 | from kivy.uix.boxlayout import BoxLayout 6 | 7 | Builder.load_string(''' 8 | #:import bluetooth plyer.bluetooth 9 | : 10 | bluetooth: bluetooth 11 | orientation: 'vertical' 12 | padding: '50dp' 13 | spacing: '20dp' 14 | BoxLayout: 15 | orientation: 'horizontal' 16 | size_hint_y: 0.3 17 | Button: 18 | text: 'Get Bluetooth status' 19 | on_release: 20 | root.get_info() 21 | Label: 22 | text: str(root.text) 23 | Label: 24 | text: str(root.info) 25 | ''') 26 | 27 | 28 | class BluetoothInterface(BoxLayout): 29 | '''Root Widget.''' 30 | 31 | info = ObjectProperty() 32 | text = StringProperty() 33 | 34 | text = "Bluetooth: " 35 | 36 | def get_info(self): 37 | self.info = str(self.bluetooth.info) 38 | 39 | 40 | class BluetoothApp(App): 41 | 42 | def build(self): 43 | return BluetoothInterface() 44 | 45 | def on_pause(self): 46 | return True 47 | 48 | 49 | if __name__ == "__main__": 50 | BluetoothApp().run() 51 | -------------------------------------------------------------------------------- /examples/brightness/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.lang import Builder 4 | from plyer import brightness 5 | 6 | Builder.load_string(''' 7 | : 8 | orientation: 'vertical' 9 | Label: 10 | text: 'Adjust the slider to increase \\n or decrease the brightness' 11 | Slider: 12 | id: slider 13 | min: 0 14 | max: 100 15 | value: root.get_current_brightness() 16 | on_value: root.set_brightness(slider.value) 17 | Label: 18 | text: 'Current brightness = ' + str(slider.value) 19 | ''') 20 | 21 | 22 | class BrightnessInterface(BoxLayout): 23 | 24 | def set_brightness(self, level): 25 | brightness.set_level(level) 26 | 27 | def get_current_brightness(self): 28 | return brightness.current_level() 29 | 30 | 31 | class BrightnessApp(App): 32 | 33 | def build(self): 34 | return BrightnessInterface() 35 | 36 | 37 | if __name__ == '__main__': 38 | BrightnessApp().run() 39 | -------------------------------------------------------------------------------- /examples/call/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.uix.button import Button 4 | from kivy.lang import Builder 5 | from kivy.properties import StringProperty 6 | from plyer import call 7 | 8 | Builder.load_string(''' 9 | #: import Platform kivy.utils.platform 10 | : 11 | orientation: 'vertical' 12 | Label: 13 | BoxLayout: 14 | size_hint_y: None 15 | size: (400,100) 16 | TextInput: 17 | id: number 18 | hint_text: "Enter Number" 19 | multiline: False 20 | MakeCallButton: 21 | tel: number.text 22 | text: 'Make call via this app' 23 | on_release: self.call() 24 | Label: 25 | text: "OR" 26 | DialCallButton: 27 | size_hint_y: None 28 | size: (400,100) 29 | disabled: True if Platform == 'ios' else False 30 | text: "Dial call via phone" 31 | on_release: self.dial() 32 | Label: 33 | 34 | ''') 35 | 36 | 37 | class CallInterface(BoxLayout): 38 | pass 39 | 40 | 41 | class DialCallButton(Button): 42 | 43 | def dial(self, *args): 44 | call.dialcall() 45 | 46 | 47 | class MakeCallButton(Button): 48 | tel = StringProperty() 49 | 50 | def call(self, *args): 51 | call.makecall(tel=self.tel) 52 | 53 | 54 | class CallApp(App): 55 | 56 | def build(self): 57 | return CallInterface() 58 | 59 | def on_pause(self): 60 | return True 61 | 62 | 63 | if __name__ == "__main__": 64 | app = CallApp() 65 | app.run() 66 | -------------------------------------------------------------------------------- /examples/camera/basic/README: -------------------------------------------------------------------------------- 1 | Basic camera example. 2 | Default picture is saved as /sdcard/org.test.cameraexample/enter_file_name_here.jpg 3 | 4 | 5 | Python-for-android compilation: 6 | 7 | ./distribute.sh -m 'kivy plyer' 8 | cd dist/default 9 | ./build.py --org.test.cameraexample --name "Kivy Camera Example" \ 10 | --dir /path/to/plyer/examples/camera/basic --version 1.0 \ 11 | debug installd 12 | 13 | 14 | Buildozer: 15 | 16 | cd /path/to/plyer/examples/camera/basic 17 | # edit the buildozer.spec if required, then 18 | buildozer android debug deploy run 19 | -------------------------------------------------------------------------------- /examples/camera/basic/camerademo.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | : 3 | FloatLayout: 4 | Label: 5 | id: path_label 6 | text: 'Working Directory:' 7 | pos_hint: {'x': 0.25, 'y': 0.7} 8 | size_hint: 0.5, 0.1 9 | 10 | TextInput: 11 | id: filename_text 12 | text: 'enter_file_name_here.jpg' 13 | pos_hint: {'x': 0.25, 'y': .6} 14 | size_hint: 0.5, 0.1 15 | multiline: False 16 | 17 | Button: 18 | text: 'Take picture from camera!' 19 | pos_hint: {'x': 0.25, 'y': .3} 20 | size_hint: 0.5, 0.2 21 | on_press: root.do_capture() 22 | 23 | : 24 | size_hint: .7, .4 25 | title: "Attention" 26 | 27 | BoxLayout: 28 | orientation: 'vertical' 29 | padding: 10 30 | spacing: 20 31 | 32 | Label: 33 | id: message_label 34 | size_hint_y: 0.4 35 | text: "Label" 36 | Button: 37 | text: 'Dismiss' 38 | size_hint_y: 0.4 39 | on_press: root.dismiss() 40 | 41 | -------------------------------------------------------------------------------- /examples/camera/basic/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic camera example 3 | Default picture is saved as 4 | /sdcard/org.test.cameraexample/enter_file_name_here.jpg 5 | """ 6 | 7 | from os import getcwd 8 | from os.path import exists 9 | 10 | from kivy.app import App 11 | from kivy.uix.floatlayout import FloatLayout 12 | from kivy.uix.popup import Popup 13 | import kivy 14 | 15 | from plyer import camera 16 | 17 | kivy.require('1.8.0') 18 | 19 | 20 | class CameraDemo(FloatLayout): 21 | def __init__(self): 22 | super().__init__() 23 | self.cwd = getcwd() + "/" 24 | self.ids.path_label.text = self.cwd 25 | 26 | def do_capture(self): 27 | filepath = self.cwd + self.ids.filename_text.text 28 | 29 | if exists(filepath): 30 | popup = MsgPopup("Picture with this name already exists!") 31 | popup.open() 32 | return False 33 | 34 | try: 35 | camera.take_picture(filename=filepath, 36 | on_complete=self.camera_callback) 37 | except NotImplementedError: 38 | popup = MsgPopup( 39 | "This feature has not yet been implemented for this platform.") 40 | popup.open() 41 | 42 | def camera_callback(self, filepath): 43 | if exists(filepath): 44 | popup = MsgPopup("Picture saved!") 45 | popup.open() 46 | else: 47 | popup = MsgPopup("Could not save your picture!") 48 | popup.open() 49 | 50 | 51 | class CameraDemoApp(App): 52 | def __init__(self): 53 | super().__init__() 54 | self.demo = None 55 | 56 | def build(self): 57 | self.demo = CameraDemo() 58 | return self.demo 59 | 60 | def on_pause(self): 61 | return True 62 | 63 | def on_resume(self): 64 | pass 65 | 66 | 67 | class MsgPopup(Popup): 68 | def __init__(self, msg): 69 | super().__init__() 70 | self.ids.message_label.text = msg 71 | 72 | 73 | if __name__ == '__main__': 74 | CameraDemoApp().run() 75 | -------------------------------------------------------------------------------- /examples/compass/README: -------------------------------------------------------------------------------- 1 | Compass example. 2 | 3 | 4 | Python-for-android compilation: 5 | 6 | ./distribute.sh -m 'kivy plyer' 7 | cd dist/default 8 | ./build.py --package org.test.compassexample --name "Kivy Compass" \ 9 | --dir /path/to/plyer/examples/compass --version 1.0 \ 10 | debug installd 11 | 12 | 13 | Buildozer: 14 | 15 | cd /path/to/plyer/examples/compass 16 | # edit the buildozer.spec if required, then 17 | buildozer android debug deploy run 18 | -------------------------------------------------------------------------------- /examples/email/main.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.app import App 3 | from kivy.uix.boxlayout import BoxLayout 4 | from kivy.uix.button import Button 5 | from kivy.lang import Builder 6 | from kivy.properties import StringProperty, BooleanProperty 7 | 8 | from plyer import email 9 | 10 | Builder.load_string(''' 11 | : 12 | orientation: 'vertical' 13 | BoxLayout: 14 | Label: 15 | text: 'Recipient:' 16 | TextInput: 17 | id: recipient 18 | BoxLayout: 19 | Label: 20 | text: 'Subject:' 21 | TextInput: 22 | id: subject 23 | BoxLayout: 24 | Label: 25 | text: 'text' 26 | TextInput: 27 | id: text 28 | BoxLayout: 29 | Label: 30 | text: 'create chooser?' 31 | CheckBox: 32 | id: create_chooser 33 | IntentButton: 34 | email_recipient: recipient.text 35 | email_subject: subject.text 36 | email_text: text.text 37 | create_chooser: create_chooser.active 38 | text: 'Send email' 39 | size_hint_y: None 40 | height: sp(40) 41 | on_release: self.send_email() 42 | ''') 43 | 44 | 45 | class EmailInterface(BoxLayout): 46 | pass 47 | 48 | 49 | class IntentButton(Button): 50 | email_recipient = StringProperty() 51 | email_subject = StringProperty() 52 | email_text = StringProperty() 53 | create_chooser = BooleanProperty() 54 | 55 | def send_email(self, *args): 56 | email.send(recipient=self.email_recipient, 57 | subject=self.email_subject, 58 | text=self.email_text, 59 | create_chooser=self.create_chooser) 60 | 61 | 62 | class EmailApp(App): 63 | def build(self): 64 | return EmailInterface() 65 | 66 | def on_pause(self): 67 | return True 68 | 69 | 70 | if __name__ == "__main__": 71 | EmailApp().run() 72 | -------------------------------------------------------------------------------- /examples/filechooser/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Example of an Android filechooser. 3 | ''' 4 | 5 | from textwrap import dedent 6 | 7 | from plyer import filechooser 8 | 9 | from kivy.app import App 10 | from kivy.lang import Builder 11 | from kivy.properties import ListProperty 12 | from kivy.uix.button import Button 13 | 14 | 15 | class FileChoose(Button): 16 | ''' 17 | Button that triggers 'filechooser.open_file()' and processes 18 | the data response from filechooser Activity. 19 | ''' 20 | 21 | selection = ListProperty([]) 22 | 23 | def choose(self): 24 | ''' 25 | Call plyer filechooser API to run a filechooser Activity. 26 | ''' 27 | filechooser.open_file(on_selection=self.handle_selection) 28 | 29 | def handle_selection(self, selection): 30 | ''' 31 | Callback function for handling the selection response from Activity. 32 | ''' 33 | self.selection = selection 34 | 35 | def on_selection(self, *a, **k): 36 | ''' 37 | Update TextInput.text after FileChoose.selection is changed 38 | via FileChoose.handle_selection. 39 | ''' 40 | App.get_running_app().root.ids.result.text = str(self.selection) 41 | 42 | 43 | class ChooserApp(App): 44 | ''' 45 | Application class with root built in KV. 46 | ''' 47 | 48 | def build(self): 49 | return Builder.load_string(dedent(''' 50 | : 51 | 52 | BoxLayout: 53 | 54 | BoxLayout: 55 | orientation: 'vertical' 56 | 57 | TextInput: 58 | id: result 59 | text: '' 60 | hint_text: 'selected path' 61 | 62 | FileChoose: 63 | size_hint_y: 0.1 64 | on_release: self.choose() 65 | text: 'Select a file' 66 | ''')) 67 | 68 | 69 | if __name__ == '__main__': 70 | ChooserApp().run() 71 | -------------------------------------------------------------------------------- /examples/flash/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.lang import Builder 4 | from plyer import flash 5 | 6 | Builder.load_string(''' 7 | : 8 | Button: 9 | text: "Turn On" 10 | on_release: root.turn_on() 11 | 12 | Button: 13 | text: "Turn off" 14 | on_release: root.turn_off() 15 | 16 | Button: 17 | text: "Release" 18 | on_release: root.release() 19 | 20 | ''') 21 | 22 | 23 | class FlashInterface(BoxLayout): 24 | 25 | def turn_on(self): 26 | flash.on() 27 | 28 | def turn_off(self): 29 | flash.off() 30 | 31 | def release(self): 32 | flash.release() 33 | 34 | 35 | class FlashApp(App): 36 | 37 | def build(self): 38 | return FlashInterface() 39 | 40 | def on_pause(self): 41 | return True 42 | 43 | 44 | if __name__ == "__main__": 45 | app = FlashApp() 46 | app.run() 47 | -------------------------------------------------------------------------------- /examples/gps/README: -------------------------------------------------------------------------------- 1 | Python-for-android compilation: 2 | 3 | ./distribute.sh -m 'kivy plyer' 4 | cd dist/default 5 | ./build.py --package org.test.gps --name "GPS example" \ 6 | --private ~/path/to/plyer/examples/gps --version 1 \ 7 | --permission ACCESS_FINE_LOCATION \ 8 | --permission ACCESS_COARSE_LOCATION \ 9 | --permission INTERNET \ 10 | --window \ 11 | --orientation portrait \ 12 | debug installd 13 | 14 | -------------------------------------------------------------------------------- /examples/gravity/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.uix.boxlayout import BoxLayout 5 | 6 | from plyer import gravity 7 | 8 | 9 | Builder.load_string(''' 10 | : 11 | orientation: 'vertical' 12 | 13 | Label: 14 | id: x_label 15 | text: 'X: ' 16 | 17 | Label: 18 | id: y_label 19 | text: 'Y: ' 20 | 21 | Label: 22 | id: z_label 23 | text: 'Z: ' 24 | 25 | Label: 26 | id: status 27 | text: '' 28 | 29 | BoxLayout: 30 | size_hint_y: None 31 | height: '48dp' 32 | padding: '4dp' 33 | 34 | ToggleButton: 35 | id: toggle_button 36 | text: 'Start Gravity Sensor' 37 | on_press: root.do_toggle() 38 | 39 | ''') 40 | 41 | 42 | class GravityInterface(BoxLayout): 43 | def __init__(self): 44 | super().__init__() 45 | self.sensorEnabled = False 46 | 47 | def do_toggle(self): 48 | try: 49 | if not self.sensorEnabled: 50 | gravity.enable() 51 | Clock.schedule_interval(self.get_gravity, 1 / 20.) 52 | 53 | self.sensorEnabled = True 54 | self.ids.toggle_button.text = "Stop Gravity Sensor" 55 | else: 56 | gravity.disable() 57 | Clock.unschedule(self.get_gravity) 58 | 59 | self.sensorEnabled = False 60 | self.ids.toggle_button.text = "Start Gravity Sensor" 61 | except NotImplementedError: 62 | import traceback 63 | traceback.print_exc() 64 | status = "Gravity sensor is not implemented " \ 65 | "for your platform" 66 | self.ids.status.text = status 67 | 68 | def get_gravity(self, dt): 69 | val = gravity.gravity 70 | 71 | if not val == (None, None, None): 72 | self.ids.x_label.text = "X: " + str(val[0]) 73 | self.ids.y_label.text = "Y: " + str(val[1]) 74 | self.ids.z_label.text = "Z: " + str(val[2]) 75 | 76 | 77 | class GravityApp(App): 78 | def build(self): 79 | return GravityInterface() 80 | 81 | 82 | if __name__ == '__main__': 83 | GravityApp().run() 84 | -------------------------------------------------------------------------------- /examples/humidity/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.properties import NumericProperty 5 | from kivy.properties import ObjectProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | 9 | Builder.load_string(''' 10 | #:import facade plyer.humidity 11 | : 12 | facade: facade 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '20dp' 16 | BoxLayout: 17 | orientation: 'horizontal' 18 | size_hint_y: 0.3 19 | Button: 20 | id: button_enable 21 | text: 'Enable' 22 | disabled: False 23 | on_release: 24 | root.enable() 25 | button_disable.disabled = not button_disable.disabled 26 | button_enable.disabled = not button_enable.disabled 27 | Button: 28 | id: button_disable 29 | text: 'Disable' 30 | disabled: True 31 | on_release: 32 | root.disable() 33 | button_disable.disabled = not button_disable.disabled 34 | button_enable.disabled = not button_enable.disabled 35 | Label: 36 | text: 'Humidity: ' + str(root.humidity) 37 | ''') 38 | 39 | 40 | class HumidityInterface(BoxLayout): 41 | '''Root Widget.''' 42 | humidity = NumericProperty(0) 43 | facade = ObjectProperty() 44 | 45 | def enable(self): 46 | self.facade.enable() 47 | Clock.schedule_interval(self.tell, 1 / 20.) 48 | 49 | def disable(self): 50 | self.facade.disable() 51 | Clock.unschedule(self.tell) 52 | 53 | def tell(self, dt): 54 | if self.facade.tell is not None: 55 | self.humidity = self.facade.tell 56 | 57 | 58 | class HumidityApp(App): 59 | 60 | def build(self): 61 | return HumidityInterface() 62 | 63 | def on_pause(self): 64 | return True 65 | 66 | 67 | if __name__ == "__main__": 68 | HumidityApp().run() 69 | -------------------------------------------------------------------------------- /examples/light/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.properties import NumericProperty 5 | from kivy.properties import ObjectProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | 9 | Builder.load_string(''' 10 | #:import light plyer.light 11 | : 12 | light: light 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '50dp' 16 | 17 | BoxLayout: 18 | orientation: 'horizontal' 19 | size_hint_y: 0.3 20 | Button: 21 | id: button_enable 22 | text: 'Enable' 23 | disabled: False 24 | on_release: 25 | root.enable() 26 | button_disable.disabled = not button_disable.disabled 27 | button_enable.disabled = not button_enable.disabled 28 | Button: 29 | id: button_disable 30 | text: 'Disable' 31 | disabled: True 32 | on_release: 33 | root.disable() 34 | button_disable.disabled = not button_disable.disabled 35 | button_enable.disabled = not button_enable.disabled 36 | 37 | Label: 38 | text: 'Current illumination:' + str(root.illumination) + ' lx.' 39 | 40 | 41 | ''') 42 | 43 | 44 | class LightInterface(BoxLayout): 45 | '''Root Widget.''' 46 | 47 | light = ObjectProperty() 48 | illumination = NumericProperty() 49 | 50 | def enable(self): 51 | self.light.enable() 52 | Clock.schedule_interval(self.get_illumination, 1 / 20.) 53 | 54 | def disable(self): 55 | self.light.disable() 56 | Clock.unschedule(self.get_illumination) 57 | 58 | def get_illumination(self, dt): 59 | self.illumination = self.light.illumination or self.illumination 60 | 61 | 62 | class LightApp(App): 63 | 64 | def build(self): 65 | return LightInterface() 66 | 67 | def on_pause(self): 68 | return True 69 | 70 | 71 | if __name__ == '__main__': 72 | LightApp().run() 73 | -------------------------------------------------------------------------------- /examples/notification/main.py: -------------------------------------------------------------------------------- 1 | from os.path import join, dirname, realpath 2 | 3 | import kivy 4 | from kivy.app import App 5 | from kivy.uix.boxlayout import BoxLayout 6 | 7 | from plyer import notification 8 | from plyer.utils import platform 9 | 10 | kivy.require('1.8.0') 11 | 12 | 13 | class NotificationDemo(BoxLayout): 14 | 15 | def do_notify(self, mode='normal'): 16 | title = self.ids.notification_title.text 17 | message = self.ids.notification_text.text 18 | ticker = self.ids.ticker_text.text 19 | kwargs = {'title': title, 'message': message, 'ticker': ticker} 20 | 21 | if mode == 'fancy': 22 | kwargs['app_name'] = "Plyer Notification Example" 23 | if platform == "win": 24 | kwargs['app_icon'] = join(dirname(realpath(__file__)), 25 | 'plyer-icon.ico') 26 | kwargs['timeout'] = 4 27 | else: 28 | kwargs['app_icon'] = join(dirname(realpath(__file__)), 29 | 'plyer-icon.png') 30 | elif mode == 'toast': 31 | kwargs['toast'] = True 32 | notification.notify(**kwargs) 33 | 34 | 35 | class NotificationDemoApp(App): 36 | def build(self): 37 | return NotificationDemo() 38 | 39 | def on_pause(self): 40 | return True 41 | 42 | 43 | if __name__ == '__main__': 44 | NotificationDemoApp().run() 45 | -------------------------------------------------------------------------------- /examples/notification/notificationdemo.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | : 3 | orientation: 'vertical' 4 | Widget: 5 | BoxLayout: 6 | orientation: 'horizontal' 7 | size_hint: 1, None 8 | TextInput: 9 | id: notification_title 10 | text: 'Put title here' 11 | size_hint: 1, None 12 | TextInput: 13 | id: notification_text 14 | text: 'Put message here' 15 | size_hint: 1, None 16 | TextInput: 17 | id: ticker_text 18 | text: 'New notification' 19 | size_hint: 1, None 20 | Button: 21 | text: 'Toast Notification' 22 | size_hint: 1, None 23 | on_release: root.do_notify(mode='toast') 24 | Button: 25 | text: 'Simple Notification' 26 | size_hint: 1, None 27 | on_release: root.do_notify(mode='normal') 28 | Button: 29 | text: 'Fancy Notification' 30 | size_hint: 1, None 31 | on_release: root.do_notify(mode='fancy') 32 | Widget: 33 | -------------------------------------------------------------------------------- /examples/notification/plyer-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/examples/notification/plyer-icon.ico -------------------------------------------------------------------------------- /examples/notification/plyer-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/examples/notification/plyer-icon.png -------------------------------------------------------------------------------- /examples/orientation/main.py: -------------------------------------------------------------------------------- 1 | 2 | from kivy.base import runTouchApp 3 | 4 | from kivy.lang import Builder 5 | 6 | interface = Builder.load_string(''' 7 | #:import orientation plyer.orientation 8 | 9 | : 10 | text_size: self.size 11 | valign: 'middle' 12 | halign: 'center' 13 | 14 | BoxLayout: 15 | orientation: 'horizontal' 16 | GridLayout: 17 | size_hint_x: 2 18 | cols: 2 19 | WrapButton: 20 | text: 'portrait' 21 | on_release: orientation.set_portrait() 22 | WrapButton: 23 | text: 'portrait reverse' 24 | on_release: orientation.set_portrait(reverse=True) 25 | WrapButton: 26 | text: 'landscape' 27 | on_release: orientation.set_landscape() 28 | WrapButton: 29 | text: 'landscape reverse' 30 | on_release: orientation.set_landscape(reverse=True) 31 | WrapButton: 32 | text: 'free sensor' 33 | on_release: orientation.set_sensor(mode='any') 34 | Widget: 35 | WrapButton: 36 | text: 'landscape sensor' 37 | on_release: orientation.set_sensor(mode='landscape') 38 | WrapButton: 39 | text: 'portrait sensor' 40 | on_release: orientation.set_sensor(mode='portrait') 41 | ''') 42 | 43 | runTouchApp(interface) 44 | -------------------------------------------------------------------------------- /examples/proximity/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.properties import BooleanProperty 5 | from kivy.properties import ObjectProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | 9 | Builder.load_string(''' 10 | #:import proximity plyer.proximity 11 | : 12 | proximity: proximity 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '20dp' 16 | 17 | BoxLayout: 18 | orientation: 'horizontal' 19 | size_hint_y: 0.3 20 | Button: 21 | id: button_enable 22 | text: 'Enable' 23 | disabled: False 24 | on_release: 25 | root.enable() 26 | button_disable.disabled = not button_disable.disabled 27 | button_enable.disabled = not button_enable.disabled 28 | Button: 29 | id: button_disable 30 | text: 'Disable' 31 | disabled: True 32 | on_release: 33 | root.disable() 34 | button_disable.disabled = not button_disable.disabled 35 | button_enable.disabled = not button_enable.disabled 36 | 37 | Label: 38 | text: 'Does Proximity Sensor detect something?' 39 | Label: 40 | text: 'Yes' if root.is_near else 'No' 41 | 42 | Widget: 43 | Label: 44 | text: 'Cover with your hand' 45 | Label: 46 | text: 'a top part of phone to see result.' 47 | ''') 48 | 49 | 50 | class ProximityInterface(BoxLayout): 51 | '''Root Widget.''' 52 | 53 | proximity = ObjectProperty() 54 | is_near = BooleanProperty(False) 55 | 56 | def enable(self): 57 | self.proximity.enable() 58 | Clock.schedule_interval(self.get_proxime, 1 / 20.) 59 | 60 | def disable(self): 61 | self.proximity.disable() 62 | Clock.unschedule(self.get_proxime) 63 | 64 | def get_proxime(self, dt): 65 | self.is_near = self.proximity.proximity 66 | 67 | 68 | class ProximityApp(App): 69 | 70 | def build(self): 71 | return ProximityInterface() 72 | 73 | def on_pause(self): 74 | return True 75 | 76 | 77 | if __name__ == "__main__": 78 | ProximityApp().run() 79 | -------------------------------------------------------------------------------- /examples/screenshot/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.lang import Builder 3 | from kivy.uix.boxlayout import BoxLayout 4 | 5 | 6 | Builder.load_string(''' 7 | #:import screenshot plyer.screenshot 8 | : 9 | orientation: 'vertical' 10 | padding: '50dp' 11 | spacing: '20dp' 12 | Label: 13 | size_hint_y: None 14 | height: sp(40) 15 | text: 'Screenshot Location: ' + str(screenshot.file_path) 16 | 17 | Button: 18 | text: 'Capture Screenshot' 19 | on_release: screenshot.capture() 20 | ''') 21 | 22 | 23 | class ScreenshotDemo(BoxLayout): 24 | '''Root Widget.''' 25 | 26 | 27 | class ScreenshotApp(App): 28 | 29 | def build(self): 30 | return ScreenshotDemo() 31 | 32 | 33 | if __name__ == "__main__": 34 | ScreenshotApp().run() 35 | -------------------------------------------------------------------------------- /examples/sms/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.boxlayout import BoxLayout 3 | from kivy.uix.button import Button 4 | from kivy.lang import Builder 5 | from kivy.properties import StringProperty 6 | 7 | from plyer import sms 8 | 9 | Builder.load_string(''' 10 | : 11 | orientation: 'vertical' 12 | BoxLayout: 13 | size_hint_y: None 14 | height: sp(30) 15 | Label: 16 | text: 'Recipient:' 17 | TextInput: 18 | id: recipient 19 | multiline: False 20 | on_text_validate: message.focus = True 21 | BoxLayout: 22 | Label: 23 | text: 'Message:' 24 | TextInput: 25 | id: message 26 | IntentButton: 27 | sms_recipient: recipient.text 28 | sms_message: message.text 29 | text: 'Send SMS' 30 | size_hint_y: None 31 | height: sp(40) 32 | on_release: self.send_sms() 33 | ''') 34 | 35 | 36 | class SmsInterface(BoxLayout): 37 | pass 38 | 39 | 40 | class IntentButton(Button): 41 | sms_recipient = StringProperty() 42 | sms_message = StringProperty() 43 | 44 | def send_sms(self, *args): 45 | sms.send(recipient=self.sms_recipient, message=self.sms_message) 46 | 47 | 48 | class SmsApp(App): 49 | def build(self): 50 | return SmsInterface() 51 | 52 | def on_pause(self): 53 | return True 54 | 55 | 56 | if __name__ == "__main__": 57 | SmsApp().run() 58 | -------------------------------------------------------------------------------- /examples/speech2text/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.uix.boxlayout import BoxLayout 5 | 6 | from plyer import stt 7 | 8 | Builder.load_string(''' 9 | #:import stt plyer.stt 10 | 11 | : 12 | orientation: 'vertical' 13 | Label: 14 | size_hint_y: None 15 | height: sp(40) 16 | text: 'Is supported: %s' % stt.exist() 17 | Label: 18 | size_hint_y: None 19 | height: sp(40) 20 | text: 'Possible Matches' 21 | TextInput: 22 | id: results 23 | hint_text: 'results (auto stop)' 24 | TextInput: 25 | id: partial 26 | hint_text: 'partial results (manual stop)' 27 | TextInput: 28 | id: errors 29 | hint_text: 'errors' 30 | Button: 31 | id: start_button 32 | text: 'Start Listening' 33 | on_release: root.start_listening() 34 | ''') 35 | 36 | 37 | class SpeechInterface(BoxLayout): 38 | '''Root Widget.''' 39 | 40 | def start_listening(self): 41 | if stt.listening: 42 | self.stop_listening() 43 | return 44 | 45 | start_button = self.ids.start_button 46 | start_button.text = 'Stop' 47 | 48 | self.ids.results.text = '' 49 | self.ids.partial.text = '' 50 | 51 | stt.start() 52 | 53 | Clock.schedule_interval(self.check_state, 1 / 5) 54 | 55 | def stop_listening(self): 56 | start_button = self.ids.start_button 57 | start_button.text = 'Start Listening' 58 | 59 | stt.stop() 60 | self.update() 61 | 62 | Clock.unschedule(self.check_state) 63 | 64 | def check_state(self, dt): 65 | # if the recognizer service stops, change UI 66 | if not stt.listening: 67 | self.stop_listening() 68 | 69 | def update(self): 70 | self.ids.partial.text = '\n'.join(stt.partial_results) 71 | self.ids.results.text = '\n'.join(stt.results) 72 | 73 | 74 | class SpeechApp(App): 75 | 76 | def build(self): 77 | return SpeechInterface() 78 | 79 | def on_pause(self): 80 | return True 81 | 82 | 83 | if __name__ == "__main__": 84 | SpeechApp().run() 85 | -------------------------------------------------------------------------------- /examples/storagepath/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Storage Path Example. 3 | ''' 4 | 5 | from kivy.lang import Builder 6 | from kivy.app import App 7 | from kivy.uix.boxlayout import BoxLayout 8 | 9 | Builder.load_string(''' 10 | #: import storagepath plyer.storagepath 11 | : 12 | BoxLayout: 13 | orientation: 'vertical' 14 | BoxLayout: 15 | Button: 16 | text: 'Home' 17 | on_press: label.text = str(storagepath.get_home_dir()) 18 | Button: 19 | text: 'External Storage' 20 | on_press: 21 | label.text = str(storagepath.get_external_storage_dir()) 22 | BoxLayout: 23 | Button: 24 | text: 'Root' 25 | on_press: label.text = str(storagepath.get_root_dir()) 26 | Button: 27 | text: 'Documents' 28 | on_press: label.text = str(storagepath.get_documents_dir()) 29 | BoxLayout: 30 | Button: 31 | text: 'Downloads' 32 | on_press: label.text = str(storagepath.get_downloads_dir()) 33 | Button: 34 | text: 'Videos' 35 | on_press: label.text = str(storagepath.get_videos_dir()) 36 | BoxLayout: 37 | Button: 38 | text: 'Music' 39 | on_press: label.text = str(storagepath.get_music_dir()) 40 | Button: 41 | text: 'Pictures' 42 | on_press: label.text = str(storagepath.get_pictures_dir()) 43 | Button: 44 | text: 'Applications' 45 | on_press: label.text = str(storagepath.get_application_dir()) 46 | Label: 47 | id: label 48 | ''') 49 | 50 | 51 | class StoragePathInterface(BoxLayout): 52 | pass 53 | 54 | 55 | class StoragePathApp(App): 56 | 57 | def build(self): 58 | return StoragePathInterface() 59 | 60 | 61 | if __name__ == "__main__": 62 | StoragePathApp().run() 63 | -------------------------------------------------------------------------------- /examples/temperature/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.properties import NumericProperty 5 | from kivy.properties import ObjectProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | 9 | Builder.load_string(''' 10 | #:import temperature plyer.temperature 11 | : 12 | temperature: temperature 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '20dp' 16 | BoxLayout: 17 | orientation: 'horizontal' 18 | size_hint_y: 0.3 19 | Button: 20 | id: button_enable 21 | text: 'Enable' 22 | disabled: False 23 | on_release: 24 | root.enable() 25 | button_disable.disabled = not button_disable.disabled 26 | button_enable.disabled = not button_enable.disabled 27 | Button: 28 | id: button_disable 29 | text: 'Disable' 30 | disabled: True 31 | on_release: 32 | root.disable() 33 | button_disable.disabled = not button_disable.disabled 34 | button_enable.disabled = not button_enable.disabled 35 | Label: 36 | text: 'Current air temperature: ' + str(root.temp) + ' degrees C.' 37 | ''') 38 | 39 | 40 | class TemperatureInterface(BoxLayout): 41 | '''Root Widget.''' 42 | 43 | temperature = ObjectProperty() 44 | temp = NumericProperty() 45 | 46 | def enable(self): 47 | self.temperature.enable() 48 | Clock.schedule_interval(self.get_temperature, 1 / 20.) 49 | 50 | def disable(self): 51 | self.temperature.disable() 52 | Clock.unschedule(self.get_temperature) 53 | 54 | def get_temperature(self, dt): 55 | self.temp = self.temperature.temperature or self.temp 56 | 57 | 58 | class TemperatureApp(App): 59 | 60 | def build(self): 61 | return TemperatureInterface() 62 | 63 | def on_pause(self): 64 | return True 65 | 66 | 67 | if __name__ == "__main__": 68 | TemperatureApp().run() 69 | -------------------------------------------------------------------------------- /examples/text2speech/main.py: -------------------------------------------------------------------------------- 1 | import kivy 2 | from kivy.app import App 3 | from kivy.uix.boxlayout import BoxLayout 4 | from kivy.uix.popup import Popup 5 | 6 | from plyer import tts 7 | 8 | kivy.require('1.8.0') 9 | 10 | 11 | class Text2SpeechDemo(BoxLayout): 12 | def do_read(self): 13 | try: 14 | tts.speak(self.ids.notification_text.text) 15 | except NotImplementedError: 16 | popup = ErrorPopup() 17 | popup.open() 18 | 19 | 20 | class Text2SpeechDemoApp(App): 21 | def build(self): 22 | return Text2SpeechDemo() 23 | 24 | def on_pause(self): 25 | return True 26 | 27 | 28 | class ErrorPopup(Popup): 29 | pass 30 | 31 | 32 | if __name__ == '__main__': 33 | Text2SpeechDemoApp().run() 34 | -------------------------------------------------------------------------------- /examples/text2speech/text2speechdemo.kv: -------------------------------------------------------------------------------- 1 | #:kivy 1.8.0 2 | : 3 | BoxLayout: 4 | orientation: 'vertical' 5 | padding: 20 6 | 7 | TextInput: 8 | id: notification_text 9 | text: 'Put message here' 10 | 11 | Button: 12 | text: 'Read' 13 | size_hint_y: 0.2 14 | on_press: root.do_read() 15 | 16 | : 17 | size_hint: .7, .4 18 | title: "Error" 19 | 20 | BoxLayout: 21 | orientation: 'vertical' 22 | padding: 10 23 | spacing: 20 24 | 25 | Label: 26 | size_hint_y: 0.4 27 | text: "This feature has not yet been implemented in Plyer." 28 | Button: 29 | text: 'Dismiss' 30 | size_hint_y: 0.4 31 | on_press: root.dismiss() 32 | -------------------------------------------------------------------------------- /examples/uniqueid/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.lang import Builder 3 | from kivy.properties import ObjectProperty 4 | from kivy.properties import StringProperty 5 | from kivy.uix.boxlayout import BoxLayout 6 | from plyer.utils import platform 7 | 8 | 9 | Builder.load_string(''' 10 | #:import uniqueid plyer.uniqueid 11 | : 12 | uniqueid: uniqueid 13 | orientation: 'vertical' 14 | padding: '50dp' 15 | spacing: '20dp' 16 | BoxLayout: 17 | orientation: 'horizontal' 18 | size_hint_y: 0.3 19 | Button: 20 | text: 'Get Unique ID' 21 | on_release: 22 | root.get_uid() 23 | Label: 24 | text: str(root.text) 25 | Label: 26 | text: str(root.uid) 27 | ''') 28 | 29 | 30 | class UniqueIDInterface(BoxLayout): 31 | '''Root Widget.''' 32 | 33 | uniqueid = ObjectProperty() 34 | uid = StringProperty() 35 | text = StringProperty() 36 | 37 | if platform == "android": 38 | text = "Android ID: " 39 | elif platform == "ios": 40 | text = "UUID: " 41 | elif platform == "win": 42 | text = "Machine GUID: " 43 | else: 44 | text = "Serial Number: " 45 | 46 | def get_uid(self): 47 | self.uid = self.uniqueid.id or self.uid 48 | 49 | 50 | class UniqueIDApp(App): 51 | 52 | def build(self): 53 | return UniqueIDInterface() 54 | 55 | def on_pause(self): 56 | return True 57 | 58 | 59 | if __name__ == "__main__": 60 | UniqueIDApp().run() 61 | -------------------------------------------------------------------------------- /examples/vibrator/main.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.lang import Builder 3 | from kivy.uix.boxlayout import BoxLayout 4 | 5 | Builder.load_string(''' 6 | #:import vibrator plyer.vibrator 7 | : 8 | orientation: 'vertical' 9 | Label: 10 | size_hint_y: None 11 | height: sp(40) 12 | text: 'vibrator exists: ' + str(vibrator.exists()) 13 | Button: 14 | text: 'vibrate 10s' 15 | on_release: vibrator.vibrate(10) 16 | Button: 17 | text: 'vibrate 1s' 18 | on_release: vibrator.vibrate(1) 19 | Button: 20 | text: 'vibrate 0.1s' 21 | on_release: vibrator.vibrate(0.1) 22 | Button: 23 | text: 'cancel vibration' 24 | on_release: vibrator.cancel() 25 | TextInput: 26 | id: ti 27 | text: '0.5,0.5,1,2,0.1,0.1,0.1,0.1,0.1,0.1' 28 | Button: 29 | text: 'vibrate pattern' 30 | on_release: 31 | vibrator.pattern([float(n) for n in ti.text.split(',')]) 32 | 33 | ''') 34 | 35 | 36 | class VibrationInterface(BoxLayout): 37 | '''Root Widget.''' 38 | pass 39 | 40 | 41 | class VibrationApp(App): 42 | 43 | def build(self): 44 | return VibrationInterface() 45 | 46 | def on_pause(self): 47 | return True 48 | 49 | 50 | if __name__ == "__main__": 51 | VibrationApp().run() 52 | -------------------------------------------------------------------------------- /examples/voip/echoServer.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | host = "0.0.0.0" 4 | port = 8080 5 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | server_socket.bind((host, port)) 7 | print(f"Server listening on {host}:{port}") 8 | server_socket.listen() 9 | 10 | try: 11 | while True: 12 | client_socket, client_address = server_socket.accept() 13 | print(f"Connection established with {client_address}") 14 | while True: 15 | data = client_socket.recv(1024) 16 | if not data: 17 | break 18 | client_socket.sendall(data) 19 | client_socket.close() 20 | print(f"Connection closed with {client_address}") 21 | except KeyboardInterrupt: 22 | print("\nServer shutting down...") 23 | finally: 24 | server_socket.close() 25 | -------------------------------------------------------------------------------- /examples/voip/node VOIP server.js: -------------------------------------------------------------------------------- 1 | const net = require("net"); 2 | 3 | const VOIPserver = net.createServer((socket) => { 4 | let clientAddress = socket.remoteAddress; 5 | console.log(`Client connected: ${clientAddress}`); 6 | 7 | // When data is received, send it back to sender for testing 8 | socket.on("data", (data) => { 9 | socket.write(data); 10 | }); 11 | /* 12 | Socket.on(end) will not catch socket closure 13 | due to javascript and java handling sockets differrently, 14 | causing an error. 15 | */ 16 | socket.on("error", (err) => { 17 | console.log(`Client disconnected: ${clientAddress}`); 18 | }); 19 | }); 20 | 21 | const PORT = 8080; 22 | VOIPserver.listen(PORT, () => { 23 | console.log(`VOIP server running on ${PORT}`); 24 | }); 25 | -------------------------------------------------------------------------------- /plyer/facades/accelerometer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Accelerometer 3 | ============ 4 | 5 | The accelerometer is a motion sensor that detects the change (delta) in 6 | movement relative to the current device orientation, in three dimensions 7 | along the x, y, and z axis. 8 | 9 | The :class:`Accelerometer` provides access to public methods to 10 | use accelerometer of your device. 11 | 12 | Simple Examples 13 | --------------- 14 | 15 | To enable accelerometer:: 16 | 17 | >>> from plyer import accelerometer 18 | >>> accelerometer.enable() 19 | 20 | To disable accelerometer:: 21 | 22 | >>> accelerometer.disable() 23 | 24 | To get the acceleration:: 25 | 26 | >>> accelerometer.acceleration 27 | (-10.048464775085449, 6.825869083404541, 7.7260890007019043) 28 | 29 | Supported Plaforms 30 | ------------------ 31 | Android, iOS, OS X, Linux 32 | 33 | ''' 34 | 35 | 36 | class Accelerometer: 37 | ''' 38 | Accelerometer facade. 39 | ''' 40 | 41 | @property 42 | def acceleration(self): 43 | ''' 44 | Property that returns values of the current acceleration 45 | sensors, as a (x, y, z) tuple. Returns (None, None, None) 46 | if no data is currently available. 47 | ''' 48 | return self.get_acceleration() 49 | 50 | def enable(self): 51 | ''' 52 | Activate the accelerometer sensor. Throws an error if the 53 | hardware is not available or not implemented on. 54 | ''' 55 | self._enable() 56 | 57 | def disable(self): 58 | ''' 59 | Disable the accelerometer sensor. 60 | ''' 61 | self._disable() 62 | 63 | def get_acceleration(self): 64 | return self._get_acceleration() 65 | 66 | # private 67 | 68 | def _enable(self): 69 | raise NotImplementedError() 70 | 71 | def _disable(self): 72 | raise NotImplementedError() 73 | 74 | def _get_acceleration(self): 75 | raise NotImplementedError() 76 | -------------------------------------------------------------------------------- /plyer/facades/barometer.py: -------------------------------------------------------------------------------- 1 | class Barometer: 2 | '''Barometer facade. 3 | 4 | Barometer sensor is used to measure the ambient air pressure in hPa. 5 | 6 | With method `enable` you can turn on pressure sensor and 'disable' 7 | method stops the sensor. 8 | 9 | Use property `pressure` to get current air pressure in hPa. 10 | 11 | .. versionadded:: 1.2.5 12 | 13 | Supported Platforms:: Android, iOS 14 | ''' 15 | 16 | @property 17 | def pressure(self): 18 | '''Current air pressure in hPa.''' 19 | return self._get_pressure() 20 | 21 | def _get_pressure(self, **kwargs): 22 | raise NotImplementedError() 23 | 24 | def _enable(self, **kwargs): 25 | raise NotImplementedError() 26 | 27 | def enable(self): 28 | '''Enable barometer sensor.''' 29 | self._enable() 30 | 31 | def _disable(self, **kwargs): 32 | raise NotImplementedError() 33 | 34 | def disable(self): 35 | '''Disable barometer sensor.''' 36 | self._disable() 37 | -------------------------------------------------------------------------------- /plyer/facades/battery.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Battery 3 | ======= 4 | 5 | The :class:`Battery` provides information about the battery of your device. 6 | 7 | .. note:: 8 | On Android the `BATTERY_STATS` permission is needed. 9 | 10 | Simple Example 11 | --------------- 12 | 13 | To get battery status:: 14 | 15 | >>> from plyer import battery 16 | >>> battery.status 17 | {'percentage': 82.0, 'isCharging': False} 18 | 19 | Supported Platforms 20 | ------------------- 21 | Android, iOS, Windows, OS X, Linux 22 | 23 | ''' 24 | 25 | 26 | class Battery: 27 | ''' 28 | Battery info facade. 29 | ''' 30 | 31 | @property 32 | def status(self): 33 | ''' 34 | Property that contains a dict with the following fields: 35 | * **isCharging** *(bool)*: Battery is charging 36 | * **percentage** *(float)*: Battery charge remaining 37 | 38 | .. warning:: 39 | If any of the fields is not readable, it is set as 40 | None. 41 | ''' 42 | return self.get_state() 43 | 44 | def get_state(self): 45 | ''' 46 | Public method for filling battery.status via platform-specific 47 | API in plyer.platforms. 48 | ''' 49 | return self._get_state() 50 | 51 | # private 52 | 53 | def _get_state(self): 54 | raise NotImplementedError() 55 | -------------------------------------------------------------------------------- /plyer/facades/bluetooth.py: -------------------------------------------------------------------------------- 1 | '''Bluetooth facade. 2 | 3 | Returns the following: 4 | 5 | * Bluetooth info 6 | 7 | Simple Example 8 | -------------- 9 | 10 | To get the bluetooth status info:: 11 | todo: will be extended to get additional bluetooth info 12 | todo: will be extended to allow bluetooth connections etc. 13 | 14 | >>> from plyer import bluetooth 15 | >>> bluetooth 16 | 'on' or 'off' 17 | 18 | Supported Platforms 19 | ------------------- 20 | Android, OS X 21 | 22 | ''' 23 | 24 | 25 | class Bluetooth: 26 | ''' 27 | Bluetooth facade. 28 | ''' 29 | 30 | @property 31 | def info(self): 32 | ''' 33 | Property that returns the info (currently status) of the bluetooth. 34 | ''' 35 | return self.get_info() 36 | 37 | def get_info(self): 38 | return self._get_info() 39 | 40 | # private 41 | 42 | def _get_info(self, **kwargs): 43 | raise NotImplementedError() 44 | -------------------------------------------------------------------------------- /plyer/facades/brightness.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Brightness 3 | ========== 4 | 5 | This API helps you to control the brightness of your primary display screen. 6 | 7 | The :class:`Brightness` provides access to public methods to control the 8 | brightness of screen. 9 | 10 | NOTE:: For Android, make sure to add permission, WRITE_SETTINGS 11 | 12 | Simple Examples 13 | --------------- 14 | To know the current brightness level of device:: 15 | 16 | >>> from plyer import brightness 17 | >>> brightness.current_level() 18 | 19 | To set the brightness level to half of maximum:: 20 | 21 | >>> from plyer import brightness 22 | >>> brightness.set_level(50) 23 | 24 | Supported Platforms 25 | ------------------- 26 | Android, iOS, Linux 27 | ''' 28 | 29 | 30 | class Brightness: 31 | ''' 32 | Brightness facade. 33 | ''' 34 | 35 | def current_level(self): 36 | ''' 37 | Know the current level of device's brightness. 38 | ''' 39 | return self._current_level() 40 | 41 | def set_level(self, level): 42 | ''' 43 | Adjust the brightness of the screen. 44 | Minimum brightness level:: 1 45 | Maximum brightness level:: 100 46 | 47 | :param level: New level of brightness between 1 and 100 48 | :type level: int 49 | ''' 50 | return self._set_level(level) 51 | 52 | # private 53 | 54 | def _set_level(self, level): 55 | raise NotImplementedError() 56 | 57 | def _current_level(self): 58 | raise NotImplementedError() 59 | -------------------------------------------------------------------------------- /plyer/facades/call.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Call 3 | ==== 4 | 5 | The :class:`Call` provides access to calling feature of your device. 6 | 7 | .. note:: 8 | - On Android your app needs the `CALL_PHONE` or `CALL_PRIVILEGED` 9 | permission in order to make calls. 10 | 11 | - Dialing call feature in not supported yet in iOS devices. 12 | 13 | Simple Examples 14 | --------------- 15 | 16 | To make call:: 17 | 18 | >>> from plyer import call 19 | >>> tel = 9999222299 20 | >>> call.makecall(tel=tel) 21 | 22 | To dial call:: 23 | 24 | >>> call.dialcall() 25 | 26 | Supported Platforms 27 | ------------------- 28 | Android, iOS 29 | 30 | ''' 31 | 32 | 33 | class Call: 34 | ''' 35 | Call facade. 36 | ''' 37 | 38 | def makecall(self, tel): 39 | ''' 40 | Make calls using your device. 41 | 42 | :param tel: The reciever 43 | :type tel: number 44 | ''' 45 | self._makecall(tel=tel) 46 | 47 | def dialcall(self): 48 | ''' 49 | Opens dialing interface. 50 | ''' 51 | self._dialcall() 52 | 53 | # private 54 | 55 | def _makecall(self, **kwargs): 56 | raise NotImplementedError() 57 | 58 | def _dialcall(self, **kwargs): 59 | raise NotImplementedError() 60 | -------------------------------------------------------------------------------- /plyer/facades/devicename.py: -------------------------------------------------------------------------------- 1 | '''DeviceName facade. 2 | 3 | Returns the following depending on the platform: 4 | 5 | * **Android**: Android Device name 6 | * **Linux**: Hostname of the machine 7 | * **OS X**: Hostname of the machine 8 | * **Windows**: Hostname of the machine 9 | 10 | Simple Example 11 | -------------- 12 | 13 | To get the Device Name:: 14 | 15 | >>> from plyer import devicename 16 | >>> devicename.device_name 17 | 'Oneplus 3' 18 | 19 | .. versionadded:: 2.1.0 20 | - first release 21 | 22 | 23 | Supported Platforms 24 | ------------------- 25 | Android, Windows, OS X, Linux 26 | 27 | ''' 28 | 29 | 30 | class DeviceName: 31 | ''' 32 | DeviceName facade. 33 | ''' 34 | 35 | @property 36 | def device_name(self): 37 | ''' 38 | Property that returns the device name of the platform. 39 | ''' 40 | return self._get_device_name() 41 | 42 | # private 43 | def _get_device_name(self): 44 | raise NotImplementedError() 45 | -------------------------------------------------------------------------------- /plyer/facades/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Email 3 | ===== 4 | 5 | The :class:`Email` provides access to public methods to use email of your 6 | device. 7 | 8 | .. note:: 9 | On Android `INTERNET` permission is needed. 10 | 11 | Simple Examples 12 | --------------- 13 | 14 | To send an e-mail:: 15 | 16 | >>> from plyer import email 17 | >>> recipient = 'abc@gmail.com' 18 | >>> subject = 'Hi' 19 | >>> text = 'This is an example.' 20 | >>> create_chooser = False 21 | >>> email.send(recipient=recipient, subject=subject, text=text, 22 | create_chooser=create_chooser) 23 | 24 | >>> # opens email interface where user can change the content. 25 | 26 | Supported Platforms 27 | ------------------- 28 | Android, iOS, Windows, OS X, Linux 29 | 30 | ''' 31 | 32 | 33 | class Email: 34 | ''' 35 | Email facade. 36 | ''' 37 | 38 | def send(self, recipient=None, subject=None, text=None, 39 | create_chooser=None): 40 | ''' 41 | Open an email client message send window, prepopulated with the 42 | given arguments. 43 | 44 | :param recipient: Recipient of the message (str) 45 | :param subject: Subject of the message (str) 46 | :param text: Main body of the message (str) 47 | :param create_chooser: Whether to display a program chooser to 48 | handle the message (bool) 49 | 50 | .. note:: create_chooser is only supported on Android 51 | ''' 52 | self._send(recipient=recipient, subject=subject, text=text, 53 | create_chooser=create_chooser) 54 | 55 | # private 56 | 57 | def _send(self, **kwargs): 58 | raise NotImplementedError() 59 | -------------------------------------------------------------------------------- /plyer/facades/flash.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | ''' 3 | Flash 4 | ===== 5 | 6 | The :class:`Flash` provides access to public methods to use flash of your 7 | device. 8 | 9 | .. note:: 10 | In android you need CAMERA, FLASHLIGHT permissions 11 | to access flash. 12 | 13 | .. versionadded:: 1.2.5 14 | 15 | This can be used to activate the flash of your camera on 16 | Android and iOS. 17 | 18 | Simple Examples 19 | --------------- 20 | 21 | To turn on flash:: 22 | 23 | >>> from plyer import flash 24 | >>> flash.on() 25 | 26 | To turn off flash:: 27 | 28 | >>> flash.off() 29 | 30 | To release flash:: 31 | 32 | >>> flash.release() 33 | 34 | Supported Platforms 35 | ------------------- 36 | Android, iOS 37 | 38 | ''' 39 | 40 | 41 | class Flash: 42 | """ 43 | Flash facade. 44 | """ 45 | 46 | def on(self): 47 | """ 48 | Activate the flash 49 | """ 50 | self._on() 51 | 52 | def off(self): 53 | """ 54 | Deactiavte the flash 55 | """ 56 | self._off() 57 | 58 | def release(self): 59 | """ 60 | Release any access to the Flash / Camera. 61 | Call this when you're done using the Flash. 62 | This will release the Camera, and stop any process. 63 | 64 | Next call to `_on` will reactivate it. 65 | """ 66 | self._release() 67 | 68 | # private 69 | 70 | def _on(self): 71 | raise NotImplementedError() 72 | 73 | def _off(self): 74 | raise NotImplementedError() 75 | 76 | def _release(self): 77 | pass 78 | -------------------------------------------------------------------------------- /plyer/facades/gravity.py: -------------------------------------------------------------------------------- 1 | class Gravity: 2 | '''Gravity facade. 3 | 4 | .. versionadded:: 1.2.5 5 | 6 | Supported Platforms:: Android 7 | 8 | ''' 9 | 10 | @property 11 | def gravity(self): 12 | '''Property that returns values of the current gravity force 13 | as a (x, y, z) tuple. Returns (None, None, None) 14 | if no data is currently available. 15 | ''' 16 | return self._get_gravity() 17 | 18 | def enable(self): 19 | '''Activate the gravity sensor. Throws an error if the 20 | hardware is not available or not implemented on. 21 | ''' 22 | self._enable() 23 | 24 | def disable(self): 25 | '''Disable the gravity sensor. 26 | ''' 27 | self._disable() 28 | 29 | # private 30 | 31 | def _enable(self): 32 | raise NotImplementedError() 33 | 34 | def _disable(self): 35 | raise NotImplementedError() 36 | 37 | def _get_gravity(self): 38 | raise NotImplementedError() 39 | -------------------------------------------------------------------------------- /plyer/facades/humidity.py: -------------------------------------------------------------------------------- 1 | class Humidity: 2 | '''Humidity facade. 3 | Humidity sensor returns value of humidity. 4 | With method `enable` you can turn on Humidity sensor and 5 | 'disable' method stops the sensor. 6 | Use property `tell` to get humidity value. 7 | 8 | Supported Platforms 9 | ------------------- 10 | Android 11 | ''' 12 | 13 | @property 14 | def tell(self): 15 | '''Current humidity''' 16 | return self._get_humidity() 17 | 18 | def enable(self): 19 | '''Enable Humidity sensor.''' 20 | self._enable() 21 | 22 | def disable(self): 23 | '''Disable Humidity sensor.''' 24 | self._disable() 25 | 26 | # private 27 | def _get_humidity(self, **kwargs): 28 | raise NotImplementedError() 29 | 30 | def _enable(self, **kwargs): 31 | raise NotImplementedError() 32 | 33 | def _disable(self, **kwargs): 34 | raise NotImplementedError() 35 | -------------------------------------------------------------------------------- /plyer/facades/keystore.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Keystore 3 | ======= 4 | The :class:`Keystore` provides a mechanism for securing/storing 5 | cryptographic keys (such as user credentials) in a container. 6 | Typically needed to support authentication APIs such as OAuth2 7 | .. note:: 8 | Typically needed to support authentication APIs such as OAuth2 9 | 10 | Supported Platforms 11 | ------------------- 12 | Android, iOS, Windows, OS X, Linux 13 | --------------- 14 | ''' 15 | 16 | 17 | class Keystore: 18 | ''' 19 | Keystore facade 20 | ''' 21 | 22 | def set_key(self, servicename, key, value, **kwargs): 23 | self._set_key(servicename, key, value, **kwargs) 24 | 25 | def _set_key(self, servicename, key, value, **kwargs): 26 | raise NotImplementedError() 27 | 28 | def get_key(self, servicename, key, **kwargs): 29 | return self._get_key(servicename, key) 30 | 31 | def _get_key(self, servicename, key, **kwargs): 32 | raise NotImplementedError() 33 | -------------------------------------------------------------------------------- /plyer/facades/light.py: -------------------------------------------------------------------------------- 1 | class Light: 2 | '''Light facade. 3 | 4 | Light sensor measures the ambient light level(illumination) in lx. 5 | Common uses include controlling screen brightness. 6 | 7 | With method `enable` you can turn on the sensor and 8 | `disable` method stops the sensor. 9 | 10 | Use property `illumination` to get current illumination in lx. 11 | 12 | .. versionadded:: 1.2.5 13 | 14 | Supported Platforms:: Android 15 | ''' 16 | 17 | @property 18 | def illumination(self): 19 | '''Current illumination in lx.''' 20 | return self._get_illumination() 21 | 22 | def enable(self): 23 | '''Enable light sensor.''' 24 | self._enable() 25 | 26 | def disable(self): 27 | '''Disable light sensor.''' 28 | self._disable() 29 | 30 | # private 31 | 32 | def _get_illumination(self, **kwargs): 33 | raise NotImplementedError() 34 | 35 | def _enable(self, **kwargs): 36 | raise NotImplementedError() 37 | 38 | def _disable(self, **kwargs): 39 | raise NotImplementedError() 40 | -------------------------------------------------------------------------------- /plyer/facades/orientation.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Orientation 3 | ========== 4 | 5 | The :class:`Orientation` provides access to public methods to set orientation 6 | of your device. 7 | 8 | .. note:: 9 | These settings are generally guidelines, the operating 10 | system may choose to ignore them, or they may be overridden by 11 | other system components. 12 | 13 | .. versionadded:: 1.2.4 14 | 15 | Simple Examples 16 | --------------- 17 | 18 | To set landscape:: 19 | 20 | >>> from plyer import orientation 21 | >>> orientation.set_landscape() 22 | 23 | To set portrait:: 24 | 25 | >>> orientation.set_portrait() 26 | 27 | To set sensor:: 28 | 29 | >>> orientation.set_sensor() 30 | 31 | Supported Platforms 32 | ------------------- 33 | Android, Linux 34 | 35 | ''' 36 | 37 | 38 | class Orientation: 39 | ''' 40 | Orientation facade. 41 | ''' 42 | 43 | def set_landscape(self, reverse=False): 44 | ''' 45 | Rotate the app to a landscape orientation. 46 | 47 | :param reverse: If True, uses the opposite of the natural 48 | orientation. 49 | ''' 50 | self._set_landscape(reverse=reverse) 51 | 52 | def set_portrait(self, reverse=False): 53 | ''' 54 | Rotate the app to a portrait orientation. 55 | 56 | :param reverse: If True, uses the opposite of the natural 57 | orientation. 58 | ''' 59 | self._set_portrait(reverse=reverse) 60 | 61 | def set_sensor(self, mode='any'): 62 | ''' 63 | Rotate freely following sensor information from the device. 64 | 65 | :param mode: The rotation mode, should be one of 'any' (rotate 66 | to any orientation), 'landscape' (choose nearest 67 | landscape mode) or 'portrait' (choose nearest 68 | portrait mode). Defaults to 'any'. 69 | ''' 70 | self._set_sensor(mode=mode) 71 | 72 | # private 73 | 74 | def _set_landscape(self, **kwargs): 75 | raise NotImplementedError() 76 | 77 | def _set_portrait(self, **kwargs): 78 | raise NotImplementedError() 79 | 80 | def _set_sensor(self, **kwargs): 81 | raise NotImplementedError() 82 | -------------------------------------------------------------------------------- /plyer/facades/processors.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Number of Processors 3 | ======= 4 | The :class:`Processors` provides a information on the number of 5 | processors in a system 6 | .. note:: 7 | Deprecated in favor of `cpu` 8 | 9 | Simple Example 10 | --------------- 11 | To get processors status:: 12 | >>> from plyer import processors 13 | >>> processors.status 14 | {'Number_of_Processors': '4'} 15 | Supported Platforms 16 | ------------------- 17 | Linux 18 | ''' 19 | 20 | 21 | class Processors: 22 | ''' 23 | Number of Processors info facade. 24 | ''' 25 | 26 | @property 27 | def status(self): 28 | ''' 29 | Property that contains a dict with the following fields: 30 | * **Number_of_Processors** *(int)*: Number of Processors in 31 | the system 32 | .. warning:: 33 | If any of the fields is not readable, it is set as 34 | None. 35 | ''' 36 | return self.get_state() 37 | 38 | def get_state(self): 39 | return self._get_state() 40 | 41 | def _get_state(self): 42 | raise NotImplementedError() 43 | -------------------------------------------------------------------------------- /plyer/facades/proximity.py: -------------------------------------------------------------------------------- 1 | class Proximity: 2 | '''Proximity facade. 3 | 4 | The proximity sensor is commonly used to determine distance whether 5 | phone is close to your head. Commonly is used when you have a call 6 | and you stick your phone with your head. Then screen of phone turns off. 7 | 8 | Use method `enable` to turn on proximity sensor and method `disable` for 9 | turn off. 10 | 11 | To check if some object (or your head) is near sensor check values from 12 | property `proximity`. It returns `True` when object is close. 13 | 14 | .. versionadded:: 1.2.5 15 | 16 | Supported Platforms::Android 17 | ''' 18 | 19 | @property 20 | def proximity(self): 21 | '''Return True or False depending if there is an object or not. 22 | 23 | :return: True if there is an object. Otherwise False. 24 | ''' 25 | return self._get_proximity() 26 | 27 | def _enable(self, **kwargs): 28 | raise NotImplementedError() 29 | 30 | def enable(self): 31 | '''Enable the proximity sensor. 32 | ''' 33 | self._enable() 34 | 35 | def _disable(self, **kwargs): 36 | raise NotImplementedError() 37 | 38 | def disable(self): 39 | '''Disable the proximity sensor. 40 | ''' 41 | self._disable() 42 | 43 | def _get_proximity(self): 44 | raise NotImplementedError() 45 | -------------------------------------------------------------------------------- /plyer/facades/screenshot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Screenshot 3 | ========== 4 | 5 | The :class:`Screenshot` is used for capturing a digital image of what 6 | is currently visible on the monitor. 7 | 8 | The default path for taking screenshot is set in each platform implementation. 9 | 10 | Simple Examples 11 | --------------- 12 | 13 | To get the file path:: 14 | 15 | >>> screenshot.file_path 16 | '/sdcard/test.jpg' 17 | 18 | To set the file path:: 19 | 20 | >>> screenshot.file_path = '/Users/OSXUser/Pictures/screenshot.png' 21 | 22 | To take screenshot:: 23 | 24 | >>> from plyer import screenshot 25 | >>> screenshot.capture() 26 | ''' 27 | 28 | 29 | class Screenshot: 30 | ''' 31 | Screenshot facade. 32 | ''' 33 | 34 | _file_path = '' 35 | 36 | def __init__(self, file_path=None): 37 | self._file_path = file_path 38 | 39 | def capture(self): 40 | self._capture() 41 | 42 | @property 43 | def file_path(self): 44 | return self._file_path 45 | 46 | @file_path.setter 47 | def file_path(self, location): 48 | ''' 49 | Location of the screenshot. 50 | ''' 51 | 52 | self._file_path = location 53 | 54 | # private 55 | 56 | def _capture(self, **kwargs): 57 | raise NotImplementedError() 58 | -------------------------------------------------------------------------------- /plyer/facades/sms.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Sms 3 | ==== 4 | 5 | The :class:`Sms` provides access to sending Sms from your device. 6 | 7 | .. note:: 8 | 9 | On Android your app needs the SEND_SMS permission in order to 10 | send sms messages. 11 | 12 | .. versionadded:: 1.2.0 13 | 14 | Simple Examples 15 | --------------- 16 | 17 | To send sms:: 18 | 19 | >>> from plyer import sms 20 | >>> recipient = 9999222299 21 | >>> message = 'This is an example.' 22 | >>> sms.send(recipient=recipient, message=message) 23 | 24 | Supported Platforms 25 | ------------------- 26 | Android, iOS, macOS 27 | 28 | ''' 29 | 30 | 31 | class Sms: 32 | ''' 33 | Sms facade. 34 | ''' 35 | 36 | def send(self, recipient, message, mode=None, **kwargs): 37 | ''' 38 | Send SMS or open SMS interface. 39 | Includes optional `mode` parameter for macOS that can be set to 40 | `'SMS'` if carrier-activated device is correctly paired and 41 | configured to macOS. 42 | 43 | :param recipient: The receiver 44 | :param message: the message 45 | :param mode: (optional, macOS only), can be set to 'iMessage' 46 | (default) or 'SMS' 47 | 48 | :type recipient: number 49 | :type message: str 50 | :type mode: str 51 | ''' 52 | self._send(recipient=recipient, message=message, mode=mode, **kwargs) 53 | 54 | # private 55 | 56 | def _send(self, **kwargs): 57 | raise NotImplementedError() 58 | -------------------------------------------------------------------------------- /plyer/facades/spatialorientation.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | class SpatialOrientation: 5 | '''Spatial Orientation facade. 6 | 7 | Computes the device's orientation based on the rotation matrix. 8 | 9 | .. versionadded:: 1.3.1 10 | ''' 11 | 12 | @property 13 | def orientation(self): 14 | '''Property that returns values of the current device orientation 15 | as a (azimuth, pitch, roll) tuple. 16 | 17 | Azimuth, angle of rotation about the -z axis. This value represents the 18 | angle between the device's y axis and the magnetic north pole. 19 | The range of values is -π to π. 20 | 21 | Pitch, angle of rotation about the x axis. This value represents the 22 | angle between a plane parallel to the device's screen and a plane 23 | parallel to the ground. 24 | The range of values is -π to π. 25 | 26 | Roll, angle of rotation about the y axis. This value represents the 27 | angle between a plane perpendicular to the device's screen and a plane 28 | perpendicular to the ground. 29 | The range of values is -π/2 to π/2. 30 | 31 | Returns (None, None, None) if no data is currently available. 32 | 33 | Supported Platforms:: Android 34 | ''' 35 | return self._get_orientation() or (None, None, None) 36 | 37 | def _get_orientation(self): 38 | raise NotImplementedError() 39 | 40 | def enable_listener(self): 41 | '''Enable the orientation sensor. 42 | ''' 43 | self._enable_listener() 44 | 45 | def _enable_listener(self, **kwargs): 46 | raise NotImplementedError() 47 | 48 | def disable_listener(self): 49 | '''Disable the orientation sensor. 50 | ''' 51 | self._disable_listener() 52 | 53 | def _disable_listener(self, **kwargs): 54 | raise NotImplementedError() 55 | -------------------------------------------------------------------------------- /plyer/facades/temperature.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | class Temperature: 5 | '''Temperature facade. 6 | 7 | Temperature sensor is used to measure the ambient room temperature in 8 | degrees Celsius (°C) 9 | With method `enable` you can turn on temperature sensor and 'disable' 10 | method stops the sensor. 11 | Use property `temperature` to get ambient air temperature in degree C. 12 | 13 | .. versionadded:: 1.2.5 14 | 15 | Supported Platforms:: Android 16 | 17 | ''' 18 | 19 | @property 20 | def temperature(self): 21 | '''Current air temperature in degree C.''' 22 | return self._get_temperature() 23 | 24 | def enable(self): 25 | '''Enable temperature sensor.''' 26 | self._enable() 27 | 28 | def disable(self): 29 | '''Disable temperature sensor.''' 30 | self._disable() 31 | 32 | # private 33 | 34 | def _get_temperature(self, **kwargs): 35 | raise NotImplementedError() 36 | 37 | def _enable(self, **kwargs): 38 | raise NotImplementedError() 39 | 40 | def _disable(self, **kwargs): 41 | raise NotImplementedError() 42 | -------------------------------------------------------------------------------- /plyer/facades/tts.py: -------------------------------------------------------------------------------- 1 | ''' 2 | TTS 3 | ==== 4 | 5 | The :class:`TTS` provides provides access to public methods to 6 | use text to speech of your device. 7 | 8 | Simple Examples 9 | --------------- 10 | 11 | To speak:: 12 | 13 | >>> from plyer import tts 14 | >>> tts.speak(message=message) 15 | 16 | Supported Platforms 17 | ------------------- 18 | Android, iOS, Windows, OS X, Linux 19 | 20 | ''' 21 | 22 | 23 | class TTS: 24 | ''' 25 | TextToSpeech facade. 26 | ''' 27 | 28 | def speak(self, message=''): 29 | '''Use text to speech capabilities to speak the message. 30 | 31 | :param message: What to speak 32 | :type message: str 33 | ''' 34 | self._speak(message=message) 35 | 36 | # private 37 | 38 | def _speak(self, **kwargs): 39 | raise NotImplementedError() 40 | -------------------------------------------------------------------------------- /plyer/facades/uniqueid.py: -------------------------------------------------------------------------------- 1 | '''UniqueID facade. 2 | 3 | Returns the following depending on the platform: 4 | 5 | * **Android**: Android ID 6 | * **OS X**: Serial number of the device 7 | * **Linux**: Serial number using lshw 8 | * **Windows**: MachineGUID from regkey 9 | * **iOS**: UUID 10 | 11 | Simple Example 12 | -------------- 13 | 14 | To get the unique ID:: 15 | 16 | >>> from plyer import uniqueid 17 | >>> uniqueid.id 18 | '1b1a7a4958e2a845' 19 | 20 | .. versionadded:: 1.2.0 21 | 22 | .. versionchanged:: 1.2.4 23 | On Android returns Android ID instead of IMEI. 24 | 25 | Supported Platforms 26 | ------------------- 27 | Android, iOS, Windows, OS X, Linux 28 | 29 | ''' 30 | 31 | 32 | class UniqueID: 33 | ''' 34 | UniqueID facade. 35 | ''' 36 | 37 | @property 38 | def id(self): 39 | ''' 40 | Property that returns the unique id of the platform. 41 | ''' 42 | return self.get_uid() 43 | 44 | def get_uid(self): 45 | ''' 46 | Public method for receiving unique ID via platform-specific 47 | API in plyer.platforms. 48 | ''' 49 | return self._get_uid() 50 | 51 | # private 52 | 53 | def _get_uid(self): 54 | raise NotImplementedError() 55 | -------------------------------------------------------------------------------- /plyer/facades/voip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Voip 3 | ==== 4 | 5 | The :class:`VOIP` facade offers a comprehensive suite of methods for 6 | managing VoIP (Voice over IP) calls, including initiating and ending calls, 7 | managing call streams, handling microphone permissions, and overseeing 8 | network connections. 9 | 10 | Supported Platforms 11 | ------------------- 12 | Android 13 | iOS 14 | 15 | Example Usage 16 | ------------- 17 | 18 | To start a VOIP call: 19 | 20 | >>> from plyer import voip 21 | >>> voip.start_call( 22 | dst_address = "192.168.1.67" 23 | dst_port = 8080 24 | ) 25 | 26 | To end a VOIP call: 27 | 28 | >>> voip.end_call() 29 | """ 30 | 31 | 32 | class Voip: 33 | ''' 34 | Voip facade. 35 | ''' 36 | 37 | def start_call(self, **kwargs): 38 | ''' 39 | Start a VOIP call. This establishes the connection, microphone 40 | stream, and speaker stream. 41 | 42 | :param dst_address: Sets server IP address or root domain 43 | :type dst_address: string 44 | :param dst_port: Sets server destination port 45 | :type dst_port: integer 46 | :param client_id: Allows authentication of caller 47 | :type client_id: string 48 | :param timeout: Limits time for connection 49 | :type timeout: integer 50 | :param ssl: Enables SSL/TLS 51 | :type ssl: boolean 52 | :param tls_version: Allows TLS version selection 53 | :type tls_version: string 54 | :param debug: Displays debug logs 55 | :type debug: boolean 56 | ''' 57 | self._start_call(**kwargs) 58 | 59 | def end_call(self): 60 | ''' 61 | End the VOIP call, stopping all streams and closing connections. 62 | ''' 63 | self._end_call() 64 | 65 | # Private methods implemented by the platform-specific class 66 | 67 | def _start_call(self, **kwargs): 68 | raise NotImplementedError() 69 | 70 | def _end_call(self): 71 | raise NotImplementedError() 72 | -------------------------------------------------------------------------------- /plyer/platforms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/android/__init__.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | from jnius import autoclass 3 | 4 | ANDROID_VERSION = autoclass('android.os.Build$VERSION') 5 | SDK_INT = ANDROID_VERSION.SDK_INT 6 | 7 | try: 8 | from android import config 9 | ns = config.JAVA_NAMESPACE 10 | except (ImportError, AttributeError): 11 | ns = 'org.renpy.android' 12 | 13 | if 'PYTHON_SERVICE_ARGUMENT' in environ: 14 | PythonService = autoclass(ns + '.PythonService') 15 | activity = PythonService.mService 16 | else: 17 | PythonActivity = autoclass(ns + '.PythonActivity') 18 | activity = PythonActivity.mActivity 19 | -------------------------------------------------------------------------------- /plyer/platforms/android/audio.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | 3 | from plyer.facades.audio import Audio 4 | 5 | # Recorder Classes 6 | MediaRecorder = autoclass('android.media.MediaRecorder') 7 | AudioSource = autoclass('android.media.MediaRecorder$AudioSource') 8 | OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') 9 | AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder') 10 | 11 | # Player Classes 12 | MediaPlayer = autoclass('android.media.MediaPlayer') 13 | 14 | 15 | class AndroidAudio(Audio): 16 | '''Audio for android. 17 | 18 | For recording audio we use MediaRecorder Android class. 19 | For playing audio we use MediaPlayer Android class. 20 | ''' 21 | 22 | def __init__(self, file_path=None): 23 | default_path = '/sdcard/testrecorder.3gp' 24 | super().__init__(file_path or default_path) 25 | 26 | self._recorder = None 27 | self._player = None 28 | 29 | def _start(self): 30 | self._recorder = MediaRecorder() 31 | self._recorder.setAudioSource(AudioSource.DEFAULT) 32 | self._recorder.setOutputFormat(OutputFormat.DEFAULT) 33 | self._recorder.setAudioEncoder(AudioEncoder.DEFAULT) 34 | self._recorder.setOutputFile(self.file_path) 35 | 36 | self._recorder.prepare() 37 | self._recorder.start() 38 | 39 | def _stop(self): 40 | if self._recorder: 41 | self._recorder.stop() 42 | self._recorder.release() 43 | self._recorder = None 44 | 45 | if self._player: 46 | self._player.stop() 47 | self._player.release() 48 | self._player = None 49 | 50 | def _play(self): 51 | self._player = MediaPlayer() 52 | self._player.setDataSource(self.file_path) 53 | self._player.prepare() 54 | self._player.start() 55 | 56 | 57 | def instance(): 58 | return AndroidAudio() 59 | -------------------------------------------------------------------------------- /plyer/platforms/android/barometer.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | from jnius import cast 3 | from jnius import java_method 4 | from jnius import PythonJavaClass 5 | 6 | from plyer.facades import Barometer 7 | from plyer.platforms.android import activity 8 | 9 | ActivityInfo = autoclass('android.content.pm.ActivityInfo') 10 | Context = autoclass('android.content.Context') 11 | Sensor = autoclass('android.hardware.Sensor') 12 | SensorManager = autoclass('android.hardware.SensorManager') 13 | 14 | 15 | class BarometerSensorListener(PythonJavaClass): 16 | __javainterfaces__ = ['android/hardware/SensorEventListener'] 17 | 18 | def __init__(self): 19 | super().__init__() 20 | service = activity.getSystemService(Context.SENSOR_SERVICE) 21 | self.SensorManager = cast('android.hardware.SensorManager', service) 22 | 23 | self.sensor = self.SensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) 24 | self.value = None 25 | 26 | def enable(self): 27 | self.SensorManager.registerListener( 28 | self, self.sensor, 29 | SensorManager.SENSOR_DELAY_NORMAL 30 | ) 31 | 32 | def disable(self): 33 | self.SensorManager.unregisterListener(self, self.sensor) 34 | 35 | @java_method('(Landroid/hardware/SensorEvent;)V') 36 | def onSensorChanged(self, event): 37 | self.value = event.values[0] 38 | 39 | @java_method('(Landroid/hardware/Sensor;I)V') 40 | def onAccuracyChanged(self, sensor, accuracy): 41 | pass 42 | 43 | 44 | class AndroidBarometer(Barometer): 45 | 46 | listener = None 47 | 48 | def _get_pressure(self): 49 | if self.listener and self.listener.value: 50 | pressure = self.listener.value 51 | return pressure 52 | 53 | def _enable(self): 54 | if not self.listener: 55 | self.listener = BarometerSensorListener() 56 | self.listener.enable() 57 | 58 | def _disable(self): 59 | if self.listener: 60 | self.listener.disable() 61 | delattr(self, 'listener') 62 | 63 | 64 | def instance(): 65 | return AndroidBarometer() 66 | -------------------------------------------------------------------------------- /plyer/platforms/android/battery.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Android API for plyer.battery. 3 | ''' 4 | 5 | from jnius import autoclass, cast 6 | from plyer.platforms.android import activity 7 | from plyer.facades import Battery 8 | 9 | Intent = autoclass('android.content.Intent') 10 | BatteryManager = autoclass('android.os.BatteryManager') 11 | IntentFilter = autoclass('android.content.IntentFilter') 12 | 13 | 14 | class AndroidBattery(Battery): 15 | ''' 16 | Implementation of Android battery API. 17 | ''' 18 | 19 | def _get_state(self): 20 | status = {"isCharging": None, "percentage": None} 21 | 22 | ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) 23 | 24 | battery_status = cast( 25 | 'android.content.Intent', 26 | activity.registerReceiver(None, ifilter) 27 | ) 28 | 29 | query = battery_status.getIntExtra(BatteryManager.EXTRA_STATUS, -1) 30 | is_charging = query == BatteryManager.BATTERY_STATUS_CHARGING 31 | is_full = query == BatteryManager.BATTERY_STATUS_FULL 32 | 33 | level = battery_status.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) 34 | scale = battery_status.getIntExtra(BatteryManager.EXTRA_SCALE, -1) 35 | percentage = (level / float(scale)) * 100 36 | 37 | status['isCharging'] = is_charging or is_full 38 | status['percentage'] = percentage 39 | 40 | return status 41 | 42 | 43 | def instance(): 44 | ''' 45 | Instance for facade proxy. 46 | ''' 47 | return AndroidBattery() 48 | -------------------------------------------------------------------------------- /plyer/platforms/android/bluetooth.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Android API for plyer.bluetooth. 3 | ''' 4 | 5 | from jnius import autoclass 6 | from plyer.platforms.android import activity 7 | from plyer.facades import Bluetooth 8 | 9 | Global = autoclass('android.provider.Settings$Global') 10 | 11 | 12 | class AndroidBluetooth(Bluetooth): 13 | ''' 14 | Implementation of Android Bluetooth API. 15 | ''' 16 | 17 | def _get_info(self): 18 | bluetooth_enabled = Global.getString( 19 | activity.getContentResolver(), 20 | Global.BLUETOOTH_ON 21 | ) 22 | status = 'off' 23 | if bluetooth_enabled: 24 | status = 'on' 25 | return status 26 | 27 | 28 | def instance(): 29 | ''' 30 | Instance for facade proxy. 31 | ''' 32 | return AndroidBluetooth() 33 | -------------------------------------------------------------------------------- /plyer/platforms/android/brightness.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Android Brightness 3 | ------------------ 4 | ''' 5 | 6 | from jnius import autoclass 7 | from plyer.facades import Brightness 8 | from android import mActivity 9 | 10 | System = autoclass('android.provider.Settings$System') 11 | 12 | 13 | class AndroidBrightness(Brightness): 14 | 15 | def _current_level(self): 16 | 17 | System.putInt( 18 | mActivity.getContentResolver(), 19 | System.SCREEN_BRIGHTNESS_MODE, 20 | System.SCREEN_BRIGHTNESS_MODE_MANUAL) 21 | cr_level = System.getInt( 22 | mActivity.getContentResolver(), 23 | System.SCREEN_BRIGHTNESS) 24 | return (cr_level / 255.) * 100 25 | 26 | def _set_level(self, level): 27 | System.putInt( 28 | mActivity.getContentResolver(), 29 | System.SCREEN_BRIGHTNESS, 30 | (level / 100.) * 255) 31 | 32 | 33 | def instance(): 34 | return AndroidBrightness() 35 | -------------------------------------------------------------------------------- /plyer/platforms/android/call.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Android Call 3 | ----------- 4 | ''' 5 | 6 | from jnius import autoclass 7 | from plyer.facades import Call 8 | from plyer.platforms.android import activity 9 | 10 | Intent = autoclass('android.content.Intent') 11 | uri = autoclass('android.net.Uri') 12 | 13 | 14 | class AndroidCall(Call): 15 | 16 | def _makecall(self, **kwargs): 17 | 18 | intent = Intent(Intent.ACTION_CALL) 19 | tel = kwargs.get('tel') 20 | intent.setData(uri.parse("tel:{}".format(tel))) 21 | activity.startActivity(intent) 22 | 23 | def _dialcall(self, **kwargs): 24 | intent_ = Intent(Intent.ACTION_DIAL) 25 | activity.startActivity(intent_) 26 | 27 | 28 | def instance(): 29 | return AndroidCall() 30 | -------------------------------------------------------------------------------- /plyer/platforms/android/devicename.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Android API for plyer.devicename. 3 | ''' 4 | 5 | from jnius import autoclass 6 | from plyer.facades import DeviceName 7 | 8 | Build = autoclass('android.os.Build') 9 | 10 | 11 | class AndroidDeviceName(DeviceName): 12 | ''' 13 | Implementation of Android devicename API. 14 | ''' 15 | 16 | def _get_device_name(self): 17 | """ 18 | Method to get the device name aka model in an android environment. 19 | 20 | Changed the implementation from 'android.provider.Settings.Global' to 21 | 'android.os.Build' because 'android.provider.Settings.Global' was 22 | introduced in API 17 whereas 'android.os.Build' is present since API 1 23 | 24 | Thereby making this method more backward compatible. 25 | """ 26 | return Build.MODEL 27 | 28 | 29 | def instance(): 30 | ''' 31 | Instance for facade proxy. 32 | ''' 33 | return AndroidDeviceName() 34 | -------------------------------------------------------------------------------- /plyer/platforms/android/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Android API for plyer.email. 3 | ''' 4 | 5 | from jnius import autoclass, cast 6 | from plyer.facades import Email 7 | from plyer.platforms.android import activity 8 | 9 | Intent = autoclass('android.content.Intent') 10 | AndroidString = autoclass('java.lang.String') 11 | 12 | 13 | class AndroidEmail(Email): 14 | ''' 15 | Implementation of Android email API. 16 | ''' 17 | 18 | def _send(self, **kwargs): 19 | intent = Intent(Intent.ACTION_SEND) 20 | intent.setType('text/plain') 21 | 22 | recipient = kwargs.get('recipient') 23 | subject = kwargs.get('subject') 24 | text = kwargs.get('text') 25 | create_chooser = kwargs.get('create_chooser') 26 | 27 | if recipient: 28 | intent.putExtra(Intent.EXTRA_EMAIL, [recipient]) 29 | if subject: 30 | android_subject = cast( 31 | 'java.lang.CharSequence', 32 | AndroidString(subject) 33 | ) 34 | intent.putExtra(Intent.EXTRA_SUBJECT, android_subject) 35 | if text: 36 | android_text = cast( 37 | 'java.lang.CharSequence', 38 | AndroidString(text) 39 | ) 40 | intent.putExtra(Intent.EXTRA_TEXT, android_text) 41 | 42 | if create_chooser: 43 | chooser_title = cast( 44 | 'java.lang.CharSequence', 45 | AndroidString('Send message with:') 46 | ) 47 | activity.startActivity( 48 | Intent.createChooser(intent, chooser_title) 49 | ) 50 | else: 51 | activity.startActivity(intent) 52 | 53 | 54 | def instance(): 55 | ''' 56 | Instance for facade proxy. 57 | ''' 58 | return AndroidEmail() 59 | -------------------------------------------------------------------------------- /plyer/platforms/android/flash.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Flash 4 | ----- 5 | """ 6 | 7 | from plyer.facades import Flash 8 | from jnius import autoclass 9 | from plyer.platforms.android import activity 10 | 11 | Camera = autoclass("android.hardware.Camera") 12 | CameraParameters = autoclass("android.hardware.Camera$Parameters") 13 | SurfaceTexture = autoclass("android.graphics.SurfaceTexture") 14 | PackageManager = autoclass('android.content.pm.PackageManager') 15 | pm = activity.getPackageManager() 16 | flash_available = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) 17 | 18 | 19 | class AndroidFlash(Flash): 20 | _camera = None 21 | 22 | def _on(self): 23 | if self._camera is None: 24 | self._camera_open() 25 | if not self._camera: 26 | return 27 | self._camera.setParameters(self._f_on) 28 | 29 | def _off(self): 30 | if not self._camera: 31 | return 32 | self._camera.setParameters(self._f_off) 33 | 34 | def _release(self): 35 | if not self._camera: 36 | return 37 | self._camera.stopPreview() 38 | self._camera.release() 39 | self._camera = None 40 | 41 | def _camera_open(self): 42 | if not flash_available: 43 | return 44 | self._camera = Camera.open() 45 | self._f_on = Camera.getParameters() 46 | self._f_off = Camera.getParameters() 47 | self._f_on.setFlashMode(CameraParameters.FLASH_MODE_TORCH) 48 | self._f_off.setFlashMode(CameraParameters.FLASH_MODE_OFF) 49 | self._camera.startPreview() 50 | # Need this for Nexus 5 51 | self._camera.setPreviewTexture(SurfaceTexture(0)) 52 | 53 | 54 | def instance(): 55 | return AndroidFlash() 56 | -------------------------------------------------------------------------------- /plyer/platforms/android/irblaster.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | 3 | from plyer.facades import IrBlaster 4 | from plyer.platforms.android import activity, SDK_INT, ANDROID_VERSION 5 | 6 | if SDK_INT >= 19: 7 | Context = autoclass('android.content.Context') 8 | ir_manager = activity.getSystemService(Context.CONSUMER_IR_SERVICE) 9 | else: 10 | ir_manager = None 11 | 12 | 13 | class AndroidIrBlaster(IrBlaster): 14 | def _exists(self): 15 | if ir_manager and ir_manager.hasIrEmitter(): 16 | return True 17 | return False 18 | 19 | @property 20 | def multiply_pulse(self): 21 | '''Android 4.4.3+ uses microseconds instead of period counts 22 | ''' 23 | return not (SDK_INT == 19 24 | and int(str(ANDROID_VERSION.RELEASE).rsplit('.', 1)[-1]) 25 | < 3) 26 | 27 | def _get_frequencies(self): 28 | if not ir_manager: 29 | return None 30 | 31 | if hasattr(self, '_frequencies'): 32 | return self._frequencies 33 | 34 | ir_frequencies = ir_manager.getCarrierFrequencies() 35 | if not ir_frequencies: 36 | return [] 37 | 38 | frequencies = [] 39 | for freqrange in ir_frequencies: 40 | freq = (freqrange.getMinFrequency(), freqrange.getMaxFrequency()) 41 | frequencies.append(freq) 42 | 43 | self._frequencies = frequencies 44 | return frequencies 45 | 46 | def _transmit(self, frequency, pattern, mode): 47 | if self.multiply_pulse and mode == 'period': 48 | pattern = self.periods_to_microseconds(frequency, pattern) 49 | elif not self.multiply_pulse and mode == 'microseconds': 50 | pattern = self.microseconds_to_periods(frequency, pattern) 51 | ir_manager.transmit(frequency, pattern) 52 | 53 | 54 | def instance(): 55 | return AndroidIrBlaster() 56 | -------------------------------------------------------------------------------- /plyer/platforms/android/keystore.py: -------------------------------------------------------------------------------- 1 | from plyer.facades import Keystore 2 | from plyer.platforms.android import activity 3 | 4 | 5 | class AndroidKeystore(Keystore): 6 | 7 | def _set_key(self, servicename, key, value, **kwargs): 8 | mode = kwargs.get("mode", 0) 9 | settings = activity.getSharedPreferences(servicename, mode) 10 | editor = settings.edit() 11 | editor.putString(key, value) 12 | editor.commit() 13 | 14 | def _get_key(self, servicename, key, **kwargs): 15 | mode = kwargs.get("mode", 0) 16 | default = kwargs.get("default", "__None") 17 | settings = activity.getSharedPreferences(servicename, mode) 18 | ret = settings.getString(key, default) 19 | if ret == "__None": 20 | ret = None 21 | return ret 22 | 23 | 24 | def instance(): 25 | return AndroidKeystore() 26 | -------------------------------------------------------------------------------- /plyer/platforms/android/light.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | from jnius import cast 3 | from jnius import java_method 4 | from jnius import PythonJavaClass 5 | 6 | from plyer.facades import Light 7 | from plyer.platforms.android import activity 8 | 9 | Context = autoclass('android.content.Context') 10 | Sensor = autoclass('android.hardware.Sensor') 11 | SensorManager = autoclass('android.hardware.SensorManager') 12 | 13 | 14 | class LightSensorListener(PythonJavaClass): 15 | __javainterfaces__ = ['android/hardware/SensorEventListener'] 16 | 17 | def __init__(self): 18 | super().__init__() 19 | service = activity.getSystemService(Context.SENSOR_SERVICE) 20 | self.SensorManager = cast('android.hardware.SensorManager', service) 21 | self.sensor = self.SensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) 22 | self.value = None 23 | 24 | def enable(self): 25 | self.SensorManager.registerListener( 26 | self, self.sensor, 27 | SensorManager.SENSOR_DELAY_NORMAL 28 | ) 29 | 30 | def disable(self): 31 | self.SensorManager.unregisterListener(self, self.sensor) 32 | 33 | @java_method('(Landroid/hardware/SensorEvent;)V') 34 | def onSensorChanged(self, event): 35 | self.value = event.values[0] 36 | 37 | @java_method('(Landroid/hardware/Sensor;I)V') 38 | def onAccuracyChanged(self, sensor, accuracy): 39 | pass 40 | 41 | 42 | class AndroidLight(Light): 43 | 44 | listener = None 45 | 46 | def _get_illumination(self): 47 | if self.listener and self.listener.value: 48 | light = self.listener.value 49 | return light 50 | 51 | def _enable(self): 52 | if not self.listener: 53 | self.listener = LightSensorListener() 54 | self.listener.enable() 55 | 56 | def _disable(self): 57 | if self.listener: 58 | self.listener.disable() 59 | delattr(self, 'listener') 60 | 61 | 62 | def instance(): 63 | return AndroidLight() 64 | -------------------------------------------------------------------------------- /plyer/platforms/android/orientation.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | from plyer.platforms.android import activity 3 | from plyer.facades import Orientation 4 | 5 | ActivityInfo = autoclass('android.content.pm.ActivityInfo') 6 | 7 | 8 | class AndroidOrientation(Orientation): 9 | 10 | def _set_landscape(self, **kwargs): 11 | reverse = kwargs.get('reverse') 12 | if reverse: 13 | activity.setRequestedOrientation( 14 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) 15 | else: 16 | activity.setRequestedOrientation( 17 | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) 18 | 19 | def _set_portrait(self, **kwargs): 20 | reverse = kwargs.get('reverse') 21 | if reverse: 22 | activity.setRequestedOrientation( 23 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) 24 | else: 25 | activity.setRequestedOrientation( 26 | ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) 27 | 28 | def _set_sensor(self, **kwargs): 29 | mode = kwargs.get('mode') 30 | 31 | if mode == 'any': 32 | activity.setRequestedOrientation( 33 | ActivityInfo.SCREEN_ORIENTATION_SENSOR) 34 | elif mode == 'landscape': 35 | activity.setRequestedOrientation( 36 | ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) 37 | elif mode == 'portrait': 38 | activity.setRequestedOrientation( 39 | ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) 40 | 41 | 42 | def instance(): 43 | return AndroidOrientation() 44 | -------------------------------------------------------------------------------- /plyer/platforms/android/proximity.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | from jnius import cast 3 | from jnius import java_method 4 | from jnius import PythonJavaClass 5 | 6 | from plyer.platforms.android import activity 7 | from plyer.facades import Proximity 8 | 9 | ActivityInfo = autoclass('android.content.pm.ActivityInfo') 10 | Context = autoclass('android.content.Context') 11 | Sensor = autoclass('android.hardware.Sensor') 12 | SensorManager = autoclass('android.hardware.SensorManager') 13 | 14 | 15 | class ProximitySensorListener(PythonJavaClass): 16 | __javainterfaces__ = ['android/hardware/SensorEventListener'] 17 | 18 | def __init__(self): 19 | super().__init__() 20 | service = activity.getSystemService(Context.SENSOR_SERVICE) 21 | self.SensorManager = cast('android.hardware.SensorManager', service) 22 | 23 | self.sensor = self.SensorManager.getDefaultSensor( 24 | Sensor.TYPE_PROXIMITY) 25 | self.value = None 26 | 27 | def enable(self): 28 | self.SensorManager.registerListener( 29 | self, self.sensor, 30 | SensorManager.SENSOR_DELAY_NORMAL 31 | ) 32 | 33 | def disable(self): 34 | self.SensorManager.unregisterListener(self, self.sensor) 35 | 36 | @java_method('(Landroid/hardware/SensorEvent;)V') 37 | def onSensorChanged(self, event): 38 | self.value = event.values[0] 39 | 40 | @java_method('(Landroid/hardware/Sensor;I)V') 41 | def onAccuracyChanged(self, sensor, accuracy): 42 | pass 43 | 44 | 45 | class AndroidProximity(Proximity): 46 | 47 | listener = None 48 | 49 | def _enable(self, **kwargs): 50 | if not self.listener: 51 | self.listener = ProximitySensorListener() 52 | self.listener.enable() 53 | 54 | def _disable(self, **kwargs): 55 | if self.listener: 56 | self.listener.disable() 57 | delattr(self, 'listener') 58 | 59 | def _get_proximity(self): 60 | if self.listener: 61 | value = self.listener.value 62 | # value is 0.0 when proxime sensor is covered. In other case 63 | # value is 5.0 because in smartphone, optical proximity sensors 64 | # are used. 65 | return value < 5.0 66 | 67 | 68 | def instance(): 69 | return AndroidProximity() 70 | -------------------------------------------------------------------------------- /plyer/platforms/android/sms.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Android SMS 3 | ----------- 4 | ''' 5 | 6 | from jnius import autoclass 7 | from plyer.facades import Sms 8 | 9 | SmsManager = autoclass('android.telephony.SmsManager') 10 | 11 | 12 | class AndroidSms(Sms): 13 | 14 | def _send(self, **kwargs): 15 | sms = SmsManager.getDefault() 16 | 17 | recipient = kwargs.get('recipient') 18 | message = kwargs.get('message') 19 | 20 | if sms: 21 | sms.sendTextMessage(recipient, None, message, None, None) 22 | 23 | 24 | def instance(): 25 | return AndroidSms() 26 | -------------------------------------------------------------------------------- /plyer/platforms/android/temperature.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass 2 | from jnius import cast 3 | from jnius import java_method 4 | from jnius import PythonJavaClass 5 | 6 | from plyer.facades import Temperature 7 | from plyer.platforms.android import activity 8 | 9 | ActivityInfo = autoclass('android.content.pm.ActivityInfo') 10 | Context = autoclass('android.content.Context') 11 | Sensor = autoclass('android.hardware.Sensor') 12 | SensorManager = autoclass('android.hardware.SensorManager') 13 | 14 | 15 | class TemperatureSensorListener(PythonJavaClass): 16 | __javainterfaces__ = ['android/hardware/SensorEventListener'] 17 | 18 | def __init__(self): 19 | super().__init__() 20 | service = activity.getSystemService(Context.SENSOR_SERVICE) 21 | self.SensorManager = cast('android.hardware.SensorManager', service) 22 | 23 | self.sensor = self.SensorManager.getDefaultSensor( 24 | Sensor.TYPE_AMBIENT_TEMPERATURE) 25 | self.value = None 26 | 27 | def enable(self): 28 | self.SensorManager.registerListener( 29 | self, self.sensor, 30 | SensorManager.SENSOR_DELAY_NORMAL 31 | ) 32 | 33 | def disable(self): 34 | self.SensorManager.unregisterListener(self, self.sensor) 35 | 36 | @java_method('(Landroid/hardware/SensorEvent;)V') 37 | def onSensorChanged(self, event): 38 | self.value = event.values[0] 39 | 40 | @java_method('(Landroid/hardware/Sensor;I)V') 41 | def onAccuracyChanged(self, sensor, accuracy): 42 | pass 43 | 44 | 45 | class AndroidTemperature(Temperature): 46 | 47 | listener = None 48 | 49 | def _get_temperature(self): 50 | if self.listener and self.listener.value: 51 | temperature = self.listener.value 52 | return temperature 53 | 54 | def _enable(self): 55 | if not self.listener: 56 | self.listener = TemperatureSensorListener() 57 | self.listener.enable() 58 | 59 | def _disable(self): 60 | if self.listener: 61 | self.listener.disable() 62 | delattr(self, 'listener') 63 | 64 | 65 | def instance(): 66 | return AndroidTemperature() 67 | -------------------------------------------------------------------------------- /plyer/platforms/android/tts.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | from jnius import autoclass 3 | from plyer.facades import TTS 4 | from plyer.platforms.android import activity 5 | 6 | Locale = autoclass('java.util.Locale') 7 | TextToSpeech = autoclass('android.speech.tts.TextToSpeech') 8 | 9 | 10 | class AndroidTextToSpeech(TTS): 11 | def _speak(self, **kwargs): 12 | tts = TextToSpeech(activity, None) 13 | 14 | tts.setLanguage(Locale.US) 15 | 16 | retries = 0 # First try rarely succeeds due to some timing issue 17 | message = kwargs.get('message') 18 | 19 | # first try for while loop 20 | speak_status = tts.speak( 21 | message, TextToSpeech.QUEUE_FLUSH, None 22 | ) 23 | 24 | # -1 indicates error. Let's wait and then try again 25 | while retries < 100 and speak_status == -1: 26 | sleep(0.1) 27 | retries += 1 28 | speak_status = tts.speak( 29 | message, TextToSpeech.QUEUE_FLUSH, None 30 | ) 31 | 32 | 33 | def instance(): 34 | return AndroidTextToSpeech() 35 | -------------------------------------------------------------------------------- /plyer/platforms/android/uniqueid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Android API for plyer.uniqueid. 3 | ''' 4 | 5 | from jnius import autoclass 6 | from plyer.platforms.android import activity 7 | from plyer.facades import UniqueID 8 | 9 | Secure = autoclass('android.provider.Settings$Secure') 10 | 11 | 12 | class AndroidUniqueID(UniqueID): 13 | ''' 14 | Implementation of Android uniqueid API. 15 | ''' 16 | 17 | def _get_uid(self): 18 | return Secure.getString( 19 | activity.getContentResolver(), 20 | Secure.ANDROID_ID 21 | ) 22 | 23 | 24 | def instance(): 25 | ''' 26 | Instance for facade proxy. 27 | ''' 28 | return AndroidUniqueID() 29 | -------------------------------------------------------------------------------- /plyer/platforms/android/vibrator.py: -------------------------------------------------------------------------------- 1 | """Implementation Vibrator for Android.""" 2 | 3 | from jnius import autoclass, cast 4 | from plyer.facades import Vibrator 5 | from plyer.platforms.android import activity 6 | from plyer.platforms.android import SDK_INT 7 | 8 | Context = autoclass("android.content.Context") 9 | vibrator_service = activity.getSystemService(Context.VIBRATOR_SERVICE) 10 | vibrator = cast("android.os.Vibrator", vibrator_service) 11 | if SDK_INT >= 26: 12 | VibrationEffect = autoclass("android.os.VibrationEffect") 13 | 14 | 15 | class AndroidVibrator(Vibrator): 16 | """Android Vibrator class. 17 | 18 | Supported features: 19 | * vibrate for some period of time. 20 | * vibrate from given pattern. 21 | * cancel vibration. 22 | * check whether Vibrator exists. 23 | """ 24 | 25 | def _vibrate(self, time=None, **kwargs): 26 | if vibrator: 27 | if SDK_INT >= 26: 28 | vibrator.vibrate( 29 | VibrationEffect.createOneShot( 30 | int(1000 * time), VibrationEffect.DEFAULT_AMPLITUDE 31 | ) 32 | ) 33 | else: 34 | vibrator.vibrate(int(1000 * time)) 35 | 36 | def _pattern(self, pattern=None, repeat=None, **kwargs): 37 | pattern = [int(1000 * time) for time in pattern] 38 | 39 | if vibrator: 40 | if SDK_INT >= 26: 41 | vibrator.vibrate( 42 | VibrationEffect.createWaveform(pattern, repeat) 43 | ) 44 | else: 45 | vibrator.vibrate(pattern, repeat) 46 | 47 | def _exists(self, **kwargs): 48 | if SDK_INT >= 11: 49 | return vibrator.hasVibrator() 50 | elif vibrator_service is None: 51 | raise NotImplementedError() 52 | return True 53 | 54 | def _cancel(self, **kwargs): 55 | vibrator.cancel() 56 | 57 | 58 | def instance(): 59 | """Returns Vibrator with android features. 60 | 61 | :return: instance of class AndroidVibrator 62 | """ 63 | return AndroidVibrator() 64 | -------------------------------------------------------------------------------- /plyer/platforms/ios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/ios/accelerometer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS accelerometer 3 | ----------------- 4 | 5 | Taken from: http://pyobjus.readthedocs.org/en/latest/pyobjus_ios.html \ 6 | #accessing-accelerometer 7 | ''' 8 | 9 | from plyer.facades import Accelerometer 10 | from pyobjus import autoclass 11 | 12 | 13 | class IosAccelerometer(Accelerometer): 14 | 15 | def __init__(self): 16 | super().__init__() 17 | self.bridge = autoclass('bridge').alloc().init() 18 | self.bridge.motionManager.setAccelerometerUpdateInterval_(0.1) 19 | 20 | def _enable(self): 21 | self.bridge.startAccelerometer() 22 | 23 | def _disable(self): 24 | self.bridge.stopAccelerometer() 25 | 26 | def _get_acceleration(self): 27 | return ( 28 | self.bridge.ac_x, 29 | self.bridge.ac_y, 30 | self.bridge.ac_z) 31 | 32 | 33 | def instance(): 34 | return IosAccelerometer() 35 | -------------------------------------------------------------------------------- /plyer/platforms/ios/barometer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Barometer 3 | ------------- 4 | ''' 5 | 6 | from plyer.facades import Barometer 7 | from pyobjus import autoclass 8 | 9 | 10 | class iOSBarometer(Barometer): 11 | 12 | def __init__(self): 13 | super().__init__() 14 | self.bridge = autoclass('bridge').alloc().init() 15 | 16 | def _enable(self): 17 | self.bridge.startRelativeAltitude() 18 | 19 | def _disable(self): 20 | self.bridge.stopRelativeAltitude() 21 | 22 | def _get_pressure(self): 23 | ''' 24 | 1 kPa = 10 hPa 25 | ''' 26 | return ( 27 | self.bridge.pressure * 10) 28 | 29 | 30 | def instance(): 31 | return iOSBarometer() 32 | -------------------------------------------------------------------------------- /plyer/platforms/ios/battery.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of iOS API for plyer.battery. 3 | ''' 4 | 5 | from pyobjus import autoclass 6 | from pyobjus.dylib_manager import load_framework 7 | from plyer.facades import Battery 8 | 9 | load_framework('/System/Library/Frameworks/UIKit.framework') 10 | UIDevice = autoclass('UIDevice') 11 | 12 | 13 | class IOSBattery(Battery): 14 | ''' 15 | Implementation of iOS battery API. 16 | ''' 17 | 18 | def __init__(self): 19 | super().__init__() 20 | self.device = UIDevice.currentDevice() 21 | 22 | def _get_state(self): 23 | status = {"isCharging": None, "percentage": None} 24 | 25 | if not self.device.batteryMonitoringEnabled: 26 | self.device.setBatteryMonitoringEnabled_(True) 27 | 28 | if self.device.batteryState == 0: 29 | is_charging = None 30 | elif self.device.batteryState == 2: 31 | is_charging = True 32 | else: 33 | is_charging = False 34 | 35 | percentage = self.device.batteryLevel * 100. 36 | 37 | status['isCharging'] = is_charging 38 | status['percentage'] = percentage 39 | 40 | return status 41 | 42 | 43 | def instance(): 44 | ''' 45 | Instance for facade proxy. 46 | ''' 47 | return IOSBattery() 48 | -------------------------------------------------------------------------------- /plyer/platforms/ios/brightness.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Brightness 3 | -------------- 4 | ''' 5 | 6 | from pyobjus import autoclass 7 | from plyer.facades import Brightness 8 | from pyobjus.dylib_manager import load_framework 9 | 10 | load_framework('/System/Library/Frameworks/UIKit.framework') 11 | UIScreen = autoclass('UIScreen') 12 | 13 | 14 | class iOSBrightness(Brightness): 15 | 16 | def __init__(self): 17 | self.screen = UIScreen.mainScreen() 18 | 19 | def _current_level(self): 20 | return self.screen.brightness * 100 21 | 22 | def set_level(self, level): 23 | self.screen.brightness = level / 100 24 | 25 | 26 | def instance(): 27 | return iOSBrightness() 28 | -------------------------------------------------------------------------------- /plyer/platforms/ios/call.py: -------------------------------------------------------------------------------- 1 | ''' 2 | IOS Call 3 | ---------- 4 | ''' 5 | 6 | from plyer.facades import Call 7 | from pyobjus import autoclass, objc_str 8 | 9 | NSURL = autoclass('NSURL') 10 | NSString = autoclass('NSString') 11 | UIApplication = autoclass('UIApplication') 12 | 13 | 14 | class IOSCall(Call): 15 | 16 | def _makecall(self, **kwargs): 17 | tel = kwargs.get('tel') 18 | url = "tel://" + tel 19 | nsurl = NSURL.alloc().initWithString_(objc_str(url)) 20 | 21 | UIApplication.sharedApplication().openURL_(nsurl) 22 | 23 | def _dialcall(self, **kwargs): 24 | pass 25 | # Not possible, Access not provided by iPhone SDK 26 | 27 | 28 | def instance(): 29 | return IOSCall() 30 | -------------------------------------------------------------------------------- /plyer/platforms/ios/camera.py: -------------------------------------------------------------------------------- 1 | from os import remove 2 | from plyer.facades import Camera 3 | 4 | from plyer.utils import reify 5 | 6 | 7 | class iOSCamera(Camera): 8 | 9 | @reify 10 | def photos(self): 11 | # pyPhotoLibrary is a ios recipe/module that 12 | # interacts with the gallery and the camera on ios. 13 | from photolibrary import PhotosLibrary 14 | return PhotosLibrary() 15 | 16 | def _take_picture(self, on_complete, filename=None): 17 | assert on_complete is not None 18 | self.on_complete = on_complete 19 | self.filename = filename 20 | photos = self.photos 21 | 22 | if not photos.isCameraAvailable(): 23 | # no camera hardware 24 | return False 25 | 26 | photos.bind(on_image_captured=self.capture_callback) 27 | self._capture_filename = filename 28 | photos.capture_image(filename) 29 | return True 30 | 31 | def capture_callback(self, photolibrary): 32 | # Image was chosen 33 | 34 | # unbind 35 | self.photos.unbind(on_image_captured=self.capture_callback) 36 | 37 | if self.on_complete(self.filename): 38 | self._remove(self.filename) 39 | 40 | def _take_video(self, on_complete, filename=None): 41 | assert on_complete is not None 42 | raise NotImplementedError 43 | 44 | def _remove(self, fn): 45 | try: 46 | remove(fn) 47 | except OSError: 48 | print('Could not remove photo!') 49 | 50 | 51 | def instance(): 52 | return iOSCamera() 53 | -------------------------------------------------------------------------------- /plyer/platforms/ios/compass.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Compass 3 | ----------- 4 | ''' 5 | 6 | from plyer.facades import Compass 7 | from pyobjus import autoclass 8 | 9 | 10 | class IosCompass(Compass): 11 | 12 | def __init__(self): 13 | super().__init__() 14 | self.bridge = autoclass('bridge').alloc().init() 15 | self.bridge.motionManager.setMagnetometerUpdateInterval_(0.1) 16 | self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) 17 | 18 | def _enable(self): 19 | self.bridge.startMagnetometer() 20 | self.bridge.startDeviceMotionWithReferenceFrame() 21 | 22 | def _disable(self): 23 | self.bridge.stopMagnetometer() 24 | self.bridge.stopDeviceMotion() 25 | 26 | def _get_orientation(self): 27 | return ( 28 | self.bridge.mf_x, 29 | self.bridge.mf_y, 30 | self.bridge.mf_z) 31 | 32 | def _get_field_uncalib(self): 33 | return ( 34 | self.bridge.mg_x, 35 | self.bridge.mg_y, 36 | self.bridge.mg_z, 37 | self.bridge.mg_x - self.bridge.mf_x, 38 | self.bridge.mg_y - self.bridge.mf_y, 39 | self.bridge.mg_z - self.bridge.mf_z) 40 | 41 | 42 | def instance(): 43 | return IosCompass() 44 | -------------------------------------------------------------------------------- /plyer/platforms/ios/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of iOS API for plyer.email. 3 | ''' 4 | 5 | try: 6 | from urllib.parse import quote 7 | except ImportError: 8 | from urllib import quote 9 | 10 | from plyer.facades import Email 11 | from pyobjus import autoclass, objc_str 12 | from pyobjus.dylib_manager import load_framework 13 | 14 | load_framework('/System/Library/Frameworks/UIKit.framework') 15 | 16 | NSURL = autoclass('NSURL') 17 | NSString = autoclass('NSString') 18 | UIApplication = autoclass('UIApplication') 19 | 20 | 21 | class IOSEmail(Email): 22 | ''' 23 | Implementation of iOS battery API. 24 | ''' 25 | 26 | def _send(self, **kwargs): 27 | recipient = kwargs.get('recipient') 28 | subject = kwargs.get('subject') 29 | text = kwargs.get('text') 30 | 31 | uri = "mailto:" 32 | if recipient: 33 | uri += str(recipient) 34 | if subject: 35 | uri += "?" if "?" not in uri else "&" 36 | uri += "subject=" 37 | uri += quote(str(subject)) 38 | if text: 39 | uri += "?" if "?" not in uri else "&" 40 | uri += "body=" 41 | uri += quote(str(text)) 42 | 43 | nsurl = NSURL.alloc().initWithString_(objc_str(uri)) 44 | 45 | UIApplication.sharedApplication().openURL_(nsurl) 46 | 47 | 48 | def instance(): 49 | ''' 50 | Instance for facade proxy. 51 | ''' 52 | return IOSEmail() 53 | -------------------------------------------------------------------------------- /plyer/platforms/ios/flash.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Flash 4 | ----- 5 | """ 6 | from plyer.facades import Flash 7 | from pyobjus import autoclass 8 | 9 | NSString = autoclass("NSString") 10 | AVCaptureDevice = autoclass("AVCaptureDevice") 11 | AVMediaTypeVideo = NSString.alloc().initWithUTF8String_("vide") 12 | AVCaptureTorchModeOff = 0 13 | AVCaptureTorchModeOn = 1 14 | 15 | 16 | class IosFlash(Flash): 17 | _camera = None 18 | 19 | def _on(self): 20 | if self._camera is None: 21 | self._camera_open() 22 | if not self._camera: 23 | return 24 | self._camera.lockForConfiguration_(None) 25 | try: 26 | self._camera.setTorchMode(AVCaptureTorchModeOn) 27 | finally: 28 | self._camera.unlockForConfiguration() 29 | 30 | def _off(self): 31 | if not self._camera: 32 | return 33 | self._camera.lockForConfiguration_(None) 34 | try: 35 | self._camera.setTorchMode(AVCaptureTorchModeOff) 36 | finally: 37 | self._camera.unlockForConfiguration() 38 | 39 | def _release(self): 40 | pass 41 | 42 | def _camera_open(self): 43 | device = AVCaptureDevice.defaultDeviceWithMediaType_(AVMediaTypeVideo) 44 | if not device: 45 | return 46 | self._camera = device 47 | 48 | 49 | def instance(): 50 | return IosFlash() 51 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Headers/Voip.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface Voip : NSObject 5 | @property (nonatomic, assign) BOOL callActive; 6 | @property (nonatomic, assign) BOOL hasReceivedFirstPacket; 7 | @property (nonatomic, assign) AVAudioFormat* inputAudioFormat; 8 | @property (nonatomic, assign) AVAudioFormat* outputAudioFormat; 9 | @property (nonatomic, assign) AVAudioPlayerNode* audioPlayerNode; 10 | 11 | - (BOOL)requestMicrophonePermission; 12 | - (void)sendClientID:(NSString *)string; 13 | - (void)installTapOnBus:(AVAudioNode *)node bufferSize:(AVAudioFrameCount)bufferSize; 14 | - (void) receiveAudioData; 15 | - (void)connect:(NSString *)address port:(uint16_t)port ssl:(BOOL)ssl tlsVersion:(NSString *)tlsVersion timeout:(int)timeout; 16 | - (BOOL)connected; 17 | - (void)disconnect; 18 | @end 19 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/Voip.framework/Info.plist -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.private.swiftinterface: -------------------------------------------------------------------------------- 1 | // swift-interface-format-version: 1.0 2 | // swift-compiler-version: Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1) 3 | // swift-module-flags: -target arm64-apple-ios18.2 -enable-objc-interop -enable-library-evolution -swift-version 6 -enforce-exclusivity=checked -O -module-name Voip 4 | // swift-module-flags-ignorable: -no-verify-emitted-module-interface 5 | import AVFoundation 6 | import Foundation 7 | import Network 8 | import Swift 9 | @_exported import Voip 10 | import _Concurrency 11 | import _StringProcessing 12 | import _SwiftConcurrencyShims 13 | @_inheritsConvenienceInitializers @objc public class NetworkClient : ObjectiveC.NSObject, @unchecked Swift.Sendable { 14 | @objc public var connectionHandler: ((Foundation.Data) -> Swift.Void)? 15 | @objc public var isConnected: Swift.Bool 16 | @objc public func requestMicrophonePermission() -> Swift.Bool 17 | @objc public func connectToServer(host: Swift.String, port: Swift.UInt16, ssl: Swift.Bool, tlsVersion: Swift.String, timeout: Swift.Int) 18 | @objc public func sendMessage(data: Foundation.Data) 19 | @objc public func sendClientID(string: Swift.String) 20 | @objc public func disconnect() 21 | @objc override dynamic public init() 22 | @objc deinit 23 | } 24 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.swiftdoc -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.swiftinterface: -------------------------------------------------------------------------------- 1 | // swift-interface-format-version: 1.0 2 | // swift-compiler-version: Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1) 3 | // swift-module-flags: -target arm64-apple-ios18.2 -enable-objc-interop -enable-library-evolution -swift-version 6 -enforce-exclusivity=checked -O -module-name Voip 4 | // swift-module-flags-ignorable: -no-verify-emitted-module-interface 5 | import AVFoundation 6 | import Foundation 7 | import Network 8 | import Swift 9 | @_exported import Voip 10 | import _Concurrency 11 | import _StringProcessing 12 | import _SwiftConcurrencyShims 13 | @_inheritsConvenienceInitializers @objc public class NetworkClient : ObjectiveC.NSObject, @unchecked Swift.Sendable { 14 | @objc public var connectionHandler: ((Foundation.Data) -> Swift.Void)? 15 | @objc public var isConnected: Swift.Bool 16 | @objc public func requestMicrophonePermission() -> Swift.Bool 17 | @objc public func connectToServer(host: Swift.String, port: Swift.UInt16, ssl: Swift.Bool, tlsVersion: Swift.String, timeout: Swift.Int) 18 | @objc public func sendMessage(data: Foundation.Data) 19 | @objc public func sendClientID(string: Swift.String) 20 | @objc public func disconnect() 21 | @objc override dynamic public init() 22 | @objc deinit 23 | } 24 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios.swiftmodule -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Voip { 2 | umbrella header "Voip.h" 3 | export * 4 | 5 | module * { export * } 6 | } 7 | 8 | module Voip.Swift { 9 | header "Voip-Swift.h" 10 | requires objc 11 | } 12 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/Voip.framework/Voip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/Voip.framework/Voip -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Headers/Voip.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface Voip : NSObject 5 | @property (nonatomic, assign) BOOL callActive; 6 | @property (nonatomic, assign) BOOL hasReceivedFirstPacket; 7 | @property (nonatomic, assign) AVAudioFormat* inputAudioFormat; 8 | @property (nonatomic, assign) AVAudioFormat* outputAudioFormat; 9 | @property (nonatomic, assign) AVAudioPlayerNode* audioPlayerNode; 10 | 11 | - (BOOL)requestMicrophonePermission; 12 | - (void)sendClientID:(NSString *)string; 13 | - (void)installTapOnBus:(AVAudioNode *)node bufferSize:(AVAudioFrameCount)bufferSize; 14 | - (void) receiveAudioData; 15 | - (void)connect:(NSString *)address port:(uint16_t)port ssl:(BOOL)ssl tlsVersion:(NSString *)tlsVersion timeout:(int)timeout; 16 | - (BOOL)connected; 17 | - (void)disconnect; 18 | @end 19 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/Voip.framework/Info.plist -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/Project/arm64-apple-ios-simulator.swiftsourceinfo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/Project/arm64-apple-ios-simulator.swiftsourceinfo -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios-simulator.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABIRoot": { 3 | "kind": "Root", 4 | "name": "NO_MODULE", 5 | "printedName": "NO_MODULE", 6 | "json_format_version": 8 7 | }, 8 | "ConstValues": [] 9 | } -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios-simulator.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios-simulator.swiftdoc -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios-simulator.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/Voip.swiftmodule/arm64-apple-ios-simulator.swiftmodule -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Voip { 2 | umbrella header "Voip.h" 3 | export * 4 | 5 | module * { export * } 6 | } 7 | 8 | module Voip.Swift { 9 | header "Voip-Swift.h" 10 | requires objc 11 | } 12 | -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/Voip.framework/Voip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/Voip.framework/Voip -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/simulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/ios/frameworks/simulator/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/ios/frameworks/utils.py: -------------------------------------------------------------------------------- 1 | from plyer.platforms import ios 2 | from os.path import join, dirname 3 | from os import environ 4 | from pyobjus.dylib_manager import load_framework 5 | 6 | 7 | def load_plyer_framework(framework): 8 | framework_directory = join(dirname(ios.__file__), "frameworks") 9 | if 'SIMULATOR_DEVICE_NAME' in environ: 10 | path = join(framework_directory, "simulator", framework) 11 | else: 12 | path = join(framework_directory, framework) 13 | load_framework(path) 14 | -------------------------------------------------------------------------------- /plyer/platforms/ios/gravity.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Gravity 3 | ----------- 4 | 5 | ''' 6 | 7 | from plyer.facades import Gravity 8 | from pyobjus import autoclass 9 | 10 | 11 | class iOSGravity(Gravity): 12 | 13 | def __init__(self): 14 | self.bridge = autoclass('bridge').alloc().init() 15 | self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) 16 | 17 | def _enable(self): 18 | self.bridge.startDeviceMotion() 19 | 20 | def _disable(self): 21 | self.bridge.stopDeviceMotion() 22 | 23 | def _get_gravity(self): 24 | return ( 25 | self.bridge.g_x, 26 | self.bridge.g_y, 27 | self.bridge.g_z) 28 | 29 | 30 | def instance(): 31 | return iOSGravity() 32 | -------------------------------------------------------------------------------- /plyer/platforms/ios/gyroscope.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Gyroscope 3 | --------------------- 4 | ''' 5 | 6 | from plyer.facades import Gyroscope 7 | from pyobjus import autoclass 8 | 9 | from pyobjus.dylib_manager import load_framework 10 | 11 | load_framework('/System/Library/Frameworks/UIKit.framework') 12 | UIDevice = autoclass('UIDevice') 13 | 14 | device = UIDevice.currentDevice() 15 | 16 | 17 | class IosGyroscope(Gyroscope): 18 | 19 | def __init__(self): 20 | super().__init__() 21 | self.bridge = autoclass('bridge').alloc().init() 22 | 23 | if int(device.systemVersion.UTF8String().split('.')[0]) <= 4: 24 | self.bridge.motionManager.setGyroscopeUpdateInterval_(0.1) 25 | else: 26 | self.bridge.motionManager.setGyroUpdateInterval_(0.1) 27 | 28 | self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) 29 | 30 | def _enable(self): 31 | self.bridge.startGyroscope() 32 | self.bridge.startDeviceMotion() 33 | 34 | def _disable(self): 35 | self.bridge.stopGyroscope() 36 | self.bridge.stopDeviceMotion() 37 | 38 | def _get_orientation(self): 39 | return ( 40 | self.bridge.rotation_rate_x, 41 | self.bridge.rotation_rate_y, 42 | self.bridge.rotation_rate_z) 43 | 44 | def _get_rotation_uncalib(self): 45 | return ( 46 | self.bridge.gy_x, 47 | self.bridge.gy_y, 48 | self.bridge.gy_z, 49 | self.bridge.gy_x - self.bridge.rotation_rate_x, 50 | self.bridge.gy_y - self.bridge.rotation_rate_y, 51 | self.bridge.gy_z - self.bridge.rotation_rate_z) 52 | 53 | 54 | def instance(): 55 | return IosGyroscope() 56 | -------------------------------------------------------------------------------- /plyer/platforms/ios/keystore.py: -------------------------------------------------------------------------------- 1 | from plyer.facades import Keystore 2 | from pyobjus import autoclass, objc_str 3 | 4 | NSUserDefaults = autoclass('NSUserDefaults') 5 | 6 | 7 | class IosKeystore(Keystore): 8 | 9 | def _set_key(self, servicename, key, value, **kwargs): 10 | NSUserDefaults.standardUserDefaults().setObject_forKey_( 11 | objc_str(value), objc_str(key)) 12 | 13 | def _get_key(self, servicename, key, **kwargs): 14 | ret = NSUserDefaults.standardUserDefaults().stringForKey_( 15 | objc_str(key)) 16 | if ret is not None: 17 | return ret.UTF8String() 18 | else: 19 | return ret 20 | 21 | 22 | def instance(): 23 | return IosKeystore() 24 | -------------------------------------------------------------------------------- /plyer/platforms/ios/sms.py: -------------------------------------------------------------------------------- 1 | ''' 2 | IOS Sms 3 | ---------- 4 | ''' 5 | 6 | from plyer.facades import Sms 7 | from pyobjus import autoclass, objc_str 8 | from pyobjus.dylib_manager import load_framework 9 | 10 | NSURL = autoclass('NSURL') 11 | NSString = autoclass('NSString') 12 | UIApplication = autoclass('UIApplication') 13 | load_framework('/System/Library/Frameworks/MessageUI.framework') 14 | 15 | 16 | class IOSSms(Sms): 17 | 18 | def _send(self, **kwargs): 19 | ''' 20 | This method provides sending messages to recipients. 21 | 22 | Expects 2 parameters in kwargs: 23 | - recipient: String type 24 | - message: String type 25 | 26 | Opens a message interface with recipient and message information. 27 | ''' 28 | recipient = kwargs.get('recipient') 29 | message = kwargs.get('message') 30 | url = "sms:" 31 | if recipient: 32 | # Apple has not supported multiple recipients yet. 33 | url += str(recipient) 34 | if message: 35 | # Apple has to supported it yet. 36 | pass 37 | 38 | nsurl = NSURL.alloc().initWithString_(objc_str(url)) 39 | UIApplication.sharedApplication().openURL_(nsurl) 40 | 41 | 42 | def instance(): 43 | return IOSSms() 44 | -------------------------------------------------------------------------------- /plyer/platforms/ios/spatialorientation.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Spatial Orientation 3 | ----------------------- 4 | 5 | ''' 6 | 7 | from plyer.facades import SpatialOrientation 8 | from pyobjus import autoclass 9 | 10 | 11 | class iOSSpatialOrientation(SpatialOrientation): 12 | 13 | def __init__(self): 14 | self.bridge = autoclass('bridge').alloc().init() 15 | self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1) 16 | 17 | def _enable_listener(self): 18 | self.bridge.startDeviceMotion() 19 | 20 | def _disable_listener(self): 21 | self.bridge.stopDeviceMotion() 22 | 23 | def _get_orientation(self): 24 | return ( 25 | self.bridge.sp_yaw, 26 | self.bridge.sp_pitch, 27 | self.bridge.sp_roll) 28 | 29 | 30 | def instance(): 31 | return iOSSpatialOrientation() 32 | -------------------------------------------------------------------------------- /plyer/platforms/ios/storagepath.py: -------------------------------------------------------------------------------- 1 | ''' 2 | iOS Storage Path 3 | -------------------- 4 | ''' 5 | 6 | from plyer.facades import StoragePath 7 | from pyobjus import autoclass 8 | import os 9 | 10 | NSFileManager = autoclass('NSFileManager') 11 | 12 | # Directory constants (NSSearchPathDirectory enumeration) 13 | NSApplicationDirectory = 1 14 | NSDocumentDirectory = 9 15 | NSDownloadsDirectory = 15 16 | NSMoviesDirectory = 17 17 | NSMusicDirectory = 18 18 | NSPicturesDirectory = 19 19 | 20 | 21 | class iOSStoragePath(StoragePath): 22 | 23 | def __init__(self): 24 | self.defaultManager = NSFileManager.defaultManager() 25 | 26 | def _get_home_dir(self): 27 | return os.path.expanduser('~/') 28 | 29 | def _get_external_storage_dir(self): 30 | return 'This feature is not implemented for this platform.' 31 | 32 | def _get_root_dir(self): 33 | return 'This feature is not implemented for this platform.' 34 | 35 | def _get_documents_dir(self): 36 | return self.defaultManager.URLsForDirectory_inDomains_( 37 | NSDocumentDirectory, 1).firstObject().absoluteString.UTF8String() 38 | 39 | def _get_downloads_dir(self): 40 | return self.defaultManager.URLsForDirectory_inDomains_( 41 | NSDownloadsDirectory, 1).firstObject().absoluteString.UTF8String() 42 | 43 | def _get_videos_dir(self): 44 | return self.defaultManager.URLsForDirectory_inDomains_( 45 | NSMoviesDirectory, 1).firstObject().absoluteString.UTF8String() 46 | 47 | def _get_music_dir(self): 48 | return self.defaultManager.URLsForDirectory_inDomains_( 49 | NSMusicDirectory, 1).firstObject().absoluteString.UTF8String() 50 | 51 | def _get_pictures_dir(self): 52 | return self.defaultManager.URLsForDirectory_inDomains_( 53 | NSPicturesDirectory, 1).firstObject().absoluteString.UTF8String() 54 | 55 | def _get_application_dir(self): 56 | return self.defaultManager.URLsForDirectory_inDomains_( 57 | NSApplicationDirectory, 1).firstObject().absoluteString.\ 58 | UTF8String() 59 | 60 | 61 | def instance(): 62 | return iOSStoragePath() 63 | -------------------------------------------------------------------------------- /plyer/platforms/ios/tts.py: -------------------------------------------------------------------------------- 1 | from pyobjus import autoclass, objc_str 2 | from pyobjus.dylib_manager import load_framework 3 | 4 | from plyer.facades import TTS 5 | 6 | load_framework('/System/Library/Frameworks/AVFoundation.framework') 7 | AVSpeechUtterance = autoclass('AVSpeechUtterance') 8 | AVSpeechSynthesizer = autoclass('AVSpeechSynthesizer') 9 | AVSpeechSynthesisVoice = autoclass('AVSpeechSynthesisVoice') 10 | 11 | 12 | class iOSTextToSpeech(TTS): 13 | def __init__(self): 14 | super().__init__() 15 | self.synth = AVSpeechSynthesizer.alloc().init() 16 | self.voice = None 17 | 18 | def _set_locale(self, locale="en-US"): 19 | self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_( 20 | objc_str(locale) 21 | ) 22 | 23 | def _speak(self, **kwargs): 24 | message = kwargs.get('message') 25 | 26 | if not self.voice: 27 | self._set_locale() 28 | 29 | utterance = \ 30 | AVSpeechUtterance.speechUtteranceWithString_(objc_str(message)) 31 | 32 | utterance.voice = self.voice 33 | self.synth.speakUtterance_(utterance) 34 | 35 | 36 | def instance(): 37 | return iOSTextToSpeech() 38 | -------------------------------------------------------------------------------- /plyer/platforms/ios/uniqueid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of iOS API for plyer.uniqueid. 3 | ''' 4 | 5 | from pyobjus import autoclass 6 | from pyobjus.dylib_manager import load_framework 7 | from plyer.facades import UniqueID 8 | 9 | load_framework('/System/Library/Frameworks/UIKit.framework') 10 | UIDevice = autoclass('UIDevice') 11 | 12 | 13 | class IOSUniqueID(UniqueID): 14 | ''' 15 | Implementation of iOS uniqueid API. 16 | ''' 17 | 18 | def _get_uid(self): 19 | uuid = UIDevice.currentDevice().identifierForVendor.UUIDString() 20 | return uuid.UTF8String() 21 | 22 | 23 | def instance(): 24 | ''' 25 | Instance for facade proxy. 26 | ''' 27 | return IOSUniqueID() 28 | -------------------------------------------------------------------------------- /plyer/platforms/ios/vibrator.py: -------------------------------------------------------------------------------- 1 | '''Implementation Vibrator for iOS. 2 | 3 | Install: Add AudioToolbox framework to your application. 4 | ''' 5 | 6 | import ctypes 7 | from plyer.facades import Vibrator 8 | 9 | 10 | class IosVibrator(Vibrator): 11 | '''iOS Vibrator class. 12 | 13 | iOS doesn't support any feature. 14 | All time, pattern, repetition are ignored. 15 | ''' 16 | 17 | def __init__(self): 18 | super().__init__() 19 | try: 20 | self._func = ctypes.CDLL(None).AudioServicesPlaySystemSound 21 | except AttributeError: 22 | self._func = None 23 | 24 | def _vibrate(self, time=None, **kwargs): 25 | # kSystemSoundID_Vibrate is 0x00000FFF 26 | self._func(0xFFF) 27 | 28 | def _pattern(self, pattern=None, repeat=None, **kwargs): 29 | self._vibrate() 30 | 31 | def _exists(self, **kwargs): 32 | return self._func is not None 33 | 34 | def _cancel(self, **kwargs): 35 | pass 36 | 37 | 38 | def instance(): 39 | '''Returns Vibrator 40 | 41 | :return: instance of class IosVibrator 42 | ''' 43 | return IosVibrator() 44 | -------------------------------------------------------------------------------- /plyer/platforms/linux/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/linux/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/linux/accelerometer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Linux accelerometer 3 | --------------------- 4 | ''' 5 | 6 | from plyer.facades import Accelerometer 7 | import glob 8 | import re 9 | 10 | 11 | class LinuxAccelerometer(Accelerometer): 12 | 13 | def _enable(self): 14 | pass 15 | 16 | def _disable(self): 17 | pass 18 | 19 | def _get_acceleration(self): 20 | try: 21 | pos = glob.glob("/sys/devices/platform/*/position")[0] 22 | except IndexError: 23 | raise Exception('Could not enable accelerometer!') 24 | 25 | with open(pos, "r") as p: 26 | t = p.read() 27 | coords = re.findall(r"[-]?\d+\.?\d*", t) 28 | # Apparently the acceleration on sysfs goes from -1000 to 1000. 29 | # I divide it by 100 to make it equivalent to Android. 30 | # The negative is because the coordinates are inverted on Linux 31 | return [float(i) / -100 for i in coords] 32 | 33 | 34 | def instance(): 35 | return LinuxAccelerometer() 36 | -------------------------------------------------------------------------------- /plyer/platforms/linux/brightness.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Linux Brightness 3 | ---------------- 4 | 5 | ''' 6 | 7 | from plyer.facades import Brightness 8 | import subprocess 9 | import os 10 | 11 | 12 | class LinuxBrightness(Brightness): 13 | 14 | def __init__(self): 15 | if os.system("which xbacklight"): 16 | msg = ("It looks like 'xbacklight' is not installed. Try " 17 | "installing it with your distribution's package manager.") 18 | raise Exception(msg) 19 | 20 | def _current_level(self): 21 | cr_level = subprocess.check_output(["xbacklight", "-get"]) 22 | return str(cr_level) 23 | 24 | def _set_level(self, level): 25 | subprocess.call(["xbacklight", "-set", str(level)]) 26 | 27 | 28 | def instance(): 29 | return LinuxBrightness() 30 | -------------------------------------------------------------------------------- /plyer/platforms/linux/devicename.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Linux API for plyer.devicename. 3 | ''' 4 | 5 | import socket 6 | from plyer.facades import DeviceName 7 | 8 | 9 | class LinuxDeviceName(DeviceName): 10 | ''' 11 | Implementation of Linux DeviceName API. 12 | ''' 13 | 14 | def _get_device_name(self): 15 | hostname = socket.gethostname() 16 | return hostname 17 | 18 | 19 | def instance(): 20 | ''' 21 | Instance for facade proxy. 22 | ''' 23 | return LinuxDeviceName() 24 | -------------------------------------------------------------------------------- /plyer/platforms/linux/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Linux API for plyer.email. 3 | ''' 4 | 5 | import subprocess 6 | try: 7 | from urllib.parse import quote 8 | except ImportError: 9 | from urllib import quote 10 | from plyer.facades import Email 11 | from plyer.utils import whereis_exe 12 | 13 | 14 | class LinuxEmail(Email): 15 | ''' 16 | Implementation of Linux email API. 17 | ''' 18 | 19 | def _send(self, **kwargs): 20 | recipient = kwargs.get('recipient') 21 | subject = kwargs.get('subject') 22 | text = kwargs.get('text') 23 | 24 | uri = "mailto:" 25 | if recipient: 26 | uri += str(recipient) 27 | if subject: 28 | uri += "?" if "?" not in uri else "&" 29 | uri += "subject=" 30 | uri += quote(str(subject)) 31 | if text: 32 | uri += "?" if "?" not in uri else "&" 33 | uri += "body=" 34 | uri += quote(str(text)) 35 | 36 | subprocess.Popen(["xdg-open", uri]) 37 | 38 | 39 | def instance(): 40 | ''' 41 | Instance for facade proxy. 42 | ''' 43 | import sys 44 | if whereis_exe('xdg-open'): 45 | return LinuxEmail() 46 | sys.stderr.write("xdg-open not found.") 47 | return Email() 48 | -------------------------------------------------------------------------------- /plyer/platforms/linux/keystore.py: -------------------------------------------------------------------------------- 1 | try: 2 | import keyring 3 | except ImportError: 4 | raise NotImplementedError() 5 | 6 | from plyer.facades import Keystore 7 | 8 | 9 | class LinuxKeystore(Keystore): 10 | 11 | def _set_key(self, servicename, key, value, **kwargs): 12 | keyring.set_password(servicename, key, value) 13 | 14 | def _get_key(self, servicename, key, **kwargs): 15 | return keyring.get_password(servicename, key) 16 | 17 | 18 | def instance(): 19 | return LinuxKeystore() 20 | -------------------------------------------------------------------------------- /plyer/platforms/linux/orientation.py: -------------------------------------------------------------------------------- 1 | import subprocess as sb 2 | from plyer.facades import Orientation 3 | 4 | 5 | class LinuxOrientation(Orientation): 6 | 7 | def _set_landscape(self, **kwargs): 8 | self.rotate = 'normal' 9 | self.screen = sb.check_output( 10 | "xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1", 11 | shell=True 12 | ) 13 | self.screen = self.screen.decode('utf-8').split('\n')[0] 14 | sb.call(["xrandr", "--output", self.screen, "--rotate", self.rotate]) 15 | 16 | def _set_portrait(self, **kwargs): 17 | self.rotate = 'left' 18 | self.screen = sb.check_output( 19 | "xrandr -q | grep ' connected' | head -n 1 | cut -d ' ' -f1", 20 | shell=True 21 | ) 22 | self.screen = self.screen.decode('utf-8').split('\n')[0] 23 | sb.call(["xrandr", "--output", self.screen, "--rotate", self.rotate]) 24 | 25 | 26 | def instance(): 27 | return LinuxOrientation() 28 | -------------------------------------------------------------------------------- /plyer/platforms/linux/processors.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from plyer.facades import Processors 3 | from plyer.utils import whereis_exe 4 | 5 | from os import environ 6 | 7 | 8 | class LinuxProcessors(Processors): 9 | def _get_state(self): 10 | old_lang = environ.get('LANG') 11 | environ['LANG'] = 'C' 12 | 13 | status = {"Number_of_Processors": None} 14 | 15 | dev = "--all" 16 | nproc_process = Popen( 17 | ["nproc", dev], 18 | stdout=PIPE 19 | ) 20 | output = nproc_process.communicate()[0] 21 | 22 | environ['LANG'] = old_lang 23 | 24 | if not output: 25 | return status 26 | 27 | status['Number_of_Processors'] = output.rstrip() 28 | 29 | return status 30 | 31 | 32 | def instance(): 33 | import sys 34 | if whereis_exe('nproc'): 35 | return LinuxProcessors() 36 | sys.stderr.write("nproc not found.") 37 | return Processors() 38 | -------------------------------------------------------------------------------- /plyer/platforms/linux/screenshot.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from os.path import join 3 | from plyer.facades import Screenshot 4 | from plyer.utils import whereis_exe 5 | from plyer.platforms.linux.storagepath import LinuxStoragePath 6 | 7 | 8 | class LinuxScreenshot(Screenshot): 9 | def __init__(self, file_path=None): 10 | default_path = join( 11 | LinuxStoragePath().get_pictures_dir(), 12 | 'screenshot.xwd' 13 | ) 14 | super().__init__(file_path or default_path) 15 | 16 | def _capture(self): 17 | # call xwd and redirect bytes from stdout to file 18 | with open(self.file_path, 'wb') as fle: 19 | subprocess.call([ 20 | # quiet, full screen root window 21 | 'xwd', '-silent', '-root', 22 | ], stdout=fle) 23 | 24 | 25 | def instance(): 26 | if whereis_exe('xwd'): 27 | return LinuxScreenshot() 28 | else: 29 | return Screenshot() 30 | -------------------------------------------------------------------------------- /plyer/platforms/linux/tts.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from plyer.facades import TTS 3 | from plyer.utils import whereis_exe 4 | 5 | 6 | class EspeakTextToSpeech(TTS): 7 | ''' Speaks using the espeak program 8 | ''' 9 | def _speak(self, **kwargs): 10 | subprocess.call(["espeak", kwargs.get('message')]) 11 | 12 | 13 | class FliteTextToSpeech(TTS): 14 | ''' Speaks using the flite program 15 | ''' 16 | def _speak(self, **kwargs): 17 | subprocess.call(["flite", "-t", kwargs.get('message'), "play"]) 18 | 19 | 20 | def instance(): 21 | if whereis_exe('espeak'): 22 | return EspeakTextToSpeech() 23 | elif whereis_exe('flite'): 24 | return FliteTextToSpeech() 25 | return TTS() 26 | -------------------------------------------------------------------------------- /plyer/platforms/linux/uniqueid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Linux API for plyer.uniqueid. 3 | ''' 4 | 5 | from os import environ 6 | from subprocess import Popen, PIPE 7 | from plyer.facades import UniqueID 8 | from plyer.utils import whereis_exe 9 | 10 | 11 | class LinuxUniqueID(UniqueID): 12 | ''' 13 | Implementation of Linux uniqueid API. 14 | ''' 15 | 16 | def _get_uid(self): 17 | old_lang = environ.get('LANG') 18 | environ['LANG'] = 'C' 19 | stdout = Popen( 20 | ["lshw", "-quiet"], 21 | stdout=PIPE, stderr=PIPE 22 | ).communicate()[0].decode('utf-8') 23 | 24 | output = u'' 25 | for line in stdout.splitlines(): 26 | if 'serial:' not in line: 27 | continue 28 | output = line 29 | break 30 | 31 | environ['LANG'] = old_lang or u'' 32 | result = None 33 | 34 | if output: 35 | result = output.split()[1] 36 | return result 37 | 38 | 39 | def instance(): 40 | ''' 41 | Instance for facade proxy. 42 | ''' 43 | import sys 44 | if whereis_exe('lshw'): 45 | return LinuxUniqueID() 46 | sys.stderr.write("lshw not found.") 47 | return UniqueID() 48 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/macosx/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/macosx/accelerometer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | MacOSX accelerometer 3 | --------------------- 4 | ''' 5 | 6 | from plyer.facades import Accelerometer 7 | from plyer.platforms.macosx.libs import osx_motion_sensor 8 | 9 | 10 | class OSXAccelerometer(Accelerometer): 11 | def _enable(self): 12 | try: 13 | osx_motion_sensor.get_coord() 14 | except Exception: 15 | raise Exception('Could not enable motion sensor on this macbook!') 16 | 17 | def _disable(self): 18 | pass 19 | 20 | def _get_acceleration(self): 21 | return osx_motion_sensor.get_coord() 22 | 23 | 24 | def instance(): 25 | return OSXAccelerometer() 26 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/battery.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.battery. 3 | ''' 4 | 5 | from os import environ 6 | from subprocess import Popen, PIPE 7 | from plyer.facades import Battery 8 | from plyer.utils import whereis_exe 9 | 10 | 11 | class OSXBattery(Battery): 12 | ''' 13 | Implementation of MacOS battery API. 14 | ''' 15 | 16 | def _get_state(self): 17 | old_lang = environ.get('LANG', '') 18 | environ['LANG'] = 'C' 19 | 20 | status = {"isCharging": None, "percentage": None} 21 | 22 | ioreg_process = Popen( 23 | ["ioreg", "-rc", "AppleSmartBattery"], 24 | stdout=PIPE 25 | ) 26 | output = ioreg_process.communicate()[0] 27 | 28 | environ['LANG'] = old_lang 29 | 30 | if not output: 31 | return status 32 | 33 | is_charging = max_capacity = current_capacity = None 34 | for line in output.decode('utf-8').splitlines(): 35 | if 'IsCharging' in line: 36 | is_charging = line.rpartition('=')[-1].strip() 37 | if 'MaxCapacity' in line: 38 | max_capacity = float(line.rpartition('=')[-1].strip()) 39 | if 'CurrentCapacity' in line: 40 | current_capacity = float(line.rpartition('=')[-1].strip()) 41 | 42 | if is_charging: 43 | status['isCharging'] = is_charging == "Yes" 44 | 45 | if current_capacity and max_capacity: 46 | status['percentage'] = 100.0 * current_capacity / max_capacity 47 | 48 | return status 49 | 50 | 51 | def instance(): 52 | ''' 53 | Instance for facade proxy. 54 | ''' 55 | import sys 56 | if whereis_exe('ioreg'): 57 | return OSXBattery() 58 | sys.stderr.write("ioreg not found.") 59 | return Battery() 60 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/bluetooth.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.bluetooth. 3 | ''' 4 | 5 | from subprocess import Popen, PIPE 6 | from plyer.facades import Bluetooth 7 | from plyer.utils import whereis_exe 8 | 9 | from os import environ 10 | 11 | 12 | class OSXBluetooth(Bluetooth): 13 | ''' 14 | Implementation of MacOS bluetooth API. 15 | ''' 16 | 17 | def _get_info(self): 18 | old_lang = environ.get('LANG') 19 | environ['LANG'] = 'C' 20 | 21 | sys_profiler_process = Popen( 22 | ["system_profiler", "SPBluetoothDataType"], 23 | stdout=PIPE 24 | ) 25 | 26 | stdout = sys_profiler_process.communicate()[0].decode('utf-8') 27 | output = stdout.splitlines() 28 | 29 | lines = [] 30 | for line in output: 31 | if 'Bluetooth Power' not in line: 32 | continue 33 | lines.append(line) 34 | 35 | if old_lang is None: 36 | environ.pop('LANG') 37 | else: 38 | environ['LANG'] = old_lang 39 | 40 | if output and len(lines) == 1: 41 | return lines[0].split()[2] 42 | else: 43 | return None 44 | 45 | 46 | def instance(): 47 | ''' 48 | Instance for facade proxy. 49 | ''' 50 | import sys 51 | if whereis_exe('system_profiler'): 52 | return OSXBluetooth() 53 | sys.stderr.write("system_profiler not found.") 54 | return Bluetooth() 55 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/cpu.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.cpu. 3 | ''' 4 | 5 | from subprocess import Popen, PIPE 6 | from plyer.facades import CPU 7 | from plyer.utils import whereis_exe 8 | 9 | 10 | class OSXCPU(CPU): 11 | ''' 12 | Implementation of MacOS CPU API. 13 | ''' 14 | 15 | @staticmethod 16 | def _sockets(): 17 | return 18 | 19 | def _physical(self): 20 | # cores 21 | physical = None 22 | 23 | _physical = Popen( 24 | ['sysctl', '-n', 'hw.physicalcpu_max'], 25 | stdout=PIPE 26 | ) 27 | output = _physical.communicate()[0].decode('utf-8').strip() 28 | if output: 29 | physical = int(output) 30 | return physical 31 | 32 | def _logical(self): 33 | # cores * threads 34 | logical = None 35 | 36 | _logical = Popen( 37 | ['sysctl', '-n', 'hw.logicalcpu_max'], 38 | stdout=PIPE 39 | ) 40 | output = _logical.communicate()[0].decode('utf-8').strip() 41 | if output: 42 | logical = int(output) 43 | return logical 44 | 45 | @staticmethod 46 | def _cache(): 47 | return 48 | 49 | @staticmethod 50 | def _numa(): 51 | return 52 | 53 | 54 | def instance(): 55 | ''' 56 | Instance for facade proxy. 57 | ''' 58 | import sys 59 | if whereis_exe('sysctl'): 60 | return OSXCPU() 61 | sys.stderr.write('sysctl not found.') 62 | return CPU() 63 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/devicename.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOSX API for plyer.devicename. 3 | ''' 4 | 5 | import socket 6 | from plyer.facades import DeviceName 7 | 8 | 9 | class OSXDeviceName(DeviceName): 10 | ''' 11 | Implementation of MacOSX DeviceName API. 12 | ''' 13 | 14 | def _get_device_name(self): 15 | hostname = socket.gethostname() 16 | return hostname 17 | 18 | 19 | def instance(): 20 | ''' 21 | Instance for facade proxy. 22 | ''' 23 | return OSXDeviceName() 24 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.email. 3 | ''' 4 | 5 | import subprocess 6 | 7 | try: 8 | from urllib.parse import quote 9 | except ImportError: 10 | from urllib import quote 11 | 12 | from plyer.facades import Email 13 | from plyer.utils import whereis_exe 14 | 15 | 16 | class MacOSXEmail(Email): 17 | ''' 18 | Implementation of MacOS email API. 19 | ''' 20 | 21 | def _send(self, **kwargs): 22 | recipient = kwargs.get('recipient') 23 | subject = kwargs.get('subject') 24 | text = kwargs.get('text') 25 | 26 | uri = "mailto:" 27 | if recipient: 28 | uri += str(recipient) 29 | if subject: 30 | uri += "?" if "?" not in uri else "&" 31 | uri += "subject=" 32 | uri += quote(str(subject)) 33 | if text: 34 | uri += "?" if "?" not in uri else "&" 35 | uri += "body=" 36 | uri += quote(str(text)) 37 | 38 | subprocess.Popen(["open", uri]) 39 | 40 | 41 | def instance(): 42 | ''' 43 | Instance for facade proxy. 44 | ''' 45 | import sys 46 | if whereis_exe('open'): 47 | return MacOSXEmail() 48 | sys.stderr.write("open not found.") 49 | return Email() 50 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/keystore.py: -------------------------------------------------------------------------------- 1 | try: 2 | import keyring 3 | except ImportError: 4 | raise NotImplementedError() 5 | 6 | from plyer.facades import Keystore 7 | 8 | 9 | class OSXKeystore(Keystore): 10 | 11 | def _set_key(self, servicename, key, value, **kwargs): 12 | keyring.set_password(servicename, key, value) 13 | 14 | def _get_key(self, servicename, key, **kwargs): 15 | return keyring.get_password(servicename, key) 16 | 17 | 18 | def instance(): 19 | return OSXKeystore() 20 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/macosx/libs/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/macosx/libs/osx_paths.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import os 3 | 4 | 5 | def NSIterateSearchPaths(directory): 6 | LibraryPath = ("/System/Library/Frameworks/CoreFoundation.framework/" 7 | "Versions/A/CoreFoundation") 8 | CoreFound = ctypes.cdll.LoadLibrary(LibraryPath) 9 | NSStartSearchPathEnumeration = CoreFound.NSStartSearchPathEnumeration 10 | NSGetNextSearchPathEnumeration = CoreFound.NSGetNextSearchPathEnumeration 11 | PATH_MAX = os.pathconf('/', os.pathconf_names['PC_PATH_MAX']) 12 | PATH_ENCODING = 'utf8' 13 | path_buffer = ctypes.create_string_buffer(PATH_MAX) 14 | # paths = [] <- fixme, possible list of paths in directory 15 | state = NSStartSearchPathEnumeration(directory, 1) 16 | while True: 17 | state = NSGetNextSearchPathEnumeration(state, path_buffer) 18 | if state == 0: 19 | break 20 | path = os.path.expanduser(path_buffer.value.decode(PATH_ENCODING)) 21 | return path 22 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/notification.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.notification. 3 | ''' 4 | 5 | from plyer.facades import Notification 6 | 7 | from pyobjus import ( 8 | autoclass, protocol, objc_str, ObjcBOOL 9 | ) 10 | from pyobjus.dylib_manager import ( 11 | load_framework, INCLUDE 12 | ) 13 | 14 | load_framework(INCLUDE.AppKit) 15 | load_framework(INCLUDE.Foundation) 16 | 17 | NSUserNotification = autoclass('NSUserNotification') 18 | NSUserNotificationCenter = autoclass('NSUserNotificationCenter') 19 | 20 | 21 | class OSXNotification(Notification): 22 | ''' 23 | Implementation of MacOS notification API. 24 | ''' 25 | 26 | def _notify(self, **kwargs): 27 | title = kwargs.get('title', '') 28 | message = kwargs.get('message', '') 29 | app_name = kwargs.get('app_name', '') 30 | # app_icon, timeout, ticker are not supported (yet) 31 | 32 | notification = NSUserNotification.alloc().init() 33 | notification.setTitle_(objc_str(title)) 34 | notification.setSubtitle_(objc_str(app_name)) 35 | notification.setInformativeText_(objc_str(message)) 36 | 37 | usrnotifctr = NSUserNotificationCenter.defaultUserNotificationCenter() 38 | usrnotifctr.setDelegate_(self) 39 | usrnotifctr.deliverNotification_(notification) 40 | 41 | @protocol('NSUserNotificationCenterDelegate') 42 | def userNotificationCenter_shouldPresentNotification_( 43 | self, center, notification): 44 | return ObjcBOOL(True) 45 | 46 | 47 | def instance(): 48 | ''' 49 | Instance for facade proxy. 50 | ''' 51 | return OSXNotification() 52 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/screenshot.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from os.path import join 3 | from plyer.facades import Screenshot 4 | from plyer.utils import whereis_exe 5 | from plyer.platforms.macosx.storagepath import OSXStoragePath 6 | 7 | 8 | class OSXScreenshot(Screenshot): 9 | def __init__(self, file_path=None): 10 | default_path = join( 11 | OSXStoragePath().get_pictures_dir().replace('file://', ''), 12 | 'screenshot.png' 13 | ) 14 | super().__init__(file_path or default_path) 15 | 16 | def _capture(self): 17 | subprocess.call([ 18 | 'screencapture', 19 | self.file_path 20 | ]) 21 | 22 | 23 | def instance(): 24 | if whereis_exe('screencapture'): 25 | return OSXScreenshot() 26 | else: 27 | return Screenshot() 28 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/sms.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from plyer.facades import Sms as SMS 3 | from plyer.utils import whereis_exe 4 | 5 | 6 | class MacOSSMS(SMS): 7 | ''' 8 | Implementation of macOS' Messages API 9 | ''' 10 | 11 | def _send(self, **kwargs): 12 | ''' 13 | Will send `message` to `recipient` via Messages app 14 | 15 | By default, if `mode` is not explicitly set, `iMessage` is used. 16 | In order to use `SMS` mode, a valid carrier-activated device must 17 | be connected and configured. 18 | ''' 19 | 20 | recipient = kwargs.get('recipient') 21 | message = kwargs.get('message') 22 | mode = kwargs.get('mode') # Supported modes: iMessage (default), SMS 23 | if not mode: 24 | mode = 'iMessage' 25 | 26 | APPLESCRIPT = f"""tell application "Messages" 27 | set targetService to 1st account whose service type = {mode} 28 | set targetBuddy to participant "{recipient}" of targetService 29 | send "{message}" to targetBuddy 30 | end tell""" 31 | 32 | osascript_process = Popen( 33 | ['osascript', '-e', APPLESCRIPT], stdout=PIPE, stderr=PIPE) 34 | stdout, stderr = osascript_process.communicate() 35 | 36 | 37 | def instance(): 38 | import sys 39 | if whereis_exe('osascript'): 40 | return MacOSSMS() 41 | sys.stderr.write('osascript not found.') 42 | return SMS() 43 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/tts.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from plyer.facades import TTS 3 | from plyer.utils import whereis_exe 4 | 5 | 6 | class NativeSayTextToSpeech(TTS): 7 | '''Speaks using the native OSX 'say' command 8 | ''' 9 | def _speak(self, **kwargs): 10 | subprocess.call(["say", kwargs.get('message')]) 11 | 12 | 13 | class EspeakTextToSpeech(TTS): 14 | '''Speaks using the espeak program 15 | ''' 16 | def _speak(self, **kwargs): 17 | subprocess.call(["espeak", kwargs.get('message')]) 18 | 19 | 20 | def instance(): 21 | if whereis_exe('say'): 22 | return NativeSayTextToSpeech() 23 | elif whereis_exe('espeak'): 24 | return EspeakTextToSpeech() 25 | return TTS() 26 | -------------------------------------------------------------------------------- /plyer/platforms/macosx/uniqueid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of MacOS API for plyer.uniqueid. 3 | ''' 4 | 5 | from os import environ 6 | from subprocess import Popen, PIPE 7 | from plyer.facades import UniqueID 8 | from plyer.utils import whereis_exe 9 | 10 | 11 | class OSXUniqueID(UniqueID): 12 | ''' 13 | Implementation of MacOS uniqueid API. 14 | ''' 15 | 16 | def _get_uid(self): 17 | old_lang = environ.get('LANG') 18 | environ['LANG'] = 'C' 19 | 20 | ioreg_process = Popen(["ioreg", "-l"], stdout=PIPE) 21 | grep_process = Popen( 22 | ["grep", "IOPlatformSerialNumber"], 23 | stdin=ioreg_process.stdout, stdout=PIPE 24 | ) 25 | ioreg_process.stdout.close() 26 | output = grep_process.communicate()[0] 27 | 28 | if old_lang is None: 29 | environ.pop('LANG') 30 | else: 31 | environ['LANG'] = old_lang 32 | 33 | result = None 34 | if output: 35 | result = output.split()[3][1:-1] 36 | return result 37 | 38 | 39 | def instance(): 40 | ''' 41 | Instance for facade proxy. 42 | ''' 43 | import sys 44 | if whereis_exe('ioreg'): 45 | return OSXUniqueID() 46 | sys.stderr.write("ioreg not found.") 47 | return UniqueID() 48 | -------------------------------------------------------------------------------- /plyer/platforms/win/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/win/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/win/battery.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Windows API for plyer.battery. 3 | ''' 4 | 5 | from plyer.platforms.win.libs.batterystatus import battery_status 6 | from plyer.facades import Battery 7 | from ctypes.wintypes import BYTE 8 | 9 | 10 | class WinBattery(Battery): 11 | ''' 12 | Implementation of Windows battery API. 13 | ''' 14 | 15 | def _get_state(self): 16 | CHARGING = BYTE(8).value 17 | UNKNOWN_STATUS = BYTE(255).value 18 | status = {"isCharging": None, "percentage": None} 19 | 20 | query = battery_status() 21 | 22 | if not query: 23 | return status 24 | 25 | status["isCharging"] = (query["BatteryFlag"] != UNKNOWN_STATUS) and \ 26 | (query["BatteryFlag"] & CHARGING > 0) 27 | status["percentage"] = query["BatteryLifePercent"] 28 | 29 | return status 30 | 31 | 32 | def instance(): 33 | ''' 34 | Instance for facade proxy. 35 | ''' 36 | return WinBattery() 37 | -------------------------------------------------------------------------------- /plyer/platforms/win/devicename.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Win API for plyer.devicename. 3 | ''' 4 | 5 | import socket 6 | from plyer.facades import DeviceName 7 | 8 | 9 | class WinDeviceName(DeviceName): 10 | ''' 11 | Implementation of Linux DeviceName API. 12 | ''' 13 | 14 | def _get_device_name(self): 15 | hostname = socket.gethostname() 16 | return hostname 17 | 18 | 19 | def instance(): 20 | ''' 21 | Instance for facade proxy. 22 | ''' 23 | return WinDeviceName() 24 | -------------------------------------------------------------------------------- /plyer/platforms/win/email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Windows API for plyer.email. 3 | ''' 4 | 5 | import os 6 | try: 7 | from urllib.parse import quote 8 | except ImportError: 9 | from urllib import quote 10 | from plyer.facades import Email 11 | 12 | 13 | class WindowsEmail(Email): 14 | ''' 15 | Implementation of Windows email API. 16 | ''' 17 | 18 | def _send(self, **kwargs): 19 | recipient = kwargs.get('recipient') 20 | subject = kwargs.get('subject') 21 | text = kwargs.get('text') 22 | 23 | uri = "mailto:" 24 | if recipient: 25 | uri += str(recipient) 26 | if subject: 27 | uri += "?" if "?" not in uri else "&" 28 | uri += "subject=" 29 | uri += quote(str(subject)) 30 | if text: 31 | uri += "?" if "?" not in uri else "&" 32 | uri += "body=" 33 | uri += quote(str(text)) 34 | 35 | # WE + startfile are available only on Windows 36 | try: 37 | os.startfile(uri) 38 | except WindowsError: 39 | print("Warning: unable to find a program able to send emails.") 40 | 41 | 42 | def instance(): 43 | ''' 44 | Instance for facade proxy. 45 | ''' 46 | return WindowsEmail() 47 | -------------------------------------------------------------------------------- /plyer/platforms/win/keystore.py: -------------------------------------------------------------------------------- 1 | try: 2 | import keyring 3 | except Exception: 4 | raise NotImplementedError() 5 | 6 | from plyer.facades import Keystore 7 | 8 | 9 | class WinKeystore(Keystore): 10 | 11 | def _set_key(self, servicename, key, value, **kwargs): 12 | keyring.set_password(servicename, key, value) 13 | 14 | def _get_key(self, servicename, key, **kwargs): 15 | return keyring.get_password(servicename, key) 16 | 17 | 18 | def instance(): 19 | return WinKeystore() 20 | -------------------------------------------------------------------------------- /plyer/platforms/win/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/platforms/win/libs/__init__.py -------------------------------------------------------------------------------- /plyer/platforms/win/libs/batterystatus.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Windows API helper for plyer.battery. 3 | ''' 4 | 5 | __all__ = ('battery_status') 6 | 7 | 8 | import ctypes 9 | from plyer.platforms.win.libs import win_api_defs 10 | 11 | 12 | def battery_status(): 13 | ''' 14 | Implementation of Windows system power status API for plyer.battery. 15 | ''' 16 | 17 | status = win_api_defs.SYSTEM_POWER_STATUS() 18 | if not win_api_defs.GetSystemPowerStatus(ctypes.pointer(status)): 19 | raise Exception('Could not get system power status.') 20 | 21 | return dict( 22 | (field, getattr(status, field)) 23 | for field, _ in status._fields_ 24 | ) 25 | -------------------------------------------------------------------------------- /plyer/platforms/win/notification.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Windows API for plyer.notification. 3 | ''' 4 | 5 | from threading import Thread as thread 6 | 7 | from plyer.facades import Notification 8 | from plyer.platforms.win.libs.balloontip import balloon_tip 9 | 10 | 11 | class WindowsNotification(Notification): 12 | ''' 13 | Implementation of Windows notification/balloon API. 14 | ''' 15 | 16 | def _notify(self, **kwargs): 17 | thread(target=balloon_tip, kwargs=kwargs).start() 18 | 19 | 20 | def instance(): 21 | ''' 22 | Instance for facade proxy. 23 | ''' 24 | return WindowsNotification() 25 | -------------------------------------------------------------------------------- /plyer/platforms/win/storagepath.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Windows Storage Path 3 | -------------------- 4 | ''' 5 | 6 | from plyer.facades import StoragePath 7 | from os.path import expanduser 8 | from plyer.platforms.win.libs.win_api_defs import get_PATH 9 | from uuid import UUID 10 | 11 | 12 | class WinStoragePath(StoragePath): 13 | 14 | def _get_home_dir(self): 15 | return expanduser('~') 16 | 17 | def _get_external_storage_dir(self): 18 | ''' 19 | To do. 20 | ''' 21 | return "Method not implemented for current platform." 22 | 23 | def _get_root_dir(self): 24 | folderid = UUID('{F38BF404-1D43-42F2-9305-67DE0B28FC23}') 25 | return get_PATH(folderid) 26 | 27 | def _get_documents_dir(self): 28 | folderid = UUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}') 29 | return get_PATH(folderid) 30 | 31 | def _get_downloads_dir(self): 32 | folderid = UUID('{374DE290-123F-4565-9164-39C4925E467B}') 33 | return get_PATH(folderid) 34 | 35 | def _get_videos_dir(self): 36 | folderid = UUID('{18989B1D-99B5-455B-841C-AB7C74E4DDFC}') 37 | return get_PATH(folderid) 38 | 39 | def _get_music_dir(self): 40 | folderid = UUID('{4BD8D571-6D19-48D3-BE97-422220080E43}') 41 | return get_PATH(folderid) 42 | 43 | def _get_pictures_dir(self): 44 | folderid = UUID('{33E28130-4E1E-4676-835A-98395C3BC3BB}') 45 | return get_PATH(folderid) 46 | 47 | def _get_application_dir(self): 48 | folderid = UUID('{905E63B6-C1BF-494E-B29C-65B732D3D21A}') 49 | return get_PATH(folderid) 50 | 51 | 52 | def instance(): 53 | return WinStoragePath() 54 | -------------------------------------------------------------------------------- /plyer/platforms/win/tts.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from plyer.facades import TTS 3 | from plyer.utils import whereis_exe 4 | 5 | 6 | class EspeakTextToSpeech(TTS): 7 | ''' Speaks using the espeak program 8 | ''' 9 | def _speak(self, **kwargs): 10 | subprocess.call(["espeak", kwargs.get('message')]) 11 | 12 | 13 | def instance(): 14 | if whereis_exe('espeak.exe'): 15 | return EspeakTextToSpeech() 16 | return TTS() 17 | -------------------------------------------------------------------------------- /plyer/platforms/win/uniqueid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module of Windows API for plyer.uniqueid. 3 | ''' 4 | 5 | try: 6 | import _winreg as regedit 7 | except ImportError: 8 | try: 9 | import winreg as regedit 10 | except ImportError: 11 | raise NotImplementedError() 12 | 13 | from plyer.facades import UniqueID 14 | 15 | 16 | class WinUniqueID(UniqueID): 17 | ''' 18 | Implementation of Windows battery API. 19 | ''' 20 | 21 | def _get_uid(self): 22 | # Win XP+, REG QUERY KEY /V VALUE, case-insensitive 23 | handle = regedit.OpenKey( 24 | regedit.HKEY_LOCAL_MACHINE, 25 | r"SOFTWARE\\Microsoft\\Cryptography", 0, 26 | regedit.KEY_READ | regedit.KEY_WOW64_64KEY 27 | ) 28 | value, _ = regedit.QueryValueEx(handle, "MachineGuid") 29 | return value 30 | 31 | 32 | def instance(): 33 | ''' 34 | Instance for facade proxy. 35 | ''' 36 | return WinUniqueID() 37 | -------------------------------------------------------------------------------- /plyer/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/tests/__init__.py -------------------------------------------------------------------------------- /plyer/tests/common.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Common objects for testing 3 | ========================== 4 | 5 | * :class:`PlatformTest` - used as a decorator, allows running a test function 6 | only on a specific platform (see `plyer.utils.platform`). 7 | * :func:`platform_import` - manual import of a platform specific class instead 8 | of using `plyer.facades.*` proxies. 9 | ''' 10 | 11 | import traceback 12 | from os import sep 13 | from os.path import normpath, splitdrive 14 | from plyer.utils import platform as plyer_platform 15 | 16 | 17 | class PlatformTest: 18 | ''' 19 | Class for the @PlatformTest decorator to prevent running tests 20 | calling platform dependent API on different platforms. 21 | ''' 22 | 23 | def __init__(self, platform): 24 | self.platform = platform 25 | 26 | def __call__(self, func): 27 | platform = self.platform 28 | 29 | if platform != plyer_platform: 30 | print("Skipping test '{}' - not on '{}'".format( 31 | func.__name__, platform 32 | )) 33 | func = self.eat 34 | return func 35 | 36 | @staticmethod 37 | def eat(*args, **kwargs): 38 | ''' 39 | Simply eat all positional and keyword arguments 40 | and return None as an empty function. 41 | ''' 42 | 43 | 44 | def platform_import(platform, module_name, whereis_exe=None): 45 | ''' 46 | Import platform API directly instead of through Proxy. 47 | ''' 48 | 49 | try: 50 | module = 'plyer.platforms.{}.{}'.format( 51 | platform, module_name 52 | ) 53 | mod = __import__(module, fromlist='.') 54 | 55 | except ImportError as exc: 56 | print(vars(exc)) 57 | traceback.print_exc() 58 | 59 | if whereis_exe: 60 | mod.whereis_exe = whereis_exe 61 | return mod 62 | 63 | 64 | def splitpath(path): 65 | ''' 66 | Split string path into a list of folders (+ file if available). 67 | ''' 68 | if path[0] == sep and path[1] != sep: 69 | path = path[1:] 70 | path = normpath(path).split(sep) 71 | else: 72 | drive, path = splitdrive(path) 73 | if path[0] == sep and path[1] != sep: 74 | path = path[1:] 75 | path = [drive, ] + normpath(path).split(sep) 76 | return path 77 | -------------------------------------------------------------------------------- /plyer/tests/images/kivy32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivy/plyer/0cfc2363891f916ad2911d4c0c1f4c3f7f317843/plyer/tests/images/kivy32.ico -------------------------------------------------------------------------------- /plyer/tests/test_email.py: -------------------------------------------------------------------------------- 1 | ''' 2 | TestEmail 3 | ========= 4 | 5 | Tested platforms: 6 | 7 | * Windows 8 | ''' 9 | 10 | import unittest 11 | from unittest.mock import Mock, patch 12 | 13 | from plyer.tests.common import PlatformTest, platform_import 14 | 15 | 16 | class TestEmail(unittest.TestCase): 17 | ''' 18 | TestCase for plyer.email. 19 | ''' 20 | 21 | @staticmethod 22 | @PlatformTest('win') 23 | def test_email_win(): 24 | ''' 25 | Test starting Windows email client for plyer.email. 26 | ''' 27 | email = platform_import( 28 | platform='win', 29 | module_name='email' 30 | ) 31 | 32 | try: 33 | test_mailto = 'mailto:recipient?subject=subject&body=text' 34 | with patch(target='os.startfile', new=Mock()) as startfile: 35 | email.instance().send( 36 | recipient='recipient', 37 | subject='subject', 38 | text='text' 39 | ) 40 | startfile.assert_called_once_with(test_mailto) 41 | except WindowsError: 42 | # if WE is raised, email client isn't found, 43 | # but the platform code works correctly 44 | print('Mail client not found!') 45 | 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | --------------------------------------------------------------------------------