Monday, April 14, 2008

Android Developer Challenger: Electron yet another demo

As promised in Android Developer Challenger: Electron and a slightly more interesting demo here you are !
Another Electron demo, this time we have an extremely simple Web Activity application driven from the Electron test web service.



We can see in this test how we start the activity, wait for its progress until loaded completely, scroll down, follow a link and finishing.

What kind of application being tested would you like to see ?

Android Developer Challenger: Electron and a slightly more interesting demo

In my previous post about Android Developer Challenger: Electron Acceptance and Unit Testing Framework we could watch some demos about two simple cases.
Here I'm presenting a slightly more interesting one: driving the Touch Paint application through the test web service.
I hope you enjoy it.



We can see how the activity is started, non-fade is selected from the menu, pointer commands are sent and translated to events by Electron and finally fade is enabled, wait some time and the application is finished.

Still, more to come.

Saturday, April 12, 2008

Android Developer Challenger: Electron Acceptance and Unit Testing Framework

My first intention was to submit a different application, but in the process of developing this application and trying to apply Test Driven Development and Design techniques, I found that although Google is providing JUnit in Android, it was not fully supported, fully documented, fully whatever.

Some Unit Tests can be run in Eclipse, but you were not be testing on the real target. So be careful on that.

Google also provides application instrumentation code through Instrumentation class. When running with instrumentation turned on, this class will be instantiated for you before any of the application code, allowing you to monitor all of the interaction the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml's .

In the process of writing my own library and tools, I discovered Phil Smith's positron an Open Source library, so I decided to extend his excellent work and bring some new and missing pieces to the table.

And although positron is the anti-particle of the electron, and they annihilate if collide, take for granted that it's not the idea here, but more the conservation of the energy and the generation of some "visible light and waves".

Featured components

Android Electron Acceptance And Unit Tests
Android Electron Acceptance and Unit Test contains some sample applications and their tests:
  • Temperature Converter
  • Touch Paint
  • Web Activity
Android Electron Test Results
Android Electron Test Results is the application used to display test results.
In this video demonstration, Temperature Converter application is tested and some tests failures are showed, because the temperature conversion function is not yet implemented and returns always zero, as an step in the Test Driven Development process.



There's also a web service like interface to drive the tests, as showed in this video.




Electron-JavaSE
Electron Java SE version to drive the tests from the development environment. The ZIP contains the needed components to run Electron, Javadocs and some sample test scripts.

Tests can be scripted, and for example, the test we saw in the video demonstration before can be written as

#! /usr/bin/env electron
start?package=com.codtech.android&\
class=com.codtech.android.adc.tdd.TemperatureConverterActivity

press 123
DOWN
DOWN
CLICK
@celsius.text
assert 123
@fahrenheit.text
assert 253 123C_=>_253F
finish
Stay tuned. More on Electron later.

Saturday, March 22, 2008

Automating port redirection in Android

If you are developing an application that uses port redirection you should open the emulator's console and issue the redir commands every time you restart the emulator. This is documented by Google in android emulator page.
To redirect port you need to
telnet localhost 5554

to open the emulator's console and then add the desired redirection commands, like
redir add tcp:5000:5000

But, is there another way than typing the commands every time ?
It's much likely that you make a mistake and wonder why your application stopped working until you realize your typing mistake.
I couldn't find any other documented way of automating port redirection, so I decided to go my way.

Python to the rescue
This is clearly a task for expect, but instead of trying to remember how to program TCL I decided to go with the python alternative: pexpect.

The script is named, pretty obviously, android-redir.py:

Download the script from here.

#!/usr/bin/env python
"""
$Id$
"""

__license__ = """

Copyright (C) 2008 Diego Torres Milano

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA
"""

__name__ = 'android port redirection'
__version__ = '0.3'
__rev__ = '$Revision$'

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# redirections can be added to ~/android/redir.conf, one per line,
# as an example, 'tcp:5000:6000' will route any packet sent to the host's
# TCP port 5000 to TCP port 6000 of the emulated device
#
# default emulated device:
host = 'localhost'
port = 5554
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import sys
import os
from optparse import OptionParser
import pexpect

usage = "usage: %prog [options] [{tcp|udp}:hostport:emuport] ..."

parser = OptionParser(usage=usage, version="%prog " + __version__)
parser.add_option("-v", "--verbose", dest="verbose", default=False,
action="store_true",
help="verbose output [default: %default]")
parser.add_option("", "--host", dest="host", default=host,
help="HOST where emulator is running [default: %default]")
parser.add_option("-p", "--port", dest="port", default=port,
help="emulator console PORT [default: %default]")
parser.add_option("-n", "--dry-run", dest="dry_run", default=False,
action="store_true",
help="Don't execute the commands")
(options, args) = parser.parse_args()

redirs = []
cmd = 'telnet %s %d' % (options.host, options.port)

try:
for redir in open(os.path.expanduser("~") + "/.android/redir.conf"):
redirs.append(redir[:-1])
except:
pass

for redir in args:
redirs.append(redir)

if options.verbose:
print cmd
if len(redirs) > 0:
print "adding redirections:"
for redir in redirs:
print "\t" + redir

if options.dry_run:
sys.exit(0)

if len(redirs) == 0:
print >>sys.stderr, "ERROR: redirection list is empty."
sys.exit(1)

child = pexpect.spawn(cmd)
child.expect("OK")

for redir in redirs:
child.sendline("redir add " + redir)
r = child.expect(["OK", "KO: host port already active", "KO:.*"])
if r == 2:
raise Exception(child.after)

if options.verbose:
child.sendline("redir list")
child.expect("OK")
print child.before

child.sendline("quit")


