Category Archives: Android

Android content provider: querying and deleting

We’ve seen how to write a ContentProvider and how to insert data into it, so now we’ll look at getting data out of it. The insertion Activity used a ContentProvider to store simple text notes entered by the user. We’ll now write another app which accesses the data and displays it in a ListView. Clicking on an entry in this list deletes the note. Here’s the complete Activity for this app:

package growe.ex09acontentproviderviewer;

import android.os.Bundle;
import android.app.ListActivity;
import android.database.Cursor;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.ListView;

public class ViewDataActivity extends ListActivity {
	ListView entryList;
	SimpleCursorAdapter adapter;
	String[] projection =
		{
			EntriesContract._ID, EntriesContract.ENTRY_DATETIME, EntriesContract.ENTRY_TEXT
		};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		entryList = getListView();

		Cursor cursor = getContentResolver().query(EntriesContract.CONTENT_URI, projection,
				null, null, null);
		adapter = new SimpleCursorAdapter(getApplicationContext(),
				R.layout.listrow, cursor,
				new String[]{EntriesContract.ENTRY_DATETIME, EntriesContract.ENTRY_TEXT},
				new int[]{R.id.dateText, R.id.entryText}, 0);
		entryList.setAdapter(adapter);
	}

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		getContentResolver().delete(EntriesContract.CONTENT_URI,
				EntriesContract._ID + " = " + id, null);
		Cursor newCursor = getContentResolver().query(EntriesContract.CONTENT_URI, projection,
				null, null, null);
		adapter.changeCursor(newCursor);
		super.onListItemClick(l, v, position, id);
	}
}

Look at onCreate() first. Since this Activity inherits ListActivity (line 10), its layout is based on a ListView, which we obtain in line 21. Recall that we need an adapter to manage a ListView. Rather than write our own, we can use Android’s SimpleCursorAdapter. The idea is that we first obtain the data we want from the database (line 23) and then send this data to a SimpleCursorAdapter which displays it in the ListView. Let’s look at these two things in turn.

As usual, we use a ContentResolver to access the ContentProvider on line 23. This time, we want to query the database. In a query, we need to specify the database table from which to get the data, and also specify a filter to identify which data we want. You’ll see that the query() method has 5 arguments, which are:

  • the URI to the ContentProvider
  • projection, which is a list of columns we want for each row of retrieved data
  • selection, which is a filter to be applied to the data (equivalent to an SQL WHERE clause)
  • selection arguments (if we use a ? as a placeholder in the selection, this is where we give the arguments to fill in)
  • a sort order

In our simple example, we just want all the rows in the table, so the last 3 arguments are null. However, the query() method is defined in the ContentProvider, so if we’ve written our own ContentProvider, we need to implement the query() method. Here’s the method (for the full code of the EntriesContentProvider class, see the earlier post).

	@Override
	public Cursor query(Uri contentUri, String[] projection,
			String selection, String[] selectionArgs,
			String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(EntriesContract.ENTRIES_TABLE_NAME);
		Cursor cursor = queryBuilder.query(database, projection, selection,
				selectionArgs, null, null, sortOrder);
		return cursor;
	}

We use the SQLiteQueryBuilder  helper class to build the query by first adding the table name and then calling the query builder’s own query() method. A couple of notes here. First, the contentUri isn’t used in this method. A more general implementation allows activities to register to receive notifications whenever the data is changed in the database. Doing that here would lead us a bit astray from our main purpose, so we’ll defer discussion of that to a future post.

Second, there doesn’t seem to be any way of passing the table name as a parameter into the override of the ContentProvider’s query() method, so it’s effectively hard-coded here when we call setTables(). We could, of course, define a different query() method that takes the table name as an additional parameter, but here all our queries come from the same table so there’s no point in doing this.

Finally, the query builder’s query() method has a couple of parameters not present in the ContentProvider’s query() method. These allow for ‘having’ and ‘group by’ clauses to be specified, but again, we won’t use those here.

The Cursor object that is returned by queryBuilder.query() is a pointer to a row in the result set provided by the query. We can iterate over the cursor to visit each row of the result set in turn. Since all we want to do here is display all the results in a ListView, we can just pass the cursor to the SimpleCursorAdapter constructor on line 25 in the ViewDataActivity listing above. The adapter then takes care of iterating through the cursor and creating the list.

The arguments of the constructor on lines 25 to 28 need a bit of explanation. The second argument (R.layout.listrow) points to the layout resource defining the interface of a single view in the list. Here, we’ve just assigned two TextViews, one for the date/time and one for the message. The next argument is the cursor.

The next two arguments are the ‘from’ string array and the ‘to’ int array. The ‘from’ array gives a list of columns from each row of the database that we want to display in the user interface elements of a list element. The ‘to’ array gives the correspoinding IDs of the UI elements in which we display the data. The last argument (0) is a flag that we don’t use here.

Running the app at this point will produce a list showing all the messages that were entered in a prior run of the first app we described in the previous post. As a final touch, we’ll add a click handler for each list item that deletes the corresponding element from the database and updates the ListView. This handler is shown on line 33.

We first call the ContentProvider’s delete() method to delete the item from the database. This method’s code is:

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		database.delete(EntriesContract.ENTRIES_TABLE_NAME, where, whereArgs);
		return 0;
	}

The uri is again not used here (it would be used if we’re notifying the list that data has changed). The ‘where’ argument specifies a filter to apply to the delete operation. When we called delete above, we specified that only a particular _id should be deleted. Be careful with this argument; if you leave it as null, everything gets deleted, just like in SQL!

The whereArgs argument provides values if we use wildcards in the where statement (which we don’t here). This call deletes the entry from the database but doesn’t update the ListView.

Returning to the ViewDataActivity code above, we see on lines 36 to 38 that we query the ContentProvider after the delete and change the cursor in the adapter. This causes the display to update with the deleted item removed.

As I mentioned, there is a more general way in which we can automatically update a ListView whenever its underlying data is deleted, inserted or updated, but we’ll leave that for later.

Advertisements

Android content providers

An Android app sometimes needs data produced or stored in another app. As you might expect, a database is used as the repository for the data, and (given the correct permissions) this data is available to any number of apps. The database used by Android is SQLite which, as the name implies, is a streamlined database that uses SQL as its language.

Rather than deal directly with the database, an app uses a ContentProvider as the interface between an app and the underlying data. To illustrate how ContentProviders are written and used, we’ll develop two simple apps that can exchange data. The first app has an interface that allows the user to enter a short text note which is then stored in a ContentProvider. The second app displays the notes in a ListView and allows the user to delete a note by clicking on it. We’ll look at the first app in this post and the second one in a future post.

We’ll begin with the main Activity called InsertDataActivity:

package growe.ex09contentproviderwriter;

import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;

public class InsertDataActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_insert_data);
	}

	public void addEntryClick(View view)
	{
		EditText entryBox = (EditText) findViewById(R.id.newEntryEditText);
		String entryText = entryBox.getText().toString();
		ContentValues newEntry = new ContentValues();
		newEntry.put(EntriesContract.ENTRY_TEXT, entryText);
		getContentResolver().insert(EntriesContract.CONTENT_URI, newEntry);
	}
}

The view for this activity consists of an EditText box into which the user types the note and a button which adds the note, along with the date and time it was added, to the ContentProvider. The addEntryClick() method is the button’s event handler.

To understand lines 21 to 23 (which add the new note to the ContentProvider), we need to back up a bit and discuss how a ContentProvider works. A ContentProvider resides on an Android device at a particular location, which contains the Java package name within which the ContentProvider is defined. This package name is known as the authority of the ContentProvider. In this case, the package name is growe.ex09contentproviderwriter. The ContentProvider contains within it the SQLite database that holds the data, so once the app wishing to access the ContentProvider has found it using the authority, it also needs to send in the name of the database table it wishes to access.

