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 four different 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 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 also can 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 also can 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, 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 Chapter 9, 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, as shown in Figure 11-1:

  • 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 on, 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 smartphone, tablet, e-reader, or iTV.
  • 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 previous fields to be included in the intent. This allows very complex intents to be created.

9781430247883_Fig11-01.jpg

Figure 11-1 .  The anatomy of an Intent object

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 because 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.

MIME TYPES

MIME stands for “Multipurpose Internet Mail Extensions” and was originally designed for e-mail servers to define their support for different types of data. It has since been extended to other server definitions of supported data and content types, and to communication protocols (such as HTTP) data type definitions, and now to Android OS to define content data types as well. Suffice it to say that MIME has become a standard for defining content data types in a myriad of computing environments. Examples of MIME definition include the following:

  • Content-Type: text/plain
  • Content-Type: image/jpeg
  • Content-Type: audio/mp3
  • Content-Type: video/mp4
  • Content-Type: application/msword

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 and 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 private interapplication 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 which 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 which 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) 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.

Because 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 clock 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 ContentProviders project folder (via a right-click and Close Project) and create a new IntentFilters Android project with the following parameters, as shown in Figure 11-2:
    • Project name: IntentFilters
    • Application name: IntentFilters
    • Package name: seventh.example.intentfilters
    • Create custom launcher icon: Selected (checked)
    • Build target: Android 4.1 (API 16)
    • Min SDK version: Android 2.2 (API 8)

    9781430247883_Fig11-02.jpg

    Figure 11-2 .  Creating our IntentFilters project in Eclipse New Android Application dialog series

  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 IntentFilters folder, and select New image Class, which opens a dialog (Figure 11-3) that will create a new Java activity class in the same folder as the MainActivity.java class our New Android Application Project series of dialogs just created.

    9781430247883_Fig11-03.jpg

    Figure 11-3 .  Creating new Java AlternateActivity class by right-click on IntentFilters project folder

  3. If you right-clicked on the IntentFilters folder to do the New image Class operation, the dialog will already have the first field filled out, with the Source folder field set to IntentFilters/src. In the next field, you can either type in the seventh.example.intentfilters package name we created in our New Android Application Project dialog, or you can click the Browse button to the right of the field, and select this package from the bottom of the list.
  4. Next we need to fill out the Name field, which will name our class. Let’s use AlternateActivity as it’s the alternate activity to the MainActivity that we’ll use in this exercise.
  5. Leave the Modifiers as set. Because 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 android.app package in Android OS.

