Sunday, November 28, 2010

Robolectric with Roboguice in the TDD mix

My last blog post was about a little skeleton I did with every piece of fw I needed to start a new Android application using TDD. Phil Goodwin post a comment saying I should try Robolectric. So here's my blog post about it :)

Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds.


So before adding it to my skeleton I did a little test. In one of my projects I have an util class that reads some cars from a json file located at /res/raw. You can see how it went in this mail list thread.

Ok, next step to place Robolectric inside my skeleton was to mavenize it.
I opened an issue, created a new mail list thread and started working on it. Fortunately Christian Williams was interested and we started hacking the pom together.

At the time I am writing this the mvn support is working and we are missing the upload to mvn central. I guess next release of Robolectric will have the jar available through mvn central.

For the test project I was using robotium and Android-mock. I will remove them since Robolectric is ready to join my test project's pom as a dependency :)

Shorts steps to install robolectric in your mvn repo.

* Clone https://github.com/mosabua/maven-android-sdk-deployer
* mvn clean install
* Clone https://github.com/pivotal/robolectric/tree/master
* mvn clean install

My test-app's pom.xml looks like this. Finally tests don't need an android emulator to run.

My new project uses Roboguice and the first Activity we made is a SplashScreen. The layout is just a RelativeLayout with a background set, an ImageView with the app's logo in the center of the screen and a progressBar. The Activity's code looks like this:

public class SplashActivity extends GuiceActivity implements ISplashView {

@InjectView(R.id.splash_progress_bar)
private ProgressBar mProgressBar;

@Inject
private SplashPresenter mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter.setView(this);
setContentView(R.layout.splash);
}
....
}



So our first test in the application was the simple shouldHaveALogo() mentioned in the robolectric's user guide. We struggle a little with @mgv making Roboguice with Robolectric work together but we did it. This is how:

To make it work, we needed to extend RobolectricTestRunner.

public class XTestRunner extends RobolectricTestRunner {

public XTestRunner(Class testClass) throws InitializationError {
super(testClass, "../X-app/AndroidManifest.xml",
"../X-app/res");
}

@Override
protected Application createApplication() {
return new IoCApplication();
}
}


IoCApplication is just the class that extends GuiceApplication. Yep, the one that overrides addApplicationModules.

The test ends being:

@RunWith(XTestRunner.class)
public class SplashActivityTest {

private SplashActivity splashActivity;

@Before
public void setUp() throws Exception {
splashActivity = new SplashActivity();
splashActivity.onCreate(null);
}

@Test
public void shouldHaveLoadingLogo() throws Exception {
ImageView logo = (ImageView) splashActivity.findViewById(R.id.splash_logo);
ShadowImageView shadowLogo = Robolectric.shadowOf(logo);
assertThat(shadowLogo.getResourceId(), equalTo(R.drawable.loading_logo));
}
}



The constructor is used to point Robolectric to our app project's AndroidManifest and res folder. Nothing hard there.

The magic comes in the overriden method createApplication(). The parent class use it to set the application context to Robolectric.java class in this method:

public void setupApplicationState(String projectRoot, String resourceDir) {
ResourceLoader resourceLoader = createResourceLoader(projectRoot, resourceDir);

Robolectric.bindDefaultShadowClasses();
Robolectric.resetStaticState();
Robolectric.application = ShadowApplication.bind(createApplication(), resourceLoader);
}



What do we need the application for?
Because it's used in GuiceActivity. If you check the onCreate() method, it does this:

@Override
protected void onCreate(Bundle savedInstanceState) {
final Injector injector = getInjector();
scope = injector.getInstance(ContextScope.class);
scope.enter(this);
injector.injectMembers(this);
super.onCreate(savedInstanceState);
}


getInjector()? Where's does it came from?

public Injector getInjector() {
return ((GuiceApplication) getApplication()).getInjector();
}


Conclusion: Every injection with Roboguice uses GuiceApplication.
Robolectric looks like a very powerful test fw. We will definitely use it for this new project we are working on.

Next step: ConnectivityManager Shadow!

Wednesday, November 17, 2010

TDD skeleton for Android

During this days at work my task was to research about adding tests to an Android application. The truth is that right now we aren't doing tests at all. In this entry I will try to comment on every framework I have been looking into. Let's start!

My first step was getting one of our applications and try adding black box tests. My first stop is robotium.

