Chapter 14. Simple Phone Calls

Android provides the user with several ways of starting phone calls: from the contact list, from the call history, using a dialer that displays a 12-key dialpad on the screen, etc. All of these software modules use the same application to start a phone call. Your program can initiate a phone call in the same way: by using an Intent object to ask Android’s specialized telephony application to make the call. We’ll cover that technique in this chapter, and take a look behind the scenes at how the process works.

In Chapter 15, we’ll introduce Android classes that give you more information about telephony, such as tracking the state of the call you made.

Quick and Easy Phone Calls

Android includes an application called PhoneApp that embodies the functions of a mobile phone. Through the use of Intent objects, Android enables applications to tell other applications to perform certain operations, such as initiating a phone call. To enable your application to initiate a phone call, a method like the one in Example 14-1 will do the job.

Example 14-1. How to make a phone call
private void call() {
    try {
        Intent callIntent = new Intent(Intent.ACTION_CALL);
        callIntent.setData(Uri.parse("tel:9785551212"));
        startActivity(callIntent);
    } catch (ActivityNotFoundException activityException) {
        Log.e("dialing-example", "Call failed", activityException);
    }
}

What happens when you start a phone call depends, in part, on the telephone network. The number may be incorrect. The network may be busy or otherwise unavailable. The call can be interrupted. Here, however, you see no error-handling logic, except for catching and logging exceptions that can be thrown if Android’s system encounters a problem when finding applications that can process Intent objects. Instead, the PhoneApp application, which already has code for interpreting and remediating errors, handles the job from the time the phone call is started.

When an application just wants to start phone calls, making it handle all these contingencies is a large burden. Systems that provide a telephony API place that burden on application authors when, in most cases, all an application needs is to start a phone call—not to manage the lifecycle of a phone call.

Starting a phone call is a multistep operation. Here we’ll take a detailed look at each step in the execution of the call method shown in Example 14-1. Along the way, we’ll see how it uses Android’s system of Intent objects and Intent filters.

Creating an Example Application to Run the call Method

To test the method in Example 14-1, create a new Android project in Eclipse by selecting File → New → Project → Other.... When the “Select a Wizard” dialog appears, select Android → Android Project. When you see the new project dialog, fill it in as shown in Figure 14-1.

Press Finish to create a project named dialing-example in your Eclipse workspace. (The complete code for this example is also on the book’s website.) You will see this project in the Package Explorer pane of your Eclipse IDE. Expand the project to see a set of folders, including one named src. Expand this folder to see a package named example.dialing. Expand that package and you will see two Java source files, one of which is named dialing.java. This file contains the code in Example 14-2.

Example 14-2. Setting up an application to make phone calls
package example.dialing;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

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

This is where you will put the code that invokes our call method.

Creating a phone project in Eclipse
Figure 14-1. Creating a phone project in Eclipse

Embedding the Code Snippet in a Simple Application

Now that you have created a simple Android application, you can use it to isolate and observe operations, such as starting a phone call.

Copy the method we created in Creating an Example Application to Run the call Method to the dialing class in the dialing.java file. Then, add a line to the onCreate method that calls the call method. The results should look something like Example 14-3.

Example 14-3. The dialing class with call method and its invocation
package example.dialing;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

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

private void call() {
    try {
        Intent callIntent = new Intent(Intent.ACTION_CALL);
        callIntent.setData(Uri.parse("tel:9785551212"));
        startActivity(callIntent);
    } catch (ActivityNotFoundException activityException) {
        Log.e("dialing-example", "Call failed", activityException);
    }
}
      

Make sure your program compiles and runs. To run the program, select the Run → Run command. When the Run As dialog appears, select Android Application. If you have followed the steps in this chapter, the result should be displayed in the Android emulator window (Figure 14-2).

Dialer starts
Figure 14-2. Dialer starts

You can use the red “end” button on the phone depicted in the emulator to let the user end the simulated phone call.

Exploring the Phone Code Through the Debugger

We will use the Eclipse debugger, set breakpoints, and inspect class members in the running application to observe what is going on inside this example. The use of the debugger with Android is described in Chapter 5. If you have not used a debugger before, don’t worry: we will use a limited set of debugging capabilities here to observe a program that works correctly. Just follow the steps in this section and let the debugger show you what is happening.

