How to automate Android apps with Python
Posted 01 Dec 2017 in androidmobile appspython

In a previous post I covered a way to monitor network activity in order to scrape the data from an Android application. Sometimes this appraoch will not work, for example if the data of interest is embedded within the app or perhaps the network traffic is encrypted. For these cases I use UIautomator, which is a Python wrapper to the Android testing framework.

To setup UIautomator you will need to install the Android SDK and set the ANDROID_HOME environment variable. On Mac I ran the following to install these dependencies:

$ brew cask install android-sdk android-platform-tools
$ export ANDROID_HOME="/usr/local/Caskroom/android-platform-tools/28.0.1/"
$ pip install uiautomator

To check whether your setup is working connect your Android device by USB and try the info command to check its status:

>>> from uiautomator import device as d
>>> d.info
{u'displayRotation': 0, u'displaySizeDpY': 640, u'displaySizeDpX': 360, u'screenOn': True, u'displayWidth': 1080, u'productName': u'hammerhead', u'currentPackageName': u'com.android.settings', u'sdkInt': 23, u'displayHeight': 1776, u'naturalOrientation': True}

If you get the exception EnvironmentError: Device not attached, then you need to enable USB debugging on your Android device in order to connect.

If the above command works then you are ready to start automating interactions with your Android device. Note that instead of a physical Android device you can use an emulator such as Genymotion, however I have found some popular apps will not run on emulators.

UIautomator provides a number of ways to select elements such as by text or class. Actions can then be performed on the selected elements such as click, drag, swipe, and keyboard input. One of the most useful commands is dump(), which returns the current XML layout of your Android device:

>>> d.dump()
<?xml version="1.0" ?>
<hierarchy rotation="0">
  <node bounds="[0,0][1080,1776]" checkable="false" checked="false" class="android.widget.FrameLayout" clickable="false" content-desc="" enabled="true" focusable="false" focused="false" index="0" long-clickable="false" package="com.google.android.googlequicksearchbox" password="false" resource-id="" scrollable="false" selected="false" text="">
      <node bounds="[0,0][1080,1776]" checkable="false" checked="false" class="android.widget.LinearLayout" clickable="false" content-desc="" enabled="true" focusable="false" focused="false" index="0" long-clickable="false" package="com.google.android.googlequicksearchbox" password="false" resource-id="" scrollable="false" selected="false" text="">
            <node bounds="[0,0][1080,1776]" checkable="false" checked="false" class="android.widget.FrameLayout" clickable="false" content-desc="" enabled="true" focusable="false" focused="false" index="0" long-clickable="false" package="com.google.android.googlequicksearchbox" password="false" resource-id="android:id/content" scrollable="false" selected="false" text="">
                    <node bounds="[0,0][1080,1776]" checkable="false" checked="false" class="android.widget.FrameLayout" clickable="false" content-desc="" enabled="true" focusable="false" focused="false" index="0" long-clickable="false" package="com.google.android.googlequicksearchbox" password="false" resource-id="com.google.android.googlequicksearchbox:id/launcher" scrollable="false" selected="false" text="">
                    ...
  </node>
</hierarchy>

This is equivalent to viewing the source of a web page and shows the attributes used by each element, which can be used by selectors to identify the correct element.

Here is an example script with UIautomator:

from uiautomator import Device
d = Device('014E05DE0F02000E')
d.press.home()
d.press('Maps')
d(text='Search here').set_text('NYC')
d.press.enter()

The script will:

  • connect to a device with the given serieal number (the shortcut from uiautomator import device should not be used if multiple devices are connected)
  • navigate to the home screen
  • open an app labelled Maps (in my case Google Maps)
  • type NYC in the search bar
  • and press the ENTER key to show results

UIautomator also supports many other features such as event listeners and relative selectors, which are well documented on the github page.

blog comments powered by Disqus