Android – Automating Testing and Store Listing Screenshots with Monkeyrunner

In this tutorial we’ll be talking about Monkeyrunner and automating tasks such as preparing screenshots for Google Play Store listings.

Monkeyrunner is a high level scripting language that is capable of automating tasks such as;

  • Installing/uninstalling apps
  • Taking screenshots and saving them to your local machine
  • Sending touch events i.e. performing gestures, pressing buttons, dragging and scrolling
  • Inputting text

There are three classes in the Monkeyrunner framework these are;

  1. MonkeyRunner – Connect to devices or emulators
  2. MonkeyDevice – Represents the device you are connected to. Allows you to perform touch events, apk installations, start activities
  3. MonkeyImage – Represents the raw image captured from the device and allows you to compare images for testing

In order to run a Monkeyrunner script you need to use the monkeyrunner compiler available in the android-sdk/tools directory, rather than python to run your python code.

<yourSDKfolder>/tools

In here you’ll have an executable moneyrunner which you’ll need to use to run your python scripts.

Go ahead and create a new python file, lets call it screenshots.py, place it anywhere you’d like. Let’s start by connecting to the device, installing your APK, taking a screenshot and saving the screenshot to our current directory.

Important
You’ll need to tell MonkeyRunner where your apk file is so that the screenshot.py can install the apk. You need to run your app on a device at least once for the IDE to generate you an apk file!

You can find your APK file in the following locations:
Eclipse
– projectFolder/yourNamespace/bin/your.apk
Android Studio
– projectFolder/yourNamespace/build/outputs/apk/your.apk

Create a py file and name it Screenshot.py – it should look like the following;

from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
 
# Connects to the first device available through the adb tool
device = MonkeyRunner.waitForConnection()
 
# install the APK
device.installPackage('your.apk')
 
# declare your package name
package = 'com.your.application'
 
# declare your activity that you want to start
activity = 'com.your.application.MainActivity'
 
# prepare the whole package + activity string
activityToRun = package + '/' + activity
 
# start the activity above
device.startActivity(activityToRun)
 
# take a screenshot
screenshot = device.takeSnapshot()
 
# Writes the screenshot to a file
screenshot.writeToFile('screenshot1.png','png')

Place your APK file in the same directory as your py script, move into your androidsdk/tools directory and run your screenshot.py file in terminal.

cd into the <androidSDK>/tools
./monkeyrunner /your/path/to/screenshot.py

Now check out the <androidsdk>/tools folder and you should have a screenshot1.png.

MonkeyRunner Screenshot

You can also automate gestures on the phone as well as button presses, more on this in the next post.

Using the device object call press and pass in the keycode along with UP, DOWN or DOWN_AND_UP to simulate the gesture you want.

device.press('KEYCODE_MENU', MonkeyDevice.DOWN_AND_UP)

You can find the complete list of keycodes here – http://developer.android.com/reference/android/view/KeyEvent.html

Android – Setting User Agent or Custom Headers in a Google Volley Request Object

In this tutorial I will show you how to override the getHeaders() function within the Request Object in the Google Volley Networking Library. You might want to add headers in your request to specify request types (for sending JSON), basic login, or manually setting a User Agent.

When you send your request off add a function called getHeaders to the request. Do this by adding the function after the closing bracket of the new Request Object.

Request request = new Request(
    Method.GET,
    url,
    Listener listener,
    ErrorListener errorListener) {
        @Override
        public Map<String, String> getHeaders(){
        Map<String, String>; headers = new HashMap<String, String>();
        headers.put("User-agent", "YOUR_USER_AGENT");
        return headers;
    }
};

This is an answer I posted about on StackOverflow about the issue.
http://stackoverflow.com/a/31984068/276220

Android – Implementing a Google Maps Search Box with AutoCompleteTextView and Geocoder API

