Chapter 11. Understanding Intents and Intent Filters

This chapter will delve into intents, which are messaging objects that carry communications between the major components of your application—your activities, services, and broadcast receivers, which handle Android messaging. We have seen that Android development is highly modularized, and intents provide a way to wire these modules together to form a cohesive yet flexible application with secure, fluid communication among all of its components.

This is a fairly complex and important topic, and we are going to cover intents as they pertain to activities, services, and broadcast providers in detail. In fact, by the time we get to the end of the chapter, we will have an application that has three XML files and four different Java files open in the Eclipse IDE. Lucky we are close to the end of the book, because for a book on Android for absolute beginners, this chapter is going to seem a bit advanced. We'll chalk it up to a rapid learning process and dive right in.

What Is an Intent?

An intent is represented by the android.content.Intent class. It is in the content package because intents can be used to quickly access content providers, as we will see in this chapter. But its use is much broader than that; in fact, the Android Developer Reference says, "An intent is an abstract description of an operation to be performed," so intents can be used to quickly accomplish many tasks that would otherwise take more programming code. An intent is a sort of a programming shortcut that's built into the Android OS and programming environment.

An Intent object is basically a passive data object (a bundle of instructions, if you will) that both provides a description of some sort of standard operating system or developer created "action" that needs to be performed and passes the data which that action needs to operate on to the code receiving the intent.

In addition to a specified action, the Intent object can also contain relevant data needed to complete that action, as well as data type specifications, constants, flags, and even extra data related to the data needed by the action.

Because intents provide a detailed data and process communication structure among Android application components, they can also be rather complex data structures (objects). We'll see the various parts of an intent's structure in the next section.

There are three types of Intent objects that can be used inside the Android OS to communicate with activities, services, and broadcast receivers. In fact, there is one intent type for each of these. None of these types of Intent objects are allowed to intersect with (i.e., interfere with, or collide with, or mistakenly be used with or by) any of the other types of Intent objects. For this reason, we will cover each type of Intent object separately, so we can see how intent-based communication with activities, services, and broadcast messages differ from each other.

Android Intent Messaging via Intent Objects

Essentially, intents carry messages from one module of your application to another (activity to activity, activity to service, broadcast to activity, etc.). Intents can be sent to and from background processing services or intra-application activities or even inter-application broadcast messages. Intents are similar to the events that are found in other programming languages, except that intents can reach outside your application whereas events can't. Events are used to process user interface elements, as we have seen in previous chapters, and are internal to the blocks of programming logic you write. Intents can be passed to other applications written by other programmers, allowing them to be connected as modules of each other, if needed.

Intent object-based messages can contain up to seven different kinds of informational parts:

  • Component name. The name of the class that the intent and its action are targeting, specified by using the package name and the class name.

  • Action. A predefined type of action that is to be performed, such as ACTION_DIAL to initiate a phone dialing sequence or ACTION_VIEW to view records in a database.

  • Data. The actual data to be acted upon, such as the address of the database records to view or the phone number to dial.

  • Category. Android has predefined intents that are part of the OS that are divided into various types or categories for easy access and use. The category name tells what area of the OS the action that follows it is going to affect. For instance, CATEGORY_HOME deals with the Android Home screen. An ACTION_MAIN following a CATEGORY_HOME would cause the Home screen to be launched in the smatphone.

  • Type. This attribute specifies the type of the data using a MIME format. It's often left out as Android is usually able to infer the data type from analyzing the data itself.

  • Flags. This allows on/off flags to be sent with the intent. Flags are not used for typical intents, but allow more complicated intents to be crafted if needed by advanced developers.

  • Extras. This parameter allows any extra information that is not covered in the above fields to be included in the intent. This allows very complex intents to be created.

With these seven different types of information, the messaging construct that an Intent object communicates can become quite an intricate data structure, if you need it to be, it can also be quite simple, depending on the application use that is involved.

The first thing an Intent object usually specifies is the name of the application component you are targeting (usually a class you create); this is specified via the package and class name, like so:

ComponentName(string package, string class)

The component name is optional. If it is not specified, the Android OS will utilize all of the other information contained within the Intent object to infer what component of the application or Android OS the Intent object should be passed to for further processing. It is safer to always specify this information. On the other hand, intents are intended to be used as programming shortcuts, and for many standard or common instances, Android is designed to properly infer how to process them.

The most important part of the Intent object is the action specification. The action defines the type of operation that the intent is requesting to be performed. Some of the common action constants are listed in Table 11-1, along with their primary functions, so you can get an idea of where these intents might be utilized in the Android OS.

Table 11.1. Examples of Action Constants and Their Primary Functions

Action Constant

Target Activity

Function

ACTION_DIAL

Activity

Displays the phone dialer

ACTION_CALL

Activity

Initiates a phone call

ACTION_EDIT

Activity

Display data for user to edit

ACTION_MAIN

Activity

Start up an initial task activity

ACTION_BATTERY_LOW

Broadcast Receiver

Battery low warning message

ACTION_HEADSET_PLUG

Broadcast Receiver

Headset plug/remove message

ACTION_SCREEN_ON

Broadcast Receiver

The screen turned on message

ACTION_TIMEZONE_CHANGED

Broadcast Receiver

Time zone has changed

It is important to note that in many cases the action constant that is specified determines the type and structure of the data of the Intent object. The data parameter is as important to the overall result of the intent resolution as the specified action to be performed. Without providing the data for the action to operate on, the action is as useless as the data would be without any action to be performed on it!

The ACTION_DIAL action constant is a good example; it targets an activity and displays the smartphone dialing utility with the phone number (the data passed to it) to be dialed. The data is the phone number the user entered into the user interface, and since the action constant is ACTION_DIAL, Android can infer that the data passed to it is the phone number to be dialed.

Thus, the next most important part of the Intent object is the data component, which contains the data that is to be operated on. This is usually done via a URI object that contains information about where the data can be found.

As we learned in the previous chapter, this often turns out to be a database content provider; for instance, a SQLite database can be the target of an ACTION_VIEW or ACTION_EDIT intent action. So, to edit database record information about a person in your contacts list with the database ID of 1, we would use the following intent data structure:

ACTION_EDIT content://contacts/people/1

A closely related part of the Intent object specification is the data's MIME type, which explicitly tells Android what type of data the intent should be working with so that, for example, audio data doesn't encounter an image processing routine.