Now let’s create our user interface for our first activity, which we will leave in its default activity_main.xml file container (shown in Figure 11-4 on page 315).

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

    a.  Create a main_activity_text in your strings.xml file, which text value is set to: “You Are Currently in the MainActivity”. Or you can edit the hello world string tag to serve this purpose, so that you don’t have to delete it, as it will be unused in this exercise.

    b.  Use the android:text attribute to set the pointer to the strings.xml file value via the “@string/main_activity_button” setting.

    c.  Let’s use the android:textSize attribute to increase the text size to 18 device-independent pixels, so it’s large and readable. You can do this via the Properties Tab in Eclipse to the right of the bold TextSize parameter.

    d.  Finally, let’s use the android:centerHorizontal="true" attribute to center the text heading at the top of the screen. Note that the GLE and default XML TextView settings have done this for you, all you have to do is delete the android:centerVertical parameter to place the TextView at the top of the UI screen where it belongs.

         <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:centerHorizontal="true"
              android:text="@string/main_activity_text"
              android:textSize="18dp"
              tools:context=".MainActivity" />
  2. Next, let’s add a Button tag using the GLE (drag and drop it centered under the TextView by 30dp) and then make sure it’s XML attributes are set correctly:

    a.  Create a main_activity_button in your strings.xml file, and set it’s text value to: “Go To the Alternate Activity to use a Digital Clock” so we can point our text attribute to “@string/main_activity_button”

    b.  Set the layout_below attribute to “@+id/textView1” so we have our button defined as being underneath our TextView in our RelativeLayout container.

    c.  Now let’s make sure to center our button using the android:centerHorizontal="true".

    d.  Finally, we’ll space our UI button down a little bit with the familiar android:layout_marginTop="30dp", and we are done creating the button UI attributes as shown below.

        <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/textView1"
                android:centerHorizontal="true"
                android:layout_marginTop="30dp"
                android:text="@string/main_activity_button" />
  3. Now we’ll add an AnalogClock tag, so we can create a cool watch. Use the Graphical Layout Editor tab at the bottom of the Eclipse editor (circled in Figure 11-4) and drag the AnalogClock View element icon out of the Views List on the left, and drop and center it 30dp underneath the Button element in the UI layout view.

    9781430247883_Fig11-04.jpg

    Figure 11-4 .  Using the Eclipse Graphical Layout Editor to add and configure AnalogClock

  4. Then, either go into the Properties tab at the bottom of Eclipse, find the Misc section, and add in Layout centerHorizontal and Layout marginTop values of true and 30dp, respectively, or click the activity_main.xml tab at the bottom of the editor, and add in these 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 image Other . . . and select Properties and then OK.

  5. Next, let’s use Windows Explorer to copy the image1.png file from our earlier UI_Design/res/drawable-xhdpi folder into the IntentFilters/res/drawable-xhdpi folder, then right-click on the IntentFilters 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 image1 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/analogClock1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/button1"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="30dip"
                    android:background="@drawable/image1" />

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
             android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:centerHorizontal="true"
            android:text="@string/main_activity_text"
            android:textSize="18dp"
            tools:context=".MainActivity" />
    <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView1"
            android:centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:text="@string/main_activity_button" />
    <AnalogClock
            android:id="@+id/analogClock1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/button1"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dip"
            android:background="@drawable/image1" />
</RelativeLayout>

9781430247883_Fig11-05.jpg

Figure 11-5 .  Adding user interface elements to our activity_main.xml file and image1 file to drawable-xhdpi folder

Writing a Digital Clock Alternate Activity

Now let’s copy the user interface we have just developed in our activity_main.xml to use for our second activity, for which we’ve already created an AlternateActivity.java class.

  1. The easiest way to do this is to right-click on the activity_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 activity_main.xml right alongside activity_main.xml in the same folder. When you do this you will get a Name Conflict dialog, like the one in Figure 11-6.
  2. Eclipse sees the duplicate file names and automatically provides a simple dialog box that allows you to change the name. Change it to activity_alternate.xml and click OK.

    9781430247883_Fig11-06.jpg

    Figure 11-6 .  Specifying activity_alternate.xml as the new copy name for activity_main.xml

  3. We are ready to right-click on the new activity_alternate.xml and select Open, or select the file and hit the F3 key to open the copied file in its own editor pane, so that 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:

    a.  Change it to a DigitalClock tag.

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

    c.  Change the id to digitalClock1.

    d.  Add a textSize attribute of 40dp.

    e.  Add a textColor attribute of #7AC to add some nice blue coloring. Note that #7AC is the same as (shorthand for) #77AACC.

    f.  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/digitalClock1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/button1"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="30dip"
                    android:textSize="40dp"
                    android:textColor="#7AC"
                    android:typeface="monospace"/>
  5. Change the button text to “@string/alt_activity_button” and leave the ID at button1. 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. Be sure and add the <string> tag to strings.xml that adds the alt_activity_button name and value of “Go to the MainActivity to use an AnalogClock” as well.
    <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView1"
            android:centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:text="@string/alt_activity_button" />
  6. Finally, we change the TextView object text to read “@string/alt_activity_text” and change the android:textSize to the 17dp value so the text fits on a single line at the top of the screen.
    <TextView   android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:centerHorizontal="true"
                android:text="@string/alt_activity_text"
                android:textSize="17dp"
                tools:context=".AlternateActivity" />

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

 <TextView     android:id="@+id/textView1"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:centerHorizontal="true"
               android:text="@string/alt_activity_text"
               android:textSize="17dp"
               tools:context=".AlternateActivity" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:centerHorizontal="true"
        android:layout_marginTop="30dp"
        android:text="@string/alt_activity_button" />

    <DigitalClock
        android:id="@+id/digitalClock1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dip" />
