{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Python for automation using dtmilano's android ViewClient
Syllabus
1. What is python?
2. What is dtmilano's android ViewClient?
2a. How to install dtmilano's android ViewClient?
3. What all methods present in android viewclient?
4. How to import and use android viewclient in our project?
5. How do I automate android native/3rd party apps using android viewclient?
6. How to take screen shots using viewclient?
7. How to handle files using file library?
Let us begin the journey, I hope this one wont be boring
1. What is Python?Python is a general-purpose interpreted, interactive, object-oriented, and high-level programming language.
It was created by Guido van Rossum during 1985- 1990. Like Perl, Python source code is also available under the GNU General Public License (GPL).
2. What is Dtmilano's android viewclient?- AndroidViewClient is a 100% pure python library and tools that simplifies test script creation
and android test automation, providing higher level operations and the ability of obtaining the tree of Views present at any
given moment on the device or emulator screen and perform operations on it.
As it's 100% pure python it doesn't require monkeyrunner, jython or any interpreter other than python.
2a. How to install Dtmilano's android ViewClient?
Install Python from https://www.python.org/downloads/
Now download dtmilano's .egg file from https://pypi.python.org/pypi/androidviewclient/
Once it is done rename the downloaded file to .zip format and after that extract it to C:\Python27\Lib\site-packages.
The easiest method to install this is to use the command "easy_install". If you don't have easy_install installed, install the package python-setuptools(https://pypi.python.org/pypi/setuptools)
Once its done setup environment variable to C:\Python27\Scripts and after that run easy_install <path of downloaded dtmilano's .egg file> and you are good to go.
3. What all methods present in viewclient?- Some of the most used methods I have listed below -
Code:
'TRAVERSE_CIT', 'TRAVERSE_CITB', 'TRAVERSE_CITC', 'TRAVERSE_CITCD', 'TRAVERSE_CITCDS', 'TRAVERSE_CITG', 'TRAVERSE_CITPS', 'TRAVERSE_CITUI', 'TRAVERSE_S', '_ViewClient__findViewWithAttributeInTree', '_ViewClient__findViewWithAttributeInTreeOrRaise', '_ViewClient__findViewWithAttributeInTreeThatMatches', '_ViewClient__findViewsWithAttributeInTree', '_ViewClient__getFocusedWindowPosition', '_ViewClient__hammingDistance', '_ViewClient__levenshteinDistance', '_ViewClient__mapSerialNo', '_ViewClient__obtainAdbPath', '_ViewClient__obtainDeviceSerialNumber', '_ViewClient__parseTree', '_ViewClient__parseTreeFromUiAutomatorDump', '_ViewClient__pickleable', '_ViewClient__splitAttrs', '_ViewClient__traverse', '__del__', '__doc__', '__init__', '__module__', 'assertServiceResponse', 'connectToDeviceOrExit', 'distance', 'distanceTo', 'dump', 'excerpt', 'findViewById', 'findViewByIdOrRaise', 'findViewByTag', 'findViewByTagOrRaise', 'findViewWithAttribute', 'findViewWithAttributeOrRaise', 'findViewWithAttributeThatMatches', 'findViewWithContentDescription', 'findViewWithContentDescriptionOrRaise', 'findViewWithText', 'findViewWithTextOrRaise', 'findViewsContainingPoint', 'findViewsWithAttribute', 'getRoot', 'getSdkVersion', 'getViewIds', 'getViewsById', 'hammingDistance', 'imageDirectory', 'installPackage', 'isKeyboardShown', 'levenshteinDistance', 'list', 'longTouch', 'serviceResponse', 'setAlarm', 'setText', 'setViews', 'setViewsFromUiAutomatorDump', 'sleep', 'swipe', 'touch', 'traverse', 'traverseShowClassIdAndText', 'traverseShowClassIdTextAndBounds', 'traverseShowClassIdTextAndCenter', 'traverseShowClassIdTextAndContentDescription', 'traverseShowClassIdTextAndTag', 'traverseShowClassIdTextAndUniqueId', 'traverseShowClassIdTextContentDescriptionAndScreenshot', 'traverseShowClassIdTextPositionAndSize', 'traverseTakeScreenshot', 'writeImageToFile', 'writeViewImageToFileInDir'
4. How to import and use android viewclient?- Once you have installed android viewclient next step is to use this in our new project. Below is the code snippit for importing android view client -
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
5. How do I automate android native/3rd party apps using android ViewClient?
Pre-conditions in your device:
1. Enable USB debugging.
2. Enable stay awake option.
Pre-conditions in PC:
1. android sdk should be present.
2. connect your testing device. (double check with cmd "adb devices")
3. python env. variables are all set.
4. Create a new folder named "Automation" on your desktop.
5. Inside it create a new text file and paste the below given code and save it as Settings.py (or whatever you want to give)
Lets start-- It is very easy to automate android apps using android ViewClient. Let me teach you a simple code which will open settings application.
NOTE: This program I have written using my Nexus device running Android latest version 6.0; So the below program may give you errors. Please edit the code accordingly.
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
print "################## Settings application test ####################"
device.startActivity('com.android.settings/.Settings')
print 'TEST : PASS'
Once you write the above code, let us save it as Settings.py And then we will try to open it using IDLE software which will come pre-bundled with python as shown below:
* To execute our newly written code we gonna press F5.
Explanation on above code --
The first line of code is to import dtmilano's ViewClient method.
the second line is used to define the devices which you have connected, In my case as I have connected only one device I will not provide serialno,
Going further I will show you as how to connect to two devices and make them communicate.
6. How to take screen shots using viewclient?
- Now we will take our code to the next level, We gonna open settings app, and then we are going to click on About phone option. and also we'll take screen shot of the device to check as what is inside About phone.
NOTE: Create a folder named "Screenshots" at your current working directory(That is in our case folder named Automation) or else you will get Error saying no folder named "Screenshots" is present.
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
import re
import sys
import time
import os
from PIL import Image
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
print "################## Settings application test ####################"
device.startActivity('com.android.settings/.Settings')
print 'SUCCESFULLY OPENED SETTINGS APP'
vc.dump() ## this is used to refresh the screen.
## below is the for loop used to swipe screen five times. the input swipe command takes 4 args that is X1,Y1,X2,Y2 which can be found using "Pointer location" option present in developer settings.
for i in range(5):
device.shell('input swipe 651 1307 677 680')
vc.dump()
vc.findViewWithText("About phone").touch()##this line will click on About phone option.
print 'About phone option found and clicked'
vc.dump()
time.sleep(2)
device.takeSnapshot(reconnect=True).save(os.getcwd()+'\\Screenshots\\'+'OS_version.png') #this line will take screenshot of the device and store it in the folder named Screenshots.
print 'Screenshot taken'
print 'TEST : PASS'
Now when you execute the above code you will see the result as below -
Now let us write a code to check the current android version displayed under settings app is proper or not.
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
import re
import sys
import time
import os
from PIL import Image
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
print "################## Android OS VERSION TEST ####################"
device.startActivity('com.android.settings/.Settings')
print 'Settings app opened'
vc.dump()
for i in range(5):
device.shell('input swipe 651 1307 677 680')
vc.dump()
vc.findViewWithText("About phone").touch()
vc.dump()
print 'About phone option clicked'
device.shell('input swipe 651 1307 677 680')
vc.dump()
version = vc.findViewWithText('Android version')
if version:
vnum = vc.findViewWithText('6.0')
if vnum:
print 'Android OS version is 6.0'
vc.dump()
time.sleep(2)
device.takeSnapshot(reconnect=True).save(os.getcwd()+'\\Screenshots\\'+'OS_version.png') #this line will take screenshot of the device and store it in the folder named Screenshots.
print 'Screenshot taken'
print 'TEST : PASS'
device.press('KEYCODE_HOME')
else:
print 'Incorrect OS version'
print 'TEST : FAIL'
else:
print 'Android version string not found'
print 'TEST : FAIL'
device.press('KEYCODE_HOME')
In the above code you might be wondring as what does vc.dump() stands for, It is actually refreshes the screen and it makes easier to find required data on the screen. Without vc.dump() method there is 100% guarenty that our script gonna fail.
OK, Now we will wrtie a script which has Python functions in it.
Before this let us learn as what is 'def' in Pyhton is all about:
As we all know that we define functions to provide the required functionality. Below are some rules to define a function in Python.
[*] Function blocks begin with the keyword def followed by the function name and parentheses ( ( ) ).
[*] Any input parameters or arguments should be placed within these parentheses. You can also define parameters inside these parentheses.
[*] function names can be started with a _ or a small letter.
[*] The code block within every function starts with a colon ) and is indented.
Now I will write a simple code to show you as how def works.
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
def test():
print 'Hi I am inside a function'
test()
As you can see from the above code, there is a single tab given after the fucntion test():, these are called as indents. Once you start using IDLE for python script writing you will get to know more on these indents.
Just for testing purpose try to write your own functions using IDLE; Once you type as " def test(): " and hit ENTER without the qouats you will see a single tab occured. This is how Pyhton works. There are no curly brases in it. But only Indents. If you can handle these indents you are good to go.
And one more thing I forgot to explain you guys, That is you can Indent a region by using the shortcut ctrl+] and Dedent using the shortcut ctrl+[
For now let us write the same code; But this time we will use 'def' in it.
Code:
'''
Created on Dec 19, 2015
@author: ravi h basawa
'''
import re
import sys
import time
import os
from PIL import Image
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
def os_test():
print "################## Android OS VERSION TEST ####################"
device.startActivity('com.android.settings/.Settings')
print 'Settings app opened'
vc.dump()
for i in range(5):
device.shell('input swipe 651 1307 677 680')
vc.dump()
vc.findViewWithText("About phone").touch()
vc.dump()
print 'About phone option clicked'
device.shell('input swipe 651 1307 677 680')
vc.dump()
version = vc.findViewWithText('Android version')
if version:
vnum = vc.findViewWithText('6.0')
if vnum:
print 'Android OS version is 6.0'
vc.dump()
time.sleep(2)
device.takeSnapshot(reconnect=True).save(os.getcwd()+'\\Screenshots\\'+'OS_version.png') #this line will take screenshot of the device and store it in the folder named Screenshots.
print 'Screenshot taken'
print 'TEST : PASS'
device.press('KEYCODE_HOME')
else:
print 'Incorrect OS version'
print 'TEST : FAIL'
else:
print 'Android version string not found'
print 'TEST : FAIL'
device.press('KEYCODE_HOME')
os_test()
Output of the above code will be as below:
============= RESTART: C:\Users\ravi\Desktop\testing\Settings.py =============
################## Android OS VERSION TEST ####################
Settings app opened
About phone option clicked
Android OS version is 6.0
Screenshot taken
TEST : PASS
>>>
About file handling
Before we move to the next, Let me teach you as what is 'class' in Python and how do we use it.. Below is the small code consists of a class and one function.
Code:
'''
Created on Dec 27, 2015
@author: ravi h basawa
'''
class ctest():
def test(self, value):
print 'Hi I am inside a function and passed value as >>' + ' ' + value
c = ctest()
c.test('testing')
* From the above code we have created a class named 'ctest' and a function named 'test'.
* Once we create a class to create a function we have to give one tab inside the 'ctest' class.
* The code 'c = ctest()' is creating a object for our class.
* The next line 'c.test('testing')' will pass value to our newly created function.
I hope now u have understood as how 'class' works in Python from the above code.
Ok, Now let us try to send a message and this time let us use "Class" in the below code -
Code:
'''
Created on Jan 10, 2016
@author: ravi h basawa
'''
import os
from com.dtmilano.android.viewclient import ViewClient
device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device=device, serialno=serialno)
fd = open('MsgTest.xls',"a+")
fd.write("\nMessanger")
class msg():
def newmsg(self):
print 'Opening Message app'
device.shell('am start com.google.android.apps.messaging')
vc.dump()
vc.findViewById('com.google.android.apps.messaging:id/start_new_conversation_button').touch()
vc.dump()
f = vc.findViewWithText('Frequents')
if f:
print 'TEST PASS'
print 'add button clicked'
fd.write("\tPASS")
fd.close()
else:
print 'TEST FAIL'
fd.write("\tFAIL")
c = msg()
c.newmsg()
The above code does these jobs -
* Creates a class named 'msg()'. And inside we have created a new function named 'newmsg()'.
* This code will open google Messenger app and clicks on the 'add recipients' button.
* If the 'add recipients' button is clicked, our test case will be passed else it will fail.
* 'open' function does the job of creating a new xls file in append mode.
* 'write' function does the job of writing the data to the new line inside the appended xls file.
* 'close' function is used to close the xls file.
is there a way to handle the device keyboard, say, i would like to press a key and then close the keyboard?
Also where can i find detailed documentation?
may be you need to use pointer location(x,y coordinates).
Implicit wait and explicit wait in vc
Hi,
Iam trying to use android view client to automate a task. However, I have a requirement wait for a perticular element to load up.
Is there are function in VC to use implicit or explicit wait.
Thanks
Okay, I'm trying to make parse a json file with a JavaScriplet.
I'm going through and pulling out data based on if a specific data set of the json matches a specific word in an array.
For example,
var arr = parsed data
For i = 0 to end
If arr.data.wall.color = "Red" var wallname = arr.data.wall.name
So if I do it like that, it works great. But I want to be able to put a variable in there. If I do this, it works:
var colorname = "Red"
var arr = parsed data
For i = 0 to end
If arr.data.wall.color = colorname var wallname = arr.data.wall.name
Obviously a local variable. But I want to use a Global variable... one I've already set in a different task. So in task 1 I set ColorName = "Red"
And then in task 2:
var arr = parsed data
For i = 0 to end
If arr.data.wall.color = ColorName var wallname = arr.data.wall.name
...which doesn't work. I read else where that you need to use global() to access global variables in Scriptlets, but trying that doesn't work:
If arr.data.wall.color = global(ColorName) var wallname = arr.data.wall.name
Nor does adding the percent sign anywhere seem to work. Any ideas what I'm doing wrong?
I'm fairly certain the format is: global('VarName')
So if you were doing a JSON read from data returned in the global %HTTPD variable: JSON.parse(global('HTTPD')).current_observation.icon
Try using the apostrophe's like: global('ColorName')
Could you give me a little hand to reduce this code and make it more bearable because they (codes) will be a series of consecutive actions, and in a single command line in Tasker.
What I want to do in short?
1. Compress a file (or folder)
2. Add a name to the final file
3. Apply compression to the generated file
4. Delete ONLY contents of the folder that ended compress
5. Move the file was created before to the empty folder
Here is the code:
Code:
cd /xxx && tar -cf xxx_%DATE.xxx "$$$" && gzip -x xxx_%DATE.xxx && cd "###" && rm -r * .* && cd /xxx && mv -f xxx_%DATE.rar.gz "€€€"
And this is the description that I will offer users who use it:
Code:
[cd /'xxx'] = path to backup folder (parent dir) ; ['xxx'_%DATE] = file name (no name spaces) ; ['$$$'] = folder with files to back up ; [gzip -'x'] = compression level (1-9) ; [%DATE.'xxx'] = files container (.zip/.rar/.tar) ; ['###'] = same as -> '$$$' ; ['€€€'] = same as -> '###' & '$$$'
Mmmm, I'm sure you have several questions, so I will try to answer all possible question you could ask:
- What is the path that you think to work on?
-> If possible, in the external memory (SD Card)
- Why did you use in all commands the '&&' concatenation?
-> Just because I don't want to run the following command if the above fails for X reason. Just for security.
- What the variable '% DATE' does in your generated files?
-> All the code you see above is part of a larger task that will be used to create backups of various things, in short: BACKING UP. Well I include that variable because I want Tasker to add to the file name the date when the backup was created.
- Why do you offer the ability of choose the container file extension?
-> At first I thought it would not be possible and that failure or something would occur, but as I tested it, nothing happened. So I offer users the ability to generate the container they want. And as the final file (.gz) after compression, you can easily open it, so...
- Why do you use the quotes in some parts of the code that refer to a directory to compress, or to dump the created contents (move)?
-> Not really would take, well at least that I've experienced. But I decided to added it because, in my case, I have folders which their names have spaces, and without adding those quotation marks ("...") I can not find the directory that want to access, I guess it can happen to other people.
- When you begun using the command to create file container, like moving file, why do you position yourself in the directory first and then do the action, rather than perform all in the same code?
-> This is what I've tried so far, but do not know why I couldn't. Every combination I tested: FAILED, then I decided to separate positioning directory commands of leading compression actions, moving files, etc.
Well, I think the rest of you get the idea of that or how it works, but if not, ask to me.
At the first run python script nothing is written to the log file "cpu_temp.csv". When I run the second time then the data is written to the log file. The script is to extract the temperature from the temperature sensor MCP9808. I know the data is first written to "w1_slave" under the folder "/sys/bus/w1/devices/28-0115a4f575ff". The data for the log file are read always from this file. Can someone help me forwarding such that at the first startup script the data read from the sensor is written to the log file? I want the script started via cron. But since the first run doesn't work nothing has been logged.
Python script for reading data from MCP98808 and write to log file "cpu_temp.csv"
import subprocess
import logging
import time
from time import sleep, gmtime, strftime
import smbus
#Constant things, that don't change during run.
t_reg = 0x05
address = 0x18
bus = smbus.SMBus(1) # change to 0 for older RPi revision
def get_temp():
#The reading variable changes every time you run get_temp()
reading = bus.read_i2c_block_data(address, t_reg)
t = (reading[0] << 8) + reading[1]
temp = t & 0x0FFF
temp /= 16.0
if (t & 0x1000):
temp -= 256
return(temp)
with open("cpu_temp.csv", "a") as log:
while True:
temp = get_temp()
log.write("{0},{1}\n".format(strftime("%Y-%m-%d %H:%M:%S"),str(temp)))
time.sleep(10)
Output file cpy_temp.csv
2017-02-04 15:15:38,22.1875
2017-02-04 15:15:48,22.125
2017-02-04 15:15:58,22.125
2017-02-04 15:16:08,22.125
2017-02-04 15:16:18,22.125
Lowieke19 said:
At the first run python script nothing is written to the log file "cpu_temp.csv". When I run the second time then the data is written to the log file. The script is to extract the temperature from the temperature sensor MCP9808. I know the data is first written to "w1_slave" under the folder "/sys/bus/w1/devices/28-0115a4f575ff". The data for the log file are read always from this file. Can someone help me forwarding such that at the first startup script the data read from the sensor is written to the log file? I want the script started via cron. But since the first run doesn't work nothing has been logged.
Python script for reading data from MCP98808 and write to log file "cpu_temp.csv"
import subprocess
import logging
import time
from time import sleep, gmtime, strftime
import smbus
#Constant things, that don't change during run.
t_reg = 0x05
address = 0x18
bus = smbus.SMBus(1) # change to 0 for older RPi revision
def get_temp():
#The reading variable changes every time you run get_temp()
reading = bus.read_i2c_block_data(address, t_reg)
t = (reading[0] << 8) + reading[1]
temp = t & 0x0FFF
temp /= 16.0
if (t & 0x1000):
temp -= 256
return(temp)
with open("cpu_temp.csv", "a") as log:
while True:
temp = get_temp()
log.write("{0},{1}\n".format(strftime("%Y-%m-%d %H:%M:%S"),str(temp)))
time.sleep(10)
Output file cpy_temp.csv
2017-02-04 15:15:38,22.1875
2017-02-04 15:15:48,22.125
2017-02-04 15:15:58,22.125
2017-02-04 15:16:08,22.125
2017-02-04 15:16:18,22.125
Click to expand...
Click to collapse
When I run it line by line in the command line, this part here is having the issue.
log.write("{0},{1}\n".format(strftime("%Y-%m-%d %H:%M:%S"),str(temp)))
I hope it accented the coma before the str(temp))).
That is what my interpreter is pointing at where the issue may be. Some times it is easier to make output = "{0},{1}\n".format(strftime("%Y-%m-%d %H:%M:%S") + "," + str(temp)
then log.write(output)
Give that a shot for your debugging. I didn't fully run the code, I just gave the variables some fake values and tried it out!
Good Luck.
Hello All,
I am using a raspberry pi4 with a bullseye os - 64 bit.
In a virtual environment, I used - "pip install picamera" - to install the module
Whenever I try to import picamera, I get the following error message:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/pi/Documents/proj_trash_can/cam.py", line 2, in <module>
from picamera import PiCamera
File "/home/pi/Documents/proj_trash_can/venv/lib/python3.9/site-packages/picamera/__init__.py", line 72, in <module>
from picamera.exc import (
File "/home/pi/Documents/proj_trash_can/venv/lib/python3.9/site-packages/picamera/exc.py", line 41, in <module>
import picamera.mmal as mmal
File "/home/pi/Documents/proj_trash_can/venv/lib/python3.9/site-packages/picamera/mmal.py", line 49, in <module>
_lib = ct.CDLL('libmmal.so')
File "/usr/lib/python3.9/ctypes/__init__.py", line 374, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libmmal.so: cannot open shared object file: No such file or directory
Click to expand...
Click to collapse
I have updated and upgraded with sudo but to no luck. It seems I am missing a directory and some files. Requesting some guidance and help regarding this problem - I would be very grateful.
If any added information is needed to be provided from my side, kindly let me know how to do so and I will provide it.
Thank You.