Sunday, April 26, 2015

android: obtaining beautiful screenshots automatically

How much time have you invested obtaining screenshots for your app and then making them look nicer framing them in the corresponding device artwork?

You have to install the app, manually do the steps needed to get to the point where you want to take the screenshot to produce the most effective communication of the idea you are trying to convey, actually taking the screenshot, saving it to a file and then probably using Device Art Generator to frame it using the corresponding artwork and orientation that you have to decide and finally downloading the result.

Android Studio may simplify these steps a bit, but still is annoyingly slow and time consuming to have to check the desired options every time.



The model (i.e. Nexus 5) has to be selected every time as well as Drop Shadow and Screen Glare. The orientation is not detected and you have to Rotate it manually if incorrect.

Wouldn't it be great if you can automate all of these steps and get the screenshots without human intervention?

I have wondered the same many times and finally support for device artwork was added to AndroidViewClient/culebra.

Task automation using culebra

As an example, just let's pretend we want to prepare some screenshots to be included in the documentation or presentation about our app, which in this case would be Calculator.
Every time we create a new version of the app we want to reproduce the same scenario an take the beautiful screenshot. For the sake of simplicity, this scenario is the simple calculation "2+1=3".

Let's run

$ culebra --gui --do-not-verify-screen-dump --scale=0.5 \
    --device-art=auto --drop-shadow --glare \
    -o ~/tmp/calculator.py

This runs `culebra` GUI.
Once the window reproducing your device screen is shown, do:

  1. Press HOME, to start from a known state
  2. Touch on All Apps
  3. Touch on Calculator
  4. Click with the secondary button to open the context menu and select Long touch point using PX (the shortcut for this action is ^L)
  5. Touch on Calculator's DEL, to completely clear the screen
  6. Touch 2
  7. Touch +
  8. Touch 1
  9. Click with the secondary button to open the context menu and select Take snapshot and save to file (the shortcut is ^F)
  10. Close the window
Now we have the `calculator.py` screen generated, that every time we run it will reproduce the steps, detect the device and orientation, take the screenshot and frame it with the corresponding device artwork and will add the drop shadow and glare.



Analyzing the generated script

Let's take a closer look at the script generated by `culebra`.


#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
Copyright (C) 2013-2014  Diego Torres Milano
Created on 2015-04-26 by Culebra v10.3.0
                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \ 
                                           \_____/--<
@author: Diego Torres Milano
@author: Jennifer E. Swofford (ascii art snake)
'''


import re
import sys
import os


try:
    sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass

from com.dtmilano.android.viewclient import ViewClient

TAG = 'CULEBRA'

_s = 5
_v = '--verbose' in sys.argv


kwargs1 = {'ignoreversioncheck': False, 'verbose': False, 'ignoresecuredevice': False}
device, serialno = ViewClient.connectToDeviceOrExit(**kwargs1)
kwargs2 = {'compresseddump': True, 'startviewserver': True, 'forceviewserveruse': False, 'autodump': False, 'ignoreuiautomatorkilled': True}
vc = ViewClient(device, serialno, **kwargs2)
#vc.dump(window='-1') # FIXME: seems not needed

vc.dump(window=-1)
device.press('HOME')
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''Apps''').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''Calculator''').touch()
vc.sleep(_s)
vc.dump(window=-1)
device.longTouch(912.0, 838.0, 2000, 0)
vc.sleep(5)
vc.dump(window=-1)
vc.findViewWithTextOrRaise(u'2').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''plus''').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithTextOrRaise(u'1').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.writeImageToFile('/tmp/${serialno}-${focusedwindowname}-${timestamp}.png', 'PNG', 'auto', True, True)


The first part is the same for all culebra scripts. Some global variables, like TAG, are defined and can lately be used as parameters for some method calls like `Log.d()`.
Then the ViewClient object is created connecting to the selected device. If you don't specify any `serialno` the default device will be selected.
Once the ViewClient object is created we see the method invocations that correspond to the action we did on the GUI, closed by the invocation to `vc.writeImageToFile()` which will be creating the screenshot. In this case, and determined by the command line arguments used when we launched `culebra`, device art will be `auto` selected, and drop shadow (True) and glare (True) will be added.
The filename generation for the taken screenshot uses some keywords, replaced at save time, to avoid overwriting old ones if any.

Here is where the magic begins

As a proof-of-concept, let's physically rotate the device and run the generated script again

$ ~/tmp/calculator.py

once it runs we will obtain the correct screenshot for landscape orientation



No human intervention!