Android; A really easy tutorial on how to use Text To Speech (TTS) and how you can enter text and have it spoken

Hello readers, time for another little android post of mine. I’ve been working hard recently on some voice recognition and TTS work, so figured I’d post up some tutorials for people to see how easy it is. This is also my first post where I host content on Github so please bear with me!

TTS is actually incredibly easy, take a look at this first example activity :


/**
 * Author : James Elsey
 * Date : 26/Feb/2011
 * Title : TextToSpeechDemo
 * URL : Http://www.JamesElsey.co.uk
 */
package com.jameselsey.demo.texttospeechdemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;

/**
 * This class demonstrates checking for a TTS engine, and if one is
 * available it will spit out some speak.
 */
public class Main extends Activity implements TextToSpeech.OnInitListener
{
    private TextToSpeech mTts;
    // This code can be any value you want, its just a checksum.
    private static final int MY_DATA_CHECK_CODE = 1234;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Fire off an intent to check if a TTS engine is installed
        Intent checkIntent = new Intent();
        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
        startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);

    }

    /**
     * Executed when a new TTS is instantiated. Some static text is spoken via TTS here.
     * @param i
     */
    public void onInit(int i)
    {
        mTts.speak("Hello folks, welcome to my little demo on Text To Speech.",
                TextToSpeech.QUEUE_FLUSH,  // Drop all pending entries in the playback queue.
                null);
    }


    /**
     * This is the callback from the TTS engine check, if a TTS is installed we
     * create a new TTS instance (which in turn calls onInit), if not then we will
     * create an intent to go off and install a TTS engine
     * @param requestCode int Request code returned from the check for TTS engine.
     * @param resultCode int Result code returned from the check for TTS engine.
     * @param data Intent Intent returned from the TTS check.
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == MY_DATA_CHECK_CODE)
        {
            if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
            {
                // success, create the TTS instance
                mTts = new TextToSpeech(this, this);
            }
            else
            {
                // missing data, install it
                Intent installIntent = new Intent();
                installIntent.setAction(
                        TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
                startActivity(installIntent);
            }
        }
    }

    /**
     * Be kind, once you've finished with the TTS engine, shut it down so other
     * applications can use it without us interfering with it :)
     */
    @Override
    public void onDestroy()
    {
        // Don't forget to shutdown!
        if (mTts != null)
        {
            mTts.stop();
            mTts.shutdown();
        }
        super.onDestroy();
    }
}

Its really simple, what we do in the onCreate method is to fire off an intent to check if there is a valid TTS engine installed on the device. Think of it this way, if the device doesn’t have an engine which it can use for converting text to speech, how can this work? We use this check to see if we need to direct the user to download one.

The callback on this check is the onActivityResult, which will be invoked after the check has complete. In this method we first check the MY_DATA_CHECK_CODE, this value can be anything you like, I’ve just chosen 1234. This is basically a checksum to check that the intent we receive in the onActivityResult is the one we’re expecting.

After that we check the result code, if it is CHECK_VOICE_DATA_PASS that means there is a valid TTS engine available, so we can create a TextToSpeech instance. If we don’t get a CHECK_VOICE_DATA_PASS then we assume there is either no TTS engine, or the one we have doesn’t work properly, so we create another intent that directs the user off to install one.

If the above was a success, then by creating a new TextToSpeech object we’ll invoke the onInit method, this is quite simple all it does is call the speak method on the TextToSpeech object and passes it a static String to speak.

The QUEUE_FLUSH is a nifty little feature which flushes out anything else the engine is trying to speak.

Checkout the branch I’ve created on Github, you can download the complete source code for this and have a play.

Thats all nice and dandy, but having a static String to be spoken is a little boring, lets have a look at how we can modify this so that you can have a text box, type in some text and hit a button to have it spoken. Have a look at the following Activity, its similar to the above yet improved to do dynamic speech.

/**
* Author : James Elsey
* Date : 26/Feb/2011
* Title : TextToSpeechDemo
* URL : Http://www.JamesElsey.co.uk
*/
package com.jameselsey.demo.texttospeechdemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.widget.EditText;

/**
* This class demonstrates checking for a TTS engine, and if one is
* available it will spit out some speak based on what is in the
* text field.
*/
public class Main extends Activity implements TextToSpeech.OnInitListener
{
private TextToSpeech mTts;
// This code can be any value you want, its just a checksum.
private static final int MY_DATA_CHECK_CODE = 1234;

/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Fire off an intent to check if a TTS engine is installed
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);

}

/**
* This method is bound to the speak button so is invoked anytime that is clicked.
* @param v View default view.
*/
public void speakClicked(View v)
{
// grab the contents of the text box.
EditText editText = (EditText) findViewById(R.id.inputText);

mTts.speak(editText.getText().toString(),
TextToSpeech.QUEUE_FLUSH, // Drop all pending entries in the playback queue.
null);
}
/**
* Executed when a new TTS is instantiated. We don’t do anything here since
* our speech is now determine by the button click
* @param i
*/
public void onInit(int i)
{

}