</RelativeLayout>

9781430247883_Fig11-07.jpg

Figure 11-7 .  XML mark-up for activity_alternate.xml our second user interface activity shown in Eclipse

Wiring Up the Application

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

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

<activity
            android:name=".AlternateActivity"
            android:label="@string/title_activity_alternate" >
</activity>

9781430247883_Fig11-08.jpg

Figure 11-8 .  Adding the .AlternateActivity tag to our IntentFilters Project’s AndroidManifest.xml file

Make sure to add the <string> tag to the strings.xml file by copying the MainActivity string tag and changing the “Main” to “Alternate” (and “main” to “alternate”).

Now let’s make sure both Activities have user interfaces. Thanks to our handy New Android Application Project dialog, our MainActivity class is ready and pointing to the activity_main.xml file so that the MainActivity side of the equation is already taken care of for us. So now, all we have to worry about is the AlternateActivity class.

Copy the three import statements and the onCreate() method over to the AlternateActivity.java class and then change the R.layout specification to point to the activity_alternate XML user interface specification. Now we’ve implemented our user interface logic for each of our two Activity classes as follows:

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

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 primary MainActivity.java 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 about 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.

Because 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 alternate activity. Here is the code, which you’ll also see in Figure 11-9. The screenshot shows what your Eclipse editor pane for MainActivity.java will look like when we are finished.

        Button activity1 = (Button) findViewById(R.id.button1);
        activity1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View arg0) {
                Intent myIntent =
                new Intent(arg0.getContext(), AlternateActivity.class);
                startActivityForResult(myIntent, 0);
            }
        });

9781430247883_Fig11-09.jpg

Figure 11-9 .  Adding the MainActivity 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, from a button obtained from the View via a arg0.getContext() method call) and the activity class (AlternateActivity.class) into which we want to pass our Intent object.

We then use the startActivityForResult() method (part of the android.app.Activity 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 AlternateActivity 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-10).

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

9781430247883_Fig11-10.jpg

Figure 11-10 .  Java Code for the AlternateActivity, 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 IntentFilters project folder in the Package Explorer pane of Eclipse and then Run As image Android Application, so we can see that we can now switch back and forth between the two activities that contain the two different time utilities we created in one application. As you can see in Figure 11-11, we can switch between the two activities by clicking on the respective buttons, and we can do this as many times as we like, and the application performs as expected and does not crash. That is important, by the way, that the app does not crash under repeated use.

9781430247883_Fig11-11.jpg

Figure 11-11 .  Running our app in the Android 4.1 emulator and switching back and forth between activities

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

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 in the same process as the user interface activities. With new Android devices featuring multiple processors, the preference would be to run processing-intensive services (such as 3D rendering via OpenGL ES or audio or video playback via the MediaPlayer class) in their own thread.

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 (termed: multitasking) or in parallel with other application components or processor-intensive 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. If an Android device has a dual-core or quad-core CPU, it is also conceivable that a processing-intensive thread could even get its own processor allocated to it. Pretty cool!