However, the process of accessing a ContentProvider isn’t quite as simple as just retrieving the ContentProvider and then calling methods on it. Access to a ContentProvider is provided by yet another class called ContentResolver, which acts as a sort of lookup service for ContentProviders. A ContentResolver contains all the usual methods you’d expect for interacting with a database, such as insert(), delete(), query() and so on. Since the app we’re discussing only inserts items into the database, it’s the insert() method we’re interested in here. The insert() method we call on line 23 takes only 2 arguments. The first is the URI giving the address of the ContentProvider and the database table within which we want to insert the new data. The second argument contains the data to be inserted.

Both of these arguments probably look a bit cryptic here, so we’ll explain them one at a time. First, we have a parameter called EntriesContract.CONTENT_URI. We could have just used a bare String here, but it is more usual to define a Contract class which contains definitions of all the strings we need to interact with the ContentProvider. Here’s the EntriesContract class:

package growe.ex09acontentproviderviewer;

import android.net.Uri;

public final class EntriesContract {
	public static final String AUTHORITY = "growe.ex09contentproviderwriter";
	public static final Uri BASE_URI = Uri
			.parse("content://" + AUTHORITY + "/");
	public static final String ENTRIES_TABLE_NAME = "entrytable";
	public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_URI,
			ENTRIES_TABLE_NAME);

	public static final String _ID = "_id";
	public static final String ENTRY_DATETIME = "entryDateTime";
	public static final String ENTRY_TEXT = "entryText";
}

We define the BASE_URI which is the URI of the ContentProvider itself, without any reference to any particular database table within it. A URI for a ContentProvider always begins with content:// followed by the authority, followed by a slash.

The other strings define names we need to access the database itself. As we’ll see in a minute, the database contains a single table called ‘entrytable’, and this table has 3 columns: _id which is an autoincremented primary key, and a column for the date/time and one for the text itself.

The CONTENT_URI is built by joining the table name ‘entrytable’ onto the end of the BASE_URI, and it is this URI that we’ll use in all our interactions with the ContentProvider.

Now back to InsertDataActivity, and the second argument of the insert() method. Since we’re allowed only one argument to transmit all the data required for a new row in the database, you might wonder how we can send in values for each column in the row. The answer is that this argument is a ContentValues object, which is essentially a hash table of key-value pairs. We can use the put() method to add as many key-value pairs as we need. In this case, we need to add only one, since both the _id and the entryDateTime columns will be generated automatically when the row is added to the database (we’ll see how this is done when we discuss the ContentProvider below). On line 22, we add the entryText using the key EntriesContract.ENTRY_TEXT, which is the name of the column in the database in which the text is stored. If we had other columns to add, we just put in an additional put() call for each column.

That explains how we access and insert data into a ContentProvider, but we still haven’t created the ContentProvider, and it is here that most of the work lies. To write a custom ContentProvider, we need to inherit the ContentProvider abstract class and implement the required methods. We call our class EntriesContentProvider, and here’s the complete code:

package growe.ex09contentproviderwriter;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;

public class EntriesContentProvider extends ContentProvider {

	private static final String CREATE_LOCATION_TABLE = " CREATE TABLE " +
			EntriesContract.ENTRIES_TABLE_NAME +"(" +
			EntriesContract._ID
			+ " INTEGER PRIMARY KEY AUTOINCREMENT, "
			+ EntriesContract.ENTRY_TEXT + " TEXT NOT NULL, "
			+ EntriesContract.ENTRY_DATETIME + " DATETIME DEFAULT CURRENT_TIMESTAMP"
			+ ")";
	private static final String DB_NAME = "EntriesDB";
	private EntriesDatabaseHelper mDbHelper;
	private SQLiteDatabase database;

	protected static final class EntriesDatabaseHelper extends SQLiteOpenHelper
	{

		public EntriesDatabaseHelper(Context context) {
			super(context, DB_NAME, null, 1);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL(CREATE_LOCATION_TABLE);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		}

	}

	@Override
	public int delete(Uri arg0, String where, String[] whereArgs) {
		database.delete(EntriesContract.ENTRIES_TABLE_NAME, where, whereArgs);
		return 0;
	}

	@Override
	public String getType(Uri arg0) {
		return null;
	}

	@Override
	public Uri insert(Uri tableUri, ContentValues entry) {
		database = mDbHelper.getWritableDatabase();
		long newID = database.insert(EntriesContract.ENTRIES_TABLE_NAME, "", entry);
		if (newID > 0)
		{
			Uri newUri = ContentUris.withAppendedId(EntriesContract.CONTENT_URI, newID);
			return newUri;
		}
		throw new SQLException("Failed to add record into " + tableUri);
	}

	@Override
	public boolean onCreate() {
		mDbHelper = new EntriesDatabaseHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri arg0, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(EntriesContract.ENTRIES_TABLE_NAME);
		Cursor cursor = queryBuilder.query(database, projection, selection,
				selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), arg0);
		return cursor;
	}

	@Override
	public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
		return 0;
	}

}

If you’ve done any SQL, much of the code here will be familiar. If not, you’ll just have to trust me.

The first thing we need to do if the application hasn’t been run before is create the database table. This is done using standard SQL, though in a bit of a roundabout way. The SQL for creating the table is the String on lines 16 to 22. We use the various Strings defined in EntriesContract to specify the table name and the names of the columns within it. The _id column is defined in the SQL as the primary key and autoincremented. The entryText column is defined as of type TEXT and not allowed to be null. SQLite’s SQL allows a DATETIME stamp to be added to an inserted row as shown on line 21, where we use the current time.

Where does the database actually get created? This is a bit involved. Android provides the abstract SQLiteOpenHelper class which can be used to create the database. We’ve inherited this class on line 27 and provided a constructor and the two required methods. In onCreate() (line 35) we call execSQL to create the table within the database db, but where does db itself get created?

The sequence of events is as follows. When the ContentProvider itself is created, its onCreate() method (line 69) is called. This creates the mDbHelper object, but doesn’t actually create the database (or access it, if it already exists). In fact, the database itself is not accessed until one of the accessing methods (insert(), query(), etc) is called. In the ContentProvider’s insert() method (line 57), we make a call to mDbHelper’s getWritableDatabase(). It is this call that checks to see if the database already exists and, if so, it returns the database object. If it doesn’t exist, the helper class then creates a database object and passes this to its own onCreate() method (line 35), which then creates the table within the database. As I said, it’s a bit involved, but it ensures that only one copy of the database exists.

Finally, we’ll look at the insert() method (we’ll leave query() and delete() to the next post, since they aren’t used in the first app). The ContentProvider’s insert() method (line 57) must call the database’s insert() method in turn (line 59) to do the actual insertion. The database insert() method takes the table name and a ContentValues object containing the column name-value pairs to be inserted. The second argument is to be used only in the event that we try to insert an empty row into the database (it’s called a ‘null column hack’), but since we won’t be doing that we’ll just set it to the empty string.

The ContentProvider insert() method should return a URI to the row that was just inserted. If we have an _id column, we can construct this by appending the _id to the CONTENT_URI.

One last task remains. We need to connect the ContentProvider class with the authority name. This is done in the AndroidManifest.xml file. Within the application tag, we insert the following:

        <provider android:name=".EntriesContentProvider"
            android:authorities="growe.ex09contentproviderwriter"
            android:exported="true"
            android:enabled="true">
        </provider>

That’s about it for inserting data into a ContentProvider’s database. We’ll examine querying and deleting in the next post.