/**
* This is the callback from the TTS engine check, if a TTS is installed we
* create a new TTS instance (which in turn calls onInit), if not then we will
* create an intent to go off and install a TTS engine
* @param requestCode int Request code returned from the check for TTS engine.
* @param resultCode int Result code returned from the check for TTS engine.
* @param data Intent Intent returned from the TTS check.
*/
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == MY_DATA_CHECK_CODE)
{
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
{
// success, create the TTS instance
mTts = new TextToSpeech(this, this);
}
else
{
// missing data, install it
Intent installIntent = new Intent();
installIntent.setAction(
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}

/**
* Be kind, once you’ve finished with the TTS engine, shut it down so other
* applications can use it without us interfering with it :)
*/
@Override
public void onDestroy()
{
// Don’t forget to shutdown!
if (mTts != null)
{
mTts.stop();
mTts.shutdown();
}
super.onDestroy();
}
}

The main changes here is removing the code from the onInit method, we do this because when we create the TTS object we don’t actually know at that point what text we want spoken, so we leave it blank.

Another change is the introduction of the speakClicked method, there is a button which is bound to this method, so when that button is clicked this method is invoked (I wrote another tutorial on that, please check it out).

So when the button is clicked, we’ll take whatever text is in the EditText and have that spoken, simple as that!

I’ve put all this in a separate branch on my Github so feel free to check it out.

Lastly, don’t forget to stop and shutdown the TTS object when you’re done, you don’t want it causing issues for other applicaitons that may want to use the TTS engine.

Further reading

Monetizing your Android apps with AdMob (AdSense), an easy guide

Howdy folks, another nice and easy android guide here. If you’re like me you’re thinking about putting your first application up on the market. It might be a good idea to set-up some ads on application, hopefully making a few pennies as you go.

Its actually quite easy, if your familiar with AdSense for web sites it’ll be even more easier.

First thing you need to do, is head over to AdMob and create an account, should only take you a couple of minutes.

Once thats all setup, login and go to the “add sites” link and create an android site. You’ll be asked for some details on your app, if you don’t actually have it loaded into the marketplace yet, then just enter some dummy values since you can always modify these later.

On the next screen you should find a link to download the SDK, this is where we get started!

Download the SDK, and take the file “admob-sdk-android.jar” and put it into the libs/ directory of your application. You’ll also need to configure your IDE to put this onto the build path in order for it to be recognised.

Next we’ll need to configure our application to use the AdMob library, open up your AndroidManifest.xml file and add the following before the closing application tag.

<!-- The application's publisher ID assigned by AdMob -->
<meta-data android:value="YOUR_ID_HERE" android:name="ADMOB_PUBLISHER_ID" />
        
<!-- AdMobActivity definition -->
<activity android:name="com.admob.android.ads.AdMobActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboard|keyboardHidden" />
<!-- Track Market installs -->          
<receiver android:name="com.admob.android.ads.analytics.InstallReceiver"
android:exported="true">
<intent-filter>
   <action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>

You’ll need to replace YOUR_ID_HERE with your publisher ID. Thats easy to find, login to your AdMob account, click on Sites, then manage the settings on your application, you’ll see the code at the top of the page.

Also, make sure that your application has permissions to the internet, if it can’t access the web, how can it fetch ads?

<!-- AdMob SDK requires Internet permission -->
  <uses-permission android:name="android.permission.INTERNET" />

Next, if you don’t already have a file called res/values/attrs.xml, then create one with the following content. If you do already have that file, then work in the following

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.admob.android.ads.AdView">
<attr name="backgroundColor" format="color" />
<attr name="primaryTextColor" format="color" />
<attr name="secondaryTextColor" format="color" />
<attr name="keywords" format="string" />
<attr name="refreshInterval" format="integer" />
</declare-styleable>
</resources>

Now thats all done, lets think about actually inserting some ads into your application. I’ve added the following as the last element in my LinearLayout in my res/layout/main.xml file, which means the adds will sit along the bottom like a footer

     <com.admob.android.ads.AdView
            android:id="@+id/ad"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            myapp:backgroundColor="#000000"
            myapp:primaryTextColor="#FFFFFF"
            myapp:secondaryTextColor="#CCCCCC"/>

It will probably show up in red text in your IDE, it does in mine, but it still compiles fine so its nothing to worry about.

Be sure to add the “myapp” namespace into the LinearLayout, it must point to your package, such as :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:myapp="http://schemas.android.com/apk/res/com.jameselsey.LanguageSelection"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

Thats it, build your APK and deploy it to a device and you’ll see your ads. Sometimes it will take ~10 seconds for the ad to display, I believe this is due to the app having to make a connection to AdMob to retrieve a suitable ad.

If you want to set some test ads, then add the following snippet into the onCreate method in your activity (the one that uses the view where we declared the AdView)

 AdManager.setTestDevices(new String[]{
                AdManager.TEST_EMULATOR, // Android emulator
                "CF95DC53F383F9A836FD749F3EF439CD",
        });

The long horrible code is my device code, in order to find out what it is for your device, run the application with the following code in, and then look at LogCat, it should have the following line telling you what your device code is :

02-26 21:51:11.695: INFO/AdMobSDK(7591): To get test ads on this device use AdManager.setTestDevices( new String[] { "CF95DC53F383F9A836FD749F3EF439CD" } )

Using this test mode, it ensures that an ad is always placed in your app, and you don’t have to worry about internet connections and such, its essentially a placeholder.

Thats it, I hope I’ve helped you get setup and ready to earn a few pennies, please leave me a comment to let me know how you get on!

Further Reading

Android and SQLite, a REALLY easy tutorial that anyone can do

Howdy folks! Time for some more android fun. I’ve been meaning to explore some of the SQLite functionality for some time, as I require it in my Cerca de mí application, so heres a real quick and dirty overview of how to get going.

I know you want some source code to lift, but lets just have a quick chat about how this ol’ chesnut works.

Think of it this way, you’ve created some application on Android, and young Johnny downloads your game and plays it. He kills the arch-enemy Galron in under 2 minutes flat and he wants to save his score to brag to his chums about, how does that work in Android terms? Well, one option is to create a SQLite database for your application and use that to store application data such as high scores, usernames, perhaps even data required for your applications logic? (maybe some cool new items after killing that boss?).

Enough talk, create a new android project in the IDE of your choice and copy in the following into src/com/jameselsey/Main.java

//   Quick and dirty SQLite example for Android
 
package com.jameselsey;

import android.app.ListActivity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.List;

public class Main extends ListActivity
{

    private static String SAMPLE_TABLE_NAME = "PERSONS_TABLE";
    private SQLiteDatabase sampleDB = null;
    private List<String> results = new ArrayList<String>();
    private Cursor cursor = null;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try
        {
            sampleDB = openOrCreateDatabase("NAME", MODE_PRIVATE, null);
            createTable();
            insertData();
            lookupData();
            this.setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,results));
        }
        catch (SQLiteException se)
        {
            Log.e(getClass().getSimpleName(), "Could not create or Open the database");
        }
        finally
        {

            if (sampleDB != null)
                sampleDB.execSQL("DELETE FROM " + SAMPLE_TABLE_NAME);
            sampleDB.close();
        }

    }

    /**
     * Create a table if it doesn't already exist
     */
    private void createTable()
    {
       sampleDB.execSQL("CREATE TABLE IF NOT EXISTS " +
                    SAMPLE_TABLE_NAME +
                    " (PERSON_NAME VARCHAR, " +
                    "  COUNTRY VARCHAR, " +
                    "  AGE INT(3));");
    }

    /**
     * Insert some test data, modify as you see fit
     */
    private void insertData()
    {
        sampleDB.execSQL("INSERT INTO " + SAMPLE_TABLE_NAME + " Values ('James','ENGLAND',25);");
        sampleDB.execSQL("INSERT INTO " + SAMPLE_TABLE_NAME + " Values ('Dave','USA',18);");
        sampleDB.execSQL("INSERT INTO " + SAMPLE_TABLE_NAME + " Values ('Jean-Paul','FRANCE',33);");
        sampleDB.execSQL("INSERT INTO " + SAMPLE_TABLE_NAME + " Values ('Sergio','SPAIN',42);");
        sampleDB.execSQL("INSERT INTO " + SAMPLE_TABLE_NAME + " Values ('Hitori','JAPAN',73);");
    }

    /**
     * Run a query to get some data, then add it to a List and format as you require
     */
    private void lookupData()
    {
        cursor = sampleDB.rawQuery("SELECT PERSON_NAME, COUNTRY, AGE FROM " +
                SAMPLE_TABLE_NAME +
                " where AGE > 10 ", null);

        if (cursor != null)
        {
            if (cursor.moveToFirst())
            {
                do
                {
                    String personName = cursor.getString(cursor.getColumnIndex("PERSON_NAME"));
                    String country = cursor.getString(cursor.getColumnIndex("COUNTRY"));
                    int age = cursor.getInt(cursor.getColumnIndex("AGE"));
                    results.add("" + personName + ", " + country + ", " + age);
                } while (cursor.moveToNext());
            }
            cursor.close();
        }
    }
}