Robotium is a test framework created to make it easy to write powerful and robust automatic black-box test cases for Android applications. With the support of Robotium, test case developers can write function, system and acceptance test scenarios, spanning multiple Android activities.

Robotium has full support for Activities, Dialogs, Toasts, Menus and Context Menus.


Robotium is founded by the same company that created the maven-android-plugin that we have been using for a long time.

This framework looks great for blackbox testing but I didn't go too deep with it. I just made a test to browse around the app and check that the app shows the screen correctly. I had an issue with a SplashScreen but the feedback was great. I am really pleased with that.

When I reached this point my thought was: "Ok, now I can add blackbox test, but I also need unit tests!"
I try adding simple unit tests to the app but I failed. Classes were too dependent and there was lot's of logic inside the Activities. So I leave the real project away for a while and create a new one to start from the scratch with a new Architecture approach.

I present my last application: ioc_app. The ultimate app to turn a String to uppercase.


Link to the source code.

This project has two modules
a) ioc_app. The android application.
b) ioc_test. The test application.

Let me introduce some of the new stuff I added to this project:

1) Presenter first
I implemented this pattern based on the paper hosted in the link above. It's quite clear.

2) Proguard
When packaging an apk, all classes of all libraries used by the program will be included, this makes the apk very huge, even exceeds the capacity of dex. ProGuard can strip unused classes and methods, make it as small as possible.


I am missing adding proguard in an specific profile. It takes some time to compile and I don't want it to run unless I am building a release. Manfred, a committer in the maven-android-plugin project, told me he already did that and he uploaded to a sample project. I need to see that.

Manfred also mentioned he was trying to add obfuscation but he couldn't do it yet. Right now it's not my first priority but it's a nice to have feature.

3) Roboguice
Just to put it in a sentence: It's Google Guice for Android. I love the fact that from now on instead of writing:

mButton = (Button) findViewById(R.id.submit);


I will write:

@InjectView(R.id.submit)
private Button mButton;


Unfortunately Guice doesn't provide a mvn repo, so to use roboguice you will need to install guice-2.0-no_aop.jar in your maven repo.

4) Android-mock
I have some issues with this one. Running tests with mocks in Eclipse i'ts a PITA. Once again I got an excellent feedback but I preferred to go for the mvn approach.

Android-mock needs two jars to work. One for compile time and another for runtime. Right now it just work from mvn. I couldn't make it work from eclipse even though I have the m2eclipse-android-integration.

What's next?
* I am willing to read more about TDD. I have now everything configured to start doing it.
* I need to setup a Hudson to take care of the CI.
* I would like to start using IntelliJ IDEA but I couldn't manage to make my project run with it. If you did it, please let me know.

I'm hoping you give me some feedback about the skeleton and how to improve it!

Other blogs with useful info:
- http://dtmilano.blogspot.com/2008/03/test-driven-development-and-gui-testing.html
- http://www.rosscode.com/blog/index.php
- http://simpleprogrammer.com/

Sunday, November 14, 2010

Android Tip: TextSwitcher and ImageSwitcher

Quick android tip.

If you are using TextView or ImageView in your view and you are changing it's content when the user interacts, you can try using TextSwitcher and ImageSwitcher.

They work in a very similar way. The steps to use them are the following:

* Get the view using findViewById() or construct in your code like any normal android view.
* Set a factory using switcher.setFactory().
* Set an in animation using switcher.setInAnimation().
* Set an in animation using switcher.setOutAnimation().

So, if your code looks something like this:


private TextView mTextView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = (TextView) findViewById(R.id.your_textview);
...
}


and you are using mTextView.setText(something);

replace your onCreate's code with this:


Animation in = AnimationUtils.loadAnimation(this,
android.R.anim.fade_in);
Animation out = AnimationUtils.loadAnimation(this,
android.R.anim.fade_out);

mTextSwitcher = (TextSwitcher) findViewById(R.id.your_textview);
mTextSwitcher.setFactory(new ViewFactory() {

@Override
public View makeView() {
TextView t = new TextView(YourActivity.this);
t.setGravity(Gravity.CENTER);
return t;
}
});

mTextSwitcher.setInAnimation(in);
mTextSwitcher.setOutAnimation(out);


That's it. You get some cool animations for free.

Thursday, November 11, 2010

Nuevos dispositivos Android en Argentina