This guide will show you how implement a similar search box to the Google Maps app, using the built in AutoCompleteTextView, the Geocoder class, a custom Adapter and a few layout files.

The final result looks like:

AutocompleteTextView with Geocoder class on Android

 

Step 1 – Implement a custom Adapter to add results to the AutocompleteTextView

public class GeoAutoCompleteAdapter extends BaseAdapter implements Filterable {
 
	private static final int MAX_RESULTS = 10;
	private Context mContext;
	private List resultList = new ArrayList();
 
	public GeoAutoCompleteAdapter(Context context) {
		mContext = context;
	}
 
	@Override
	public int getCount() {
		return resultList.size();
	}
 
	@Override
	public GeoSearchResult getItem(int index) {
		return resultList.get(index);
	}
 
	@Override
	public long getItemId(int position) {
		return position;
	}
 
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if (convertView == null) {
			LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(R.layout.geo_search_result_item, parent, false);
		}
 
		((TextView) convertView.findViewById(R.id.geo_search_result_text)).setText(getItem(position).getAddress());
 
		return convertView;
	}
 
	@Override
	public Filter getFilter() {
		Filter filter = new Filter() {
			@Override
			protected FilterResults performFiltering(CharSequence constraint) {
				FilterResults filterResults = new FilterResults();
				if (constraint != null) {
					List locations = findLocations(mContext, constraint.toString());
 
					// Assign the data to the FilterResults
					filterResults.values = locations;
					filterResults.count = locations.size();
				}
				return filterResults;
			}
 
			@Override
			protected void publishResults(CharSequence constraint, FilterResults results) {
				if (results != null &amp;&amp; results.count &gt; 0) {
					resultList = (List) results.values;
					notifyDataSetChanged();
				} else {
					notifyDataSetInvalidated();
				}
			}
		};
		return filter;
	}
 
        private List<GeoSearchResult> findLocations(Context context, String query_text) {
 
		List<GeoSearchResult> geo_search_results = new ArrayList<GeoSearchResult>();
 
		Geocoder geocoder = new Geocoder(context, context.getResources().getConfiguration().locale);
                List<Address> addresses = null;
 
                try {
                     // Getting a maximum of 15 Address that matches the input text
                     addresses = geocoder.getFromLocationName(query_text, 15);
 
                     for(int i=0;i<addresses.size();i++){
                          Address address = (Address) addresses.get(i);
                          if(address.getMaxAddressLineIndex() != -1)
                          {
                         	geo_search_results.add(new GeoSearchResult(address));
                          }
                     }
 
 
                } catch (IOException e) {
                    e.printStackTrace();
                }
 
                return geo_search_results;
	}
}

This adapter class will handle the Geocoder search request as well as filtering and drawing the results in the dropdown menu. You’ll need to go ahead and create a new class in your project named GeoAutoCompleteAdapter.

The magic happens in the getFilter function which fires off the Geocoder request and returns the results.

Step 2 – Create a custom class to handle the result

I’ve used a custom class here to handle the Geocoder address result so that if you wanted to create some logic to perhaps format the string before it’s returned to the array adapter you can do so.

public class GeoSearchResult {
 
	private Address address;
 
	public GeoSearchResult(Address address)
	{
		this.address = address;
	}
 
	public String getAddress(){
 
		String display_address = "";
 
		display_address += address.getAddressLine(0) + "\n";
 
		for(int i = 1; i < address.getMaxAddressLineIndex(); i++)
		{
			display_address += address.getAddressLine(i) + ", ";
		}
 
		display_address = display_address.substring(0, display_address.length() - 2);
 
		return display_address;
	}
 
	public String toString(){
		String display_address = "";
 
		if(address.getFeatureName() != null)
		{
			display_address += address + ", ";
		}
 
		for(int i = 0; i < address.getMaxAddressLineIndex(); i++)
		{
			display_address += address.getAddressLine(i);
		}
 
		return display_address;
	}
}