This is the whole tutorial pretty much in one class. What we do is call out to various private members in the onCreate method. Firstly we need to create the table to store those highscores in (thats it Johnny hasn’t already played the game and created it already).

Next we’re going to insert some data, this is the key part. For this tutorial I’m happy inserting some static test data just to prove the point, but in your real life application you probably don’t want to do this, as you’ll be inserting data from other means.

Next we lookup the data, just a simple SQL select statement, nothing fancy here, but what we do is to create a List of results from that query, which we use to pass through an Adapter so it comes out the other end in a ListView.

That is pretty much it to be honest! I won’t post up AndroidManifest.xml or strings.xml since there are no changes there, but you might want the following res/layout/main.xml

<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
              android:layout_width="fill_parent" android:layout_height="fill_parent">
    <ListView android:id="@android:id/list" android:layout_width="fill_parent"
            android:layout_height="wrap_content" ></ListView>
</LinearLayout>

So there we have it, in under 5 minutes you can now create a SQLite database, insert some test data into it, then read it back and display on the screen. I’ll do some more in-depth SQLite tutorials in the future, please check back!

James

Android; How to implement voice recognition, a nice easy tutorial…

I’ve been searching for a simple tutorial on using voice recognition in Android but haven’t had much luck. The Google official documentation provide an example of the activity, but don’t speculate anything more than that, so you’re kind of on your own a little.