First, we will set a breakpoint where we want to start observing what is happening inside the application. To set a breakpoint, double-click on the left margin of the view that shows the program code in Eclipse. A blue dot will appear. If you change your mind and decide not to insert a breakpoint, double-click again on the blue dot, and it will disappear.

All we want is to stop execution of the program at the point where we want to start inspecting what happens to the members of this instance of the dialing class. To do this, set a breakpoint on line 21 of the program. You can tell which line you are on by clicking a line in the program. In the status bar at the bottom of the Eclipse window, you will see two numbers separated by a colon. The first number is the line number where you just clicked, and the second number is the character position on that line where the insertion point is right now.

Start the application with the debugger by selecting Run → Debug, and when the “Debug as” dialog appears, select Android Application.

The program will stop at the breakpoint, after the Android emulator appears on your screen but before the appearance of the dialer shown in Figure 14-2. Eclipse will switch to a debug perspective: a set of views configured for debugging a program instead of editing it. Eclipse will ask if you want to switch perspectives the first time you run the debugger; you can save your answer if you want Eclipse to do the same thing each time you start debugging.

In the debug perspective, the view displaying the program code will show a small arrow overlapping the blue dot in the left margin. This line of code will be highlighted. The program has stopped before executing the Java bytecodes corresponding to the Java source code on this line of the program.

Figure 14-3 shows the Eclipse window in debug perspective with contents similar to those that should appear on your screen. The main information to look for is that the program stopped executing on the line where you set the breakpoint.

Eclipse debugger stopped in call method
Figure 14-3. Eclipse debugger stopped in call method

Creating an Instance of an Intent

The line of code where the program stopped in the previous section looks like this:

Intent callIntent = new Intent(Intent.ACTION_CALL);

This creates an instance of the Intent class. Use the “step over” command to execute this line, by selecting the Run → Step Over option from the menu or any of the shortcuts available in Eclipse.

Note

“Step over” does not mean “skip.” Instead, it tells the debugger to run the entire line of code and all the method calls it contains (the Intent constructor, in this case) instead of entering the method calls and going through them line by line. It isn’t useful to see the internals of the Intent constructor. So “step over” creates the Intent and presents you with the next line of your own code.

The debugger also has commands for “stepping into” methods and “stepping out” of the method currently being executed. These commands are more convenient than setting more breakpoints and using the Resume command.

Now that we have used the new operator and the Intent constructor with an argument that specifies we want to initialize the Intent with the Intent.ACTION_CALL constant, we have an instance of the Intent class. The action we use, ACTION_CALL, will enable Android to find PhoneApp or any other program the user may install that offers the ACTION_CALL action.

Let’s take a look inside by entering the Variables view in Eclipse. You will see two columns in this view. The first column shows the names of the variables, and the second column shows their values. In our case, the names refer to instances of classes, and the values consist of the class name and the ID of the instance.

That’s not very informative! Let’s look inside these instances and see what they contain. Click on the triangle icon in the left margin next to the variable named callIntent. Now you see all the members of the Intent class and the values for this instance of the Intent class. The only member that has a nondefault value is mAction. Its value is the string "android.intent.action.CALL". This is the result of calling the Intent class’s constructor with the argument we used.

Adding Data to an Instance of an Intent

So far, our instance of the Intent class has enough information to tell the Android system we want to start a phone call, but not enough to tell it what number to call.

After creating the Intent instance with the information that means “we want to call a number,” in the next line we will add to it the number to call:

callIntent.setData(Uri.parse("tel:9785551212"));

Two things happen on this line of code: an instance of a Uri is created, and we use that instance as an argument to the setData method of the Intent class. Step over this line of code, and then let’s see what happens to the variables we are inspecting.

Look at the Variable view in Eclipse and you will see that the mData member of this instance of the Intent now refers to the instance of Uri that was returned from the parse method of the Uri class. And if you click on the triangle icon next to “mData”, you will see the members of the Uri class, including the uriString member that refers to the string tel:9785551212. Now our instance of the Intent class contains all the information we need to start a phone call.

