Kivy-Python-Android Torch App - android

There is this torch app example available online. It uses a 'Switch' to turn flashlight on. It works. I've been trying/struggling to get a 'message' output on the device when the flashlight is turned on and off and I cannot seem to get the right code. Can anyone provide a hint. Here's the working torch app code (somehow, the text 'enlightme' never appears):
from kivy.app import App
from kivy.uix.switch import Switch
from jnius import autoclass
Camera = autoclass('android.hardware.Camera')
Parameters = autoclass('android.hardware.Camera$Parameters')
__version__ = '0.1'
class FlashApp(App):
def build(self):
self.root = Switch(text='enlightenme')
self.root.bind(active=self.toggle_flash)
self.camera = None
return self.root
def toggle_flash(self, *args):
if self.camera == None:
self.camera = Camera.open()
p = self.camera.getParameters()
if self.root.active:
p.setFlashMode(Parameters.FLASH_MODE_TORCH)
self.camera.setParameters(p)
self.camera.startPreview()
else:
p.setFlashMode(Parameters.FLASH_MODE_OFF)
self.camera.stopPreview()
self.camera.setParameters(p)
self.camera.release()
self.camera = None
if __name__ == '__main__':
FlashApp().run()

Switch doesn't have a text property, and due to a quirk of Kivy's argument parsing you don't get an error due to the unused kwarg.
You should instead add a Label with your desired text.

Related

Kivy on Android get bluetooth state

I am trying to get my app to get all paired devices after programmatically enabling the bluetooth. The thing is Android only allows you getting all paired devices if the bluetooth state is on. Now I already have a pop up that pops up if there is no bluetooth which has an option to enable bluetooth successfully calling BluetoothAdapter.getDefaultAdapter().enable().
The thing is this enable call runs slow and if my subsequent line code checks for enabled state, it over runs before getting all paired devices:
if BluetoothAdapter.getDefaultAdapter().isEnabled():
this.root.get_devices()
Therefore I tried the broadcast receiver implementation from the p4a docs :
def __init__(self, **kwargs):
super().__init__(**kwargs)
if platform == 'android':
self.intent_bltooth = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
self.br = BroadcastReceiver(self.on_broadcast, actions=['state_changed'])
self.stream = Stream()
self.br.start()
def on_broadcast(self, context, intent):
print(intent.getAction(), 'see intent action')
listen = intent.getExtras()
bltooth_state = bool(listen.get('state'))
if bltooth_state:
this.root.get_devices()
print('bluetooth connected')
else:
print('bluetooth not connected')
But this gives me a constant ACTION_STATE_CHANGED intent does not exist.
So to overcome this I decided to use a more pure kivy solution in the form of a trigger that checks the bluetooth state until enabled:
def enable_bluetooth(self, *args):
this = App.get_running_app()
if platform == 'android':
blueAdapt = BluetoothAdapter.getDefaultAdapter()
blueAdapt.enable()
this.root.dialog_with_action.remove_action_button()
self.bluet_ticker() # this line gives the error TypeError: Argument 'obj' has incorrect type (expected kivy._event.EventDispatcher, got BluetoothHelper)
def bluet_ticker(self):
print('one tick')
self.bluet_tme = 1000
print('two tick')
self.bluet_tick = Clock.create_trigger(lambda dt: self.run_bluet_ticker(), 0)
print('three tick')
self.bluet_tick()
def run_bluet_ticker(self):
this = App.get_running_app()
blueAdapt = BluetoothAdapter.getDefaultAdapter()
if self.bluet_tme > 0:
if blueAdapt.isEnabled() == False:
print('not ennnnnnnnnnnnnnnnnnnnnnnnbled')
self.bluet_tme -= 1
self.bluet_tick()
else:
this.root.get_devices()
print('ennnnnnnnnnnnnnnnnnnnnnnnnnabling bbluuuuuuuuuuuuuuuuuutoottttttth')
self.bluet_tick.cancel()
else:
self.bluet_ticker()
But when the trigger function bluet_trigger() is called I get the following error: TypeError: Argument 'obj' has incorrect type (expected kivy._event.EventDispatcher, got BluetoothHelper)
Can someone please assist in either helping to point me in the right direction to find a solution to use a BroadcastReceiver or the trigger?
I am posting the answer I found from user with alias AdyWizard on discord and his project wifiscan
So the off the shelf p4a broadcast receiver does not have the bluetooth state broadcast receiver built in. You have to amend the source and add support for BluetoothAdapter state.
After my first build of the kivy app using buildozer android debug deploy. I go to the following directory /.buildozer/android/platform/python-for-android/pythonforandroid/recipes/android/src/android/broadcast.py and amend the code with the following:
def _expand_partial_name(partial_name):
if '.' in partial_name:
return partial_name # Its actually a full dotted name
else:
name = 'ACTION_{}'.format(partial_name.upper())
if hasattr(Intent, name):
return getattr(Intent, name)
elif hasattr(BluetoothAdapter, name):
return getattr(BluetoothAdapter, name)
else:
raise Exception('The intent {} doesnt exist'.format(name))
# resolve actions/categories first
Intent = autoclass('android.content.Intent')
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')
I then run the command buildozer android clean and run the build command again. The p4a broadcast receiver then works as is. Here is a working example