Step 3 – Create the list item view for the results from the AutocompleteTextView

This view will be used for each result returned from the Geocoder request. Feel free to redesign this as you see fit.

Save this under res/layout/geo_search_result.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/geo_search_result_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp" />

Step 4 – Create a custom AutoCompleteTextView so you don’t spam the Geocoder

With a normal AutoCompleteTextView the filter function fires on every keypress. If you did this with an address on the Geocoder API the requests will be throttled and no results will be returned. So implementing a custom AutoCompleteTextView you can Override the performFiltering where we can use a Handler thread which fires after a delay period. If a request is fired before the Hander is complete the initial request is killed and a new request is formed.

Create a new class in your project named DelayAutoCompleteTextView.java

public class DelayAutoCompleteTextView extends AutoCompleteTextView {
 
    private static final int MESSAGE_TEXT_CHANGED = 100;
    private static final int DEFAULT_AUTOCOMPLETE_DELAY = 750;
 
    private int mAutoCompleteDelay = DEFAULT_AUTOCOMPLETE_DELAY;
    private ProgressBar mLoadingIndicator;
 
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            DelayAutoCompleteTextView.super.performFiltering((CharSequence) msg.obj, msg.arg1);
        }
    };
 
    public DelayAutoCompleteTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public void setLoadingIndicator(ProgressBar progressBar) {
        mLoadingIndicator = progressBar;
    }
 
    public void setAutoCompleteDelay(int autoCompleteDelay) {
        mAutoCompleteDelay = autoCompleteDelay;
    }
 
    @Override
    protected void performFiltering(CharSequence text, int keyCode) {
        if (mLoadingIndicator != null) {
            mLoadingIndicator.setVisibility(View.VISIBLE);
        }
        mHandler.removeMessages(MESSAGE_TEXT_CHANGED);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_TEXT_CHANGED, text), mAutoCompleteDelay);
    }
 
    @Override
    public void onFilterComplete(int count) {
        if (mLoadingIndicator != null) {
            mLoadingIndicator.setVisibility(View.GONE);
        }
        super.onFilterComplete(count);
    }
}

Step 5 – Add the custom AutoCompleteTextView to your Activity’s layout file

In here I’ve used an X icon that allows the user to remove the current text inside the AutocompleteTextView. You can download this icon from here.

When you include your new custom component, use your own package name (e.g. com.yourthing.app)

Save this in your Activity’s layout, it contains the AutocompleteTextView and an ImageView containing the X icon for removing the text.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white">
 
    <YOUR_PACKAGE_NAME.DelayAutoCompleteTextView
        android:id="@+id/geo_autocomplete"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:imeOptions="flagNoExtractUi|actionSearch"
        android:inputType="textCapSentences"
        android:textColor="@color/header_text_color"
        android:background="@color/white"
        android:hint="@string/map_geo_search_input_hint"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="35dp"
        android:layout_marginRight="35dp"
        android:layout_marginTop="4dp"
        android:layout_marginBottom="4dp"
        android:padding="10dp"
        android:dropDownWidth="fill_parent"
    />
 
   <ImageView
        android:id="@+id/geo_autocomplete_clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_action_remove"
        android:contentDescription="@string/map_list_header_txt"
        android:layout_gravity="center_vertical|end"
        android:visibility="gone"
        android:layout_marginEnd="12dp"
        android:layout_marginRight="12dp"
	/>
</FrameLayout>

Step 6 – Assemble the components together