Hace un tiempo un amigo me preguntó qué celular comprar con android acá en Argentina y me pregunté cuál me compraría yo.
Hoy en día sólo cambiaría mi nexus one por un Samsung Galaxy S. En su momento no había llegado a Argentina, entonces le recomendé el milestone. La historia termina con el muchacho comprándose un iPhone4 porque el milestone es muy grande.

Afortunadamente eso ya no va a pasar. El Samsung Galaxy S llegó a la Argentina de la mano de Personal a 2300$ con plan Black.



Jugué un poco con el celular y la pantalla se siente enorme. El nexus one tiene 3.7' y este tiene 4' pero la diferencia es abismal. La pantalla también es muy linda. La del iphone4 me gustó más, pero de los dispositivos con android que tuve en mis manos hasta ahora, me parece el mejor. comparativa-del-samsung-galaxy-s-con-el-iphone-4.

Como desarrollador creo que lo mejor es tener un Nexus One. Todos los updates del sistema operativo llegan para ese celular. Por ejemplo, en teoría, hoy sale el OTA de android 2.3 gingerbread para el nexus.

Pero para un usuario final, hoy en día, creo que el Galaxy S es el mejor dispositivo con Android.

Al segundo me fijé en los sitios de claro y movistar para saber si también estaba disponible. Claro tiene un sitio INNAVEGABLE por lo tanto no sé si lo tienen. Por su lado, Movistar no lo tiene pero encontré esto:



Netbook con android y con pantalla touch.

Estoy a la espera del Samsung Galaxy Tab. ¿Quién lo traerá a la Argentina?

Friday, November 5, 2010

Droidcon en Londres. Día dos. (13:30pm - 18:00pm)

Última entrega del review de la droidcon.

Después de almorzar estuve viendo algún que otro stand.

Sony Ericsson LiveView
De los sponsors que estuvieron en la Droidcon, Sony Ericsson fue el más activo. En el stand mostraban sus cels, hacían sorteos y tenían LiveView para que la gente los pruebe.

El LiveView es una especie de reloj. Se usa conectado a un celular y se le manda información por medio de BlueTooth. Noten que si usa BT sólo está disponible para 2.x y futuros releases.
Todavía no está a la venta pero va a costar 40£ (80U$s).
Me imagino muchas cosas posibles con el LiveView, entre ellas un mejor manejo de notificaciones.

Perfectomobile
El stand de Vodafone también contaba con gente de perfectomobile.
Básicamente el servicio que ofrecen es poder testear tu aplicación en muchos devices.

