kivy + android intent filters - android

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

Related

Running an android service in kivy written in java - too much?

I'm trying to build an NFC card emulator in kivy (similar to https://github.com/okanatas/NFCCardEmulator).
I can't simply subclass HostApduService (i.e.)
from jnius import autoclass
HostApduService=autoclass(android.nfc.cardemulation.HostApduService)
class NfcService(HostApduService):
def processCommandApdu(self, commandApdu, extras):
# Process the incoming APDU command
# and return a response APDU
# Example: Respond with the same data as received
return self.getBytes(commandApdu)
so instead I've tried to include some java files (similar to to the ones in the github) i.e.
package nfccardemulator;
import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
public class HostCardEmulatorService extends HostApduService {
#Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
...
}
}
and some other utilities files in Java.
I've put them in the services/nfccardemulator folder and added the following to buildozer:
android.add_src = services
I've checked the apk, and the classes are compiled and present. But I'm struggling to access them via autoclass
If I say
HostCardEmulatorService = autoclass("nfccardemulator.HostCardEmulatorService")
I get an error instantiating it.
I'm not sure if I'm even setting up a Service like this correctly.
I've seen an example of PythonService i.e.
PythonService = autoclass('org.kivy.android.PythonService')
and then having to use multiprocessing on the service while the kivy process runs separately.
Is what I'm trying to do even possible currently?
Thanks

How can make pycharm confirm that the redirect from mobile app to the mobile browser was done?

Searched everywhere but I fail to find an answer.
Scenario:
Automation script done for an Android application
Pre-req:
Click on a button from the Android application with a Mobile browser redirect
What I want the script to check:
If user was successfully redirected to the Mobile browser by checking if an element from the Mobile browser page is displayed (with a text or link or xpath)
The script is made for the Application but I fail to find the correct code for it to check an element from the browser page
I will post here a small part of code from my automation script for an Android app:
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
import time
if __name__ == "__main__":
desired_cap = {
"platformName": "android",
"deviceName": "emulator-5554",
"appPackage": "com.project.ProjectName",
"appActivity": ".SplashActivity",
"automatorName": "UiAutomator2",
}
driver = webdriver.Remote ("http://localhost:4723/wd/hub",desired_cap)
#Get more gold button check
element =
driver.find_element_by_xpath("xpath here")
element.click()
print("Step 12. Get more gold button clicked")
pass
#??????Confirmation that Gold store was open IN BROWSER
wait = WebDriverWait(driver, 10)
element = driver.find_elements_by_xpath("//xpath here,*[text()='Choose your payment method']")
#wait.until(EC.visibility_of_any_elements_located((By.CLASS_NAME, "flx-j-c txt-up")))
for i in element:
if 'Choose your payment method' == i.text:
print('Step 13. User not on Me tab - PASS')
else: print('FAIL to redirect the user')
time.sleep(2)
driver.back()
pass
Error:
selenium.common.exceptions.TimeoutException: Message:
I tried to find the element by xpath, class, by text, there is no id to use for this.
I think the issue is here,
element = driver.find_elements_by_xpath("//xpath here,*[text()='Choose your payment method']")
if you pay attention then you have something like this xpath here in xpath("")
instead it should be :-
element = driver.find_elements_by_xpath("//*[text()='Choose your payment method']")
Now you will have a list here.
print(len(element))
print the size like this or do whatever you wanna do with the list. It's all upto you.
Update 1:
After seeing the HTML, I think this should work for you.
Xpath
//section[contains(#class,'gold-payment-methods')]/descendant::strong
In code :-
element = driver.find_element_by_xpath("//section[contains(#class,'gold-payment-methods')]/descendant::strong")
element.click()

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

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.

Monkey tool in android for mouse event injections

I wanto to inject mouse event into an running application ( like clicking and moving the mouse pointer). Is it possible with monkey tool ? If yes, can you give me a roadmap about how to use monkey tool in android application development platform ?
I am driving apps in emulators using Monkey.
I have a Python script that gets fed to the monkeyrunner tool. This is largely coming from the sample on the Android developer website.
It is possible to invoke activities and respond to them.
For instance here I invoke an activity, which in turn runs another one; at that point I enter 4 digits and 'click' the coordinates 100,400 on the screen corresponding to the 'ok' button.
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
if __name__ == "__main__":
# Connects to the current device, returning a MonkeyDevice object
device = MonkeyRunner.waitForConnection()
# Installs the Android package. Notice that this method returns a boolean, so you can test
# to see if the installation worked.
# device.installPackage('myproject/bin/MyApplication.apk')
# sets a variable with the package's internal name
package = 'com.mypackage'
# sets a variable with the name of an Activity in the package
activity = 'com.mypackage.myactivity'
# sets the name of the component to start
runComponent = package + '/' + activity
# Runs the component
device.startActivity(component=runComponent)
if device.getProperty('am.current.comp.class') == 'com.mypackage.anotheractivity':
device.type('0000')
# Click the 'Enter' button on screen at coordinates 100,400
device.touch(100, 600, 'DOWN_AND_UP')
I hope that helps.

Categories

Resources