The type part of the Intent object allows you to specify an explicit MIME data definition or data type that, if present, overrides any inference of the data type by the Android OS. You may already be familiar with the MIME data type declarations, as they are quite common on web servers and other types of data servers.

Another important parameter of an Intent object is the category, which is meant to give additional or more fine-tuned information about the action that is specified to execute. This is more useful with some actions than with others.

A good example of how categories help define what to do with a given action is launching the home screen on a user's Android phone via an Intent object. You use the Action constant ACTION_MAIN with a category constant CATEGORY_HOME and voila! Android launches the phone's Home screen and shows it on the display.

Finally, the extras parameter allows additional data fields to be passed with the Intent object to the activity, service or broadcast receiver. This parameter uses a Bundle object to pass a collection of data objects.

This is a slick way to allow you to piggyback any additional data or more complex data structure you wish to pass along with the Action request/message.

Intent Resolution: Implicit Intents & Explicit Intents

Intents, like events, need to be resolved so that they can be processed properly. Resolution in this case means ascertaining the appropriate component to handle the intent and its data structure.

There are two broad categories of intents—explicit intents and implicit intents. We will look at explicit intent resolution first, as it is much more straightforward. Then, we'll cover implicit Intents and see how they need to be filtered so that Android knows how to handle them properly.

Explicit Intents

Explicit intents use the component portion of the Intent object via the ComponentName data field. You'll generally use these when working with applications you have developed, as you'll know which packages and classes are appropriate for the Intent object to send an action message and data to be acted on. Because the component is specified explicitly, this type of intent is also safer as there is zero room for error in interpretation. Best programming practices dictate that you thoroughly document your code and thereby give other programmers using your intent code the proper component name information. However in the real world, this best case does not always happen and thus Android also has implicit intents and intent filters to handle other scenarios.

Other developers may not know what components to explicitly declare when working with your application, and thus explicit intents are a better fit for non-public inter-application communication. In fact, developing your application so that other developers can use the intents is what implicit intents and intent filters are all about. As noted earlier, if there is a component name specified, it will override all of the other parts of the Intent object as far as determining what code will handle the intent resolution.

There are two ways to specify a component. One way is via the setComponent() method, which uses the ComponentName object:

.setComponent(ComponentName);

The other way is using the setClass(Context, Class) method to provide the exact class to use to process the intent. Sometimes this is the only information in the intent, especially if the desired result from using the intent is simply to launch parallel activities that are internal to the application when they are needed by the user.

Implicit Intents

Implicit intents are those that don't specify the component within the intent object. This means that Android has to infer from the other parameters in the intent object what code it needs to pass the intent message to for successful processing.

Android does this inference based on a comparison of the various actions, data, and categories defined in the intent object with the code components that are available to process the intent. This is usually done via intent filters that are defined in the AndroidManifest.xml file.

Although designing classes that utilize implicit intents and intent filters is beyond the scope of an introductory book on Android programming, we will go over the concept here just to give you an idea of what can be done in Android and in what situations you would use implicit intents and intent filters. You can find more information at

developer.android.com/reference/android/content/IntentFilter.html.

Intent filters are declared in AndroidManifest.xml using the <intent-filter> tag, and they filter based on three of the seven attributes of the Intent object; action, data, and category.

Intent filters provide a description of intent object structures that need to be matched as well as a priority attribute to be used if more than one match is encountered. If no action filters are specified, the action parameter of the intent will not be tested at all, moving the testing on to the data parameter of the intent. If no data filters are specified, then only intents that contain no data will be matched. Here is an example intent-filter definition from an AndroidManifest.xml file that specifies that video MPEG4 and audio MPEG3 can be retrieved from the internet via HTTP:

<intent-filter>
<data android:mimeType="video/mp4" android:scheme="http" />
<data android:mimeType="audio/mp3" android:scheme="http" />
</intent-filter>

For Intent filtering based on data characteristics, the data parameter gets broken down into four subcategories:

  • Data type; This is the MIME data type, for instance, image/jpeg or audio/mp3,

  • Data scheme: This is written as scheme://host:port/path

  • Data authority: This is the server host and the server port (see the data scheme format above) specified together.

  • Data path: A data path is an address to the location of the data, for instance, http://www.apress.com/datafolder/file1.jpg.

Any of these you specify will be matched precisely to the content of the intent itself, for example:

content://com.apress.project:300/datafolder/files/file1

In this example, the scheme is content, the host is com.apress.project, the port is 300, and the path is datafolder/files/file1.

Since we can specify intents explicitly, we can use intent objects productively via the methodologies outlined in the rest of this chapter without having to learn the convoluted hierarchy of how to match unspecified intents. If you wish to delve into the complexities of how to set up levels of intent filters for implicit intent matching, visit the Android developer site and get ready to wrap your mind around some intense global logic structures.

Using Intents with Activities

Enough theory, let's write an Android application that uses intents to switch back and forth between two different activities— an analog watch activity and a digital clock activity—so you can see how intents are sent back and forth between more than one Activity class.

  1. First, let's close our Chapter10 project folder (via a right-click and Close Project) and create a new Chapter11 Android project with the following parameters, as shown in Figure 11-1:

    • Project name: Chapter11

    • Application name: Intents and Intent Filter Examples

    • Package name: intent.filters

    • Create activity: IntentExamples

    • Build Target: Android 1.5

    • Min SDK Version: 3

    Creating our IntentExamples project in Eclipse

    Figure 11.1. Creating our IntentExamples project in Eclipse

  2. Now we are going to create a second Activity class, so we can switch back and forth between the two activities using an intent. To do this, we need to right-click on the Chapter11 folder, and select New

    Creating our IntentExamples project in Eclipse
    Creating a new Java activity class

    Figure 11.2. Creating a new Java activity class

  3. If you right-clicked on the Chapter11 folder to do the New

    Creating a new Java activity class
  4. Next we need to fill out the Name field, which will name our class. Let's use DigitalClockActivity since that's one of the activities we'll use in this exercise.

  5. Leave the Modifiers as set. Since we are creating an Activity class, we need to extend the superclass android.app.Activity. This is the full pathname to the Activity class, which is part of the app package in Android OS.