Más de una vez llegan comentarios o reviews en el market que dicen: "En el dispositivo x no anda el feature y". Antes eran pocos los celulares con Android y en la empresa teníamos los suficientes para probar, pero ahora ya son demasiados. Me ha pasado que algunos ni los conozco :(

El servicio parece interesante pero por ahora no se puede debuggear en el aparato. Creo que es un feature clave.

Turn good ideas in to great apps
Charla de Reto Meier. Reto dio una charla muy parecida a la que está en youtube.
Saqué en limpio esto:

Los usuarios mienten, y no siguen las instrucciones, usen analytics.
No recomendó ningún sistema en especial.

Por mi parte, ahora estoy usando Flurry pero la información de los Force Closes es muy pobre. Para eso estoy usando Acra.

Android beyond the phone; Tablets, eReaders, and more
Al Sutton dio una interesante charla mencionando los distintos dispositivos con android.
Mostraron la Dell Streak. En un principio pensaba que todo dispositivo con resolución mayor a la de un celular común iba a ser hdpi pero para mi sorpresa no es así. En el caso de la Dell Streak es mdpi.

Lamentablemente no tengo una tablet y por ahora la única solución es el emulador que provee Samsung en su página. La verdad es que en mi máquina anda lento y no me entra el dispositivo en portrait, pero algo es algo.

Interface design for Android tablets
Charla a cargo de Greg Taylor de TigerSpike. Lamentablemente a Greg le tocó la "sala" principal donde no se escuchaba nada. Greg mencionó que la UX en una tablet debería ser distinta a la de un celular y mostró un video de un mock de una posible UX. Lamentablemente no lo puede subir a internet así que no lo puedo compartir :(

Thanks & Beers
La gente de adMob se puso con cervezas y vinos para disfrutar el cierre de la conferencia.
En el cierre hablé con dos españoles, Cristobal Viedma y @androides. Parece que los chicos están interesados en armar una Droidcon en Barcelona. Enhorabuena, tíos! :)
Espero poder estar allí cuando la hagan. Suerte con eso!

Conclusión
Cada vez hay más gente desarrollando Android.
Cada vez hay más dispositivos.
Cada vez hay más markets.

En muchas charlas se dijo que tenemos que hacer nuestra aplicación tablet-compatible pero la realidad es que debería cambiar la UX. ¿Vamos a tener que hacer una única aplicación para distintos dispositivos o vamos a usar a usar Android Libraries para compartir el core entre dos aplicaciones distintas?

Dentro de poco voy a tener que migrar una aplicación que estoy haciendo a tablet, así que ahí veré qué conviene. Google mencionó que Gingerbread solucionaría algunas de estos inconvenientes. Esperemos.


Thursday, November 4, 2010

Droidcon en Londres. Día dos. (9:00am - 13:30pm)



Por suerte el día dos fue mucho más organizado. Desde el hotel había elegido a qué charlas tenía ganas de ir así que tenía todo anotado en la app de android de la conf. Schedule aquí.

Driving Downloads via Intents
La charla la dio Sean Owen, autor de la librería zxing usada en la aplicación Barcode Scanner. Creería que toda persona con Android tiene instalada esta aplicación.

Sean explicó cómo usar los intents para proveer una librería al resto de las aplicaciones. Gracias a la arquitectura de Intents que tiene android una aplicación puede ofrecer una api para que el resto la use. En el caso de Barcode Scanner, la aplicación ofrece un intent para que nuestra aplicación pueda capturar códigos de QR. ¿Qué genera esto? Que Barcode Scanner tenga muchas más descargas.

Se mostró un código interesante para resolver las dependencias de otras aplicaciones mandando al usuario al market para descargarla. Link al código.

Sean concluyó que el hecho de que Barcode Scanner sea usada tanto por otras aplicaciones logra que nadie la borre y que genere muchas más descargas. El dev debería pensar qué en su app es reutilizable por otras aplicaciones y publicar su API en lugares como openintents.

Excellence in the Android user Experience
Romain Nurik fue el orador de esta charla. Romain es un google developer advocate para la parte de UI y UX. Sus slides aquí. Algunos puntos interesantes de la charla:
  • La UX del usuario no empieza en la primera pantalla, empieza en le primer review que ven, en la pantalla del market, en el ícono de la app.
  • El usuario no se va a poner a leer documentación para usar la app. La app tiene que ser simple e intuitiva.
  • Testear con usuarios reales y ver cómo interactúan con la aplicación.
  • Hacer betas para conocer la opinión del usuario.
He vivido el último punto con Swiftkey. Desde la primera versión del teclado se armó una lista de correo donde distintos usuarios fueron probando la app y pidiendo features. El día que salió en el market ese leal grupo de usuarios fue y descargó la app, llevándola a featured en poco tiempo.

Romain también tiene una página interesante con herramientas que está haciendo.

Android Reuse Models
Nuevamente Mark Murphy con otra charla interesante.
Mark explica que existen formas de agregar dependencias por medio de jars, src code, api por intent en android pero que existe un gran problema. ¿Qué pasa cuando necesitamos usar resources?

Mostró código del ejemplo que tiene subido a su repo de github.
Colormixer es una activity para que el usuario pueda elegir un color. Tiene como dependencia cwac-parcel para el correcto manejo de dependencias.

Este es un problema que estamos teniendo en el trabajo. Implementamos código para hacer action bars de manera fácil, pero venimos copiando el src de proyecto en proyecto. Estaría bueno utilizar este formado de librería y que encima lo pueda poner como dependencia en mi pom.xml

Me comuniqué con la gente de maven-android-plugin y lo hablé en un post en su lista de correo. Ya existe el issue y Hugo le puso prioridad alta. Plz entren y pónganle una estrella :)

Android and CouchDB
Tenía ganas de que esta sea una charla increíble la cual me de el puntapie inicial para empezar a usar CouchDB en Android pero no lo logró.

Aaron Miller explicó que hoy en día CouchDB funciona como un android service. Si tu aplicación necesita usar couchDB el usuario tiene que bajar CouchDB. Sé que existen formas de llevar al usuario al market para descargar pero sólo las usaría para funcionalidades extra no para el core de mi app. Cuando le preguntaron si había forma de embeber en la app dijo que todavía no está hecho y que si se hace como está hoy son como 8MB. También tuvo la gentileza de avisar que no es su prioridad.