Luckily I’ve gone through some of that pain, and should make this easy for you, post up a comment below if you think I can improve this.

I’d suggest that you create a blank project for this, get the basics, then think about merging VR into your existing applications. I’d also suggest that you copy the code below exactly as it appears, once you have that working you can begin to tweak it.

With your blank project setup, you should have an AndroidManifest file like the following

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.jameselsey"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="VoiceRecognitionDemo" android:icon="@drawable/icon"
            android:debuggable="true">
        <activity android:name=".VoiceRecognitionDemo"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest> 

And have the following in res/layout/voice_recog.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="4dip"
        android:text="Click the button and start speaking" />

    <Button android:id="@+id/speakButton"
        android:layout_width="fill_parent"
        android:onClick="speakButtonClicked"
        android:layout_height="wrap_content"
        android:text="Click Me!" />

    <ListView android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

 </LinearLayout>

And finally, this in your res/layout/main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="VoiceRecognition Demo!"
    />
</LinearLayout>

So thats your layout and configuration sorted. It will provide us with a button to start the voice recognition, and a list to present any words which the voice recognition service thought it heard. Lets now step through the actual activity and see how this works.

You should copy this into your activity :

package com.jameselsey;

import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;

/**
 * A very simple application to handle Voice Recognition intents
 * and display the results
 */
public class VoiceRecognitionDemo extends Activity
{

    private static final int REQUEST_CODE = 1234;
    private ListView wordsList;

    /**
     * Called with the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.voice_recog);

        Button speakButton = (Button) findViewById(R.id.speakButton);

        wordsList = (ListView) findViewById(R.id.list);

        // Disable button if no recognition service is present
        PackageManager pm = getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(
                new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
        if (activities.size() == 0)
        {
            speakButton.setEnabled(false);
            speakButton.setText("Recognizer not present");
        }
    }

    /**
     * Handle the action of the button being clicked
     */
    public void speakButtonClicked(View v)
    {
        startVoiceRecognitionActivity();
    }

    /**
     * Fire an intent to start the voice recognition activity.
     */
    private void startVoiceRecognitionActivity()
    {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Voice recognition Demo...");
        startActivityForResult(intent, REQUEST_CODE);
    }