Threads and Processes were originally devised for multi-tasking operating systems such as 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. Because Android OS runs on top of a full version of the Linux OS kernel, it simply passes that capability through (up) to the Android OS.

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 that processing task asynchronously in the background, while the main program continues to respond to the user. When the processing has finished, the results can be delivered to the main program and then dealt with appropriately. A service can also be used by other Android applications, so it is more extensible (widely usable) 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, all of which we’ll get into in detail 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 IntentFilters project’s MainActivity 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 activity_main.xml file by using the Graphical Layout Editor to drag out a new button and center it horizontally underneath (android:layout_centerHorizontal tag) the AnalogClock tag by a margin of 20dp.
  2. Change the android:text to “@string/start_media_button” and add a <string> tag to your strings.xml file setting that variable value to: “Start the Media Player Service” and leave the other attributes in the tag the same, as again the GLE has done all of the heavy lifting for you. Your tag should read as follows:
                <Button
                 android:id="@+id/button2"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_below="analogClock1"
                 android:layout_centerHorizontal="true"
                 android:layout_marginTop="20dp"
                 android:text="@string/start_media_button" />
  3. Next, do the exact same thing with a third button UI element (button3) that we will use to stop the media player.
  4. Change the android:text to read “@string/stop_media_button” and add a <string> tag to the strings.xml file that sets this variable to the value: “Stop the Media Player Service” so that we now have a stop button and a start button. Figure 11-12 shows both buttons in place in the Graphical Layout tab of Eclipse and Figure 11-13 shows the code in the XML Editor tab.
    <Button 
      android:id="@+id/button3"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="button2"
      android:layout_centerHorizontal="true"
      android:layout_marginTop="20dp"
      android:text="@string/stop_media_button" />

    9781430247883_Fig11-12.jpg

    Figure 11-12 .  Designing a media player service user interface in the Eclipse Graphical Layout Editor

    Figure 11-13 .  Adding the start and stop buttons for our media player service in activity_main.xml

  5. Now let’s change the AnalogClock tag attribute android:layout_marginTop to use 20dp rather than 30dp. Make the same modification for the first button (button1) so that all of our UI elements use a marginTop setting of 20dp.
    <AnalogClock
                    android:id="@+id/analogClock1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/button1"
                    android:layout_centerHorizontal="true"
                    android:layout_marginTop="20dip"
                    android:background="@drawable/image1" />
  6. Click the Graphical Layout tab at the bottom of the activity_main.xml pane to make sure the user interface layout looks good, and then check Figure 11-13 to see that your XML looks right.

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 the 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 that we added in the previous example (see Figure 11-14 for context). This is how the service tag is structured:

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

9781430247883_Fig11-14.jpg

Figure 11-14 .  Adding a .MediaPlayerService <Service> Tag to the AndroidManifest.xml file in Eclipse

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 later accessed and changed inside of 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 with a leading period, 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.

If you haven’t done so already, be sure to add the <string> tags for the Start Button and Stop Button labels as shown in Figure 11-15. We now have ten <string> tag entries in our strings.xml file and all of the UI and app labeling of activities and UI elements can be modified from one central location. The best part of all is that we have a “clean” IDE where our XML code is concerned, as Android does not like “hard coded” text values, and flags them in the IDE UI, which can be disconcerting, so we are doing things using the proper work process here for our string values.

9781430247883_Fig11-15.jpg

Figure 11-15 .  Adding the two new button labels to the strings.xml file in the XML Editor

Now that we’ve added the XML markup, 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 IntentFilters Project’s MainActivity.java Activity class.

Creating a Service

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

  1. Right-click on your IntentFilters project folder in the Eclipse Package Explorer pane on the left and select New image Class.
  2. Fill out the New Java Class dialog as follows:
    • Source folder: IntentFilters/src Default entry, as auto-specified by Eclipse
    • Package: seventh.example.intentfilters. Click Browse button and select this from the bottom of the 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-16. It will create an empty class where we can put our media player logic for creating, starting, and stopping the media player.

9781430247883_Fig11-16.jpg

Figure 11-16 .  Creating the MediaPlayerService.java class via the New Java Class dialog in Eclipse

Here (and in Figure 11-17) is the empty class that the New Java Class 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 it 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, so we’ll just leave it as is.

9781430247883_Fig11-17.jpg

Figure 11-17 .  Eclipse-created MediaPlayerService base service class created by New image Class

package seventh.example.intentfilters;

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

public class MediaPlayerService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        // 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.

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

package seventh.example.intentfilters;

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 arg0) {
        // 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-18.

9781430247883_Fig11-18.jpg

Figure 11-18 .  Adding our MediaPlayerService Class 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). This is where you would put initialization variables, for instance, if you were coding a game level. Then we’ll add an onStart( ) method and finally an onDestroy( ) method. Here’s a simple diagram of what happens when the startService(MediaPlayer) code we’ll write later is called:

startService.Media.PlayerObject > onCreate() (Initialize) > onStart() (Do) > onDestroy (Clean Up)

We will use the onCreate() method to instantiate and configure our MediaPlayer object, and load it with our audio file. Because 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. As you can see in Figure 11-18 Eclipse will tell you if it cannot find the /raw/mindtaffy.m4a file referenced in your code. We’ll add that file now before explaining the code in detail.

  1. Let’s create the IntentFilters/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 /IntentFilters/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 image Folder and enter raw in the Folder name: field.
  2. Copy mindtaffy.m4a into the new /raw folder
  3. Right-click on the IntentFilters 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 may 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, so we declare it at the top of the class to make it visible to the entire class full of methods.
    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 MPEG4 audio 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. The onDestroy() method is called when the service is closed and disposed of by Android, allowing the release of the memory locations (memory space) 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 MainActivity.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-19.

9781430247883_Fig11-19.jpg

Figure 11-19 .  Implementing the start and stop buttons to control the media player

First we have the start button.

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

As usual, we declare our startButton Button object with the button2 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 of the onClick() programming construct.

The 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 stopButton Button object, inserting the button3 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.button3);
stopButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View arg0) {
        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 IntentFilters folder and Run As image Android Application to run our app. You’ll find when you test the application that everything works perfectly with everything else; you can start and stop the media player as many times as you like, the audio plays smoothly in the background without faltering, and you can switch back and forth between the two activities as many times as you want without affecting the media playback of the audio file. See Figure 11-20.

9781430247883_Fig11-20.jpg

Figure 11-20 .  Running our media player service inside the Android 4.1 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 Android smartphone, tablet, e-reader, or iTV, whether that’s a phone call coming in, an alarm going off, or a media player finishing a file playback.

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

9781430247883_Fig11-21.jpg

Figure 11-21 .  What we have to do in XML and then in Java to create the intent and alarm

Creating the Timer User Interface via XML

So, first, let’s add an EditText tag via the Eclipse Graphical Layout Editor so that we can let users enter their own custom timer duration. Open the text field area of the GLE palette and find the numeric decimal text field (represented in the GLE UI as 42.0) and drag and drop it onto the existing UI, so that it centers both horizontally and vertically on the UI screen, as shown in Figure 11-22 below.

9781430247883_Fig11-22.jpg

Figure 11-22 .  Using the Eclipse Graphical Layout Editor to add our EditText widget using the tool-tip guide

Now open up the strings.xml file and add a <string> tag with an edit_text_hint variable that points to the text value “Enter Number of Seconds” so we can add a “Hint” to our EditText field. Then use the properties editor as shown in Figure 11-22 (on the right side) to add a hint that points to the @string/edit_text_hint string (you can use the three . . . ellipses to choose this variable once you add it to the strings.xml file and save it so that the entry is permanent).

This should place the following markup in your activity_alternate.xml file after the DigitalClock tag:

<EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:ems="10"
        android:hint="@string/edit_text_hint"
        android:inputType="numberDecimal" >
        <requestFocus />
<EditText/>

The EditText tag has an ID of editText1 and a layout_center in both vertical and horizontal planes for consistency with our prior UI design. Because EditText is a new user interface object for us, we added an android:hint attribute that says “Enter Number of Seconds” so that you would have experience with that important EditText parameter.

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 1,534 milliseconds, we can type 1.534 in this field and achieve this precise result.

We’ll also add a Button that we will call startTimer in our Java code to, well, start the timer. Let’s use the GLE to drag out a second button and center it and position it below EditText with a marginTop value of 30dp, all of which you will be able to see via the real-time tool-tip as you position the button via the GLE. We’ll accept the GLE assigned ID of button2 and edit the android:text value to read: “@string/timer_button_label” so be sure and add a <string> variable and value of “Start Timer Countdown” to the strings.xml file so we can prompt the user to action via the button label. Our strings.xml file now has a dozen variables defining all of our UI text elements as shown in Figure 11-23.

9781430247883_Fig11-23.jpg

Figure 11-23 .  Adding string tags for our Timer Utility in the strings.xml file in Eclipse

As usual, the GLE will use a now familiar android:layout_centerHorizontal="true" to center our button, and android:layout_below=“editText1” so that the UI remains consistent and well defined. The Button tag and parameters should look like the XML code below:

<Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/editText1"
        android:layout_centerHorizontal="true"
        android:marginTop="30dp"
        android:text="@string/timer_button_label" />

Figure 11-24 shows how our activity_alternate.xml file should look in the Eclipse IDE.

9781430247883_Fig11-24.jpg

Figure 11-24 .  Adding our timer user interface elements to the activity_alternate.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 image Class, and use the following parameters:
    • Source folder: IntentFilters/src Default entry, as specified by Eclipse
    • Package: seventh.example.intentfilters Click the Browse button and select from the bottom of the list
    • Name: TimerBroadcastReceiver
    • Modifiers: public
    • Superclass: android.content.BroadcastReceiver
  2. When everything is filled out, select Finish.

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

9781430247883_Fig11-25.jpg

Figure 11-25 .  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 that 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. As you know by now, you can add the Import statement below or let Eclipse do it for you when you code the Toast.makeText statement that follows.

import android.widget.Toast;

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

    public void onReceive(Context arg0, Intent arg1) {
    Toast.makeText(arg0, "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 on the screen.

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

9781430247883_Fig11-26.jpg

Figure 11-26 .  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 markup code:

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

This is fairly straightforward. We use the name attribute to assign our .T imerBroadcastReceiver 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-27).

9781430247883_Fig11-27.jpg

Figure 11-27 .  Adding a <receiver> tag to our AndroidManifest.xml file for .TimerBroadcastReceiver

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 AlternateActivity 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 AlternateActivity class will be done via several new import statements, an event handler for a click on our startTimer 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.activity_alternate);

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

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

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, or have the IDE do it for us, either way. Both of these classes are from the android.widget package:

          import android.widget.EditText;
          import android.widget.Toast;

2.  Next let’s create our startTimer Button object for the start timer countdown button and use the findViewById() method to set it to the new button2 button tag we previously added to our activity_alternate.xml file. Place the following in the onCreate() method after the existing button code:

          Button startTimer = (Button) findViewById(R.id.button2);

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 arg0) {
                  timerAlert(arg0);
               }
          });