Add this code to the activity you wish to include the AutocompleteTextView and you should now have a Google Maps search box in your app!

        private Integer THRESHOLD = 2;
	private DelayAutoCompleteTextView geo_autocomplete;
	private ImageView geo_autocomplete_clear;
 
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_geocoder);
 
	    geo_autocomplete_clear = (ImageView) findViewById(R.id.geo_autocomplete_clear);
 
	    geo_autocomplete = (DelayAutoCompleteTextView) findViewById(R.id.geo_autocomplete);
	    geo_autocomplete.setThreshold(THRESHOLD);
	    geo_autocomplete.setAdapter(new GeoAutoCompleteAdapter(this)); // 'this' is Activity instance
 
	    geo_autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
	            @Override
	            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
	                GeoSearchResult result = (GeoSearchResult) adapterView.getItemAtPosition(position);
	                geo_autocomplete.setText(result.getAddress());
	            }
	        });
 
	    geo_autocomplete.addTextChangedListener(new TextWatcher() {
 
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
 
            }
 
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
 
            @Override
            public void afterTextChanged(Editable s) {
            	if(s.length() > 0)
            	{
            		geo_autocomplete_clear.setVisibility(View.VISIBLE);
            	}
            	else
            	{
            		geo_autocomplete_clear.setVisibility(View.GONE);
            	}
            }
        });
 
	    geo_autocomplete_clear.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				geo_autocomplete.setText("");
			}
	    });
 
	}

The majority of this codebase was developed by Alex Melnykov, I’ve just implemented the Geocoder search on top of this and added in an icon to clear the search box and format the results.

Android – How to deploy your android app without the market place or google play

I’m currently working on an app which I need to deploy to a group of people however I don’t want to deploy it to the market as it’s not ready for public consumption yet.

So I did a bit of googling and found that in order to deploy your app to other android phones you can do a couple of things;

1) Upload the .apk file directly on a server somewhere on the net and let users download the apk file to install

This method will allow users to open their android browser and type in the URL (e.g. bit.ly/2345 or http://yourawesomeapp.com/app.apk). Once they have opened the page it will begin the download of the apk file and then run the user through the install process. However, its important that the user has gone to Settings > Applications > Unknown Sources and enabled this option! Otherwise the application will not install!

2) Similar to the first for deployment but you can digitally sign your app

see here; http://stackoverflow.com/a/4600933/276220

3) Get the user to download the apk file and then mount their phone on the computer and drag over the apk file to the handset

I’m not a huge fan of this one as it might be a bit too complex for some people.

Eventually I’ve used the hosted apk solution (see 1) and on a page somewhere I’ve added a url for them to type and also a qr code to scan (make sure you generate a URL qr code and not a text version otherwise the browser may not load the url). Using the QR code method will allow the user will be taken directly to the url, making the user’s lifea little bit easier as they have nothing to type.

Android – Setting screen brightness from code behind

I ran into a problem today with trying to override the screen brightness for the current activity. I found there are 2 methods of changing your screen brightness, 1 which changed the whole system brightness or the other which is to change your current activity’s brightness.

I went for the latter however I came up against some problems when I tried to use the code I found here;

http://stackoverflow.com/a/5090578/276220

 I realised my eclipse was complaining because I had the project set as an android 2.1 project rather than the 2.3.3 handset which I was debugging on.
It would seem that you used to be able to just set it with;
public void SetBright(float value) 
{
    Window mywindow = getWindow();
    WindowManager.LayoutParams lp = mywindow.getAttributes();
    lp.screenBrightness = value;
    mywindow.setAttributes(lp);
}
However when google updated android with the option to set automatic brightness that method no longer worked. Therefore you had to check if the auto brightness had been set so that you could tell android that you were going to manually override the brightness
public void setBrightness(float brightness){ 
    try{
	int brightnessMode = android.provider.Settings.System.getInt(getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE);
 
        if (brightnessMode == android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
		android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
	}
 
	WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
	layoutParams.screenBrightness = brightness;
	getWindow().setAttributes(layoutParams);
    } catch (Exception e){
        // do something useful
    }
}
I also found this which is a nice slider attached to the brightness, however their post looks pretty awful to read so I’d suggest check the stackoverflow post first before you try and understand this one 😀
http://android-er.blogspot.com/2011/02/change-system-screen-brightness-using.html