    /**
     * Handle the results from the voice recognition activity.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK)
        {
            // Populate the wordsList with the String values the recognition engine thought it heard
            ArrayList<String> matches = data.getStringArrayListExtra(
                    RecognizerIntent.EXTRA_RESULTS);
            wordsList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
                    matches));
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Breakdown of what the activity does :

Declares a request code, this is basically a checksum that we use to confirm the response when we call out to the voice recognition engine, this value could be anything you want. We also declare a ListView which will hold any words the recognition engine thought it heard.

The onCreate method does the usual initialisation when the activity is first created. This method also queries the packageManager to check if there are any packages installed that can handle intents for ACTION_RECOGNIZE_SPEECH. The reason we do this is to check we have a package installed that can do the translation, and if not we will disable the button.

The speakButtonClicked is bound to the button, so this method is invoked when the button is clicked. I wrote another tutorial on button binding so have a look at that if you don’t understand this method.

The startVoiceRecognitionActivity invokes an activity that can handle the voice recognition, setting the language mode to free form (as opposed to web form)

The onActivityResult is the callback from the above invocation, it first checks to see that the request code matches the one that was passed in, and ensures that the result is OK and not an error.

Next, the results are pulled out of the intent and set into the ListView to be displayed on the screen.

Notes on debugging :

You won’t have a great deal of luck running this on the emulator, there may be ways of using a PC microphone to direct audio input into the emulator, but that doesn’t sound like a trivial task. Your best bet is to generate the APK and transfer it over to your device (I’m running an Orange San Francisco / ZTE Blade).

If you experience any crashing errors (I did), then connect your device via USB, enable debugging, and then run the following command from a console window

<android-home>/platform-tools/adb -d logcat

What this does, is invoke the android device bridge, the -d switch specifies to run this against the physical device, and the logcat tells the ADB to print out any device logging to the console.

This means anything you do on the device will be logged into your console window, it helped me find a few null pointer issues.

That’s pretty much it, its quite a simple activity. Have a play with it and if you have any comments please let me know. You may have mixed results with what the recognition thinks you have said, I’ve had some odd surprises, let me know!

Happy coding!

Further Reading :

  1. ADB Commands
  2. Official documentation

Android; how to display a map the easy way..

I’m seeing countless questions, literally on a daily basis on StackOverflow regarding using maps on Android. To be honest I’ve never come across these problems but it seems many people have trouble using the maps in their application, so I’ll provide a clear and easy set of instructions on how to do this.

Common issues

  1. API key is incorrect
  2. You are using the standard Android emulator and not the Google APIs.
  3. You have extended Activity instead of MapActivity

The Tutorial…

The first thing you need, is an application to put your map in. For this tutorial I’m suggesting that you just create a blank android project, to get familiar with how maps work, then you can shift it into your existing application. Using IntelliJ (or Eclipse, if you must), create a new android project. I’ve called mine “MapMe”.

This will give you just one activity, such as the following :

package com.jameselsey.mapme;

import android.app.Activity;
import android.os.Bundle;

public class MapMe extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

I’d also suggest that you run this auto generated project to make sure your emulator starts up OK and you generally have a stable platform to build upon.

Next, we need to alter the emulator. The standard emulator that you get is the “android emulator”, which doesn’t allow you to use maps (at least not easily). This means that you need to create a slightly different emulator which does allow you to use maps, its quite easy.

Open up the android AVD manager (where you can create emulators / Android Virtual Devices). Click on the “available packages” option and download something called “Google APIs”, that should take a few minutes. When that is done, create a new AVD, be sure to check the “target” and make sure it is set to Google APIs. Once you’ve setup this emulator, make sure that your project is actually set to use it, by checking your run configurations and the target emulator.

Once thats done, I’d suggest you re-run the base application we’ve just created to make sure its all still OK.

OK so before we can finally jump into some code, you’ll need to go off and get an API key. This key allows you to use the Google maps API, I won’t explain how to do that here since theres already a rather good writeup from Google themselves, so go off and do that now.

Right lets hop into some code again, first thing you need to do is to add INTERNET permissions and uses-library into your AndroidManifest.xml file, mine looks as follows :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.jameselsey.mapme"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="MapMe" android:icon="@drawable/icon">
        <activity android:name="MapMe"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <uses-library android:name="com.google.android.maps" />
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest> 

Next, go into main.xml under res/layout and add the following :

<com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:apiKey="YOUR_API_KEY_HERE"
/>

Now we can start doctoring our activity.

Firstly, add the following import

import com.google.android.maps.MapActivity;

And make sure that you implement the method you inherit from MapActivity :

@Override
    protected boolean isRouteDisplayed()
    {
        return false;
    }

That is pretty much it, all you need to do now is to run the application, you should have the following end result :

Complete Source

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.jameselsey.mapme"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="MapMe" android:icon="@drawable/icon">
        <activity android:name="MapMe"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <uses-library android:name="com.google.android.maps" />
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest> 

res/layout/main.xml

<com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:apiKey="YOUR_API_KEY_HERE"
/>

MapMe Activity

package com.jameselsey.mapme;

import android.os.Bundle;
import com.google.android.maps.MapActivity;

public class MapMe extends MapActivity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected boolean isRouteDisplayed()
    {
        return false;
    }
}

Common Issues

Problems with RuntimeException : stub

java.lang.RuntimeException: stub
   at com.google.android.maps.MapView.<init> (Unknown Source)

You need to make sure that

<uses-library android:name="com.google.android.maps" />

is a child of the application tag.

Problems with importing of the com.google.* package

You may come across the following error :

java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

If there are problems with that import (com.google.*), chances are you don’t have the maps.jar on your class path. Go back into your AVD manager, check available packages, under 3rd party libraries you should find some libraries from Google Inc., download those. If that still doesn’t solve your issue, then double check that your android “facet” is correctly using the Google APIs target.

Also, make sure that you don’t have 2 copies of the maps.jar on your class path, if you already have the target specified to Google APIs, then you don’t need to implicitly specify maps.jar on the classpath.

Maps are displaying, but all I see is grey squares…

This is most likely because you have the wrong API key, please go and re-generate that again to make sure its OK.

Further Reading

  1. Official Google Documentation
  2. MobiForge Tutorial

DroidCon UK 2010; My thoughts about the conference…

Over summer 2010, my company held an in-house competition to muster up some interest in mobile
development, by providing a prize for anyone who could come up with an innovative mobile
application, platform independent. After this competition, fuelled by fascination for all things
Android, I requested to attend DroidCon UK, a conference dedicated to Android development.

Excited by the line-up of speakers, talks from Google, and celebrities of the Android world such
as Mark Murphy, this was a fantastic opportunity to go along and soak up as much knowledge as
possible, and see what people are achieving with the latest mobile technology.


Day 1 consisted of a Bar Camp style approach. At the beginning of the day all of the attendees and
speakers were gathered into the main hall, where people could suggest topics of discussion for the
day. These were quickly sorted into a timetable and as an attendee you were free to hop between
talks and participate in discussions around the areas you were interested in. I attended the following
talks in day 1

Creating responsive UIs
Erik Hellman, lead architect @ Sony Ericsson
Synopsis : This talk was aimed at detailing some of the key principles around user interface design,
the key note being “Remove unwanted processing from the primary thread”. This equated to
enforcing that any slower processing should be moved off the main thread, otherwise the UI
responsiveness would be reflected. Also it was pointed out that a major reason for applications
failing to impress the user is by not responding within 1000ms, often due to longer execution tasks
operating on the main thread. There were also some discussions around OpenGL and “skinning”
images onto 3D objects and how these can be achieved

Location based services
Nick Black, founder @ CloudMade
Synopsis : Location based services are one of the key features that mobile development has over
desktop development, providing the mobile user information about where they are located, or using
this for other purposes. This session was primarily a brainstorming session regarding how LBS can
be improved, one of the major drawbacks being that an internet connection is required in order
to use such facilities, however CloudMade have created an application that vectors all maps into a
single download, making some map functionality available offline. There was also discussion of GPS
connectivity, such as only updating your location on a coarser scale on long distance journeys to save
battery, and producing finer scale GPS when you come closer to your destination, at a street level.
There was also discussion about the future of LBS within android, such as the embedding of Android
devices into lease / rental vehicles, and also to private vehicles for use in conjunction with Pay as you
Go Insurance schemes.

Google Bootcamp
Google Inc representatives
This was a presentation put on by 4 representatives from Google USA, which was a question and
answer session. There was quite a lot of talk regarding the NDK, which I believe is the Native SDK for
Android, used for more low-level operations such as “flashing ROMs”.
Several questions were directed at the latest release of Android, code named Gingerbread
and due for release anytime soon, unfortunately Google were unable to comment on anything
regarding Gingerbread, or even confirmation of the existence of such release, contrary to a 10 foot
Gingerbread statue recently being raised on the roof of a Google office in the USA…
Other topics ranged from HTML5 support on the imminent Google TV, and the support for new
codecs, file formats, and custom formats in further releases

Freeing your Intents
OpenIntents.org
The OpenIntents organisation was established to help the Android community by publishing intents
that applications expose, allowing developers and consumers to access and share functionality that
applications provide. For example there are hundreds of weather applications available, so why
create a new one when you can use an existing intent from one of those applications? This seminar
was dedicated to discussing how we as developers can contribute to the greater good of the Android
community.

The Android Market
Mark Murphy, founder @ CommonsWare
This was one of the talks I was looking forward to, Mark is certainly one of the rock stars of the
Android world, with various books, applications and other endeavours, Mark clearly knows his stuff,
and I was itching to soak up some of that knowledge.
This talk was geared towards the publication of your applications, and some of the drawbacks of
the current Android market. Some of the key issues regarding the official Android market is that
searching for apps is not always easy, often returning incorrect results. Another key issue is the
rating system, it is easy for people to download your app, then give it poor rating out of spite.
There is no way for the application developer to comment back on these, so applications can be
downrated and given bad reputation quite easily from a few downloaders.
Another issue was the existence of multiple markets, with the Apple products there is just one
central point of contact for applications. With Android there is the official Google marketplace, but
there are also other ones such as the Orange application store and various others. Mark identified
some of the drawbacks of these such as having to publish to multiple markets, which could become
a considerable task when maintaining multiple applications. Mark also tried to generate interest
around developing an open source, community driven market place for Android applications.

Continuous Integration
Hugo Josefson, Lead developer @ JayWay
This was by far one of the best talks over the course of the two days, and without a doubt one of
the most useful practices to understand. The talk consisted of a walkthrough of the maven-android
plug-in, which allows developers to automatically build their android APK files, and how automated
tests can be integrated into this procedure. There was also a brief discussion on how Hudson can
also be brought into this, to allow one continuous flow of automated building, deployment, and
testing. I was particularly interested in how the Hudson build server can deploy android applications
to various headless emulators for testing, this would enable you do test it on various configurations
such as 2.1 devices, 1.6 devices, low res devices, higher res devices etc. This is most certainly an area
that requires further investigation.

Orange B2B
Orange Representatives
This was a brief talk by part of the Orange sales and marketing team on how Orange are embracing
this exciting new platform and investing a lot into various areas such as their own app market place.


Day 2 consisted of similar talks, but in a more structured format. Kicking off the day was a
presentation by the chief technology office from Reuters, giving a demonstration about how
important mobile devices are for journalism. He explained how android embedded devices will be
enabling journalists to capture information on laptops, phones, and cameras, and have all of these
sync directly into their news systems.

Next there was a talk from one of the vice presidents at Orange, explaining how seriously committed
Orange are to android, and how they are investing heavily in their partner-connect business, and
their own application marketplace.

Following the opening talks of the day, the next session was with Sean Owen, one of the authors
of the infamous “barcode scanner” application that is consistently amongst the top downloads in
the android market place. One of the interesting points of this conference was the sheer amount
of barcodes, people were enthusiastic about there barcodes. Their barcodes were on presentation
slides, banners, business cards, even t-shirts. Of course this app makes it all possible being able to
photograph the barcode and interpret it. One of the keynotes of this speech was to make your app
a dependency in other apps, ie make other people rely on your apps features for their own usage,
it will drive downloads for your application as the end user will require your app for the underlying
functionality.

The next talk was dedicated to JRuby on android, I must admit that this wasn’t the best talk of the
event. There was a lot of emphasis on how JRuby is quick for prototyping applications, but it appears
that the overhead involved in using JRuby on android is considerably larger than just using plain Java
on android. Android development is reasonably straightforward, so I failed to see any benefits of
using JRuby, but I attended the talk to be proved wrong.

Following on from JRuby, there was another presentation by Mark Murphy, regarding “Java and re-
use” in android. This talk was geared towards the re-usage of code when developing in android, such
as creating library projects, which can then become dependencies in other projects. For example if
you have a whole suite of applications that have common functionality, this could easily be built into
a library project, which could then become a dependency in your applications, thus promoting the
re-use of common code.


The next talk was beneficial, some best practices by Google, in which the following sins and saints
were discussed
Sins

  • Responsiveness
    You must make your application responsive, if there is lots of pauses or slow user interaction, the
    user will quickly get bored and give up on your application, possibly looking for alternatives. The user
    experience must flow well, and avoid using loading screens if at all necessary. It is also suggested to
    render the main view with empty containers, and fill in content as it becomes available, rather than
    displaying nothing for some time, then loading all the data at once.
  • Threading
    Make intelligent use of threads and async tasks to keep unwanted information away from the main
    UI thread, such as exception handling
  • Resources
    Don’t use too many resources, use only those which you must. Don’t overuse wake locks or update
    GPS locations unnecessary, it consumes battery and processing power, potentially affecting the
    responsiveness of your applications
  • Hostility
    Don’t make your application hostile, make it friendly and welcoming to the user, remember that
    the user is your #1 priority, after all they are the one paying you for your application. Make generic
    buttons do the same thing as other applications, or how the user would expect them. For example
    don’t override the menu/back buttons and provide some fancy feature. Use the menu button for
    displaying a menu, and the back button for going back Also don’t remove the task bar, people may
    want to multitask whilst using your application.
  • Arrogance
    Dont use undocumented APIs, make apps behave consistently. Support landscape and portrait
    mode, don’t assume all users have a portrait phone, some may have a landscape Google TV.
    Don’t disable rotation handling and respect the android application lifecycle model. Design your
    application for every handset in mind, don’t remove yourself from the wider market for targeting a
    single piece of hardware.

Saints

  • User Interface
    Make the application look good, use layout hierarchies, hiring a graphic designer will go a long way
  • Generosity
    Use intents to make your application open, encourage people to use your application as a building
    block for theirs.
  • Ubiquity
    Leverage the hardware
  • Utility
    Solve problems, create an app that identifies and solves a particular problem
  • Epic(ness)
    Don’t ever be just satisfied with “good”, always aim for “perfect”

The last talk of the day was geared towards the sheer volume of android devices being produced on
the market and how this will affect the industry in the future. It has been estimated from market
research that there will be 500 million tablets shipped by 2015, not including eReaders.
32% of these are android devices, if your app is sold to just 0.1% you could be targeting 160,000
users. An app of £0.69 would generate a potential £110,000 of revenue, indicating the potential of
the market.
There will be new devices, such as the Google TV which will run android and use HTML5, apps will be
targeted towards that platform also.

It was absolutely clear to see, that the big names in the telecommunications world such as Orange
and Vodafone, are taking the Android platform very seriously, and with a presentation by Christophe
François from Orange it was evident that they are investing a considerable amount of funds into
marketing and development. Having someone of Vice President level speaking at such an event
further displays their commitment to this exciting new technology.

Overall, I would say by attending DroidCon I’ve learned a lot about the platform and what it has in
store for the future. I was expecting the talks to be difficult to comprehend, but I was able to keep
up with the majority of subjects. I would certainly like to attend next year.

Binding your buttons to your activity, the easy way!

OK, so you have several buttons on your view, all linked into the same activity. Sounds simple enough, you probably have something that looks like the following :

package com.jameselsey.buttons;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

import android.widget.Button;
import android.widget.Toast;

public class ButtonsActivity extends Activity
{

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final Button button = (Button) findViewById(R.id.myButton1);
        button.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(View v)
            {
                // Perform action on clicks
                Toast.makeText(ButtonsActivity.this, "Hello!", Toast.LENGTH_SHORT).show();
            }
        });

        final Button button2 = (Button) findViewById(R.id.myButton2);
        button.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(View v)
            {
                // Perform action on clicks
                Toast.makeText(ButtonsActivity.this, "Hello Again!", Toast.LENGTH_SHORT).show();
            }
        });

    }

}

That works fine, however if you start adding more and more buttons, it’ll become quite messy. I mean seriously, do you really want to be declaring your buttons inside the onCreate method? Apart from making this method quite large in size (a potential maintenance nightmare), you’re also slightly boxing yourself in when it comes to making your code unit-testable. But fear not, I’ll let you into the secret of the “onClick” method on the button class.

Lets add a few buttons into our config xml file, such as the following :

<Button android:id="@+id/myButton1"
            android:onClick="myButtonPressed1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="Click me! 1"
            />
    <Button android:id="@+id/myButton2"
            android:onClick="myButtonPressed2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="Click me! 2"
            />

Notice the use of android:onClick, now update your activity to look something like the following. You’ll notice that the value contained in the android:onClick is actually the method name in your activity.

package com.jameselsey.buttons;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class ButtonsActivity extends Activity
{

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public void myButtonPressed1(View v)
    {
        Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
    }

    public void myButtonPressed2(View v)
    {
        Toast.makeText(this, "Hello again!", Toast.LENGTH_LONG).show();
    }

}

This way of binding buttons is a lot cleaner, and helps to remove the button declaration from the Android lifecycle methods in your class. Give it a try, its one of those smaller features of the Android SDK but makes life a great deal more pleasant!

Happy coding

Android; Continuous integration, all made lovely with Maven

Lets face it, the eclipse ADT plugin is great for getting an android application up and running quickly, but if you want an easier way to get libraries, and a continuous intergration environment for robust automated testing and building, then maven is the way to go.

IntelliJ have recently released the Early Access Program to their version X of IDEA, this comes with Android support in the community edition, meaning we’re no longer bound to using eclipse for Android development!

The first thing you want to do, is to run through the “create new project” wizard in IntelliJ, its fairly straightforward, just run through and setup a simple android applicaiton. When you’ve got that, run it up on the emulator just to make sure any autogenerated code IntelliJ created works OK.

OK so now we’re ready to start gutting it out and providing the Maven framework. First thing you need to do, in the root directory of the android project, create a file called pom.xml with the following contents

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>MavenMess</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>apk</packaging>
    <name>Maven Android Plugin - samples</name>

    <dependencies>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>android</artifactId>
            <version>2.2.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                <artifactId>maven-android-plugin</artifactId>
                <version>2.6.0</version>
                <configuration>
                    <sdk>
                        <!--  platform or api level (api level 4 = platform 1.6)-->
                        <platform>8</platform>
                    </sdk>
                    <emulator>
                        <!--  the name of the avd device to use for starting the emulator-->
                        <avd>GoogleAPIs</avd>
                    </emulator>
                    <deleteConflictingFiles>true</deleteConflictingFiles>
                    <undeployBeforeDeploy>true</undeployBeforeDeploy>
                </configuration>
                <extensions>true</extensions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <!--  version 2.3 defaults to java 1.5, so no further configuration needed-->
                <version>2.3</version>
            </plugin>
        </plugins>
    </build>
</project>

The POM file is what maven uses to build/test your project. If you’re not familiar with maven at this stage I’d suggest a quick browse of their documentation to get to grips with the fundamentals.

The above POM does a couple of tasks, firstly it tells maven to build an APK file using the packaging statement. You need this otherwise maven wouldn’t know what to create, and might create a WAR or EAR file instead.

You declare the android SDK as a provided dependancy. Next comes the build section, this is where the magic happens. The source directory tells maven where it can find all your android source files to compile.

You’ll need to declare usage of the Jayway plugin, this is a neat little plugin that I picked up from Hugo Josefson from Jayway, at DroidCon UK 2010. There is some configuration that you can set here such as the API level and which emulator to deploy the application to. The second plugin is the maven Java compiler, we need this in order to actually compile the class files.

Right so thats your POM sorted, next you can go ahead and delete the following items

  • \bin
  • \libs
  • build.properties
  • build.xml

You won’t need those files/directories, those are used for the autogenerated ant build script that the android IntelliJ plugin creates, and we’re now using maven.

Right thats it for the maven configuration, next comes actually running the application. The maven goal of “mvn install” will compile all your classes, so run that to check the above is OK.

To deploy your application to a device, you can run the maven goal of “mvn android:deploy”. These goals are great, but what about a 1 button click for building and deploying? Easy…

Along the top of IntelliJ, click on the drop down menu for run targets and create a new run/debug configuration. Using the goal of “android:deploy” will deploy the application to the emulator, but you can also setup a goal of “mvn install” which happens before deployment, as shown in the screenshot below. This will build and deploy your application to the emulator with one click.

All you need to do then is to go into your emulator and start your application, simple!

Any questions/suggestions, please let me know!

Big thanks to the guys at Jayway for sharing this, and making it possible.