Android AsyncTask

When an Android app starts, everything is running within a single thread called the main thread or UI thread. The name ‘UI thread’ comes from the fact that it is this thread which handles all interaction with the user interface. UI controls are not, in general, thread-safe, meaning that an attempt to modify them from a non-UI thread can cause things to break. As a result, you should always ensure that any use of UI components is run on the UI thread.

If our app contains code that can take a long time to run (such as downloading something off the internet), putting this code in the UI thread will block that thread until the task completes. A blocked UI thread means that none of the controls will respond to user input, making it appear that the app has hung.

Android provides the AsyncTask generic class to help with this problem. AsyncTask allows a lengthy task to be run in a background thread, but provides methods in which progress reports and a final result can be posted to the UI thread. The AsyncTask generic class has the form AsyncTask<Params, Progress, Result>, where:

  • Params is the data type of objects sent to the AsyncTask as input data
  • Progress is the data type of objects sent as progress reports while the task is running
  • Result is the data type of the final object, delivered after the task finishes.

To use AsyncTask, you must define your own class that inherits it and overrides the doInBackground() method (and possibly a few other methods).

The easiest way to see how AsyncTask works is to look at an example. We’ve created an app which lets the user enter a positive integer, and the app will then calculate all the prime numbers up to that integer. The calculation is done in an AsyncTask-derived class called CalcPrimesTask. Params (the input) consists of a single integer. We’ll deliver a progress report, in the form of a String, after each prime is found. Finally, we’ll return a List containing all the primes as the Result at the end.

We’ve made the calculation a long job by putting a 1 second delay after each prime found.

The UI provides an EditText for entry of the maximum number, a Start button to a new calculation (pressing the Start button adds a new job to the queue on each press), a Stop button to stop all tasks, and a TextView for displaying messages. Here’s the MainActivity that starts the app:

package growe.ex07asynctask;

import java.util.ArrayList;
import java.util.List;

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

public class MainActivity extends Activity {
	List<CalcPrimesTask> taskList;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		taskList = new ArrayList<CalcPrimesTask>();
	}

	public void onClickStart(View view) {
		EditText maximumEditText = (EditText) findViewById(R.id.maximumEditText);
		int maxNum = Integer.parseInt(maximumEditText.getText().toString());
		CalcPrimesTask task = new CalcPrimesTask(this);
		taskList.add(task);
		task.execute(maxNum);
		Toast.makeText(getApplicationContext(), "New run queued.", Toast.LENGTH_SHORT).show();
	}

	public void onStopClick(View view) {
		for (CalcPrimesTask task : taskList) {
			task.cancel(true);
		}
		Toast.makeText(getApplicationContext(), "All runs cancelled.", Toast.LENGTH_SHORT).show();
	}
}

In onCreate() on line 19 we create the list of tasks. The event handler for the Start button is on line 22, where we retrieve the number entered and then start up a new CalcPrimesTask to do the calculation. This task is added to the ArrayList and then the execute() method is called, which starts the task running. We also display a Toast message when the job starts. (If we hadn’t used a separate thread for the calculation, this Toast message wouldn’t appear until after the job finished because the UI thread would be blocked.)

The onStopClick() method on line 31 just calls cancel() on all tasks and displays another Toast message. The ‘true’ in the call to cancel() indicates that even if the task is currently running, it should be interrupted, if possible.

Now let’s look at CalcPrimesTask:

package growe.ex07asynctask;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.AsyncTask;
import android.widget.TextView;

public class CalcPrimesTask extends AsyncTask<Integer, String, List<Integer>> {

	Activity activity;

	public CalcPrimesTask(Activity mainActivity) {
		activity = mainActivity;
	}

	@Override
	protected List<Integer> doInBackground(Integer... params) {
		int maxNum = params[0];
		List<Integer> primeList = new ArrayList<Integer>();
		for (int i = 2; i <= maxNum ; i++) {
			int maxCalc = (int)Math.sqrt(i);
			boolean isPrime = true;
			for (int j = 2; j <= maxCalc ; j++) {
				if (i % j == 0) {
					isPrime = false;
					break;
				}
			}
			if (isPrime) {
				primeList.add(i);
				publishProgress("Prime " + i + " found.");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}
		}
		return primeList;
	}

	@Override
	protected void onProgressUpdate(String... values) {
		TextView messageView = (TextView) activity.findViewById(R.id.messageText);
		messageView.setText(values[0]);
		super.onProgressUpdate(values);
	}

	@Override
	protected void onPostExecute(List<Integer> result) {
		TextView messageView = (TextView) activity.findViewById(R.id.messageText);
		messageView.setText("Total of " + result.size() + " primes found.");
		super.onPostExecute(result);
	}
}

On line 10, we define CalcPrimesTask by providing the generic types for AsyncTask as described above.

We need to pass in the parent Activity so we can access the UI views, so we store a reference to the Activity in the constructor on line 15.

The one method we must override is doInBackground() (line 19). It takes a sequence of Integers (the parameter type Integer… means an arbitrarily large list of Integers) which is the Params type declared on line 10, and returns a List<Integer>, which is the Result type. Code within doInBackground() is run in the background or worker thread, not the UI thread, so this is where the lengthy calculation goes. The code here creates a List<Integer> in which to store the primes and uses a simple test to find all the primes up to the value entered by the user. This value was the maxNum argument to the execute() method in MainActivity (line 27), and appears as the first element in the params array, so we access it on line 20. To test if a number i is prime, we find its square root and then test to see if all numbers from 2 up to the square root divide the number. If not, the number is prime, so we add it to the list (line 32). At this point, we want to publish a progress report back to the UI to let the user know a prime was found. We can do this with the publishProgress() method (inherited from AsyncTask), whose argument must of the type Progress (a String here). publishProgress() calls onProgressUpdate() which is run on the UI thread, so it’s safe to access UI views here. We update the TextView with the message that a prime has been found.

When the background task is finished, doInBackground() must return an object of type Result (which is a List<Integer> here). This Result object is sent to onPostExecute(), which also runs in the UI thread. Here (on line 53) we just print out how many primes were found, although obviously we could have used the list to display the primes or do something else with them.

There is another method called onPreExecute() which can be overridden. It too runs on the UI thread and is called before doInBackground() starts up the background thread. It doesn’t take any arguments, however, so you can’t pass any data to it.

Android notifications and PendingIntents

Notifications

On an Android phone, the bar at the top of the screen is the notification area or status bar. On a tablet, this area is at the bottom right of the screen. In either case, it is an area that is always visible and is used by apps to post notifications about various events that have occurred. The notification area can be opened to get a list containing more detailed information about these events.

To illustrate a few of the features of notifications, we’ll modify our earlier example using Fragments so that each time the user inputs some new text, a notification is received. In addition, if the user then closes the app and then opens the notification area and clicks on a given notification item, the app is restarted displaying the text that was entered to give that notification.

Implementing these additions requires only adding a bit to the MainActivity. Since we want to generate a notification when the user presses the enterText button we can modify the event handler for this button as follows:

	@Override
	public void enterTextButtonPressed() {
		EnterTextFragment enterTextFragment =
				(EnterTextFragment) getFragmentManager().
				findFragmentById(R.id.enter_text_fragment);
		GetTextFragment getTextFragment =
				(GetTextFragment) getFragmentManager().
				findFragmentById(R.id.get_text_fragment);
		String text = enterTextFragment.getEnteredText();
		getTextFragment.setText(text);
		restartNotifyText(text);
	}