kivy + android intent filters

Im very new to kivy and python so I may have thrown myself in at the deep end here but i'm trying to figure out how to get an android intent and use it within a simple kivy app?
So im currently trying to figure out the basic design elements and just how to get stuff working. I want to be able to click on a link (for example) and launch my app and then do stuff, nothing too complicated, it just needs to be able to get the link which is passed to it from android.
I figured out this was to do with android intent filters and added the appropriate "intent_filters.xml" file to make this work.
So clicking on the link will launch my app, which is good.
However I cant figure out then how to parse the information passed to the app in android. So below is a very simple app with a button and I just want to update the button to see if my intent is being picked up or not.
Currently clicking the button causes the app to close. When i test this in the Kivy VM I am using to compile the apk it will close with a message that "platform" is not defined (which makes sense as its not in android) but it doesnt much help with testing or tracking down how to make it work.
I've read some stuff which seems to suggest that this might not be possible with kivy?
Can anyone confirm if this is possible in kivy and if so, how?
(i've tried variations on activity.getIntent(), intent.getData(), Intent.getIntent().getStringExtra(Intent.EXTRA_TEXT), Intent().getData().getPath() all to no avail)
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class TutorialApp(App):
def build(self):
self.gen_btn = Button(text='Initial',
size_hint=(.90, .10),
pos=(5, 5),
font_size=50)
self.gen_btn.bind(on_press=self.update_tutorialapp)
l = BoxLayout()
l.add_widget(self.gen_btn)
return l
def update_tutorialapp(self, *args):
st = update_button()
st.update_Message(self)
class update_button():
def update_Message(self, source):
source.gen_btn.text = "the event was called"
if platform=="android":
from jnius import cast
from jnius import autoclass
import android
import android.activity
# test for an intent passed to us
PythonActivity = autoclass('org.renpy.android.PythonActivity')
activity = PythonActivity.mActivity
intent = activity.getIntent()
intent_data = intent.getData()
source.gen_btn.text = PythonActivity.getIntent().getStringExtra(Intent.EXTRA_TEXT)
if __name__ == "__main__":
TutorialApp().run()
def build(self):
android.activity.bind(on_new_intent=self.on_new_intent) # add this line in build
def on_new_intent(self, intent):
data = intent.getData() # perform operations on intent

Offline Speech Recognition in qpython3

I have been trying to make a qpython program that uses sl4a.Android.recognizeSpeech function. The functionality works fine online.
In my phone settings, I turned on and downloaded offline speech recognition and google now works fine offline, but the python speech does not work at all, asking me to try again every single time.
Sample Code:
import sl4a
import time
droid = sl4a.Android()
def speak(text):
droid.ttsSpeak(text)
while droid.ttsIsSpeaking()[1] == True:
time.sleep(1)
def listen():
return droid.recognizeSpeech('Speak Now',None,None)
def login():
speak('Passphrase, please')
try:
phrase = listen().result.lower()
except:
phrase = droid.dialogGetPassword('Passphrase').result
print(phrase)
if phrase == 'pork chops':
speak('Welcome')
else:
speak('Access Denied')
exit(0)
login()
droid.recognizeSpeech("foo", None, None)
returns an Array with the recognized Speech in Index number 1. So if you want to access it, you have to type
return droid.recognizeSpeech("foo", None, None)[1]
Actually none of the above worked for me. So I solved that this way:
x, result, error = droid.recognizeSpeech("Speak")
The result variable stores the speech recognized from the user
Example:
import sl4a
import time
droid = sl4a.Android()
def Speak(talk):
try:
droid.ttsSpeak(talk)
while droid.ttsIsSpeaking()[1] == True:
time.sleep(2)
except:
droid.ttsSpeak("nothing to say")
def listen():
global result,error
time.sleep(1)
x, result, error = droid.recognizeSpeech("Speak")
while True:
try:
listen()
except:
print(error)
try:
if len(str(result)) > 0:
print(result)
if result == "how old are you":
Speak("I'm 1 year old")
elif result is None:
break
else:
Speak("I heard " + result)
except Exception as e:
print(e)
break

How to perform click/tap operation to the perticular bound/co-ordinates in python script for Android UI using appium

I am writing testcases in python script for android application using appium. I want to perform click/tap operation in a particular bounds/co-ordinates. but I am not able to perform it.Can any one tell me how to do it.
class firstTest(unittest.TestCase):
def tearDown(self):
"Tear down the test"
self.driver.quit()
def test_single_player_mode(self):
time.sleep(5)
element = self.driver.find_element_by_name("Enter your Email ID or User name")
element.send_keys("username")
element = self.driver.find_element_by_name("Let's get started!")
element.click()
time.sleep(5)
Till 'Lets get started it is working fine. After it in UI, I don't have any element name or id. I have only particular bound to click the element. I want to click on bound [918,154][1086,324] resource ID for above bound is "com.abc.android.cap:id/friends_selection_list" Please tell me how to do after it.
I hope you have content-description for this element. In that case you can tap on it by using it and tap method in TouchAction class.
ele = self.driver.find_element_by_accessibility_id('content-description of your element')
action = TouchAction(self.driver)
action.tap(ele).perform()
U can use Xpath of that element, for finding xpath u will need appium inspector, which is supported in MAC OSX only, so you have to use an mac and configure Android SDK and Appium on MAC in order to get it working.
Code will be like this :
#-*- coding: utf-8 -*-
__author__ = 'chetan.krishna'
import os
import time
import unittest
from time import sleep
from appium import webdriver
from pylab import *
from teamcity import is_running_under_teamcity
from teamcity.unittestpy import TeamcityTestRunner
import logging
# Returns absolute path relative to this file
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)
class AvavntiAndroidTests(unittest.TestCase):
def setUp(self):
desired_caps = {}
# Specify platform below(Android, iOS)
desired_caps['platformName'] = 'Android'
# Specify OS version(Settings->About phone -> android version)
desired_caps['platformVersion'] = '4.4.4'
# Obtain the Device name from Adb[For Android](Terminal Command: "adb devices")
desired_caps['deviceName'] = '4d0081004c8741a9'
desired_caps['noReset'] = False
# Specify the path to Application
desired_caps["app"] = PATH('AvantiMarket_v1.4.apk')
# Wait for email login activity to appear
desired_caps["appWaitActivity"]= ('com.android.avantimarket.ui.activity.EmailLoginActivity')
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
def tearDown(self):
# end the session
self.driver.quit()
def test_Avanti(self):
# wait for the login screen to appear
self.driver.implicitly_wait(20)
# set values for plotting pass and fail results
nPass = 0
nFail = 0
print('Checking login for registered user')
# Logging in as indiaone#avantilab.org
elf.driver.implicitly_wait('20')
print('Trying to login with abc#abc.org')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIATextField[1]').send_keys('abc#abc.org.org')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[1]').send_keys('12345678')
self.driver.hide_keyboard()
self.driver.implicitly_wait(10)
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIAButton[3]').click()
time.sleep(20)
self.driver.implicitly_wait(99)
try:
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[1]')
except :
print('Login failed, please check credentials and network')
else:
print('Login successful, creating pin')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[1]').send_keys('1')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[2]').send_keys('1')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[3]').send_keys('1')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[4]').send_keys('1')
self.driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[2]/UIASecureTextField[5]').send_keys('1')
self.driver.find_element_by_id('com.android.avantimarket:id/m_emailTextField').send_keys('abc#abc.org.org')
self.driver.back()
nPass= nPass+1
self.driver.implicitly_wait(20)
self.driver.find_element_by_id('com.android.avantimarket:id/m_passwordTextField').send_keys('12345678')
nPass= nPass+1
self.driver.back()
self.driver.implicitly_wait(10)
self.driver.find_element_by_name('SIGN IN').click()
self.driver.implicitly_wait(30)
time.sleep(5)
# validating for successful login
try:
self.driver.find_element_by_id('com.android.avantimarket:id/create_pin_fields_container')
print('Login successful')
nPass= nPass+1
except:
print('Login failed')
nFail = nFail + 1
else:
# Creating pin required for login
print('Creating Pin for user')
self.driver.find_element_by_id('com.android.avantimarket:id/create_pin_fields_container').send_keys('1111')
self.driver.find_element_by_id('com.android.avantimarket:id/reEnter_pin_fields_container').send_keys('1111')
self.driver.back()
self.driver.implicitly_wait(20)
self.driver.find_element_by_id('com.android.avantimarket:id/m_saveButton').click()
self.driver.implicitly_wait(10)
self.driver.find_element_by_id('com.android.avantimarket:id/btn_cominsoon_Yes').click()
self.driver.implicitly_wait(10)
time.sleep(8)
You can achieve this with Touch Actions.
action = TouchAction(self.driver)
action.tap(element=None, x=100, y=600, count=2).perform()
Or like this:
self.driver.tap([(100,600)], 500)
Where the first variable is a list of up to 5 x,y coordinates(e.g. self.driver.tap([(495,757), (200,500)], 500). The last number is duration of the tap.

KIVY not updating UI on phone sleep mode

I was again trying kivy gps example from https://github.com/kivy/plyer/blob/master/examples/gps/main.py. But the problem occurs when I press the Start button and put the phone on sleep mode(screen off), after sometimes I came back to screen and the app, only I can see a frozen gui that due to unable to update the label while on standby mode. How do I can overcome this? I want to update the label even my phone is on sleep mode.
from kivy.lang import Builder
from plyer import gps
from kivy.app import App
from kivy.properties import StringProperty
from kivy.clock import Clock
kv = '''
BoxLayout:
orientation: 'vertical'
Label:
text: app.gps_location
Label:
text: app.gps_status
BoxLayout:
size_hint_y: None
height: '48dp'
padding: '4dp'
ToggleButton:
text: 'Start' if self.state == 'normal' else 'Stop'
on_state: app.gps.start() if self.state == 'down' else app.gps.stop()
'''
def mainthread(func):
# This method is now part of Kivy 1.8.0. When it's released, remove it.
def delayed_func(*args, **kwargs):
def callback_func(dt):
func(*args, **kwargs)
Clock.schedule_once(callback_func, 0)
return delayed_func
class GpsTest(App):
def on_pause(self):
# Here you can save data if needed
return True
def on_resume(self):
# Here you can check if any data needs replacing (usually nothing)
pass
gps_location = StringProperty()
gps_status = StringProperty('Click Start to get GPS location updates')
def build(self):
self.gps = gps
try:
self.gps.configure(on_location=self.on_location,
on_status=self.on_status)
except NotImplementedError:
import traceback; traceback.print_exc()
self.gps_status = 'GPS is not implemented for your platform'
return Builder.load_string(kv)
#mainthread
def on_location(self, **kwargs):
self.gps_location = '\n'.join([
'{}={}'.format(k, v) for k, v in kwargs.items()])
#mainthread
def on_status(self, stype, status):
self.gps_status = 'type={}\n{}'.format(stype, status)
if __name__ == '__main__':
GpsTest().run()
You can't ever guarantee that the app will continue operating in sleep mode, so this probably isn't a good way to do things. Better would be to use an android service, which is possible with python-for-android. Here is a blog post about it, and it's documented here.
You can do your gps stuff in the service and sync with the app on on_resume and/or on_start.

Categories

Resources