Starting Activities through System-Wide Intents
java -> android -> system-wide intents ...
In the previous section we saw how one activity can start other child activities by creating a specific intent that includes the name of the intended class to start. Instead we could describe an "intent" to do X with data Y and let the operating system decide who can fulfill our request. For that to work, every activity needs to register the type of operations it can perform with the OS during installation, and there needs to be an event that can encapsulate the idea of "do X with data Y". Both is done via the Intent class.
The Intent class
An Intent is an object that represents an ”intention” to do something. For example, if your application wants to display a web page, it can express its “intent” to view the URI by creating an Intent instance and handing it off to the system. The system locates some other piece of code (in this case, the Browser) that knows how to handle the intent and runs it. The primary pieces of information in an intent are:
- action -- The general action to be performed, such as ACTION_VIEW, ACTION_EDIT, ACTION_MAIN, etc.
- data -- The data to operate on, such as a person record in the contacts database or a web page expressed as a Uri.
Some examples of action/data pairs are:
- ACTION_VIEW content://contacts/people/1 -- Display information about the person whose identifier is "1".
- ACTION_DIAL content://contacts/people/1 -- Display the phone dialer with the person filled in.
- ACTION_VIEW tel:123 -- Display the phone dialer with the given number filled in. Note how the VIEW action does what what is considered the most reasonable thing for a particular URI.
- ACTION_DIAL tel:123 -- Display the phone dialer with the given number filled in.
- ACTION_EDIT content://contacts/people/1 -- Edit information about the person whose identifier is "1".
- ACTION_VIEW content://contacts/people/ -- Display a list of people, which the user can browse through. This example is a typical top-level entry into the Contacts application, showing you the list of people. Selecting a particular person to view would result in a new intent { ACTION_VIEW content://contacts/N } being used to start an activity to display that person.
Example: In the previous section we created a class with a "Start Activity" button to start a specific child activity that we also created. Now let's modify that class to
- let a user select an entry from the Android contact list
- if a user is selected, display the detailed info about that user
Both activities are already available. After all, that's exactly what happens when we select the "contacts" application. So all we need to figure out is how to get the respective activity to run, and how to pass data into and out of it.
Note: there is one more thing to do before we start coding! We need to add a request for permission to read the contact information to our project's manifest via a uses-permission entry:
<uses-permission android:name="android.permission.READ_CONTACTS"> </uses-permission>
The previous project contained the class FirstMultiApp. We use that class and change the handleButtonClick method to broadcast an intent to select a name from the contact list. Thus, we create the intent:
private void handleButtonClick() { // creating our intent Intent selectContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts/people")); // now we broadcast this intent and hope some activity will kick in: startActivityForResult(selectContactIntent, INPUT_REQUEST); }
That will bring up the standard scrollable list of contacts as if you clicked the Contacts application. Two things can happen next: the user could tap a contact, or the user can cancel the selection process. But since we called startActivityForResult we know that, either way, our activity will be informed via its onActvityResult method, which we could implement as follows:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_CANCELED) Toast.makeText(this, "activity canceled", Toast.LENGTH_SHORT).show(); else if (resultCode == Activity.RESULT_OK) Toast.makeText(this, "contact selected: " + data, Toast.LENGTH_SHORT).show(); }
This will work perfectly, but we can do even better. The data returned when the user picks a name is again an intent. It includes, in particular, a data Uri to a specific name. We can use that to create another intent, this time to view that contact info, and post it via another startActivity or startActivityForResult call. Here is the modified method:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == INPUT_REQUEST) { if (resultCode == Activity.RESULT_CANCELED) Toast.makeText(this, "activity canceled", Toast.LENGTH_SHORT).show(); else if (resultCode == Activity.RESULT_OK) { Toast.makeText(this, "contact selected: " + data, Toast.LENGTH_SHORT).show(); startActivity(new Intent(Intent.ACTION_VIEW, data.getData())); } } }
To summarize: we have an activity with a button. If the button is clicked a list of names will appear and we can pick one. If we pick a name, the full info for that contact is displayed! Pretty nifty for that little coding, but even better: if the contact application is improved - say as a result of an OS upgrade - our application automatically benefits!
Exercise: Usually when we long-click on a name in the list of contacts, we can select to edit it. Does that work for our app? If not, how would you enable it? Instead of viewing a contact info, change the program to dial the selected contact's number (you may need to add another set of permissions in addition to changing the code (and doing some additional reading)).
The next step to get familiar with intents is to learn how to create activities that can serve as intent handlers for data. Every basic activity is registered to handle ACTION_MAIN via the following entry in the manifest file:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
If the launcher posts an ACTION_MAIN intent with data being the activity class name, it calls the onCreate method and thus starts the activity. We will pick up more details later, but now we have all the ingredients to write an actually useful activity in the next segment.