The only change we’ve made is the addition of a call to restartNotifyText() at the end, so let’s look at this method:

	private void restartNotifyText(String text) {
		Notification.Builder notifBuilder = new Notification.Builder(this).
				setSmallIcon(android.R.drawable.btn_star_big_on).
				setContentTitle("Text sent").setContentText(text).
				setTicker("New text arrived");
		Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
		mainIntent.putExtra(LOADED_TEXT, text);
		PendingIntent mainPendingIntent = PendingIntent.getActivity(
				getApplicationContext(), simpleNotification, mainIntent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		notifBuilder.setContentIntent(mainPendingIntent);
		NotificationManager notifManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notifManager.notify(simpleNotification++, notifBuilder.build());
	}

To create a notification, we can use the Notification.Builder class. Its constructor takes a context (the current Activity will do). Following this, we can add as much information as we want to the notification. However, there are three things we must add to any notification: a small icon, a title and the content text. The methods for adding features each return the Builder object that calls them, so they can be chained as seen here. We’ve used one of the built-in icons (it displays a yellow star) for the small icon and provided values for the title and text. In addition we’ve added a ticker which is a message that is displayed for a few seconds when the notification first appears. The ticker is optional.

For a bare bones notification, that’s all the information we need and if we didn’t want the notification to respond to clicks, we could just skip from here to line 12 to get the NotificationManager, then build the notification and send it to the device using notify().

However, to allow the notification to restart a closed app when clicked, we need to provide some more code. We can use an explicit Intent to restart the app in the way we did earlier, so we define mainIntent on line 6 to do this. Since we want the restarted app to display the text just entered, we need to save this text as an extra within the Intent (line 7). The parameter LOADED_TEXT is just a pre-defined string which serves as a key for the extra; it can be any string you like so long as it’s unique.

PendingIntents

Next, we define a PendingIntent. This is essentially a token object which contains a reference to a genuine Intent, and which can be sent to another process giving that process permission to run the enclosed Intent. PendingIntents can specify that various types of processes can be started, but here we’re interested in starting up an Activity, so we call the static PendingIntent.getActivity() method. Its arguments are

  • the usual context in which it is to run
  • an ID int which can be referred to later if we want to modify the PendingIntent. Here, we’ve defined an int called simpleNotification earlier and set its initial value to 1.
  • the Intent itself
  • a flag indicating what action should be taken with the PendingIntent. FLAG_UPDATE_CURRENT means that if this PendingIntent already exists, we should update it with the new data rather than create a new one.

Then we attach this PendingIntent to notifBuilder using setContentIntent(). Setting the content Intent in the builder provides an event handler for clicks on the notification item: clicking on the item will tell the PendingIntent to start up its Intent.

With that done, we can now use the NotificationManager to build and post the notification as before.

One final point should be mentioned. The notify() method takes two arguments. The first is an int ID that can be used to update the notification later. If you call notify() again with the same ID, it overwrites the existing notification. If you call it with a new ID that hasn’t been used before, it creates a new notification. In this example, we want a separate notification for each new bit of text that is entered, so we increment the ID each time a notification is posted.

We use the same ID for the PendingIntent and the notification. If the ID and Intent used to construct a PendingIntent are the same as those used earlier, then the old PendingIntent is overwritten rather than a new one being generated. Note that changing the text stored in the Intent’s extras bundle does not change the Intent as seen by the PendingIntent, so unless we change the ID of the PendingIntent on each call, the same PendingIntent would be attached to all the notifications. That is, each notification would display the correct entered text, but when the Activity is restarted, only the last entered text would be displayed in the app itself.

Finally, we can look at the onCreate() method:

	private static final String LOADED_TEXT = "LoadedText";
	int simpleNotification = 1;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Intent notifyIntent = getIntent();
		Bundle extras = notifyIntent.getExtras();
		if (extras != null) {
			String loadedText = extras.getString(LOADED_TEXT);
			GetTextFragment getTextFragment =
					(GetTextFragment) getFragmentManager().
					findFragmentById(R.id.get_text_fragment);
			getTextFragment.setText(loadedText);
		}
	}

We need to retrieve the Intent that was used to start (or restart) the Activity. When the app is started by clicking its icon on device’s screen, it is the main system Intent that initializes the Activity. This Intent won’t have any extras. We can use the Activity’s getIntent() method to retrieve the Intent that started the Activity, and getExtras() to get its extras Bundle. If this isn’t null, we can then retrieve the LOADED_TEXT string and set the TextView in getTextFragment to this text.

Android ListViews and adapters

The ListView is an interface element that displays a list of items. The items in the list can have a custom layout consisting of other controls such as text or images. Managing a ListView is a bit more complex than you might think so we’ll walk through a relatively simple example here to see how all the parts fit together.

The app we’ll develop displays a Fragment on the left that contains an EditText into which the user can type some text, and a couple of buttons. The addItemButton adds an item to the ListView (displayed in a Fragment on the right) that contains the entered text along with the date and time at which the item was added. The clearListButton deletes all items from the list.

We’ve seen how to add Fragments to an Activity, so we’ll use the same technique here. First we create an XML file called enter_item.xml in the res–>layout folder and use Eclipse’s Graphical Layout editor to add the EditText and the two Buttons. Then we create a class to manage this layout, which looks like this:

package com.example.ex05listview;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

public class AddItemFragment extends Fragment {
	AddItemListener activityCallback;

	public interface AddItemListener {
		public void onAddItemClick(ListItem item);
		public void onClearListClick();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		final View view = inflater.inflate(R.layout.enter_item, container, false);
		Button addItemButton = (Button) view.findViewById(R.id.addItemButton);
		addItemButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				EditText itemText = (EditText) view.findViewById(R.id.itemText);
				ListItem newItem = new ListItem(itemText.getText().toString());
				activityCallback.onAddItemClick(newItem);
			}
		});

		Button clearListButton = (Button) view.findViewById(R.id.clearListButton);
		clearListButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				activityCallback.onClearListClick();
			}
		});
		return view;
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		try {
			activityCallback = (AddItemListener) activity;
		} catch (ClassCastException e) {
			throw new ClassCastException(activity.toString() +
					" must implement AddItemListener");
		}
	}

}

This Fragment needs to send the new item to the ListView on the right and it does so via the MainActivity in order to keep the Fragments independent of each other. As before, we do this by defining an interface (line 16) which MainActivity must implement. In AddItemFragment’s onAttach() method, we test that the calling Activity implements the interface and store a reference to the Activity for future use.

In onCreateView() we inflate the layout and add event handlers to the two Buttons. The addItemButton handler retrieves the entered text from the EditText control and constructs a ListItem object. ListItem is as follows:

package com.example.ex05listview;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class ListItem {
	private String dateAdded;
	private String message;

	public ListItem(String message)
	{
		this.setMessage(message);
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
		dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London"));
		Calendar cal = Calendar.getInstance();
		setDateAdded(dateFormat.format(cal.getTime()));
	}

	String getDateAdded() {
		return dateAdded;
	}

	void setDateAdded(String dateAdded) {
		this.dateAdded = dateAdded;
	}

	String getMessage() {
		return message;
	}

	void setMessage(String message) {
		this.message = message;
	}
}

The constructor uses Java’s SimpleDateFormat and Calendar classes to get the current date and time and format it into a String (lines 15 to 18).

Back in AddItemFragment, we then send newItem to MainActivity (line 32). MainActivity just passes the ListItem along to ItemListFragment. Here’s the code for MainActivity:

package com.example.ex05listview;

import com.example.ex05listview.AddItemFragment.AddItemListener;

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

public class MainActivity extends Activity
implements AddItemListener {
	ItemListFragment itemListFragment;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		itemListFragment = (ItemListFragment) getFragmentManager().findFragmentById(R.id.itemListFragment);
	}

	@Override
	public void onAddItemClick(ListItem item) {
		itemListFragment.addItem(item);
	}

	@Override
	public void onClearListClick() {
		itemListFragment.clearList();
	}
}