Now let's create our user interface for our first activity, which we will leave in its default main.xml file container (shown in Figure 11-3).

  1. Let's expand our TextView tag with some new attributes:

    1. Start with text that reads "You Are Currently in: Activity #1"

    2. Use the android:textColor attribute to set the color to #FEA, which is the equivalent to hexadecimal #FFEEAA, a light orange-gold color.

    3. Let's use the android:textSize attribute to increase the text size to 22 device-independent pixels, so it's large and readable.

    4. Finally, let's use the android:paddingBottom="20dip" attribute to push the button user interface object down and away from the text title a little bit.

      <TextView   android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  android:text="You Are Currently in: Activity #1"
                  android:textColor="#fea"
                  android:textSize="22dip"
                  android:paddingBottom="20dip"/>
  2. Next, let's edit the Button tag attributes:

    1. Change its text label to "Go To Digital Clock: Activity #2"

    2. Set the textSize attribute to 18 pixels so we have readable text on our button.

    3. Now let's define our button size in pixels: android:layout_width="280px" and android:layout_height="60px".

    4. Finally, we'll center our UI button with the familiar android:layout_gravity="center" and we are done creating the button UI attributes.

      <Button android:id="@+id/Button01"
              android:text="Go To Digital Clock: Activity #2"
              android:textSize="18px"
              android:layout_width="280px"
              android:layout_height="60px"
              android:layout_gravity="center"/>
  3. Now we'll add an AnalogClock tag, so we can create a cool watch. Use the Layout tab at the bottom of the Eclipse editor (circled in Figure 11-3) and drag the AnalogClock View element icon out of the Views List on the left, and drop it under the Button in the UI layout view.

  4. Then, either go into the Properties tab at the bottom of Eclipse, find the Misc section, and add in Layout gravity and Layout margin top values of center and 30dip, respectively, or click the main.xml tab at the bottom of the editor, and add in the tags yourself by hand.

    Note

    If Eclipse is not showing the Properties tab at the bottom, simply go into the Window menu and select Show View

    Creating a new Java activity class
  5. Next, copy the image1.png file from our earlier Chapter7/res/drawable folder to your Chapter11/res/drawable folder, then right-click on the Chapter11 folder and use the Refresh option so that Android can see this image file inside our project.

  6. Go into the Properties tab again and find the file using the Background option, then click on the search ellipses ... to open a dialog where you can select image1.png in the drawable folder to use as a background. Here's the final AnalogClock tag:

    <AnalogClock    android:id="@+id/AnalogClock01"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginTop="30dip"
                    android:background="@drawable/image1" />

And here is the final code, which is also shown in Figure 11-3 for context:

<?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="You Are Currently in: Activity #1"
                android:textColor="#fea"
                android:textSize="22dip"
                android:paddingBottom="20dip"/>
    <Button android:id="@+id/Button01"
            android:text="Go To Digital Clock: Activity #2"
            android:textSize="18px"
            android:layout_width="280px"
            android:layout_height="60px"
            android:layout_gravity="center"/>
    <AnalogClock    android:id="@+id/AnalogClock01"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginTop="30dip"
                    android:background="@drawable/image1" />
</LinearLayout>
Adding user interface elements to our main.xml file

Figure 11.3. Adding user interface elements to our main.xml file

Writing the Digital Clock Activity

Now let's copy the user interface we just developed in our main.xml to use for our second activity, which we've already created a DigitalClockActivity.java class for.

  1. The easiest way to do this is to right-click on the main.xml file under the /res/layout folder and select Copy from the pop-up context menu, then right-click on the /res/layout folder in the Package Explorer pane (right above the file name) and select Paste, which will paste a copy of main.xml right alongside main.xml in the same folder. When you do this you will get a Name Conflict dialog like the one in Figure 4.

    Specifying digital_clock.xml as the new name for main.xml

    Figure 11.4. Specifying digital_clock.xml as the new name for main.xml

  2. Eclipse sees the duplicate file names and automatically provides a simple dialog box that allows you to change the name. Change it to digital_clock.xml and click OK.

  3. We are ready to right-click on digital_clock.xml and select Open, or hit the F3 key to open the copied file in its own editor pane, so we can change some of the key tag attributes and quickly craft a user interface for our second (digital clock) activity. Do this now.

  4. Edit the AnalogClock tag as follows:

    1. Change it to a DigitalClock tag.

    2. Remove the background image reference to image1.png.

    3. Change the id to DigitalClock01.

    4. Add a textSize attribute of 32dip.

    5. Add a textColor attribute of #ADF to add some nice blue sky coloring.

    6. Finally, add an android:typeface="monospace" attribute for readability, and we're ready to change our TextView and Button UI objects.

      <DigitalClock    android:id="@+id/DigitalClock01"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_gravity="center"
                       android:layout_marginTop="30dip"
                       android:textSize="32dip"
                       android:textColor="#adf"
                       android:typeface="monospace"/>
  5. Change the button text to "Go to Analog Watch: Activity #1" and leave the ID at Button01. Why? Because these two different XML files are going to be called by two different Activity classes, and thus the ID does not conflict. If one Activity class referenced both these XML files, we might have a naming conflict.

    <Button android:id="@+id/Button01"
               android:text="Go to Analog Watch: Activity #1"
               android:textSize="18px"
               android:layout_width="280px"
               android:layout_height="60px"
               android:layout_gravity="center"/>
  6. Finally, we change the TextView object text to read "You are Currently in: Activity #2" and change the android:textColor to the #ADF value we are using with the digital clock tag.

    <TextView    android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
                 android:text="You Are Currently in: Activity #2"
                 android:textColor="#adf"
                 android:textSize="22dip"
                 android:paddingBottom="30dip"/>

When you're done, the whole UI layout should look like this (also shown in Figure 11-5):

<?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="You Are Currently in: Activity #2"
                 android:textColor="#adf"
                 android:textSize="22dip"
                 android:paddingBottom="30dip"/>

    <Button android:id="@+id/Button01"
            android:text="Go to Analog Watch: Activity #1"
            android:textSize="18px"
            android:layout_width="280px"
            android:layout_height="60px"
            android:layout_gravity="center"/>

    <DigitalClock    android:id="@+id/DigitalClock01"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center"
                     android:layout_marginTop="30dip"
                     android:textSize="32dip"
                     android:textColor="#adf"
                     android:typeface="monospace"/>
</LinearLayout>
XML mark-up for the digital_clock.xml user interface activity

Figure 11.5. XML mark-up for the digital_clock.xml user interface activity

Wiring up the Application