Usage
This is the script help showing its usage, accessible by
$ android-redir.py --help
Usage: android-redir.py [options] [{tcp|udp}:hostport:emuport] ...

Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose output [default: False]
--host=HOST HOST where emulator is running [default: localhost]
-p PORT, --port=PORT emulator console PORT [default: 5554]
-n, --dry-run Don't execute the command


You can also create ~/.android/redir.conf file and add the redirections you want, one per line
tcp:5000:5000
udp:50001:50001


and they will be read every time the script is run.


Next time we will be looking at how to integrate this with the emulator to run both commands at the same time.

Tuesday, March 11, 2008

Test Driven Development and GUI Testing on the Android platform: Temperature Converter sample revisited

Test Driven Development and GUI Testing on the Android platform: Temperature Converter sample revisited

This document is published at http://docs.google.com/Doc?id=ddwc44gs_41dhjhbdd2

WARNING: Blogspot engine change the link, so you have to cut and paste.

Functional tests

The new Google Android SDK provides some improvements to the Instrumentation code and the ways of handling it. Using Dev Tools, now you can find and Instrumentation list entry that shows all of the classes providing Instrumentation and a way to run it.

Positron

We will be using positron positron-0.8-alpha and we will introduce one small change. Positron only presents the test results in the DDMS perspective

INFO/Positron(16414): .F..F...
INFO/Positron(16414): Time: 6.46
INFO/Positron(16414): There were 2 failures:


the idea is to use an Intent to be able to present the results as an activity: AndroidTestResults.
AndroidTestResults listen to the Intent broadcasted by Positron.
First, let's introduce this changes to the base Positron
  1. we have to define the intent
  2. using a Template Method pattern with a method called doHandleResult() implement invariant and variable parts of the algorithm
  3. define a default donHandleResult() to do nothing

public abstract class Positron extends Instrumentation { public static final String VIEW_TEST_RESULTS_ACTION = "positron.action.VIEW_TEST_RESULTS"; ... @Override public void onStart() { backup(); ByteArrayOutputStream testOutput = new ByteArrayOutputStream(1024); InstrumentedTestRunner runner = new InstrumentedTestRunner(this, new PrintStream(testOutput)); InstrumentedTestResult result = (InstrumentedTestResult)runner.doRun(suite()); Log.i(TAG, testOutput.toString()); // this implements a TEMPLATE METHOD pattern // by DTM doHandleResult(result); finishAll(); pauseButton.quit(); restore(); waitForIdleSync(); finish(result.errorCount() == 0 && result.failureCount() == 0 ? 0 : 1, result.toBundle()); } ... /** handle the result in the desired way */ protected void doHandleResult(InstrumentedTestResult result) { // do nothing } }
doHandleResult gives us the ability to handle the results in the way we want.

As part of the instrumentation of TemperatureConverter tests we have implemnented a class Positron that extends positron.Positron. In this class we need to implement the abstract method suite() returning our TestSuite, but now we are also adding our Template Method.

@Override protected void doHandleResult(InstrumentedTestResult result) { Intent intent = new Intent(VIEW_TEST_RESULTS_ACTION); intent.putExtras(result.toBundle()); intent.putExtra("suite", suite().getName()); getContext().broadcastIntent(intent); }

OverallTest

Let's change our OverallTest
package com.codtech.android.tdd.validation;
import java.io.IOException; import android.content.Intent; import com.codtech.android.tdd.R; import com.codtech.android.tdd.TemperatureConverterActivity;
/** * @author diego * */ public class OverallTest extends positron.TestCase { private static final String TAG = "OverallTests"; private TemperatureConverterActivity activity; /** * @param name */ public OverallTest(String name) { super(name); } /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); Intent intent = new Intent(getTargetContext(), TemperatureConverterActivity.class); startActivity(intent.addLaunchFlags(Intent.NEW_TASK_LAUNCH)); activity = (TemperatureConverterActivity)activity(); // Is it our application ? assertEquals(getTargetContext().getString(R.string.app_name), activity().getTitle()); // Do we have focus ? assertEquals(activity.getCelsiusEditText(), activity.getCurrentFocus()); } /* (non-Javadoc) * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception { finishAll(); } public void testConversion() { // Enter a temperature sendString("123"); // Convert press(DOWN, DOWN, CENTER); // Why 2 downs are needed ? // Verify correct conversion 123C -> 253F assertEquals("valid conversion", "253", activity.getFahrenheitEditText().getText().toString()); } public void testValidCharachters() { // Enter invalid characters (only digits and +/- are allowed String str = "-0123456789"; sendString(str); assertEquals("invalid characters", str, activity.getCelsiusEditText().getText().toString()); } public void testInvalidCharachters() { // Enter invalid characters (only digits and +/- are allowed // in this "digits" mode, some keys are automatically converted into its // numeric keypad equivalentes (i=>-, o=>+, p=>+, etc.) sendString("abcdefghpqrstuvwxyz"); assertEquals("invalid characters", "", activity.getCelsiusEditText().getText().toString()); } public void testInvalidConversion() { // Enter a temperature sendString("-274"); // Convert press(DOWN, DOWN, CENTER); // Why 2 downs are needed ? // Verify that the wrong value is still displayed assertEquals("invalid conversion", "-274", activity.getCelsiusEditText().getText().toString()); // FIXME // there's still no way to determine if the dialog is showed //assertTrue("exception raised and handled", activity.getFahrenheitEditText().getText().toString().contains("below absolute zero")); } public static void main(String[] args) { try { positron.RunTests.main(new String[] {"com.codtech.android.tdd", ".Positron", "/opt/java/android-sdk"}); } catch (IOException e) { e.printStackTrace(); } } }

Now, we can run our Acceptance Test just running OverallTest as a Java Application (right click, RunAs -> Java Application).
In order for this to work we need TemperatureConverter installed into the emulator and the emulator running.
The Instrumentation will run, and just before finishing, Positron will be broadcasting and intent ("positron.action.VIEW_TEST_RESULTS")which is then received by an IntentReceiver which in turn start an activity.

Here, there are tow videos that show the process of launching the tests from Dev Tools.





Monday, March 03, 2008

Android: Playing with Intents

Android: Playing with Intents



Android Intent Playground 2.0 is described in this post:
Diego Torres Milano's blog: Android Intent Playground 2.0

This document is published at http://docs.google.com/Doc?id=ddwc44gs_31gjdwgxfh

WARNING: Blogspot engine change the link, so you have to cut and paste.

Intents

From Google documentation: “An Intent is a simple message object that represents an ”intention” to do something. For example, if your application wants to display a web page, it expresses its “Intent” to view the URI by creating an Intent instance and handing it off to the system. The system locates some other piece of code (in this case, the Browser) that knows how to handle that Intent, and runs it. Intents can also be used to broadcast interesting events (such as a notification) system-wide.”

An intent is an abstract description of an operation to be performed. It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested IntentReceiver components, and startService(Intent, Bundle) or bindService(Intent, ServiceConnection, int) to communicate with a background Service.

Intents are one of the most distinctive components of the Android platform. They, among other things, provide the transition between Activities in a similar model as the browser model, where the user navigates from on page to the other and can also go back.

Intent attributes

Intent
primary attributes
action The general action to be performed, such as VIEW_ACTION, EDIT_ACTION, MAIN_ACTION, etc.
data The data to operate on, such as a person record in the contacts database, expressed as a Uri.
secondary attributes
category Gives additional information about the action to execute. For example, LAUNCHER_CATEGORY means it should appear in the Launcher as a top-level application, while ALTERNATIVE_CATEGORY means it should be included in a list of alternative actions the user can perform on a piece of data.
type Specifies an explicit type (a MIME type) of the intent data. Normally the type is inferred from the data itself. By setting this attribute, you disable that evaluation and force an explicit type.
component Specifies an explicit name of a component class to use for the intent. Normally this is determined by looking at the other information in the intent (the action, data/type, and categories) and matching that with a component that can handle it. If this attribute is set then none of the evaluation is performed, and this component is used exactly as is. By specifying this attribute, all of the other Intent attributes become optional.
extras - This is a Bundle of any additional information. This can be used to provide extended information to the component. For example, if we have a action to send an e-mail message, we could also include extra pieces of data here to supply a subject, body, etc.

The best way to explore all of this somewhat complex model is to try it out.

Official Google documentation has some mistakes that turn your exploration a bit more difficult.

For example in http://code.google.com/android/reference/android/content/Intent.html there are some mistakes.

Some examples of action/data pairs are: * VIEW_ACTION content://contacts/1 – Display information about the person whose identifier is “1”. * EDIT_ACTION content://contacts/1 – Edit information about the person whose identifier is “1”. * VIEW_ACTION content://contacts/ – Display a list of people, which the user can browse through. This example is a typical top-level entry into the Contacts application, showing you the list of people. Selecting a particular person to view would result in a new intent { VIEW_ACTION content://contacts/N } being used to start an activity to display that person. * PICK_ACTION content://contacts/ – Display the list of people, allowing the user to browse through them and pick one and return it to the parent activity. This could be used, for example, if an e-mail application wanted to allow the user to pick a person

Should say