We’ll get to ItemListFragment in a minute. First, we add the event handler for clearListButton in AddItemFragment (line 37) which calls onClearListClick() in MainActivity, which again just passes this along to ItemListFragment.

To build the ItemListFragment, we create a new XML file in res–>layout, but all we need to do is set the initial layout to a ListView, with an ID of “@+id/list”. Here’s the complete XML file:

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

All lines except the android:id should be created automatically in Eclipse. Here’s ItemListFragment, the Java class that manages the list fragment:

package com.example.ex05listview;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ItemListFragment extends Fragment {
	ItemListAdapter adapter;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		adapter = new ItemListAdapter(getActivity());
		adapter.addItem(new ListItem("Test item"));
		View view = inflater.inflate(R.layout.list_fragment, container, false);
		ListView listView = (ListView) view.findViewById(R.id.list);
		listView.setAdapter(adapter);
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View item, int position,
					long id) {
				TextView itemText = (TextView) item.findViewById(R.id.messageItem);
				Toast.makeText(getActivity(), itemText.getText().toString(),
						Toast.LENGTH_SHORT).show();
			}
		});
		return view;
	}

	public void addItem(ListItem item)
	{
		adapter.addItem(item);
	}

	public void clearList()
	{
		adapter.clearItems();
	}
}

On line 20, we create an ItemListAdapter. A list adapter is a class that manages the data behind a list. There are several pre-defined adapter classes, but it’s easy enough to write a custom one, so here’s ItemListAdapter:

package com.example.ex05listview;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class ItemListAdapter extends BaseAdapter {
	List<ListItem> itemList = new ArrayList<ListItem>();
	Context context;

	public ItemListAdapter(Context c) {
		context = c;
	}

	@Override
	public int getCount() {
		return itemList.size();
	}

	@Override
	public Object getItem(int position) {
		return itemList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View arg1, ViewGroup parent) {
		LayoutInflater inflater = (LayoutInflater) context.getSystemService(
				Context.LAYOUT_INFLATER_SERVICE);
		View itemView = inflater.inflate(R.layout.list_item, parent, false);
		TextView dateText = (TextView)itemView.findViewById(R.id.dateTimeAdded);
		TextView message = (TextView)itemView.findViewById(R.id.messageItem);
		dateText.setText(itemList.get(position).getDateAdded());
		message.setText(itemList.get(position).getMessage());
		return itemView;
	}

	public void addItem(ListItem item)
	{
		itemList.add(item);
		notifyDataSetChanged();
	}

	public void clearItems() {
		itemList.clear();
		notifyDataSetChanged();
	}
}

We’ve extended BaseAdapter, which is a built-in Android abstract class containing four methods we must implement which we’ll get to in a minute.

The adapter must manage the data that is displayed in the ListView as well as provide the View object for each individual element in the list. Here, we’ve stored the data in a generic List<ListItem> (line 14), though in a real world application, we’d use a more permanent location for the data. In this app, the data must be entered from scratch every time the app is run.

To add an item to the list, we use addItem() (line 48). After the item is added to the itemList object, we must call notifyDataSetChanged() to update the ListView display on screen. Similarly, to clear the list, we use clearItems() (line 54).

The three methods on lines 22 to 34 just implement methods defined in the abstract base class and should be self-explanatory. (getItemId() returns an ID number for a list item; it’s up to us how we define this ID, so we’ve just taken it to be the position of the item in the list.)

The main work in the adapter is done in creating the View (line 37). As usual, we use a LayoutInflater to inflate the layout from its XML file (which just defines two TextViews, one of the date and one for the message). We need a ‘context’ to get the inflater, so if you refer back to line 20 in the code for ItemListFragment above, you’ll see we pass in the Activity that contains the Fragment so it’s used as the context in the adapter.

Once we’ve created the View, we need to set the values of its two TextViews which we do by retrieving the data from itemList.

Finally, back in ItemListFragment (line 25) we add an event handler for a click on a list item. This pops up a Toast notification (a little black rectangle that appears for a couple of seconds on the display) containing the text of the item clicked.

Obviously there are a lot of other tweaks we could add to this program, but it should give you enough information to include a ListView in an app.

Android Fragments

One limitation of Android devices is that they can display only a single Activity at a time. This was due initially to the fact that Android was used for small screen devices such as mobile phones. With the advent of larger screen devices such as tablets, there is more space in which to display the interface. However, such devices still allow only a single Activity to be displayed at a time.

To get around this problem, starting with Android version 3 the Fragment was introduced. A Fragment is a sub-process that can be embedded within an Activity, and a single Activity can have several Fragments running at a time. A Fragment can (but need not) have a visual interface which is created in much the same way that an Activity’s interface is created.

To illustrate this, we’ll create an app that displays the two interfaces we used in the previous examples on a single screen. Start as usual by creating a new Android project with a single blank Activity in Eclipse. Eclipse doesn’t have an “Add fragment” function, but it’s still quite easy to create one. The layout of a Fragment, like that of an Activity, is defined in an XML file, so right click on the res–>layout folder in Package Explorer and select New–>Android XML file. We’ll start with the first screen that displays the Get text button we used earlier. Call this XML file get_text_fragment.xml and select RelativeLayout as its Root Element. After it’s created, open it in the Graphical Layout and add a Button and two TextViews, just as we did earlier.

Do the same for the other screen, calling the file enter_text_fragment.xml and adding a TextView, EditText and a Button.

Next we need to create Java classes to represent the Fragments and handle their events. In the src–>growe.ex04fragments (or whatever you’ve called your package) folder, right click on the folder and select New–>Class. To represent the get_text_fragment, create a class called GetTextFragment. In the Superclass box, type Fragment then Ctrl+(space), then select the Fragment class in the android.app package. (The other Fragment class in the android.support.v4.app can be used if you need your app to run on older (pre version 3) versions of Android.)

This will generate the skeleton code for your class. At this stage, we need to override only the onCreateView() method, which is most easily done in Eclipse by putting the cursor inside the class and pressing Ctrl+(space) to get a list of methods you can override. Select onCreateView() and you’ll get the code for it.

The creation of a view from an XML file requires inflating the view using a LayoutInflater. You’ll see that this is one of the arguments in onCreateView(). To create the view, delete the existing code inside this method and insert the following, so your code looks like this:

package growe.ex04fragments;

import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class GetTextFragment extends Fragment {
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.get_text_fragment, container, false);
		return view;
	}
}

The inflate method looks up the get_text_fragment.xml file and creates a Java version of the layout.

Do the same for enter_text_fragment.xml by creating a class called EnterTextFragment which looks like this:

package growe.ex04fragments;

import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class EnterTextFragment extends Fragment {
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.enter_text_fragment, container, false);
		Button sendTextButton = (Button) view.findViewById(R.id.sendTextButton);
		sendTextButton.setEnabled(false);
		return view;
	}
}

We’ve added a couple of lines to disable the Send text button when it is first displayed. The idea is that clicking on Get text enables this button and allows the user to send some text.

Now we need to embed these Fragments in the MainActivity and get them displayed on screen. First, we need to let the MainActivity’s layout know about the Fragments. This is done in activity_main.xml by adding in a couple of <fragment> tags. This can be done in Eclipse’s Graphical Layout.

Open activity_main.xml in Graphical Layout and open the Layouts menu in the Palette box on the left. Drag a Fragment onto the screen to where you want it. This will open a dialog in which you choose the Fragment class you want, so select GetTextFragment for the first Fragment. This will display a placeholder with instructions to pick the preview layout. To do this, right click on the placeholder and select Fragment Layout –>get_text_fragment (or open the Choose Layout dialog and choose it there). The layout components should then become visible.