While we're working with XML files, let's add an activity tag in our AndroidManifest.xml file so our second activity can be recognized by Android, before finishing off the configuration.

Right-click on the AndroidManifest.xml file name under your Chapter11 folder (at the bottom of the list), and select Open or hit F3. Add a second activity tag after the first tag (which our New Android Project dialog has already created) that points to the new DigitalClockActivity.java class we created earlier (see Figure 11-6). Here is the code:

<activity android:name=".DigitalClockActivity"></activity>
Adding the DigitalClockActivity tag to our AndroidManifest.xml file

Figure 11.6. Adding the DigitalClockActivity tag to our AndroidManifest.xml file

Now let's make sure both Activities have user interfaces. Thanks to our handy New Android Project dialog, our IntentExamples class is ready and pointing to the main.xml file so that the Activity #1 side of the equation is already taken care of. So all we have to worry about is the DigitalClockActivity class.

Copy the import android.os.Bundle statement and the onCreate() method over to the DigitalClockActivity.java class and change the R.layout specification to point to the digital_clock XML user interface specification. Now we've implemented our user interface logic for each of our two Activity classes as follows (and as shown in Figure 11-7):

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.digital_clock);
    }
Adding the digital clock user interface layout

Figure 11.7. Adding the digital clock user interface layout

Sending Intents

Now we need to add in our Button object and Intent object code to the onClick() event handler in each activity so each can send an intent to the other activity, allowing us to switch between the two activities using the button.

So let's get started with the main IntentExamples activity class first, and add in our familiar Button object instantiation and an onClick() event handler that will contain the code that creates our Intent object. Remember to also add in (or have Eclipse add in for you using the hover-over-redline method we learned earlier) the import statements for the android.view.View and android.widget.Button packages, as well as a new import we haven't used before called android.contact.Intent, which defines our Intent object class.

Since we've already covered adding a button and attaching it to an onClick() event handler routine, we'll get right into the two lines of code that create our Intent object and send it over to the second activity. Here is the code, which you'll also see in Figure 11-8. The screenshot shows what your Eclipse editor pane for IntentExamples.java will look like when we are finished.

Button Activity1 = (Button) findViewById(R.id.Button01);
        Activity1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent myIntent =
                  new Intent(view.getContext(), DigitalClockActivity.class);
                startActivityForResult(myIntent, 0);
            }
        });
Adding the Java code for the UI button, event listener, and Intent object

Figure 11.8. Adding the Java code for the UI button, event listener, and Intent object

To create an Intent object, we use the now familiar structure where we declare the object type (Intent) and our name for it (myIntent). We set it equal to a new Intent object using the new keyword, along with a call to the Intent class's constructor. The constructor takes the context this intent is created in (in this case, a button obtained from the View via a view.getContext() method call) and the activity class (DigitalClockActivity.class) into which we want to pass our Intent object.

We then use the startActivityForResult() method (part of the android.content.Intent class we imported) to pass the intent object we just created, myIntent, and a parameter of zero; this is what we are sending to the other activity to be acted on. This would typically consist of data that your application wants to pass from one activity class to another via the Intent object for further processing. In this case, we are simply calling (switching to) the other user interface activity.

Now let's look at the code in our DigitalClockActivity class and see how the second activity talks back to the first activity. We will skip over the Button instantiation and onClick() event handling explanations, and get right into the intent declaration and the Java code that returns a result to the calling activity class via yet another Intent object. Here's the code (also shown in Figure 11-9).

Button activity2 = (Button) findViewById(R.id.Button01);
        activity2.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent replyIntent = new Intent();
                setResult(RESULT_OK, replyIntent);
                finish();
            }
        });
Java Code for the DigitalClockActivity, event listener, and intent object

Figure 11.9. Java Code for the DigitalClockActivity, event listener, and intent object

In this activity class, in the onClick() listener we create a new (empty) intent object called replyIntent, then load it with the setResult() method, which loads a constant called RESULT_OK. When we have finished handling the intent (in this case by loading a new intent with the reply data), we call the finish() method to send the intent back, after the button in the second activity is clicked on to send us back to the first activity.

Now let's right-click on our Chapter11 folder and then Run As

Java Code for the DigitalClockActivity, event listener, and intent object
Running our app in the Android 1.5 emulator and switching back and forth between activities

Figure 11.10. Running our app in the Android 1.5 emulator and switching back and forth between activities

Next we will use an intent object to call a service, the MediaPlayer, to play some music in the background and allow us to start the music and stop the music.

To do this we first must learn what services are and how they can help us do things in the background without affecting our application's user interface functionality. We will then get into an example that uses intents with both services and activities.

Android Services: Data Processing in its own Class

Android has an entire Service class dedicated to enabling developers to create services that run apart from the main user interface program logic. These services can either run in a separate process (known as a thread in Java programming as well as in other programming languages) or the same process as the user interface activities.

A thread is an area of the operating system's memory where a program function has its own resources and can run in parallel with other applications or other application components or functions. For instance, a video player can run in a different thread from the rest of the application so that it doesn't hog all of the main application thread resources.

Threads were originally devised for multitasking operating systems like Mac, Linux, and Windows, so that if a program or task crashed, it would not bring down the entire operating system. Instead, just that thread or process could crash or lock-up, and the others that were running wouldn't be affected adversely.

A service is a type of Android application component that needs to run asynchronously (not in step with the usual flow of the user interface). For example, if you have some processing that takes a bit longer than the user is willing to wait for, you can set off the processing asynchronously in the background while the main program continues. When the processing has finished, the results can be delivered to the main program and dealt with appropriately. A service can also be used by other Android applications, so it is more extensible than an activity.

To create your own service class to offload programming tasks like calculating things or playing media such as audio or video in real-time, you need to subclass the Service class and implement at least its onCreate(), onStart(), and onDestroy() methods with your own custom programming logic. You also must declare the Service class in your AndroidManifest.xml file using the <service> tag, which we'll get into a bit later on.

Using Intents with Services