  • VIEW_ACTION content://contacts/people/1 – Display information about the person whose identifier is “1”.
  • EDIT_ACTION content://contacts/people/1 – Edit information about the person whose identifier is “1”.
  • VIEW_ACTION content://contacts/people/ – Display a list of people, which the user can browse through. This example is a typical top-level entry into the Contacts application, showing you the list of people. Selecting a particular person to view would result in a new intent { VIEW_ACTION content://contacts/people/N } being used to start an activity to display that person.
  • PICK_ACTION content://contacts/people/ – Display the list of people, allowing the user to browse through them and pick one and return it to the parent activity. This could be used, for example, if an e-mail application wanted to allow the user to pick a person

Intent Playground

To explore these relationships, I've made a simple application that allows you to play with Intents and see the results.

This application completes the intent attributes with the information provided and sometimes with other information obtained from it and then invokes the corresponding method.



AndroidIntentPlayground source and binaries can be downloaded from AndroidIntentPlayground.zip.





action

It's an AutoCompleteTextView to select an action from the list (try entering the first letter as a or c. You can also enter the desired action if it's not in the list.

See Intent attributes.

data uri

The data Uri to act on.

See Intent attributes.

type

The MIME type.

See Intent attributes.

intent

Select the kind of method to invoke

  • broadcast: use broadcastIntent(intet)
  • activity: use startActivity(intent)
  • service: use startService(intent, null)
  • resolve: use Intent.resolveActivity()

activity

When the activity is resolved the class name is showed here.

button

Press the button to send the Intent.

Examples

broadcast

Our first example is an intent broadcast. If there's an IntentReceiver which has an Intent that matches our attributes then its onReceiveIntent() method will be invoked. I've explained how to create an IntentReceiver only application in a previous post. If you have installed the SampleIntentReceiver.APK, we can try this example.

Set action to com.codtech.android.training.intent.SAMPLE_ACTION, leave the other fields blank and the intent to broadcast. Press the button and you'll see


Explanation

If we try to resolve the Intent we will receive an error, but broadcasting the intent it reaches our SampleIntentReceiver and the dialog is displayed.

This intent filter in our SampleIntentReceiver AndroidManifest.xml matches