Do the same to add enter_text_fragment to the layout. You should now be able to run the program in your emulator and see the combined layout.

Event handling in Fragments

Now the fun begins. We need to connect a button press in GetTextFragment to EnterTextFragment, and then send the entered text back to GetTextFragment when the Enter text button is pressed.  We could do this by simply relying on the IDs generated in the R.java file for the various view objects, but that is very bad design, for the following reason.

One of the advantages of using Fragments is that they encapsulate units of functionality, so they can be plugged in to apps wherever they are needed. If we wrote a Fragment so that it communicated directly with another Fragment, then both Fragments would always have to be present in any given app. To avoid this, we need to ensure that the only inter-Fragment communication occurs via the Activity that contains them. Doing this requires a fair bit of code, but it’s worth it to preserve modularity (or so I’m told :-)).

First, let’s see how to communicate the Get text button click event to MainActivity. Here’s the modified GetTextFragment class:

package growe.ex04fragments;

import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

public class GetTextFragment extends Fragment {

	GetTextListener activityCallback;
	View getTextView;

	public interface GetTextListener
	{
		public void onGetTextButtonPressed();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		getTextView = inflater.inflate(R.layout.get_text_fragment, container, false);
		Button getTextButton = (Button) getTextView.findViewById(R.id.getTextButton);
		getTextButton.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				onClickGetText(v);
			}
		});
		return getTextView;
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		try {
			activityCallback = (GetTextListener) activity;
		} catch (ClassCastException e) {
			throw new ClassCastException(activity.toString() +
					" must implement GetTextListener");
		}
	}

	public void onClickGetText(View view) {
		activityCallback.onGetTextButtonPressed();
	}

	public void setText(String text)
	{
		TextView displayed = (TextView) getTextView.findViewById(R.id.displayedText);
		displayed.setText(text);
	}
}

On lines 26 to 33 we’ve added an event handler for getTextButton clicks. We have to do it this way rather than by simply adding an ‘On Click’ handler to the button’s properties in the Eclipse Graphical Layout screen, since the latter adds the event handler to the containing Activity class rather than the Fragment class. If this code looks a bit complicated, don’t worry, as it’s always pretty much the same so you can just copy and paste it.

Ultimately, clicking getTextButton calls onClickGetText() on line 48. To see what this does, we have to back up a bit and see how the Activity communicates with Fragments it contains.

When the Activity starts up and loads the Fragment view from the XML file, it calls the onAttach() method of the Fragment class. We need to override onAttach() to provide the connection, if we want to call a method in the Activity when an event occurs in the Fragment.

We do this by defining an interface called GetTextListener on line 17. This interface specifies a method that the Activity must implement, so that it can be called in response to the button click event. So how does this all fit together?

When the Fragment is loaded, the Activity calls onAttach(), passing a reference to itself in as an argument. We attempt to save this reference on line 41 by casting it to GetTextListener. If this succeeds, this means that the Activity does implement this interface and all is well. If it fails, we throw an exception which stops the app.

Now that we have a reference to the parent Activity, we can use it on line 49 to call the onGetTextButtonPressed() method in that Activity. It is then the Activity’s responsibility to communicate with the EnterTextFragment to enable its Send text button.

To see how this works, here’s the modified MainActivity class:

package growe.ex04fragments;

import growe.ex04fragments.EnterTextFragment.EnterTextListener;
import growe.ex04fragments.GetTextFragment.GetTextListener;
import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity
implements GetTextListener, EnterTextListener
{

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

	@Override
	public void onGetTextButtonPressed() {
		EnterTextFragment enterTextFragment =
				(EnterTextFragment) getFragmentManager().
				findFragmentById(R.id.enter_text_fragment);
		enterTextFragment.enableSendTextButton();
	}

	@Override
	public void enterTextButtonPressed() {
		EnterTextFragment enterTextFragment =
				(EnterTextFragment) getFragmentManager().
				findFragmentById(R.id.enter_text_fragment);
		GetTextFragment getTextFragment =
				(GetTextFragment) getFragmentManager().
				findFragmentById(R.id.get_text_fragment);
		getTextFragment.setText(enterTextFragment.getEnteredText());
	}

}

On line 9, we implement GetTextListener (and an interface for the other fragment, but we’ll get to that in a minute), and on line 19 we implement the onGetTextButtonPressed() method. Here, we retrieve the EnterTextFragment object and call a method (which we’ll see in a minute) in that Fragment to enable its Send text button.

Now for the revised version of EnterTextFragment:

package growe.ex04fragments;

import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

public class EnterTextFragment extends Fragment {
	View enterTextView;
	EnterTextListener activityCallback;

	public interface EnterTextListener {
		public void enterTextButtonPressed();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		enterTextView = inflater.inflate(R.layout.enter_text_fragment, container, false);
		Button sendTextButton = (Button) enterTextView.findViewById(R.id.sendTextButton);
		sendTextButton.setEnabled(false);
		sendTextButton.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				sendTextButtonClicked(v);
			}
		});
		return enterTextView;
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		try {
			activityCallback = (EnterTextListener) activity;
		} catch (ClassCastException e) {
			throw new ClassCastException(activity.toString() +
					" must implement EnterTextListener");
		}
	}

	public void enableSendTextButton()
	{
		Button sendTextButton = (Button) enterTextView.findViewById(R.id.sendTextButton);
		sendTextButton.setEnabled(true);
	}

	public void sendTextButtonClicked(View view)
	{
		Button sendTextButton = (Button) enterTextView.findViewById(R.id.sendTextButton);
		sendTextButton.setEnabled(false);
		activityCallback.enterTextButtonPressed();
	}

	public String getEnteredText()
	{
		EditText enteredText = (EditText) enterTextView.findViewById(R.id.enteredText);
		return enteredText.getText().toString();
	}
}

On line 47 you see the enableSendTextButton() method that is called from the Activity. Thus the event is generated in GetTextFragment which calls a method in MainActivity which in turn calls a method in EnterTextFragment. The two Fragments don’t communicate with each other directly.

To finish things off, the user enters some text in EnterTextFragment and then clicks the Send text button to send the text back to GetTextFragment. The process is much the same as the one we’ve just described, so we’ll go through it quickly.

In EnterTextFragment, we attach an event handler for the Send text button on lines 26 to 32. EnterTextFragment defines its own interface called EnterTextListener, which MainActivity must also implement. When EnterTextFragment is loaded, MainActivity is connected to it the same way (line 37), so when Send text is clicked, sendTextButtonClicked() on line 53 is called. We disable the Send text button, then call enterTextButtonPressed() in MainActivity.

If you go back to MainActivity’s code above, this method is on line 27. We need to retrieve the entered text from EnterTextFragment and send it to GetTextFragment, so we need references to both Fragments. On line 34 we call getEnteredText() from EnterTextFragment (line 60 in EnterTextFragment’s code) and pass the retrieved String to GetTextFragment’s setText() method (line 52 in GetTextFragment’s code above). Thus again, the two Fragments do not communicate directly.

This example, of course, is artificial; you’d never write an app that does something that simple in real life. But it does allow you to see how the mechanics of using Fragments work.

A more realistic example would change the layout depending on whether the device was viewed in portrait or landscape mode. We might display both fragments side by side in landscape, but only one Fragment at a time in portrait. More on that in another post.

Android: implicit intents