To see how to control a service class via intent objects, we will need to add some user interface elements to our Chapter11 IntentExamples activity, namely two button objects that will start and stop our service. In this case, the service is the Android MediaPlayer, which needs to run in the background, independently of our user interface elements.

  1. First, let's add a Button tag to our main.xml file by copying the Button01 tag and pasting it underneath the AnalogClock tag.

  2. Change the id to startButton and the android:text to "Start the Media Player Service" and leave the other attributes in the tag the same.

    <Button android:id="@+id/startButton"
        android:text="Start the Media Player Service"
        android:textSize="18px"
        android:layout_width="280px"
        android:layout_height="60px"
    android:layout_gravity="center"/>
  3. Next, copy this startButton tag and paste the copy immediately below the startButton tag.

  4. Change the id of the copy to stopButton and the android:text to read "Stop the Media Player Service" so that we now have a stop button and a start button. Figure 11-11 shows both buttons in place in the Layout tab of Eclipse and Figure 11-12 shows the code in context.

    <Button android:id="@+id/stopButton"
        android:text="Stop the Media Player Service"
        android:textSize="18px"
        android:layout_width="280px"
        android:layout_height="60px"
        android:layout_gravity="center"/>
    Designing our media player user interface in the Eclipse Layout Editor in Portrait mode

    Figure 11.11. Designing our media player user interface in the Eclipse Layout Editor in Portrait mode

  5. Now let's change the AnalogClock tag attribute android:layout_marginTop to use 20dip rather than 30dip. Copy the attribute to the line below and change it to android:layout_marginBottom so that we have an even 20 pixels of spacing around the analog watch.

    <AnalogClock    android:id="@+id/AnalogClock01"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginTop="20dip"
                        android:layout_marginBottom="20dip"
                        android:background="@drawable/image1" />
  6. Click the Layout tab at the bottom of the main.xml pane to make sure the user interface layout looks good and check Figure 11-12 to see that your XML looks right.

    Adding start and stop buttons for our media player in main.xml

    Figure 11.12. Adding start and stop buttons for our media player in main.xml

Next we need to let our Android application know that we are going to be calling a service, and this is done via the AndroidManifest.xml file. We will edit that file next to add a service tag that points to our MediaPlayerService class, which we are going to code next. We will add this service tag right after the second activity tag we added in the previous example (see Figure 11-13 for context). This is how the service tag is structured:

<service android:enabled="true" android:name=".MediaPlayerService" />

The first attribute of the service tag android:enabled indicates that the service is enabled for use. If you set this attribute to false, the service is still declared to Android for the application and can later be enabled via Java code. As we have seen, everything that can be done in XML can also be accessed and changed in our Java code.

The second attribute, android:name, specifies the name of the service class that we will code. We are going to name it MediaPlayerService.java so we specify that in XML as .MediaPlayerService. Now we are ready to start coding the service that will play media files without interfering with the user interface code in our activity class.

Note that if you haven't created the MediaPlayerService.java class before you add the service tag, Eclipse may highlight this fact with a red X in the margin to the left of the service tag, as shown circled in Figure 11-13.

Adding a Media Player Service Tag to the AndroidManifest.xml file in Eclipse

Figure 11.13. Adding a Media Player Service Tag to the AndroidManifest.xml file in Eclipse

Now that we've added the XML mark-up, let's create the MediaPlayerService.java class, extending the Android Service class to create our own custom service class that we'll call from our IntentExamples Activity class.

Creating a Service

To do this, we will use the same work process as before:

  1. Right-click on your Chapter11 folder in the Eclipse Package Explorer pane on the left and select New

    Creating a Service
  2. Fill out the New Java Class dialog as follows:

    • Source folder: Chapter11/src. Default entry, as specified by Eclipse

    • Package: intent.filters. Click Browse button and select from list

    • Name: MediaPlayerService

    • Modifiers: public

    • Superclass: android.app.Service

  3. When everything is filled out, select Finish.

The completed dialog is shown in Figure 11-14. It will create an empty class where we can put our media player logic for creating, starting, and stopping the media player.

Creating the MediaPlayerService.java class via the New Java Class dialog

Figure 11.14. Creating the MediaPlayerService.java class via the New Java Class dialog

Below (and in Figure 11-15) is the empty class that the NewJavaClass dialog created for us, complete with the import statements that let us use the Service class, the Intent class, and the IBinder interface. We won't actually be using the IBinder interface in this example but will leave in the code. This won't affect how the app runs because it is used by a null method, onBind(). We need to keep this method here because Android expects it to be implemented when we extend the Service class, but we'll just leave it as is.

package intent.filters;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MediaPlayerService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

Note

Binding is a concept in services where your activity can talk with the Service class while it is running, and more than one time; but our example simply needs to start and stop the service, so the complexity of binding is not needed.

Android-created MediaPlayerService base service class

Figure 11.15. Android-created MediaPlayerService base service class

Here is the code that lets our MediaPlayerService class do things. I'll show you each of the bold sections in turn as I describe what they do, so you can type them in as we go:

package intent.filters;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.media.MediaPlayer;

public class MediaPlayerService extends Service {
    MediaPlayer myMediaPlayer;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        myMediaPlayer = MediaPlayer.create(this, R.raw.mindtaffy);
        myMediaPlayer.setLooping(true);
    }
@Override
    public void onStart(Intent intent, int startid) {
        myMediaPlayer.start();
    }

    @Override
    public void onDestroy() {
        myMediaPlayer.stop();
    }
}

The results are shown in Figure 11-16.

Adding our MediaPlayer onCreate, onStart and onStop methods

Figure 11.16. Adding our MediaPlayer onCreate, onStart and onStop methods

The first code structure we always add to a new class is the onCreate() method, which tells the class what to do to set itself up when it is called the first time (i.e., created).

We will use the onCreate() method to instantiate and configure our MediaPlayer object and load it with our audio file. Since the audio file is an MP3 file and already optimized for compression, we will put it in the /res/raw folder. Files in the /raw folder are left alone by the Android app compression process and are simply put into the .apk file as is. We'll do that now before explaining the code in detail.

  1. Let's create a Chapter11/res/raw folder to hold the mindtaffy.m4a file that we will call in our MediaPlayerService class. You can either create the new /raw folder under the /Chapter11/res folder using your operating system's file manager or you can right-click on the /res folder in the Eclipse Package Explorer pane and select New

    Adding our MediaPlayer onCreate, onStart and onStop methods
  2. Copy mindtaffy.m4a into the new /raw folder

  3. Right-click on the Chapter11 folder and select Refresh and, if necessary, Validate to remove any error flags you might get in Eclipse. Usually if Refresh does not make something visible to Android and get rid of error flags in Eclipse, the Validate procedure will. If it doesn't, you may have a problem and need to examine your overall application structure.