        <receiver android:name=".SampleIntentReceiver">
<intent-filter>
<action android:name="com.codtech.android.training.intent.SAMPLE_ACTION"/>
</intent-filter>
</receiver>

start activity

Set action to android.intent.action.VIEW and data uri to content://contacts/people/1, set intent to activity and the press the button.


Explanation

After pressing the button we will see our first contact displayed.

We hit a bug. It's not possible to obtain the screenshot using DDMS, instead of the contact the previuos activity is displayed.

The intent was resolved to com.google.android.contacts.ViewContactActivity and this activity is invoked by startActivity(intent).

service

You need ApiDemos installed for this to work.

Set action to com.google.android.samples.app.REMOTE_SERVICE, leave the other fields blank, and set intent to service. Press the button.

And the RemoteService will be started.

The triangle in the upper left corner indicates that the service is running.

However, if we try to stop the service, opening the sliding top panel and clicking on SampleRemoteService instead of getting this service activity we'll get LocalServiceController and we cannot stop it. Another bug ?

Workaround: Go to Home, ApiDemos → App → Service → Remote Service Controller and the Stop Service.

Explanation

The intent is used as a parameter of startService(intent, null) and the requested service is started if needed.

resolve

Se action to android.intent.action.WEB_SEARCH, enter some strings into data uri, for example cult thinclient. Set intent to resolve and the press the button. We can see that the action is resolved to com.google.android.browser.BrowserActivity. If we further change intent to activity and press the button again, we will see the browser starting and doing the Google search.

Explanation

The activity is resolved to the corresponding class, and if we use this information to start the activity we will see the web browser presenting the search.

Conclusion

This application, Intent Playground, has helped me a lot to discover some interactions between the Android components.

However, I was not able to intercept the intent used by the contacts application when you try to edit its photo. Could it be using an intent instantiated used an explicit class com.google.android.fallback.Fallback ?

Do you have any clue ?

Friday, February 29, 2008

IntentReceiver only application

IntentReceiver only application

Google Docs: This document is published at http://docs.google.com/Doc?id=ddwc44gs_27dwrnrwrk
WARNING: Blogspot engine change the link, so you have to cut and paste.

If you want to react in some way to one or various Intents and you don't need an Activity you can have an IntentReceiver only application. You class must extend IntentReceiver and you have to override onReceiveIntent method.

This has been asked many time in android groups, but there was no clear answer and ApiDemos doesn't show an example of this either. So I hope this helps and saves you some time.

Create AndroidSampleIntentReceiver project

Create AndroidSampleIntentReceiver project as usual.

You can use Dummy as the Activity and Application name. We will remove them shortly.

Delete Dummy.java

Go to the project and delete Dummy.java right clicking on it and selecting Delete from menu.

Create SampleIntentReceiver class

Right clicking on the com.codtech.android.training.sampleintentreceiver or whatever you've selected for your package, create a new Java class


and edit its content to have

/**
* Copyright © 2008 Diego Torres Milano <dtmilano at gmail dot com>
*/

package com.codtech.android.training.intentreceiver;

import android.content.Context;
import android.content.Intent;
import android.content.IntentReceiver;
import android.util.Log;
import android.widget.Toast;

/**
* @author diego
*
*/

public class SampleIntentReceiver extends IntentReceiver {
private static final String TAG = "SampleIntentReceiver";

/*
* (non-Javadoc)
*
* @see android.content.IntentReceiver#onReceiveIntent(android.content.Context,
* android.content.Intent)
*/

@Override
public void onReceiveIntent(Context context, Intent intent) {
String msg = "SampleIntentReceiver:nonReceiveIntent(context="
+ context.getPackageName()
+ ", intent=[" + intent.getAction() + ", " + intent.getType()
+ ", " + intent.getData() + "])";
Log.d(TAG, msg);
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}

}

Modify AndroidManifest.xml

Refactoring the code doesn't change the AndroidManifest.xml, perhaps a deficiency of the Eclipse ADT plugin, so we need to manually edit it. Our intention is to have an application consisting only in an IntentReceiver, with no Activity and this is precisely what we need to remove from this file.

Use the XML view in the editor.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codtech.android.training.sampleintentreceiver">

<application android:icon="@drawable/icon">
<receiver android:name=".SampleIntentReceiver">
<intent-filter>
<action android:name="com.codtech.android.training.intent.SAMPLE_ACTION"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT"/>
<data android:mimeType="image/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
</application>
</manifest>

We have just added some sample intent-filters, you can add the ones you are interested in.

Install the application

Install you application APK using

 $ adb install AndroidSampleIntentReceiver/bin/AndroidSampleIntentReceiver.apk

Once the application is installed, there's no need to run it. Android will be responsible to run it and invoke onReceiveIntent.

Broadcast an Intent

From your application code, that is another application you want it to broadcast some Intents that will be “trapped” by our SampleIntentReceiver. For example

   Intent intent = new Intent("com.codtech.android.training.intent.SAMPLE_ACTION");
// do something
broadcastIntent(intent, null);

and if everything went well


Thursday, February 28, 2008

Android: Contact photos


Android: Contact photos

You may wonder how you can add photos to your contacts in Android SDK M5. There are at least two very different ways:

  • the correct way
  • the quick and dirty hack way

There's no need to say that the correct way, using Intents didn't work at first, and even we are in an “Open Source” platform, we don't have the sources to check how the Contacts application is shaping its intent to edit the contact's photo.

I've been trying to discover this intent, but no luck yet. More on this later.

So we are destined to the quick and dirty hack.

Contacts database

It's no news that contacts, by default, are stored in a sqlite3 database. I'm putting some emphasis in this because one of the most relevant features of the Android platform is the “All applications are equal” model. So, strictly speaking, Contacts application may have been replaced. However, we will restrict this to the Android default Contacts application, including its ContentProvider. To turn our quest easier, sqlite3 command is provided in the emulator image.

Get a shell to your running emulator using

 $ adb shell

once in the shell

 # sqlite3 /data/data/com.google.android.providers.contacts/databases/contacts.db

There are some tables in this database. We are interested in the people table. If we obtain the schema

CREATE TABLE people (
_id INTEGER PRIMARY KEY,
_sync_account TEXT,
_sync_id TEXT,
_sync_time TEXT,
_sync_version TEXT,
_sync_local_id INTEGER,
_sync_dirty INTEGER,
_sync_mark INTEGER,
name TEXT NOT NULL,
notes TEXT,
photo TEXT,
company TEXT,
title TEXT,
times_contacted INTEGER NOT NULL DEFAULT 0,
last_time_contacted INTEGER,
starred INTEGER NOT NULL DEFAULT 0,
preferred_phone INTEGER REFERENCES phones(_id),
preferred_email INTEGER REFERENCES contact_methods(_id)
);

Obviously, the photo holds the reference to the contact's photo. _id is the unique id of the cont

act.

Photos

We have to store the photos in the filesystem. Let's create a directory to store them.

 # mkdir /data/data/com.google.android.providers.contacts/photos

The push some photos using

 $ adb push <local> <remote>

You can use whatever name you like, but it will be easier if you use the contact unique id to name the photo. We have seen the _id column in the schema before. If you want to verify the ids

 # sqlite3 /data/data/com.google.android.providers.contacts/databases/contacts.db 'select _id,name from people;'

will show something like this

1|Donald Duck
2|Mickey Mouse
3|Pluto
4|Minnie Mouse
5|X


thus, our photos directory should look like this after we transfer the images

-rw-rw-rw-    1 0        0            4625 Feb 27 16:05 1.png
-rw-rw-rw- 1 0 0 4715 Feb 27 16:05 2.png
-rw-rw-rw- 1 0 0 4873 Feb 27 16:05 3.png
-rw-rw-rw- 1 0 0 3085 Feb 27 16:05 4.png

Update the people table

Using sqlite3

# sqlite3 /data/data/com.google.android.providers.contacts/databases/contacts.db
sqlite> UPDATE people SET photo = '/data/data/com.google.android.providers.contacts/photos/' || _id || '.png';

Conclusion

If everything went well, you are now seeing something similar to the previous screenshot. Definitely it's not the way to go, but if you want to provide some photos of your contacts in a quick and dirty way you can do it.

You may say “But wait, what is the correct way ?”

If you touch (or click) the photo while editing a contact you will receive this Unsupported action.

I've tried to receive this Intent to no avail using this intent filter