We’ve seen how to use an explicit intent to start a second Activity from within an existing Activity by giving the name of the second Activity’s class explicitly. It is also possible to start another Activity without naming it. The rationale behind this idea is that one or more activities might exist on a given device that can perform the required action. For example, if we want to view a web page, there could be several different browsers which can do this, and the user might like to select different ones for different pages. We can use an implicit intent to do this.

To use an implicit intent, an Activity that can handle a given type of request must contain an intent-filter that specifies the type(s) of actions that Activity can perform. The intent-filter can be specified in the Java code, but it’s more usual to include it in the AndroidManifest.xml file for the app. There are many predefined action types for an intent-filter, but to keep things local, we’ll show an example in which we define our own action type which is used by two alternative Activities within the same app.

The example app we’ll use is just a copy of the explicit intent app we used before with another Activity called OtherTextActivity added to it. The layout of OtherTextActivity is the same as that of GetTextActivity, so the user can enter some text and press a button to send the text back to StartActivity.

Before we look at the Java code for creating an implicit Intent, we need to define the intent-filters for  the Activities. Up to now, we’ve let Eclipse generate the AndroidManifest.xml file and haven’t altered it. To insert an intent-filter we do need to edit it. This can be done in Eclipse either by using the GUI in the Application tab or by directly writing the XML code. Let’s see how to use the Application editor to add it.

First, find the Activity (we’ll select GetTextActivity first) to which you want to add the intent-filter in the box at the lower left. Click on it and then click on Add… to get the dialog in which you can double-click on Intent filter. Now click on the new Intent filter, then click Add… again. We’ll need to add an action and a category to the intent-filter. Double-click on Action first, and you’ll see a Name field with a drop-down menu appear. If you open this menu, you’ll see a list of all the built-in action names you can give this intent-filter. However, we’re going to create our own custom action, so just enter GET_TEXT_ACTION in the Name box.

[As a side note, we cannot use a string resource to specify an action’s name. This is a bit of a pain, since it means we need to hard-code these strings both in the XML and in the Java code.]

Now add a category to the intent-filter and this time select android.intent.category.DEFAULT from the drop-down list. Have a look at the XML code file to see the code that has been added. You should see something like this:

        <activity
            android:name="com.example.ex03implicitintent.GetTextActivity"
            android:label="@string/title_activity_get_text" >
            <intent-filter>
                <action android:name="GET_TEXT_ACTION"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

Now repeat the process for OtherTextActivity. That completes the definition of the intent-filters. Now over to the Java code. We won’t need to change the code in either GetTextActivity or OtherTextActivity, but we do need to change how the Intent defined in StartActivity is created. We do this by changing the event handler for the getTextButton as follows:

	public void onClickGetText(View view) {
		Intent getTextIntent = new Intent();
		getTextIntent.setAction("GET_TEXT_ACTION");
		List<ResolveInfo> activities = getPackageManager().
				queryIntentActivities(getTextIntent, PackageManager.MATCH_DEFAULT_ONLY);
		boolean isIntentSafe = activities.size() > 0;
		if (isIntentSafe)
		{
			startActivityForResult(getTextIntent, GET_TEXT_ACTIVITY);
		}
	}

We create the Intent with an empty constructor. Then we set the action that the Activity to which this Intent is to be sent must support. Next, we retrieve a List of Activities that support the action requested in getTextIntent. The second argument in queryIntentActivities() says we’re looking only for Activities that have a DEFAULT category.

If there are any Activities that support the requested action, the size of this list will be greater than zero, so we test for that and then call startActivityForResult() only if there are some eligible Activities. If there is only one such Activity, it is started immediately. If there are more than one (as is the case here), Android will display a chooser dialog asking the user to select which Activity should be used.

One cautionary note here: using an implicit intent to start an Activity that returns data is fine, but if you’re doing this with Activities that you didn’t write, beware that often different Activities that support a given action might return different types of data (or none at all). In such a case, it’s better to use an explicit intent and start up an Activity whose behaviour you know, so you can handle the returned data properly.

Android: explicit intents

The Intent class is used in Android programs to communicate between various types of processes. We’ll consider the case where an Intent is used to start one Activity from within another Activity, and another Intent is used to send data back from the second Activity to the first.

Create a new project in Eclipse as described in our previous post. Add a main Activity called StartActivity which is displayed when the app starts. The layout of this Activity consists of:

  • a Button with ID getTextButton that has the caption “Get text”
  • a TextView with the text “You entered:”
  • another TextView, with ID enteredText, initially blank

When the button is pressed, a second Activity, called GetTextActivity, will start that will have the following layout:

  • a horizontal LinearLayout (we’ll get to these in a later post) which contains:
    • a TextView with the text “Enter your text:”
    • an EditText, with ID userText, into which the user can type some text
  • a Button with ID sendTextButton and caption “Send text”

All of this can be set up using Eclipse’s graphical UI editor, which will create the corresponding code in the various XML files automatically.

The idea is that the user presses the “Get text” button to show the second Activity, then types some text into the EditText box and presses the “Send text” button. This returns the app to the first Activity, which displays the entered text in the enteredText TextView.

In order to do this, we need to use one Intent to start GetTextActivity from within StartActivity and another Intent to send the entered text back from GetTextActivity to StartActivity.

Here’s the code for StartActivity:

package com.example.ex02explicitintent;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.TextView;

public class StartActivity extends Activity {

	static final int GET_TEXT_ACTIVITY = 0;

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

	public void onClickGetText(View view) {
		Intent getTextIntent = new Intent(getBaseContext(), GetTextActivity.class);
		startActivityForResult(getTextIntent, GET_TEXT_ACTIVITY);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode != RESULT_OK)
		{
			return;
		}
		if (requestCode == GET_TEXT_ACTIVITY)
		{
			String userText = data.getStringExtra(getString(R.string.userTextTag));
			TextView enteredText = (TextView) findViewById(R.id.enteredTextView);
			enteredText.setText(userText);
		}
	}
}

The onClickGetText() (lines 19-22) method is the event handler for getTextButton. It illustrates how to create an explicit Intent, which is an Intent that creates a specifically named Activity. (There are also implicit Intents). Here we want a GetTextActivity to start in response to the button press, so we name this class explicitly in the Intent constructor.

We then start the Activity by calling startActivityForResult(). The ‘ForResult’ in the method name means that we expect the Activity to return a result after it finishes running. If we just wanted to start an Activity with no return data, we can call startActivity().

The startActivityForResult() method takes the Intent as its first argument, and an int label (defined on line 11) as its second argument. We need a label because if we start more than one Activity that will return data, they all call the same method onActivityResult() when they return, so we need to tag each Activity so we know which one is returning the data. (OK, since we’re creating only one Activity here, technically we don’t need a tag, but in the more general case we will so it’s a good idea to get into the habit of adding the tag.)

Before we see how to handle the returned result, we need to look at the other Activity to see how it sends back its data. Here’s the code for GetTextActivity:

package com.example.ex02explicitintent;

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

public class GetTextActivity extends Activity {

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

	public void onClickSendText(View view) {
		EditText userEditText = (EditText)findViewById(R.id.userText);
		String userText = userEditText.getText().toString();
		Intent userIntent = new Intent();
		userIntent.putExtra(getString(R.string.userTextTag), userText);
		setResult(RESULT_OK, userIntent);
		finish();
	}
}

This Activity contains only the event handler for the sendText button. We retrieve the EditText control by using its ID and extract the text from it (lines 18-19). Then we create a new Intent. This time, we’re not starting a new Activity, so the Intent doesn’t have an Activity specified in its constructor. What we do want to do is send back userText to StartActivity. We can attach data to an Intent by adding Extras to it. Each Extra consists of a key (which is a String) and a corresponding value, which here is just userText. Note that we’ve defined the key as a string resource (by adding it to the strings.xml file) and retrieved it on line 21 using getString() which looks up a string using its ID. We do it this way since we’ll need to use the same key back in StartActivity to extract the Extra from the Intent.