Implementing Our MediaPlayer Functions

Now it's time to go into the code so you can add the media player functionality to your own app:

  1. First, at the top of the MediaPlayerService class, declare a public global variable called myMediaPlayer of object type MediaPlayer. This will be accessed in one way or another by each of the methods that we'll be coding here, so we declare it at the top of the class to make it visible to the whole class.

    MediaPlayer myMediaPlayer;
  2. In the onCreate() code block, let's set the myMediaPlayer object to contain the results of a create() method call with the mindtaffy.m4a file passed as a parameter using the R.raw.mindtaffy reference. The create() method call creates an instance of the media player and loads it with the audio file that we are going to play.

  3. Next we call the setLooping() method on the myMediaPlayer object and set a true parameter so that the audio file loops while we are testing the rest of the code.

    @Override
        public void onCreate() {
            myMediaPlayer = MediaPlayer.create(this, R.raw.mindtaffy);
            myMediaPlayer.setLooping(true);
        }
  4. Now that our myMediaPlayer object has been declared, loaded with MP3 data, and set to loop when started, we can trigger it with the start() method, which we will code next in the onStart() method (onStart() is called when the service is started by our activity).

    @Override
        public void onStart(Intent intent, int startid) {
            myMediaPlayer.start();
        }
  5. In the onDestroy() method we use the stop() method to stop the myMediaPlayer object. onDestroy() is called when the service is closed and disposed of by Android, so we release memory containing the media player and the audio file when we exit the application.

    @Override
        public void onDestroy() {
            myMediaPlayer.stop();
        }

Wiring the Buttons to the Service

Now let's go back into our IntentExamples.java class using the Eclipse editor tab and add our Button objects, associated onClick() event handlers for each button, and the necessary calls to our Service class onStart() and onDestroy() methods, as shown in Figure 11-17.

Implementing the start and stop buttons to control the media player

Figure 11.17. Implementing the start and stop buttons to control the media player

First we have the start button.

Button startButton = (Button) findViewById(R.id.startButton);
        startButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                startService(new Intent(getBaseContext(), MediaPlayerService.class));
            }
        });

As usual, we declare our startButtonButton object with the startButton ID reference, then use the setOnClickListener() method to add onClick() event handling to the startButton. We are now ready to call the startService() method inside the onClick() programming construct.

startService() calls the onStart() method of the Service class we just wrote, and requires an intent object; this intent object tells Android what service to start and call the onStart() method on. We will get a little tricky here and create a new intent object inside of the startService() call using the following code structure:

startService(new Intent(getBaseContext(), MediaPlayerService.class));

To create a new intent object, we need to declare the current context as the first parameter and then pass the name of the service class we wish to call as the second parameter. In a third level of nesting (inside the new intent creation), we use another method called getBaseContext() to obtain the current context for the new intent object. As the second parameter, we will declare the name of the MediaPlayerService.class to complete the creation of a valid intent object.

Now let's go through the same procedure with the stopButtonButton object, inserting the stopButton ID reference and then using the trusty setOnClickListener() method to add onClick() event handling to our new stopButton. Now we're ready to call the stopService() method in our newest onClick() programming routine.

Button stopButton = (Button) findViewById(R.id.stopButton);
        stopButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                stopService(new Intent(getBaseContext(), MediaPlayerService.class));
            }
        });

The stopService() method calls the onDestroy() method of our Service class and requires an intent object that tells Android what service to stop (destroy) and call the onDestroy() method on.

We will create yet another new intent object in the stopService() call, using the following code structure:

stopService(new Intent(getBaseContext(), MediaPlayerService.class));

To create our final intent object, we will declare the current context as our first parameter and then pass the name of our MediaPlayerService() class as our second parameter.

Running the Application

Now we are ready to right-click our Chapter11 folder and Run As

Running the Application
Running our media player service inside the Android 1.5 emulator

Figure 11.18. Running our media player service inside the Android 1.5 emulator

Next we are going to take a look at using Intent objects with broadcast receivers, and then we will have covered all three areas of Intent use within Android.

Using Intents with Broadcast Receivers

The final type of intent object we will look at in this chapter is the broadcast receiver, which is used for communication between different applications or different areas of Android, such as the MediaPlayer or Alarm functions. These intents send, listen to, or receive messages, sort of like a head's up notification system, to let your application know what's going on around it during the ongoing operation of the smartphone, whether that's a phone call coming in, an alarm going off, or a media player finishing a file playback.

Since we already have an analog watch and a digital clock, let's add a timer and alarm function to finish off our suite. Since our analog clock user interface screen is full of UI elements, let's add the alarm functions to our digital clock user interface, as that's the most logical place to add an alarm anyway. Figure 11-19 shows a basic diagram of what we will do in XML and Java to create the intent and alarm in our next application segment.

What we have to do in XML and Java to create the intent and alarm

Figure 11.19. What we have to do in XML and Java to create the intent and alarm

Creating the Timer User Interface via XML

So, first, let's add an EditText tag so we can let users enter their own custom timer duration. Place the following markup in your digital_clock.xml file after the DigitalClock tag:

<EditText android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/timeInSeconds"
        android:layout_gravity="center"
        android:hint="Enter Number of Seconds Here!"
        android:inputType="numberDecimal"
        android:layout_marginTop="30dip"
        android:layout_marginBottom="30dip" />

The EditText tag has an ID of timeInSeconds and a layout gravity of center for consistency with our prior UI design. Since EditText is a new user interface object for us, we will add an android:hint attribute that says "Enter Number of Seconds Here!"

Note

The hint attribute is text you enter to appear in the text field when it is created by Android and placed on the screen in the layout container. This hint tells the user what to type in the field, which, in this case, is the number of seconds the timer should count down.

Next we have an important android:inputType attribute, which tells us what data type the field will contain, in this case a real number that is represented by the numberDecimal constant. The timer uses milliseconds, so if we want the timer to count 1534 milliseconds, we can type 1.534 in this field and achieve this precise result. Finally, we add two margin attributes (top and bottom) of 30dip each to space the user interface out and to make it more visually attractive to the end user.

We'll also add a Button tag called startTimer to, well, start the timer. Let's use an ID of startTimer and an android:text value of "Start Timer Countdown" to prompt the user. And we'll also use our familiar android:layout_gravity="center" to center our button, so that the UI remains consistent.