   <intent-filter>
<action android:name="android.intent.action.GET_CONTENT"/>
<data android:mimeType="image/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

but for some reason, it doesn't match. Do you have an idea ? We would like to know.

Thursday, February 14, 2008

New Android UI


Recently released Android SDK M5 changes the UI almost completely, as well as many aspect of the API that brake application code.
This is understandable as the SDK is under development and these are pre-releases.
Personally, I think that the UI will be heavily changed in next installment of the SDK preview, so it's better if you don't run changing your Android Development Challenge application because perhaps there's another big change before the deadline.
I'm just speculating.

Look at this example. If the API Demos should also be considered as a kind of informal style guide the use of the screen real estate is alarming.
Showing only four options covering the entire screen seems too much.
From the usability point of view there's also some problems. Not showing the scrollbar, how do the users guess that there are some hidden options ?

Friday, February 08, 2008

Android Emulator Speed

Some posts in Android forums have risen the question: how fast is the emulator compared with a real device ?
Honestly, I don't know. Because if you believe that there's no real devices to test out there, not even at MIT, nobody should know.
But we can investigate how the emulator is configured and how fast its CPU is.

Here is the emulator CPU configuration
# cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
BogoMIPS : 444.00
Features : swp half thumb fastmult vfp edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5
Cache type : write-through
Cache clean : not required
Cache lockdown : not supported
Cache format : Harvard
I size : 4096
I assoc : 4
I line length : 32
I sets : 32
D size : 65536
D assoc : 4
D line length : 32
D sets : 512

Hardware : Goldfish
Revision : 0000
Serial : 0000000000000000

So far, so good. The emulated ARM926EJ-S rev 5 (v5l) is running at 444 bogomips.
What's a bogomip you may wonder. Well according to the Unix Guide, BogoMips is a combination of Bogus and Mips. MIPS stands for (depending on who you ask) Millions of Instructions per Second, or Meaningless Indication of Processor Speed.

For a meaningful comparison, my laptop having Intel(R) Core(TM)2 CPU T5600 @ 1.83GHz, two cores, gives a total of 7319.

If you are running Linux and intrested in obtaining this in your box, just run

$ awk '/bogomips/ {b+=$3} END {print b}' /proc/cpuinfo

Tuesday, February 05, 2008

Undroid

If NetBeans is the IDE of your preference you should take a look at Undroid, a plugin to enable Google’s Android SDK development.
I've installed it without a problem but seems that debugging support is missing in this first release.
I will give it a try and comment the experience in another post.

Thursday, January 31, 2008

Desktop Android


This thread asks an interesting question: is it possible to run Android UI on desktop ?
Well, I think that is possible but very difficult, and almost impossible if the source code is not available. I said almost, because as the well know sports brand remind us: impossible is nothing.

In the meantime, this screenshot shows how using tweaked skins you can run the emulator in a native window, even at a bigger size. This one is 640x480.

There's no special keys or buttons (dpad) here, so you must remember this



















F1/PgUp Menu key
F2/PgDown Star key
F3 Call key
F4 End Call key
Home Home key
ESC Back Key
F7 Power button
F8 Disable/Enable all networking
F9 Start tracing (only with -trace)

F10 Stop tracing (only with -trace)
Keypad 2468 Dpad arrows
Keypad 5 Dpad center
Keypad 79 Rotate device skin
Keypad + Volume Up key
Keypad - Volume Down key
Keypad / Decrease onion alpha
Keypad * Increase onion alpha
Ctrl-K Switch between 'unicode' and 'raw' keyboard modes

Download the WINDOW-L and BIGWINDOW-L skins from http://codtech.com/downloads/android-window-l-skins.zip, unzip it in some directory of your preference (/home/android/skins in the following example) and then run the emulator

$ emulator -skindir /home/android/skins -skin BIGWINDOW-L

Tuesday, January 29, 2008

Android Radio Logger: The code is here

You may be wondering how to obtain incoming and outgoing voice and sms events data, mainly the phone number.
Well, finally you can download the code for this workaround from http://codtech.com/downloads/AndroidRadioLogger.zip
A quick and dirty solution, to keep you concentrated on the application being developed and not on how to hack android.

Run Radio Logger, and if you get the error

java.io.FileNotFoundException: /tmp/radio

don't forget to obtain a shell in the emulator running

adb shell

and once in the emulator prompt run

# cat /dev/log/radio > /tmp/radio & ### note the final ampersand

and eventually re-launch the Radio Logger application again.

When this application is running you will be able to see small notification windows at the bottom right of the screen indicating the GSM event detected, actually incoming and outgoing voice and sms, as is showed in the video. Other events should be easily added.

As always, comments are gladly welcome.

Friday, January 25, 2008

Android Radio Logger

A fancy name for a dirty hack...
It seems that there's no way to intercept android's call to obtain the incoming or outgoing number
as discussed in this thread.

Well, until now


The notification at the lower right, showing the GSM events detected is provided by the Radio Logger Activity.
Source code will be published soon, after fixing a couple of issues.
Drop me a line if you can't wait.

Thursday, January 24, 2008

Android Positron small problems fixed



If you have followed the previous tutorial: http://dtmilano.blogspot.com/2008/01/test-driven-development-and-gui-testing.html, and wanted to use positron-0.5-alpha for your tests you may have found two small problems:
  1. positron-0.5-alpha.jar is empty, in the positron downloads
  2. an IllegalArgumentException is generated while running the tests
Solution
Build positron-0.5-alpha.jar from sources and apply the patch http://code.google.com/p/android-positron/issues/attachment?aid=-2700323565965776159&name=android-positron-databasedir-svn.patch

This was already reported to positron:
  1. http://code.google.com/p/android-positron/issues/detail?id=1
  2. http://code.google.com/p/android-positron/issues/detail?id=2

Wednesday, January 23, 2008

Test Driven Development and GUI Testing on the Android platform: Temperature Converter sample

Google Docs: This article can be viewed at Test Driven Development and GUI Testing on Android platform: Temperature Converter sample

Unit tests

This article will introduce Test Driven Development on the Google Android platform. I took me some time to figure it all out and it's not always clear where to find the correct documentation, answers or resources, so I'm writing this hoping it be helpful for someone who is trying to take this approach.

This is an open topic, an we are all doing the first steps and trying to discover some functionality which is still not documented in the Android SDK, and up to now there's no sources to check what's the intention of some API code or tools.

We will be using a fairly simple example that was used in previous articles about similar subjects. That's a Temperature Converter application. Requirements are defined by our Use Case: Convert Temperature.

Using Test Driven Development we will implement this use case, trying to keep it as simple as possible not to get confused with unnecessary details.


Comments, corrections, improvements, critics are gladly welcome.

Project

  1. Create a Android project project in Eclipse (File->New Project...->Android->Android Project)
  2. Name the project something like TDD Android
  3. Package name: com.example.tdd
  4. Activity name: TemperatureConverterActivity
  5. Application name: Temperature Converter
  6. Finish


Add testing infrastructure using JUnit

We are using a different folder to keep our tests in order to permit a clear separation in case the test should be removed at production.

  1. Select TDD Android project node in Packages
  2. Right click and select New Source Folder
  3. Folder name:test
  4. Finish

Then

  1. Select the newly created test folder
  2. Select New Java Package
  3. Name: com.example.tdd
  4. Finish


Generate test classes (JUnit version)


  1. Right click in the TemperatureConverter class
  2. Select New JUnit Test Case
  3. Select JUnit 3.x
  4. Source folder: TDD Android/test
  5. Finish

Now we will add our tests, even though our TemperatureConverter class is not clearly defined. Adding the tests will help us define our TemperatureConverter class.

Define the tests

Accordingly with our use case in Temperature Converter we can define 2 tests

  • testCelsiusToFahrenheit
  • testInvalidCelsiusTemperature

to verify our tests' resluts we will use some data obtained from an external converter and we will put these figures in a conversion table. Some temperature measure background can be obtained from http://en.wikipedia.org/wiki/Temperature and an online converter can be found at http://www.onlineconversion.com/temperature.htm. We will add a conversionTable to verify our tests shortly.

Edit the source file and:

  1. add public void testCelsiusToFahrenheit()
  2. add public void testInvalidCelsiusTemperature()


And use this code snippet

