Wednesday, March 21, 2012

Selecting the adb device

How many times have your received an error message like this

error: more than one device and emulator

when you run the adb command ?

I guess many, if like me, you normally use several devices an emulators. When this happens, you should obtain the serial number using

$ adb devices

then cut & paste the desired serial number into the command line and run the desired command again. Imagine how much time is wasted if this occurs tens or even hundreds of times during your day.

Hopefully, Linux and Mac OSX (or perhaps Cygwin if you are using Windows) give you the power to change what you don't like, so the following scripts will transparently allow you to select a device from the list when there's more than one a it wasn't specified in the command line.

This script, which is called android-select-device, is the responsibly of prompting the user for the selection of the device.

#! /bin/bash
# selects an android device

PROGNAME=$(basename $0)
for opt in "$@"
   case "$opt" in
[ -n "$DEVICE_OPT" ] && exit 0
DEV=$(adb devices 2>&1 | tail -n +2 | sed '/^$/d')
if [ -z "$DEV" ]
   echo "$PROGNAME: ERROR: There's no connected devices." >&2
   exit 1
N=$(echo "$DEV" | wc -l | sed 's/ //g')

case $N in
   # only one device detected

   # more than one device detected
   PS3="Select the device to use, <Q> to quit: "
   select D in $DEV
      [ "$REPLY" = 'q' -o "$REPLY" = 'Q' ] && exit 2
      [ -n "$D" ] && break


if [ -z "$D" ]
   echo "$PROGNAME: ERROR: target device coulnd't be determined" >&2
   exit 1

# this didn't work on Darwin
# echo "-s ${D%% *}"
echo "-s $(echo ${D} | sed 's/ .*$//')"

This is the other component of our solution. This script, which we are calling my-adb will be the adb replacement which ultimately invokes the real adb.

#! /bin/bash
# This command can be used as an alias for adb and it will prompt for the
# device selection if needed
#   alias adb=my-adb

set +x
PROGNAME=$(basename $0)
ADB=$(which adb)
if [ -z "$ADB" ]
   echo "$PROGNAME: ERROR: cannot found adb"
   exit 1

set -e
if [ $# == 0 ]
   # no arguments
   exec $ADB
elif [ "$1" == 'devices' ]
   # adb devices should not accept -s, -e or -d
   exec $ADB devices
   # because of the set -e, if selecting the device fails it exits
   S=$(android-select-device "$@")
   exec $ADB $S "$@"

final step
The final step is to put this solution in place. To achieve this we need a way of replacing a normal adb command with the modified version. The alias shell's internal command is the best way of getting this done (you can add it to ~/.bash_aliases):

$ alias adb=my-adb

providing that the scripts are in your PATH and execute permission was granted.
So now, every time you type adb without specifying the target device, android-select-device will prompt you for the selection:

$ adb shell
1) 02783201431feeee device           3) emulator-5554
2) 3832380FA5F30000 device           4) emulator-5556
Select the device to use, <Q> to quit: 1

Hope this saves you some time.

Friday, March 16, 2012

Eclipse: working monkeyrunner configuration

This post is intended to help you if you have problems running monkeyrunner from Eclipse.
There are tons of messages floating around describing a variety of problems. It seems that the most problematic platform in this respect is Microsoft Windows, and Linux or Mac OSX are both much less tricky.

Using Android monkeyrunner from Eclipse is one of the all-time most popular post in this blog. Clearly, this indicates that the setup is not as straightforward as it should be, so I decided to post a detailed configuration that has been tested and is the one I mostly use to develop tools like AndroidViewClient, which has been described in latests posts like monkeyrunner: interacting with the Views.

After this brief introduction we are ready to start, firstly my Eclipse Helios configuration:

  •   Android DDMS 16.0.1.v201112150204-238534
  •   Android Development Tools 16.0.1.v201112150204-238534
  •   Android Hierarchy Viewer 16.0.1.v201112150204-238534
  •   Android Traceview 16.0.1.v201112150204-238534
  •   AspectJ Development Tools 2.1.3.e36x-20110622-1300
  •   Cross References tool (XRef) 2.1.3.e36x-20110622-1300
  •   EclEmma Java Code Coverage
  •   Eclipse EGit
  •   Eclipse IDE for Java Developers
  •   Eclipse JGit
  •   Eclipse Weaving Service Feature 2.1.3.e36x-20110622-1300
  •   Equinox Weaving SDK 1.0.0.v20100421-79--EVVFNFFsFc
  •   m2e - Maven Integration for Eclipse
  •   m2e - slf4j over logback logging (Optional)
  •   PyDev for Eclipse
  •   Pydev Mylyn Integration 0.3.0

Following this configuration we will be using one of the AndroidViewClient's example: This is showing its run configuration.







Monday, March 12, 2012

monkeyrunner: running unit tests

First of all, I hope you like the new look of the blog. Personally I think the darker background turns the posts more relevant and less distracting.

Let me know what you think.

Now, to the point. We have presented an analyzed here many monkeyrunner scripts and techniques but we haven't explicitly created some unit tests, and this is precisely what we are going to do.

As our Application Under Test (AUT) we are using the well-known Temperature Converter, which was used many times to demonstrate other concepts as well.
The source code is as usual available through github.

Additionally, we are also using the help of AndroidViewClient which is available at github too and was introduced in monkeyrunner: interacting with the Views. The central idea is to use AndroidViewClient facilities to create a unit test. Let's name this script

#! /usr/bin/env monkeyrunner

Created on 2012-03-08

@author: diego

import sys
import unittest
import subprocess
import socket
import re
from import MonkeyRunner, MonkeyDevice
import viewclient

class TemperatureConverterActivityTests(unittest.TestCase):

   def setUp(self):
      # connect to the device
      self.device = MonkeyRunner.waitForConnection(60)
      self.assertNotEqual(None, self.device)

      # start TemperatureConverter

      # clear the field
      for n in range(10):'DEL', MonkeyDevice.DOWN_AND_UP)

      # create the client
      self.viewclient = viewclient.ViewClient(self.device)

   def tearDown(self):

   def testConversion(self):
      C = '123'
      F = '253.4'



      celsius = self.viewclient.findViewById('id/celsius')['mText']
      fahrenheit = self.viewclient.findViewById('id/fahrenheit')['mText']

      self.assertEqual(celsius, C)
      self.assertEqual(fahrenheit, F)

if __name__ == '__main__':

It is noticeable how the use of AndroidViewClient simplifies this test.

So if everything goes well, providing that your emulator or device is running and reachable, you will receive this output for the test run:

$ ./ 
Ran 1 test in 8.801s


Hope this helps.