From ba8f435947c1387a01ee97ea642b3ce7ca6080d6 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 29 Mar 2012 23:03:13 -0700 Subject: docs: add 101 class about intents, Interacting with Other Apps Change-Id: I3242adafd99c34fcec4e4cece20e8c89d338c8a1 --- docs/html/training/basics/intents/filters.jd | 244 +++++++++++++++++++++++++++ docs/html/training/basics/intents/index.jd | 64 +++++++ docs/html/training/basics/intents/result.jd | 182 ++++++++++++++++++++ docs/html/training/basics/intents/sending.jd | 211 +++++++++++++++++++++++ 4 files changed, 701 insertions(+) create mode 100644 docs/html/training/basics/intents/filters.jd create mode 100644 docs/html/training/basics/intents/index.jd create mode 100644 docs/html/training/basics/intents/result.jd create mode 100644 docs/html/training/basics/intents/sending.jd (limited to 'docs/html/training/basics') diff --git a/docs/html/training/basics/intents/filters.jd b/docs/html/training/basics/intents/filters.jd new file mode 100644 index 0000000..0090c98 --- /dev/null +++ b/docs/html/training/basics/intents/filters.jd @@ -0,0 +1,244 @@ +page.title=Allowing Other Apps to Start Your Activity +parent.title=Interacting with Other Apps +parent.link=index.html + +trainingnavtop=true +previous.title=Getting a Result from an Activity +previous.link=result.html + +@jd:body + +
+
+ +

This lesson teaches you to

+
    +
  1. Add an Intent Filter
  2. +
  3. Handle the Intent in Your Activity
  4. +
  5. Return a Result
  6. +
+ +

You should also read

+ +
+
+ +

The previous two lessons focused on one side of the story: starting another app's activity from +your app. But if your app can perform an action that might be useful to another app, +your app should be prepared to respond to action requests from other apps. For instance, if you +build a social app that can share messages or photos with the user's friends, it's in your best +interest to support the {@link android.content.Intent#ACTION_SEND} intent so users can initiate a +"share" action from another app and launch your app to perform the action.

+ +

To allow other apps to start your activity, you need to add an {@code <intent-filter>} +element in your manifest file for the corresponding {@code <activity>} element.

+ +