 public void testCelsiusToFahrenheit() {
fail("Not implemented yet");
}

public void testInvalidCelsiusTemperature() {
fail("Not implemented yet");

Adding the conversion table

Also in the TemperatureConversionTest class

  1. Add an attribute private static final Map<Integer, Integer> conversionTable
  2. Set its default value to new HashMap<Integer, Integer>()
  3. Shift + Ctrl + O to resolve the imports

Initialization code

  1. Initialize the conversion table to
static { // initialize (c, f) pairs conversionTable.put(0, 32); conversionTable.put(100, 212); conversionTable.put(-1, 30); conversionTable.put(-100, -148); conversionTable.put(32, 90); conversionTable.put(-40, -40); conversionTable.put(-273, -459); }

just after its definition

Run the tests

Right click on the TDD Android project node and select Run as JUnit Test We will se how, as expected. our tests fail.

Notice that these tests will run outside the Android emulator. Later we will include these tests to be run by the emulator itself.

Complete the tests


Now, once our test infrastructure is setup, let's proceed to define our tests.

  1. Add
public void testCelsiusToFahrenheit () 
for (int c: conversionTable.keySet()) {
int f = conversionTable.get(c);
String msg = "" + c + "C -> " + f + "F";
assertEquals(msg, f, TemperatureConverter.celsiusToFahrenheit(c));
}
}
  1. Add
   public void invalidCelsiusTemperature () {
try {
int f = TemperatureConverter.celsiusToFahrenheit(-274);
} catch (RuntimeException ex) {
if (ex.getMessage().contains("below absolute zero")) {
return;
}
else {
fail("Undetected temperature below absolute zero: " + ex.getMessage());
}

}

fail("Undetected temperature below absolute zero: no exception generated");

}
  1. If some imports are missing just press Shift + Ctrl + O

Creating the real code

We have now the tests for code that still doesn't exist. If everything was fine, you should see a light bulb in the line where TemperatureConverter.celsiusToFahrenheit is invoked and giving you the alternative to

Create Method celsiusToFarenheit(int) in tdd.TemperatureConverter 
  1. Create the method public static int celsiusToFahrenheit(int celsius) in TemperatureConverter (Notice the int return type and the celsius parameter)
  2. Return any value, say 0, or raise an exception indicating that it's not implemented yet.

Running the tests

Our Test Driven Development is starting to appear.

Go to the JUnit tab and Rerun Tests

These tests will fail, because we haven't already implemented the celsiusToFahrenheit conversion method.

We obtain two failures but by very different reasons as before:

  1. testCelsiusToFahrenheit failed because a wrong conversion result was obtained (remember that we returned a constant 0)
  2. testInvalidCelsiusTemperature failed because no exception was generated for an invalid temperature

So, what's left is to go there an implement it.

celsiusToFahrenheit

Replace this

return 0

by

return (int)Math.round(celsius * 1.8 + 32);

Remember to change parameters name to celsius (from c) if it's not already named.

Running the tests again, and we will see how one test succeed but the other fails with the message

Undetected temperature below absolute zero: no exception generated

That's because we are expecting an exception to be thrown if the temperature is below the absolute zero but our first attempt doesn't do that.

Let's add this condition, but we first need the ABSOLUTE_ZERO_C constant.

Add ABSOLUTE_ZERO_C constant

TemperatureConverter class editor window.

  1. Add
    public static final int ABSOLUTE_ZERO_C = -273;

Modify celsiusToFahrenheit

Add the absolute zero check
  public static int celsiusToFahrenheit(int celsius) {
if (celsius < ABSOLUTE_ZERO_C) {
throw new RuntimeException("Invalid temperature: " + celsius + " below absolute zero");
}
return (int)Math.round(celsius * 1.8 + 32);

}

Run the tests

Run the tests again and we can verify that in our third attempt both tests passed.

Functional tests

We now have our TemperatureConverter class tested and ready to be included in our Android project.

Create the layout

Select main.xml in layout and add
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout id="@+id/linear" android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical">
<TextView id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dip"
android:text="Enter the temperature and press Convert">
</TextView>
<TableLayout id="@+id/table" android:layout_width="fill_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:stretchColumns="1">
<TableRow>
<TextView id="@+id/celsius_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:textStyle="bold"
android:textAlign="end"
android:text="Celsius">
</TextView>
<EditText id="@+id/celsius"
android:padding="3dip"
android:numeric="true"
android:digits="-+0123456789"
android:textAlign="end"
android:singleLine="true"
android:scrollHorizontally="true"
android:nextFocusDown="@+id/convert"
>
</EditText>
</TableRow>
<TableRow>
<TextView id="@+id/fahrenheit_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:textStyle="bold"
android:textAlign="end"
android:text="Fahrenheit">
</TextView>
<EditText id="@+id/fahrenheit"
android:padding="3dip"
android:textAlign="end"
android:singleLine="true"
android:scrollHorizontally="true">
</EditText>
</TableRow>
</TableLayout>
<Button id="@+id/convert" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dip"
android:text="Convert"
android:textAlign="center"
android:layout_gravity="center_horizontal"
android:nextFocusUp="@+id/celsius"
>
</Button>
</LinearLayout>

There's a lot of documentation and tutorials online in case you need to know more about Android layouts. We now have an empty form.

Change the theme, if you want, in AndroidManifest.xml, adding

android:theme="@android:style/Theme.Dark"

to <application>

We will obtain this when we run as Android Application

Of course this is not functional yet.

Positron

provides an instrumentation and some support classes to help writing acceptance tests. It is provided as a jar that gets bundled with your application. Acceptance tests are written in junit, extending a custom base class. Positron can be downloaded from http://code.google.com/p/android-positron/downloads/list.

Positron jar must be added to build path. In this example we are using positron-0.4-alpha, but if by the time you read this there's a newer version you may use it.

Validation

Create com.example.tdd.validation package in test folder

Create OverallTest test case

In the newly created package com.example.tdd.validation create a new JUnit test case

  1. Source folder: TDD Android/test
  2. Package: com.example.tdd.validation
  3. Name: OverallTest
  4. Superclass: positron.TestCase
  5. Finish

Create Positron class

In the package com.example.tdd create the Positron class extending positron.Positron

  1. Source folder: TDD Android/test
  2. Package: com.example.tdd
  3. Name: Positron
  4. Superclass: positron.Positron
  5. Finish

Then, create our test suite including OverallTest tests. Other tests can also be added.

 @Override
protected TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(OverallTest.class);
return suite;
}

Create the test


In OverallTest create the testConversion test case