Why use a URI? All mobile numbers conform to the E.164 standard, so why not use a String object containing a valid E.164 number? A URI has the advantage of generality. All parts of Android are replaceable and the components of Android that handle this particular Intent object could be augmented or replaced by a module that can also connect VoIP calls with SIP URIs or Gmail addresses.

Initiating a Phone Call

The next line in our program looks like this:

startActivity(callIntent);

This looks like we want to start an Activity, using the Intent object we created. But why don’t we need to specify an instance, or even a class, when we call startActivity? Because our program is an instance of the Activity class. We are calling a method of the class this object is an instance of. We could have used the following instead:

this.startActivity(callIntent);

Our program is already an Activity, but we now want to start a new instance of the Activity class—one that can handle the Intent instance we created. The Android framework handles the call by searching for an Intent that matches our request for ACTION_CALL. Let’s step over this line and see what happens.

Now the arrow in the left margin of the code view points to the last line of the call method, just before the method returns. The emulator window shows the Android call status application displaying the number we specified. It should look like Figure 14-2, shown earlier in this chapter.

The fact that we stepped over this line of code and can now continue executing our program means that making a phone call this way is asynchronous: it allows our program to continue running while the dialer program makes the phone call.

Android is a collection of applications, and the application you are debugging places no restrictions on other applications that can be running at the same time.

Exception Handling

What if something goes wrong? The code in the call method that starts the dialer is wrapped in a try/catch block. The catch statement contains a line of code that logs an error if the startActivity method throws an exception of the type ActivityNotFoundException. If a method can throw an exception that indicates an error, the call to that method should be in a try/catch block that catches that type of exception. In this case, we use Android’s logging facility to record the error.

Note

We do not catch all exceptions, because unexpected exceptions indicate failures a program cannot, in general, recover from.

Let’s make an exception happen. We can do this by removing part of the data needed to have the startActivity method call work correctly. Comment out line 22 of the code, as shown:

// callIntent.setData(Uri.parse("tel:9785551212"));

Now make some changes to breakpoints. Clear the breakpoint on line 21, and set a breakpoint on line 25, where, in the catch clause, the method of the Log class is called to log the caught exception. Use the Run → Debug command again to start the program.

This time you will see execution stop at the new breakpoint you set. You will also see that an exception has been thrown. The Debug view in Eclipse shows a stack backtrace, a list of all the methods called when the exception was thrown. The Variables view shows that activityException now refers to the exception that was thrown. Look at the members of the exception to see the information this exception provides.

If you examine the exception that was thrown (you can do this by hovering your mouse over activityException) you will see that the explanation for the exception reads “No activity found to handle intent.” That is, in the case of an Intent object created with Intent.ACTION_CALL as the argument to the constructor, it also needs the data of the Intent to be set correctly in order to find an activity to process that Intent.

Android Application-Level Modularity and Telephony

Getting modularity right is difficult. In the case of Android, the problem is especially difficult: mobile phones were not designed to have replaceable software components, but Android is all about replaceable, modular parts. Every part of the Android application environment, even core components that handle phone calls and talk to the mobile radio, can be replaced by code you can write.

How do you avoid perplexing program authors with too much complexity managing the interfaces and the versions of the interfaces between modules? The mobile radio in a handset has a particularly complex interface. In addition to the obvious functionality for starting and ending phone calls and reporting state and error conditions, it also encompasses critical functions such as emergency calls, and obscure functions such as “MMI codes” that enable users to access features of the phone and mobile network through special dialing strings.

Android provides a practical, usable, and flexible system for modularity for telephony applications. It uses the Android system of Intent objects and activities that listen for Intent objects that indicate they should handle a particular request. In this case, we see that the Intent class and the activities and data you need to specify when making a phone call are easy to use. We also see that application-level modularity is a boon to practicality: because you don’t need to track the inner workings of a phone call—PhoneApp does it for you.

Android does all of this without replacing, modifying, or adding requirements to the modularity tools provided by Java. You still have class libraries, reflection, and other tools for making and using existing Java software modules.

In the next chapter, you will see what happens inside of Android’s telephony software, all the way down to how the mobile radio is commanded to start a phone call.

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

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