<Button android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:id="@+id/startTimer"
        android:text="Start Timer Countdown" />

Figure 11-20 shows how our digital_clock.xml file should look in the Eclipse IDE.

Adding our timer user interface elements to the digital_clock.xml file

Figure 11.20. Adding our timer user interface elements to the digital_clock.xml file

Creating a Timer Broadcast Receiver

Now let's create our TimerBroadcastReceiver class, which is a subclass of the BroadcastReceiver class.

  1. As we are now used to doing, let's create a new class using New

    Creating a Timer Broadcast Receiver
    • Source folder: Chapter11/src. Default entry, as specified by Eclipse

    • Package: intent.filters. Click Browse button and select from list

    • Name: TimerBroadcastReceiver

    • Modifiers: public

    • Superclass: android.content.BroadcastReceiver

  2. When everything is filled out, select Finish.

Figure 11-21 shows what your New Java Class dialog should look like when you've entered all of the relevant new Java class information.

Creating a new TimerBroadcastReceiver class via the New Java Class dialog in Eclipse

Figure 11.21. Creating a new TimerBroadcastReceiver class via the New Java Class dialog in Eclipse

Now we have an empty class shell with all of the import statements that we need for our BroadcastReceiver class and an empty onReceive() method for us to fill out with our programming logic. The onReceive()method will receive our intent and broadcast a message via the Toast class, which will notify us regarding the alarm status.

Let's add an import statement for the Toast widget so we can use it to broadcast a message to the user when the broadcast receiver is used. It is important to note that this Toast message could be replaced by anything we want to happen when our timer goes off, including but not limited to playing a song, playing a ringtone, vibrating the phone, playing a video, or anything else you can think of.

import android.widget.Toast;

The Toast widget's makeText() method can be coded as follows:

public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Alarm Notification", Toast.LENGTH_LONG).show();
    }

We first pass the Toast widget the context parameter received along with the onReceive() call and then tell it what to write to the screen (our "Alarm Notification" string) and how long to show it on the screen (the LENGTH_LONG constant). We then append the show() method to makeText() to draw the message to the screen.

Figure 11-22 shows how all of this should look on the TimerBroadcastReceiver tab in the Eclipse IDE.

Our TimerBroadcastReceiver class

Figure 11.22. Our TimerBroadcastReceiver class

Configuring the AndroidManifest.xml file <receiver> Tag

Now we need to declare our broadcast receiver using the receiver tag in our AndroidManifest.xml file so it is registered for use with Android. We will do this right after the service tag that we added in the previous section, entering the following line of XML mark-up code:

<receiver android:name=".TimerBroadcastReceiver" android:enabled="true" />

This is fairly straightforward. We use the name attribute to assign our TimerBroadcastReceiver class name to the receiver declaration tag and then enable it for use in our application by setting the android:enabled attribute to true so that the broadcast receiver is live (Figure 11-23).

Adding a <receiver> tag to our AndroidManifest.xml file

Figure 11.23. Adding a <receiver> tag to our AndroidManifest.xml file

Now our broadcast receiver is set up to notify users via a Toast message when the broadcast receiver is utilized. The next thing we need to do is add the code to our DigitalClockActivity class to implement an alarm clock function that triggers this Broadcast Receiver class via an intent object, so we can see how all this works together.

Implementing our Intent

The modifications to our DigitalClockActivity class will be done via several new import statements, an event handler for a click on our start timer countdown button, and the timerAlert() method that we will write to do all the heavy lifting to implement the new timer functionality to our application and to trigger our broadcast receiver class using intent objects.

Let's start with the onCreate() method:

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.digital_clock);

        Button activity2 = (Button) findViewById(R.id.Button01);
        activity2.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent replyIntent = new Intent();
                setResult(RESULT_OK, replyIntent);
                finish();
            }
        });

        Button startTimer = (Button) findViewById(R.id.startTimer);
        startTimer.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                timerAlert(view);
            }
        });
    }

Now let's see how to add the highlighted code, starting with the import statements needed.

  1. We need to import the two new widgets that we are going to use to implement editable text and a toast notification message. Both of these classes are from the android.widget package:

    import android.widget.EditText;
    import android.widget.Toast;
  2. Next let's create our startTimerButton object for the start timer countdown button and use the findViewById() method to set it to the new startTimerbutton tag we previously added to our digital_clock.xml file. Place the following in the onCreate() method after the existing button code:

    Button startTimer = (Button) findViewById(R.id.startTimer);
  3. Now we'll add a setOnClickListener() method to handle events generated by the startTimer button. Inside of that construct we will create an onClick() method that calls a timerAlert() method, which holds all of the relevant program logic to set up intents and construct the alarm feature for our digital clock activity:

    startTimer.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    timerAlert(view);
                }
            });

We will pass the view variable (the onClick view or button that was passed from the onClick() method) to the timerAlert() method so that it has the context needed for the PendingIntent. Here is the code for the timerAlert() method, which we will go over line by line:

public void timerAlert(View view) {
    EditText textField = (EditText) findViewById(R.id.timeInSeconds);
    int i = Integer.parseInt(textField.getText().toString());
    Intent timerIntent = new Intent(this, TimerBroadcastReceiver.class);
    PendingIntent myPendingIntent =
      PendingIntent.getBroadcast(this.getApplicationContext(), 0, timerIntent, 0);
    AlarmManager myAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    myAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
      (i * 1000), myPendingIntent);
    Toast.makeText(this, "Alarm is set for " + i + " seconds!",
                   Toast.LENGTH_LONG).show();
}
  1. First, we need two import statements for two new classes from the android.app package.

    • android.app.AlarmManager is a class that manages alarm functions for the Android OS.

    • android.app.PendingIntent is a class that allows intents to be pending, which means they can be scheduled. This means they can be handled by classes in Android even if the calling class is paused, missing, asleep, stopped, or destroyed before the called intent has been processed. This is important for an alarm, especially if the timer is set to hours rather than minutes or seconds, because the phone could run out of charge before the Intent was ever satisfied.

    import android.app.AlarmManager;
    import android.app.PendingIntent;
  2. The timerAlert() method is void because it just performs some tasks relating to setting up intents and alarm functions. It takes a View object named view as its passed parameter.

    public void timerAlert(View view) {
  3. The first thing we do in this method's code block is to declare the EditText object, name it textField, and locate it in our digital_clock.xml layout definition via its timeInSeconds ID parameter.

    EditText textField = (EditText) findViewById(R.id.timeInSeconds);
  4. Once we have the textField object we can use the getText() method along with the toString() method to get the string that the user types into the field. We then use the parseInt() method to convert that string value into an integer value and store it in the i variable of type int (or integer). Later we will use this integer value with our set() method to set the alarm.

    int i = Integer.parseInt(textField.getText().toString());
  5. In the third line of code we declare an Intent object that we name timerIntent and set it to a new Intent object with the context of this and the class of TimerBroadcastReceiver.class as we have done in the previous sections of this chapter. We will use this timerIntent object in the PendingIntent object.

    Intent timerIntent = new Intent(this, TimerBroadcastReceiver.class);
  6. Now let's create a PendingIntent object called myPendingIntent and set it to the result of the getBroadcast() method call; this takes four parameters:

    • The context

    • Code

    • The intent object we want to use as a PendingIntent

    • Any constants

      Note

      In this case we need no code or constants so we simply pass the current context, which we get using the getApplicationContext() method and the timerIntent object we created just prior in the previous line of code.

      PendingIntent myPendingIntent =
            PendingIntent.getBroadcast(this.getApplicationContext(), 0, timerIntent, 0);
  7. Now we are ready to create our alarm using the AlarmManager class. To do this we declare an AlarmManager object named myAlarmManager and call the getSystemService() method with the ALARM_SERVICE constant to specify that we want to get the alarm system service and set it to the myAlarmManager object. Once we have defined myAlarmManager as an alarm service object we can use the set() method to configure it for our use in the application.

    AlarmManager myAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
  8. The next line in the code block is the one that ties everything else together. The set() method we will use on our myAlarmManager object has three parameters:

    • TYPE: The type of alarm trigger we wish to set.

    • TRIGGER TIME: The alarm will trigger when it reaches this system time.

    • OPERATION: The PendingIntent object containing the context and target intent code we wrote in TimerBroadcastReceiver.java, as specified using the getBroadcast() method.

    myAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
          (i * 1000), myPendingIntent);

In our incarnation of the set() method on the myAlarmManager object, we first specify the AlarmManager.RTC_WAKEUP, which uses the Real Time Clock (RTC) method and wakeup constant to specify that we want to wake up the phone (if it is asleep) to deliver the alarm. The RTC method uses the system clock in milliseconds as its time reference.

Using RTC only (without the _WAKEUP) will not wake the phone up if it triggers while the phone is asleep, and thus will be delivered only when the phone wakes up again. This makes it not nearly as accurate as the RTC_WAKEUP constant. You can imagine how handy it is to be able to wake up your phone at a certain discreet time even if it is asleep, so it's a good thing we are exposing you to this handy class here.

The next parameter we need to specify is the precise system time, in milliseconds, to trigger the alarm. We will wax a bit tricky here, and we will specify this middle parameter using a bit of inline programming logic.

We call the currentTimeMillis() method on the Android System object to get the current system time in milliseconds, then we add to the system time the number of seconds specified by our user in milliseconds, by multiplying the number of seconds in variable i by 1000, since there are 1000 milliseconds in one second. The system time is calculated in milliseconds since 1970, so it is a discrete number that we can simply add our timer milliseconds value to.

This numeric result gives us the exact system time in milliseconds when the alarm needs to be triggered, and puts it into the set() method's second parameter, when our inline code is evaluated at runtime. As we have seen, Java allows some fairly powerful programming constructs to be created using just a single line of programming code.

Finally, we will specify the myPendingIntent object as our third parameter. This object, created earlier with two lines of code, was loaded with the current context and the timerIntent object that we created earlier with three lines of code. The timerIntent object references our TimerBroadcastReceiver class, which will ultimately be called when the alarm is triggered, and will send a Toast to the screen to tell our end user that the time is up.

The final line of code sends a familiar Toast message to the end user, confirming that the alarm has been set for the proper number of seconds. This is done by passing the Toast makeText() method the current context (this) along with the Toast.LENGTH_LONG constant and two strings with the i variable between them like this:

Toast.makeText(this, "Alarm is set for " + i + " seconds!", Toast.LENGTH_LONG).show();

As we've seen here, Java is very flexible in how it allows us to mix different data types. Figure 11-24 shows our newly enhanced DigitalClockActivity class with the new import statements, onCreate() method and timerAlert() method modifications shown. Notice along the top of the IDE that we now have seven open tabs with XML and Java code that we have either modified or written. This is the most robust application we've written so far! Now we will run and test our new app.

Adding the startTimer button UI code and timerAlert() method

Figure 11.24. Adding the startTimer button UI code and timerAlert() method

Running the Timer Application via the Android 1.5 Emulator

Let's right-click on our Chapter11 folder and select Run As

Running the Timer Application via the Android 1.5 Emulator

This shows us that all of the different types of intents can work seamlessly together in Android and that they don't interfere with each other, as we noted at the beginning of the chapter.

We can now go back and forth between the analog and digital activities using the intents we created; turn on the music and go back and forth while it is playing; and use the timer function while the digital audio is playing back as a service.

In the digital clock activity, we can use the editable text field to set our timer value and the start timer countdown button to trigger the broadcast intent, which broadcasts a Toast to the screen when the specified number of seconds has passed.

Figure 11-25 shows the application running in the Android 1.5 emulator displaying the digital clock, the timer function, and the button that allows us to switch back and forth between our two different activities and their user interfaces.

Running our timerAlert() method in the Android 1.5 emulator to show broadcast receiver intents

Figure 11.25. Running our timerAlert() method in the Android 1.5 emulator to show broadcast receiver intents

Summary

In this chapter, we've seen how different parts of the Android OS and the developer's application components communicate to form a cohesive and seamless application. From user interface activities to background processing services and systems utilities, intent objects are used in integral ways to pass requests for processing actions on data structures between different types of application components.

This serves to enforce a modular, logical programming work process on the Android applications developer, which ultimately increases security, decreases bugs and poorly constructed code, and attempts to facilitate the kind of optimization that will be needed in the mobile embedded environment of smartphones.

Ultimately, the proper use of intents and the creative structuring of application components is what set the successful Android developer apart from the crowd, so be sure to practice using intents and delve deeper into this area of the Android developer documentation whenever you get a chance.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
98.82.120.188