Mi conclusión:
Todo muy lindo, pero si Google no permite instalar apps como dependencias al instalar mi propia app CouchDB va a mantenerse sentado en el sillón.

Después de esta charla se lanzó el almuerzo.
Mañana escribiré algo de las charlas de la tarde.

Wednesday, November 3, 2010

Droidcon en Londres. Día uno



Bueno, estoy de vuelta en casa después de una semana en Londres.
Tuve la suerte de participar en la Droidcon así que voy a hacer una pequeña reseña de las cosas que vi y me llamaron la atención.

Día 1:
El primer día fue en formato barcamp. Distintos participantes hicieron una fila y la gente presente votaba qué charlas se daban. La organización no fue la mejor. Acá está el schedule del día:





Algunos puntos interesantes de las charlas que vi:

No usar intent.action.BOOT_COMPLETED a menos que sea REALMENTE necesario.
Android permite escuchar distintos eventos del sistema. Uno de ellos es el BOOT_COMPLETED. Algunos usos:

  • Settear alarmas (las alarmas se pierden cuando el cel se reinicia)
  • Iniciar un servicio
  • Iniciar o schedulear descargas detenidas al reiniciar el teléfono
¿Cuál es el problema?
Si, por ejemplo, necesitamos schedulear descargas de nuestra aplicación y esas descargas no existen, vamos a usar tiempo de inicio del celular en verificar si hay algo que hacer o no.

¿Solución?
Settear el Receiver off por defecto y habilitarlo cuando es necesario.

La presentación fue dada por @ErikHellman de SonyEricsson. A la espera de los slides.

Google Bootcamp 1
Primera charla de google.
Por más que muchos pegaron con preguntas sobre el futuro de Android la respuesta fue siempre la misma: "No podemos responder nada de eso".

Pregunté sobre una pregunta que vi en stackoverflow sobre animaciones en ListView.

Me respondió Roman. Comentó que las ListView utilizan vistas normales, y que era cuestión de obtener la View y aplicarle una animación. (Más tarde lo busqué y pude hacer un código funcional con él. Lo organizaré y lo subiré)

Continuous Integration con maven y hudson
La charla la dio el autor de maven-android-plugin. Los slides están acá.

En la parte de Q&A le comenté que lo habíamos probado en el trabajo pero que teníamos el problema de que nuestro hudson corría en un server sin X.
Matthias Käppler(@twoofour) mencionó que la nueva versión del plugin de android para hudson ya tiene eso incorporado. Será cuestión de probarlo :)

Mercados alternativos
Realmente estoy sorprendido de la cantidad de markets nuevos que hay.
Me enteré que ya existe mercado de Orange y de Vodaphone. Da la sensación que cada telco va a tener su propio market. Creo que es una buena noticia porque por ahora en el AndroidMarket sólo se puede utilizar cc y mucha gente no tiene. El hecho de que las telcos tengan market asegura que clientes puedan comprar la aplicación y que se les facture en la cuenta mensual. Dolor de cabeza para el desarrollador, pero es una opción más.

MetaMarket model
Charla por Mark Murphy (Commonsware). Para los que no lo conocen Mark es una persona MUY activa en la comunidad android. Es el único con gold badge en android en stackoverflow.

Mark plantea que el android market está bueno, pero que le faltan cosas. Ayudado por el auditorio se armó una lista de cosas que molestan tanto al desarrollador como al usuario. Entre ellas:
  • No se puede responder a los comentarios
  • No hay suficiente info de la aplicación screenshots/descripción/video
  • Las búsquedas no funcionan bien
  • No hay analytics
  • Falta de mejores detalles de la puntuación
Mark mencionó que estaría bueno que la información del market sea más pública y que la comunidad tiene que encontrar una mejor manera de manejar esto. Mencionó que el: "Google, fixeá esto" no siempre funciona :)
Habían quedado en armar una lista de correo para hacer algo con esto, pero por ahora no tengo noticias.

App Circus
En el viaje tuve la posibilidad de conocer a la gente con la cual trabajé en SwiftKey.
Ben Medlock, uno de los fundadores de TouchType, presentó Swiftkey en la App Circus.

Swiftkey salió primera y Ben se ganó una "camisa" de Fórmula 1 autografiada por Lewis Hamilton :)

El día cerró con una Hackathon en la cuál aproveché y hice el código de la animación del listado que mencioné anteriormente. Un placer poder codear y tener googlers para sacarse las dudas.

Próximamente análisis del día dos.