We will pass the arg0 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.editText1);
    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();
}

4.  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;

5.  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) {

6.  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 activity_alternate.xml layout definition via its editText1 ID parameter.

          EditText textField = (EditText) findViewById(R.id.editText1);

7.  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 duration.

          int i = Integer.parseInt(textField.getText().toString());

8.  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);

9.  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 use zeroes in those slots, and 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);

10.  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);

11.  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 wake-up 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 constant 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 1,000 because there are 1,000 milliseconds in one second. The system time is calculated in milliseconds since the calendar year 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-28 shows our newly enhanced AlternateActivity class with the new import statements, onCreate() method and timerAlert() method modifications shown. Notice in the Package Explorer pane that we now have seven actively used project files with XML and Java code that we have either modified or written. Actually, if you include the AndroidManifest we have eight files, four Java and four XML that we have customized for this project! This is the most robust application we’ve written so far! Now we will run and test our new app in the Android 4.1 emulator.

9781430247883_Fig11-28.jpg

Figure 11-28 .  Adding the startTimer button UI code and timerAlert() method

Running the Timer Application via the Android 4.1 Emulator

Let’s right-click on our IntentFilters folder, and select Run As image Android Application, and get right into our final IntentFilters project application. You’ll find that when you run this application that all three sections we’ve added work perfectly together.

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 (main) and digital (alternate) activities using the intents we created; turn on the audio 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 alternate 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-29 shows the application running in the Android 4.1 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.

9781430247883_Fig11-29.jpg

Figure 11-29 .  Running our timerAlert() method in the Android 4.1 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, tablets, e-readers, and iTV sets.

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