When your app is installed on a device, the system identifies your intent +filters and adds the information to an internal catalog of intents supported by all installed apps. +When an app calls {@link android.app.Activity#startActivity +startActivity()} or {@link android.app.Activity#startActivityForResult startActivityForResult()}, +with an implicit intent, the system finds which activity (or activities) can respond to the +intent.

+ + + +

Add an Intent Filter

+ +

In order to properly define which intents your activity can handle, each intent filter you add +should be as specific as possible in terms of the type of action and data the activity +accepts.

+ +

The system may send a given {@link android.content.Intent} to an activity if that activity has +an intent filter fulfills the following criteria of the {@link android.content.Intent} object:

+ +
+
Action
+
A string naming the action to perform. Usually one of the platform-defined values such +as {@link android.content.Intent#ACTION_SEND} or {@link android.content.Intent#ACTION_VIEW}. +

Specify this in your intent filter with the {@code <action>} element. +The value you specify in this element must be the full string name for the action, instead of the +API constant (see the examples below).

+ +
Data
+
A description of the data associated with the intent. +

Specify this in your intent filter with the {@code <data>} element. Using one +or more attributes in this element, you can specify just the MIME type, just a URI prefix, +just a URI scheme, or a combination of these and others that indicate the data type +accepted.

+

Note: If you don't need to declare specifics about the data +{@link android.net.Uri} (such as when your activity handles to other kind of "extra" data, instead +of a URI), you should specify only the {@code android:mimeType} attribute to declare the type of +data your activity handles, such as {@code text/plain} or {@code image/jpeg}.

+
+
Category
+
Provides an additional way to characterize the activity handling the intent, usually related +to the user gesture or location from which it's started. There are several different categories +supported by the system, but most are rarely used. However, all implicit intents are defined with +{@link android.content.Intent#CATEGORY_DEFAULT} by default. +

Specify this in your intent filter with the {@code <category>} +element.

+
+ +

In your intent filter, you can declare which criteria your activity accepts +by declaring each of them with corresponding XML elements nested in the {@code <intent-filter>} +element.

+ +

For example, here's an activity with an intent filter that handles the {@link +android.content.Intent#ACTION_SEND} intent when the data type is either text or an image:

+ +
+<activity android:name="ShareActivity">
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+        <data android:mimeType="image/*"/>
+    </intent-filter>
+</activity>
+
+ +

Each incoming intent specifies only one action and one data type, but it's OK to declare multiple +instances of the {@code +<action>}, {@code +<category>}, and {@code +<data>} elements in each +{@code +<intent-filter>}.

+ +

If any two pairs of action and data are mutually exclusive in +their behaviors, you should create separate intent filters to specify which actions are acceptable +when paired with which data types.

+ +

For example, suppose your activity handles both text and images for both the {@link +android.content.Intent#ACTION_SEND} and {@link +android.content.Intent#ACTION_SENDTO} intents. In this case, you must define two separate +intent filters for the two actions because a {@link +android.content.Intent#ACTION_SENDTO} intent must use the data {@link android.net.Uri} to specify +the recipient's address using the {@code send} or {@code sendto} URI scheme. For example:

+ +
+<activity android:name="ShareActivity">
+    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
+    <intent-filter>
+        <action android:name="android.intent.action.SENDTO"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:scheme="sms" />
+        <data android:scheme="smsto" />
+    </intent-filter>
+    <!-- filter for sending text or images; accepts SEND action and text or image data -->
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="image/*"/>
+        <data android:mimeType="text/plain"/>
+    </intent-filter>
+</activity>
+
+ +

Note: In order to receive implicit intents, you must include the +{@link android.content.Intent#CATEGORY_DEFAULT} category in the intent filter. The methods {@link +android.app.Activity#startActivity startActivity()} and {@link +android.app.Activity#startActivityForResult startActivityForResult()} treat all intents as if they +contained the {@link android.content.Intent#CATEGORY_DEFAULT} category. If you do not declare it, no +implicit intents will resolve to your activity.

+ +

For more information about sending and receiving {@link android.content.Intent#ACTION_SEND} +intents that perform social sharing behaviors, see the lesson about Receiving Content from Other Apps.

+ + +

Handle the Intent in Your Activity

+ +

In order to decide what action to take in your activity, you can read the {@link +android.content.Intent} that was used to start it.

+ +

As your activity starts, call {@link android.app.Activity#getIntent()} to retrieve the +{@link android.content.Intent} that started the activity. You can do so at any time during the +lifecycle of the activity, but you should generally do so during early callbacks such as +{@link android.app.Activity#onCreate onCreate()} or {@link android.app.Activity#onStart()}.

+ +

For example:

+ +
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    setContentView(R.layout.main);
+
+    // Get the intent that started this activity
+    Intent intent = getIntent();
+    Uri data = intent.getData();
+
+    // Figure out what to do based on the intent type
+    if (intent.getType().indexOf("image/") != -1) {
+        // Handle intents with image data ...
+    } else if (intent.getType().equals("text/plain")) {
+        // Handle intents with text ...
+    }
+}
+
+ + +

Return a Result

+ +

If you want to return a result to the activity that invoked yours, simply call {@link +android.app.Activity#setResult(int,Intent) setResult()} to specify the result code and result {@link +android.content.Intent}. When your operation is done and the user should return to the original +activity, call {@link android.app.Activity#finish()} to close (and destroy) your activity. For +example:

+ +
+// Create intent to deliver some kind of result data
+Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
+setResult(Activity.RESULT_OK, result);
+finish();
+
+ +

You must always specify a result code with the result. Generally, it's either {@link +android.app.Activity#RESULT_OK} or {@link android.app.Activity#RESULT_CANCELED}. You can then +provide additional data with an {@link android.content.Intent}, as necessary.

+ +

Note: The result is set to {@link +android.app.Activity#RESULT_CANCELED} by default. So, if the user presses the Back +button before completing the action and before you set the result, the original activity receives +the "canceled" result.

+ +

If you simply need to return an integer that indicates one of several result options, you can set +the result code to any value higher than 0. If you use the result code to deliver an integer and you +have no need to include the {@link android.content.Intent}, you can call {@link +android.app.Activity#setResult(int) setResult()} and pass only a result code. For example:

+ +
+setResult(RESULT_COLOR_RED);
+finish();
+
+ +

In this case, there might be only a handful of possible results, so the result code is a locally +defined integer (greater than 0). This works well when you're returning a result to an activity +in your own app, because the activity that receives the result can reference the public +constant to determine the value of the result code.

+ +

Note: There's no need to check whether your activity was started +with {@link +android.app.Activity#startActivity startActivity()} or {@link +android.app.Activity#startActivityForResult startActivityForResult()}. Simply call {@link +android.app.Activity#setResult(int,Intent) setResult()} if the intent that started your activity +might expect a result. If the originating activity had called {@link +android.app.Activity#startActivityForResult startActivityForResult()}, then the system delivers it +the result you supply to {@link android.app.Activity#setResult(int,Intent) setResult()}; otherwise, +the result is ignored.

+ + + + + + diff --git a/docs/html/training/basics/intents/index.jd b/docs/html/training/basics/intents/index.jd new file mode 100644 index 0000000..c661d98 --- /dev/null +++ b/docs/html/training/basics/intents/index.jd @@ -0,0 +1,64 @@ +page.title=Interacting with Other Apps + +trainingnavtop=true +startpage=true +next.title=Sending the User to Another App +next.link=sending.html + +@jd:body + +
+
+ +

Dependencies and prerequisites

+ + + +

You should also read

+ + +
+
+ +

An Android app typically has several activities. Each activity displays a +user interface that allows the user to perform a specific task (such as view a map or take a photo). +To take the user from one activity to another, your app must use an {@link +android.content.Intent} to define your app's "intent" to do something. When you pass an +{@link android.content.Intent} to the system with a method such as {@link +android.app.Activity#startActivity startActivity()}, the system uses the {@link +android.content.Intent} to identify and start the appropriate app component. Using intents even +allows your app to start an activity that is contained in a separate app.

+ +

An {@link android.content.Intent} can be explicit in order to start a specific component +(a specific {@link android.app.Activity} instance) or implicit in order to start any +component that can handle the intended action (such as "capture a photo").

+ +

This class shows you how to use an {@link android.content.Intent} to perform some basic +interactions with other apps, such as start another app, receive a result from that app, and +make your app able to respond to intents from other apps.

+ +

Lessons

+ +
+
Sending the User to Another App
+
Shows how you can create implicit intents to launch other apps that can perform an +action.
+
Getting a Result from an Activity
+
Shows how to start another activity and receive a result from the activity.
+
Allowing Other Apps to Start Your Activity
+
Shows how to make activities in your app open for use by other apps by defining +intent filters that declare the implicit intents your app accepts.
+
+ diff --git a/docs/html/training/basics/intents/result.jd b/docs/html/training/basics/intents/result.jd new file mode 100644 index 0000000..0086913 --- /dev/null +++ b/docs/html/training/basics/intents/result.jd @@ -0,0 +1,182 @@ +page.title=Getting a Result from an Activity +parent.title=Interacting with Other Apps +parent.link=index.html + +trainingnavtop=true +previous.title=Sending the User to Another App +previous.link=sending.html +next.title=Allowing Other Apps to Start Your Activity +next.link=filters.html + +@jd:body + +
+
+ +

This lesson teaches you to

+
    +
  1. Start the Activity
  2. +
  3. Receive the Result
  4. +
+ +

You should also read

+ + +
+
+ +

Starting another activity doesn't have to be one-way. You can also start another activity and +receive a result back. To receive a result, call {@link android.app.Activity#startActivityForResult +startActivityForResult()} (instead of {@link android.app.Activity#startActivity +startActivity()}).

+ +

For example, your app can start a camera app and receive the captured photo as a result. Or, you +might start the People app in order for the user to select a +contact and you'll receive the contact details as a result.

+ +

Of course, the activity that responds must be designed to return a result. When it does, it +sends the result as another {@link android.content.Intent} object. Your activity receives it in +the {@link android.app.Activity#onActivityResult onActivityResult()} callback.

+ +

Note: You can use explicit or implicit intents when you call +{@link android.app.Activity#startActivityForResult startActivityForResult()}. When starting one of +your own activities to receive a result, you should use an explicit intent to ensure that you +receive the expected result.

+ + +

Start the Activity

+ +

There's nothing special about the {@link android.content.Intent} object you use when starting +an activity for a result, but you do need to pass an additional integer argument to the {@link +android.app.Activity#startActivityForResult startActivityForResult()} method.

+ +

The integer argument is a "request code" that identifies your request. When you receive the +result {@link android.content.Intent}, the callback provides the same request code so that your +app can properly identify the result and determine how to handle it.

+ +

For example, here's how to start an activity that allows the user to pick a contact:

+ +
+static final int PICK_CONTACT_REQUEST = 1;  // The request code
+...
+private void pickContact() {
+    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, new Uri("content://contacts"));
+    pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
+    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
+}
+
+ + +

Receive the Result

+ +

When the user is done with the subsequent activity and returns, the system calls your activity's +{@link android.app.Activity#onActivityResult onActivityResult()} method. This method includes three +arguments:

+ + + +

For example, here's how you can handle the result for the "pick a contact" intent:

+ +
+@Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    // Check which request we're responding to
+    if (requestCode == PICK_CONTACT_REQUEST) {
+        // Make sure the request was successful
+        if (resultCode == RESULT_OK) {
+            // The user picked a contact.
+            // The Intent's data Uri identifies which contact was selected.
+
+            // Do something with the contact here (bigger example below)
+        }
+    }
+}
+
+ +

In this example, the result {@link android.content.Intent} returned by +Android's Contacts or People app provides a content {@link android.net.Uri} that identifies the +contact the user selected.

+ +

In order to successfully handle the result, you must understand what the format of the result +{@link android.content.Intent} will be. Doing so is easy when the activity returning a result is +one of your own activities. Apps included with the Android platform offer their own APIs that +you can count on for specific result data. For instance, the People app (Contacts app on some older +versions) always returns a result with the content URI that identifies the selected contact, and the +Camera app returns a {@link android.graphics.Bitmap} in the {@code "data"} extra (see the class +about Capturing Photos).

+ + +

Bonus: Read the contact data

+ +

The code above showing how to get a result from the People app doesn't go into +details about how to actually read the data from the result, because it requires more advanced +discussion about content +providers. However, if you're curious, here's some more code that shows how to query the +result data to get the phone number from the selected contact:

+ +
+@Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    // Check which request it is that we're responding to
+    if (requestCode == PICK_CONTACT_REQUEST) {
+        // Make sure the request was successful
+        if (resultCode == RESULT_OK) {
+            // Get the URI that points to the selected contact
+            Uri contactUri = data.getData();
+            // We only need the NUMBER column, because there will be only one row in the result
+            String[] projection = {Phone.NUMBER};
+
+            // Perform the query on the contact to get the NUMBER column
+            // We don't need a selection or sort order (there's only one result for the given URI)
+            // CAUTION: The query() method should be called from a separate thread to avoid blocking
+            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
+            // Consider using {@link android.content.CursorLoader} to perform the query.
+            Cursor cursor = getContentResolver()
+                    .query(contactUri, projection, null, null, null);
+            cursor.moveToFirst();
+
+            // Retrieve the phone number from the NUMBER column
+            int column = cursor.getColumnIndex(Phone.NUMBER);
+            String number = cursor.getString(column);
+
+            // Do something with the phone number...
+        }
+    }
+}
+
+ +

Note: Before Android 2.3 (API level 9), performing a query +on the {@link android.provider.ContactsContract.Contacts Contacts Provider} (like the one shown +above) requires that your app declare the {@link +android.Manifest.permission#READ_CONTACTS} permission (see Security and Permissions). However, +beginning with Android 2.3, the Contacts/People app grants your app a temporary +permission to read from the Contacts Provider when it returns you a result. The temporary permission +applies only to the specific contact requested, so you cannot query a contact other than the one +specified by the intent's {@link android.net.Uri}, unless you do declare the {@link +android.Manifest.permission#READ_CONTACTS} permission.

+ + + + + + + + + + + + + + + diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd new file mode 100644 index 0000000..a71c8f9 --- /dev/null +++ b/docs/html/training/basics/intents/sending.jd @@ -0,0 +1,211 @@ +page.title=Sending the User to Another App +parent.title=Interacting with Other Apps +parent.link=index.html + +trainingnavtop=true +next.title=Getting a Result from an Activity +next.link=result.html + +@jd:body + + +
+
+ +

This lesson teaches you to

+
    +
  1. Build an Implicit Intent
  2. +
  3. Verify There is an App to Receive the Intent
  4. +
  5. Start an Activity with the Intent
  6. +
+ +

You should also read

+ + +
+
+ +

One of Android's most important features is an app's ability to send the user to another app +based on an "action" it would like to perform. For example, if +your app has the address of a business that you'd like to show on a map, you don't have to build +an activity in your app that shows a map. Instead, you can send a out a request to view the address +using an {@link android.content.Intent}. The Android system then starts an app that's able to view +the address on a map.

+ +

As shown in the first class, Building +Your First App, you must use intents to navigate between activities in your own app. You +generally do so with an explicit intent, which defines the exact class name of the +component you want to start. However, when you want to have a separate app perform an action, such +as "view a map," you must use an implicit intent.

+ +

This lesson shows you how to create an implicit intent for a particular action, and how to use it +to start an activity that performs the action in another app.

+ + + +

Build an Implicit Intent

+ +

Implicit intents do not declare the class name of the component to start, but instead declare an +action to perform. The action specifies the thing you want to do, such as view, +edit, send, or get something. Intents often also include data associated +with the action, such as the address you want to view, or the email message you want to send. +Depending on the intent you want to create, the data might be a {@link android.net.Uri}, +one of several other data types, or the intent might not need data at all.

+ +

If your data is a {@link android.net.Uri}, there's a simple {@link +android.content.Intent#Intent(String,Uri) Intent()} constructor you can use define the action and +data.

+ +

For example, here's how to create an intent to initiate a phone call using the {@link +android.net.Uri} data to specify the telephone number:

+ +
+Uri number = Uri.parse("tel:5551234");
+Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
+
+ +

When your app invokes this intent by calling {@link android.app.Activity#startActivity +startActivity()}, the Phone app initiates a call to the given phone number.

+ +

Here are a couple other intents and their action and {@link android.net.Uri} data +pairs:

+ + + +

Other kinds of implicit intents require "extra" data that provide different data types, +such as a string. You can add one or more pieces of extra data using the various {@link +android.content.Intent#putExtra(String,String) putExtra()} methods.

+ +

By default, the system determines the appropriate MIME type required by an intent based on the +{@link android.net.Uri} data that's included. If you don't include a {@link android.net.Uri} in the +intent, you should usually use {@link android.content.Intent#setType setType()} to specify the type +of data associated with the intent. Setting the MIME type further specifies which kinds of +activities should receive the intent.

+ +

Here are some more intents that add extra data to specify the desired action:

+ + + +

Note: It's important that you define your {@link +android.content.Intent} to be as specific as possible. For example, if you want to display an image +using the {@link android.content.Intent#ACTION_VIEW} intent, you should specify a MIME type of +{@code image/*}. This prevents apps that can "view" other types of data (like a map app) from being +triggered by the intent.

+ + + +

Verify There is an App to Receive the Intent

+ +

Although the Android platform guarantees that certain intents will resolve to one of the +built-in apps (such as the Phone, Email, or Calendar app), you should always include a +verification step before invoking an intent.

+ +

Caution: If you invoke an intent and there is no app +available on the device that can handle the intent, your app will crash.

+ +

To verify there is an activity available that can respond to the intent, call {@link +android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()} to get a list +of activities capable of handling your {@link android.content.Intent}. If the returned {@link +java.util.List} is not empty, you can safely use the intent. For example:

+ +
+PackageManager packageManager = {@link android.content.Context#getPackageManager()};
+List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
+boolean isIntentSafe = activities.size() > 0;
+
+ +

If isIntentSafe is true, then at least one app will respond to +the intent. If it is false, then there aren't any apps to handle the intent.

+ +

Note: You should perform this check when your activity first +starts in case you need to disable the feature that uses the intent before the user attempts to use +it. If you know of a specific app that can handle the intent, you can also provide a link for the +user to download the app (see how to link to an app on Google +Play).

+ + +

Start an Activity with the Intent

+ +
+ +

Figure 1. Example of the selection dialog that appears +when more than one app can handle an intent.

+
+ +

Once you have created your {@link android.content.Intent} and set the extra info, call {@link +android.app.Activity#startActivity startActivity()} to send it to the system. If the system +identifies more than one activity that can handle the intent, it displays a dialog for the user to +select which app to use, as shown in figure 1. If there is only one activity that handles the +intent, the system immediately starts it.

+ +
+startActivity(intent);
+
+ +

Here's a complete example that shows how to create an intent to view a map, verify that an +app exists to handle the intent, then start it:

+ +
+// Build the intent
+Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
+Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
+
+// Verify it resolves
+PackageManager packageManager = {@link android.content.Context#getPackageManager()};
+List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
+boolean isIntentSafe = activities.size() > 0;
+  
+// Start an activity if it's safe
+if (isIntentSafe) {
+    startActivity(mapIntent);
+}
+
+ + + + -- cgit v1.1