After adding userText to the Intent, we use setResult() on line 22 to attach the Intent to the GetTextActivity. The first argument to setResult() is a result code to indicate the status of GetTextActivity when it finishes. This result code is also sent back to StartActivity and can be checked to determine what to do with the returned Intent.

Finally, we call finish() to shut down GetTextActivity and send the Intent back to StartActivity.

Returning to the code above for StartActivity, the onActivityResult() method is called when GetTextActivity finishes. The arguments in this method are:

  • requestCode: the tag that was attached to the Activity that has just finished
  • resultCode: the result code that was set in the Activity that has just finished
  • data: the Intent returned by the other Activity

On line 26, we check the resultCode to ensure that GetTextActivity finished properly. Then we check the requestCode to ensure that it really is GetTextActivity that is calling onActivityResult(). If so, then we extract the userText from the Intent, again using the string resource for the tag. Then we retrieve the enteredText TextView control and set its text to userText.

Android programming: a simple activity

To get started programming an Android application, I’m assuming you have the Eclipse IDE with Android Developer Tools (ADT) installed. You can get this package here. To test your apps, you’ll need to set up one or more Android emulators, which you can do using the Android Virtual Device Manager from within Eclipse (there should be an icon on the default toolbar, or you can get it from the Window menu).

You may find that the emulator takes ages (well, several minutes) to start up. If so, and if you have the right kind of Intel processor, you should consider installing the Intel Hardware Acceleration Execution Manager (HAXM). Once installed (assuming your system supports it), select the Intel Atom option for the CPU/ABI in the Android Virtual Device Manager when setting up your emulator. This cuts the startup time down to a few seconds.

Assuming you’ve got everything installed, let’s create an Android app. In Eclipse, click the ‘New’ icon and select Android–>Android Application Project, then Next. Enter the app, project and package names in the next dialog, then click ‘Next’ again. In the Configure Project dialog, we’ll start with the most basic app, so uncheck the ‘Create custom launcher icon’ and ‘Create activity’ boxes, then click Finish. Eclipse will now create a skeleton project.

For example, suppose we want to create a simply app that displays a button that toggles its text between ‘Off’ and ‘On’ when you click it. We’ll call the package growe.ex01onoff.

Activities

You won’t be able to run this project, since there’s no Java source code yet, so the first thing we need to do is add some. An Android app is built using one or more Activity objects, so we’ll add an Activity to our project. To do this, right click on the package in the Package Explorer and select ‘New…’, then pick Android–>Android Activity and Next. Then select ‘Blank activity’ and Next. In the Blank Activity dialog, give the activity a name such as OnOffActivity and accept the generated layout name and title. Click the Launcher Activity box since we want this activity to be launched when the app starts.

You can click Finish here, since clicking Next just gives you a Preview dialog where you can see the pending changes to the various files in your project that adding this activity will make.

You should now find that Eclipse has created a Java file called OnOffActivity.java, with the following code:

package growe.ex01onoff;

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

public class OnOffActivity extends Activity {

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

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.on_off, menu);
		return true;
	}

}

The onCreateOptionsMenu() method (not surprisingly) creates a menu, but we don’t want one in this simple app so you can safely comment out this code for now.

The onCreate() method is called when this Activity starts, but since an app can have several Activities, how does it know that we want this one to be the startup Activity? The answer is in the AndroidManifest.xml file, which contains the lines:

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="growe.ex01onoff.OnOffActivity"
            android:label="@string/title_activity_on_off" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

Within the activity tag, we see the name of the OnOffActivity and an intent-filter. The action of  android.intent.action.MAIN indicates that it is the entry point of the app. (We’ll worry about intents later.)

Resources

Going back to the Java code, the call to setContentView() draws the user interface. The argument of this call illustrates the way apps refer to resources that are defined elsewhere, usually in XML files. Every Android app creates a Java class called R. The source code for R can be found in the gen folder and, since it’s generated by Eclipse, it shouldn’t be edited since any changes will just be overwritten. Within the R class are defined other classes that contain int codes referring to the resources that are defined in the various XML files within the res folder. Within R, you’ll find a class called layout which contains an int named activity_on_off. If you look within the res folder, you’ll find a layout folder which contains the file activity_on_off.xml. This file contains the definitions of the user interface elements that are to be drawn on the screen when this layout is used. The file generated by Eclipse when an activity is created contains a single TextView which displays the text ‘Hello world!’.

We want the interface to display a button instead. We could do this by editing the XML file directly, but Eclipse has a graphical editor which allows interfaces to built visually. To do this, open the activity_on_off.xml file and click on the Graphical Layout tab at the bottom. Click on the TextView and delete it, then select a button from the Form Widgets menu on the left and drag it onto the window. You’ll notice that a Properties box on the right displays all the properties of this button that can be set. We want the button to start off displaying the caption ‘Off’. One way of doing this is just to write Off in the Text property, but there is a more general way of doing this using a String resource. 

Why should we bother with this more general way of defining strings? A major reason is that it makes apps easier to internationalize by writing strings in different languages. We can write a set of string resources for each language we want to support and then just load in the correct set of strings when we change languages. This works only if each string is given a resource name rather than hard coded.

To add a new String resource, open the res–>values–>strings.xml file. If you look at the XML code first, you’ll see several strings already defined. To add a new String, go to the Resources tab at the bottom, click the Add… button and then double-click String in the dialog. Give the resource a name like offText and a value of Off. While you’re at it, add another String resource called onText with a value of On, which we’ll use later. Make sure to save strings.xml before proceeding.

Now go back to activity_on_off.xml and select the button again. In the properties box click the … button for the Text property and you should see a list of all the string resources defined in strings.xml. Select offText, and the button’s caption should now be Off.

At this point you can run the app in your emulator and you should see the white screen with the Off button on it. Pressing the button has no effect, of course, because we haven’t added an event handler to the button yet.

Event handlers

Unlike many other IDEs, it seems that Eclipse doesn’t provide any shortcuts to adding event handlers to components, so the only option is just to write in the code by hand.

There are two ways of attaching an event handler to a button. The easiest is to find the On Click property of the button (listed in the View section in the properties box) and provide the name of a method that will handle the click event. This method must be placed in the Activity class that contains the button in its layout. If we specify the event handler as toggleOnOff, then we can modify the OnOffActivity class as follows:

package growe.ex01onoff;

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

public class OnOffActivity extends Activity {
	boolean on = false;

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

	public void toggleOnOff(View view) {
		Button onOffButton = (Button) view;
		onOffButton.setText(on ? R.string.offText : R.string.onText);
		on = !on;
	}
}

The event handler must always have the signature shown. It must be public, void, and have a single argument of class View (which is the View that triggered the event; in this case, the button). Here, we’ve defined a boolean variable on which stores the state of the button and we set the text of the button according to the state of this variable, then toggle the variable.

The second way of attaching an event handler to a button is done entirely within the Activity class’s onCreate() method:

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_on_off);

		Button onOffButton = (Button) findViewById(R.id.onOffButton);
		onOffButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View view) {
				Button button = (Button) view;
				button.setText(on ? R.string.offText : R.string.onText);
				on = !on;
			}
		});
	}

First, we need to find the button, which we do using the findViewById() method on line 6. Each UI component is assigned an ID number in the R class, and this method retrieves a reference to the component using this ID. Next, we attach an OnClickListener to the button using setOnClickListener(). Within this listener, we override the onClick() method, which becomes the event handler, so it contains the same code as we had earlier.