 public void testConversion() {
Intent intent = new Intent(getTargetContext(), TemperatureConverterActivity.class);
startActivity(intent.addLaunchFlags(Intent.NEW_TASK_LAUNCH));

TemperatureConverterActivity activity = (TemperatureConverterActivity)activity();

// Is it our application ?
assertEquals(getTargetContext().getString(R.string.app_name), activity().getTitle());

// Do we have focus ?
assertEquals(activity.getCelsiusEditText(), activity.getCurrentFocus());

// Enter a temperature
sendString("123");

// Convert
press(DOWN, CENTER);

// Verify correct conversion 123C -> 253F
assertEquals("253", activity.getFahrenheitEditText().getText().toString());

}

This basically represents what we defines in our Use Case: Convert Temperature:

Actor Action System Response
1. The user enters a temperature in Celsius, and then press Convert 2. The system converts the temperature to Fahrenheit and the result is presented to the user
3. The user wants to enter another temperature and continues from 1 or presses Back to exit. 4. The process finishes.
Alternative Courses
Temperature is below absolute zero Indicate error
Invalid input characters entered Indicate error

Finally, add this to tearDown

 protected void tearDown() throws Exception {
finishAll();
}

Instrumentation

Add the instrumentation definition to AndroidManifest.xml
<instrumentation class=".Positron" android:functionalTest="true" android:targetPackage="com.example.tdd" android:label="Temperature Converter Acceptance Tests"/>


Complete TemperatureConverterActivity

In order to compile these tests we have to add the fileds related with the views widget in the GUI
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

// Find text fields.
celsiusEditText = (EditText)findViewById(R.id.celsius);
fahrenheitEditText = (EditText)findViewById(R.id.fahrenheit);
convertButton = (Button)findViewById(R.id.convert);

// disable fahrenheit field
fahrenheitEditText.setEnabled(false);

}

Also add the fields proposed by the IDE

private EditText celsiusEditText;;
private EditText fahrenheitEditText;
private Button convertButton;

and using Source->Generate Getters and Setters... generate the corresponding getters.

Run the tests again

This time let's run the positron tests in the emulator. To achieve this

adb shell "/system/bin/am instrument -w com.example.tdd/.Positron"

and change to the DDMS inside Eclipse, and look for the LogCat window

I/Positron(1091): .F
I/Positron(1091): Time: 1.332
I/Positron(1091): There was 1 failure:
I/Positron(1091): 1) testConversion(com.example.tdd.validation.OverallTest)junit.framework.ComparisonFailure: expected:<253> but was:<>
I/Positron(1091): at com.example.tdd.validation.OverallTest.testConversion(OverallTest.java:62)
I/Positron(1091): at java.lang.reflect.Method.invokeNative(Native Method)
I/Positron(1091): at positron.harness.InstrumentedTestResult.run(InstrumentedTestResult.java:37)
I/Positron(1091): at junit.extensions.TestDecorator.basicRun(TestDecorator.java:22)
I/Positron(1091): at junit.extensions.TestSetup$1.protect(TestSetup.java:19)
I/Positron(1091): at junit.extensions.TestSetup.run(TestSetup.java:23)
I/Positron(1091): at positron.Positron.onStart(Positron.java:62)
I/Positron(1091): at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1115)
I/Positron(1091): FAILURES!!!
I/Positron(1091): Tests run: 1, Failures: 1, Errors: 0

Again, as expected the test failed because the conversion functionality is not yet implemented.

Implementing conversion functionality in TemperatureConverterActivity

Add OnClickListener to convert button


It's an interface definition for a callback to be invoked when a view, a button in this case, is clicked. We need to implement the onClick abstract method, which in our case will call the convert helper method to carry away the actual conversion.

        // Hook up button presses to the appropriate event handler.
convertButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
convert();
}
});

Add convert method

Helper method to get the temperature value entered into the Celsius text field, convert it into Integer and call celsiusToFahrenheit method in TemperatureConverter class.

   protected void convert() {
try {
int f = TemperatureConverter.celsiusToFahrenheit(Integer.parseInt(celsiusEditText.getText().toString()));
fahrenheitEditText.setText(String.valueOf(f));
}
catch (Exception ex) {
fahrenheitEditText.setText(ex.toString());
}

}

Run tests again

Now, when we run our tests again, we obtain
I/Positron(1334): Time: 1.368
I/Positron(1334): OK (1 test)

Let's convert our unit test into Positron

In the previous unit test TemperatureConverterTest let's change the base class into positron.TestCase. You may also have to comment out the generated constructor because (until now) there's no positron.TestCase constructor receiving a String.

Run tests again

Running the tests again now we can see all of the tests are run
I/Positron(1430): Time: 2.408
I/Positron(1430): OK (4 tests)