2.3: Start Activities with Implicit Intents
Contents:
- What you should already KNOW
- What you will LEARN
- What you will DO
- App overview
- Task 1. Create new project and layout
- Task 2. Implement open website
- Task 3. Implement open location
- Task 4. Implement share this text
- Task 5. Receive implicit intents
- Coding challenge
- Summary
- Related concept
- Learn more
In a previous section you learned about explicit intents -- activating a specific activity in your app or a different app by sending an intent with the fully-qualified class name of that activity. In this section you'll learn more about implicit intents, and how you can use them to activate activities as well.
Implicit intents allow you to activate an activity if you know the action, but not the specific app or activity that will handle that action. For example, if you want your app to take a photo, or send email, or display a location on a map, you typically do not care which specific app or activity actually performs these actions.
Conversely, your activities can declare one or more intent filters in the Android manifest that advertise that activity's ability to accept implicit intents and to define the particular type of intents it will accept.
To match your request with a specific app installed on the device, the Android system matches your implicit intent with an activity whose intent filters indicate that they can perform that action. If there are multiple apps installed that match, the user is presented with an app chooser that lets them select which app they want to use to handle that intent.
In this practical you'll build an app that sends three implicit intents: to open a URL in a web browser, to open a location on a map, and to share a bit of text. Sharing -- sending a piece of information to other people through email or social media -- is a common and popular feature in many apps. For the sharing action we'll use the ShareCompat.IntentBuilder class, which makes it easy to build intents for sharing data.
Finally, we'll create a simple intent receiver app that accepts implicit intents for a specific action.
What you should already KNOW
From the previous practicals, you should be able to:
- Create and use activities.
- Create and send intents between activities.
What you will LEARN
You will learn to:
- Create implicit intents, and use their actions and categories.
- Use the ShareCompat.IntentBuilder helper class to easily create implicit intents for sharing data.
- Advertise that your app can accept implicit intents by declaring intent filters in the Andriod manifest
What you will DO
In this practical you will:
- Create a new app to send implicit intents.
- Implement two implicit intents that open a web page and open a location on a map.
- Implement an action to share a snippet of text.
- Create a new app that can accept implicit intents for opening a web page.
App overview
In this section you'll create a new app with one activity and three options for actions: open a web site, open a location on a map, and share a snippet of text. All of the text fields are editable (EditText), but contain default values.
Task 1. Create new project and layout
For this exercise, you'll create a new project and app called Implicit Intents with a new layout.
1.1 Create the project
- Start Android Studio and create a new Android Studio project. Call your application "Implicit Intents."
- Choose Empty Activity for the project template. Click Next.
- Accept the default activity name (MainActivity). Make sure the Generate Layout file box is checked. Click Finish.
1.2 Create the layout
In this task, create the layout for the app. Use a LinearLayout, three Buttons, and three EditTexts, like this:
Edit res/values/strings.xml to include these string resources:
<string name="edittext_uri">http://developer.android.com</string> <string name="button_uri">Open Website</string> <string name="edittext_loc">Golden Gate Bridge</string> <string name="button_loc">Open Location</string> <string name="edittext_share">\'Twas brillig and the slithy toves</string> <string name="button_share">Share This Text</string>
- Change the layout to LinearLayout. Add the android:orientation attribute and give it the value "vertical."
<LinearLayout 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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.android.implicitintents.MainActivity" android:orientation="vertical">
- Remove the "Hello World" TextView.
Add an EditText and a Button to the layout for the Open Website function. Use these attribute values:
Attribute (EditText) Value (EditText) android:id "@+id/website_edittext" android:layout_width "match_parent" android:layout_height "wrap_content" android:text "@string/edittext_uri" Attribute (Button) Value (Button) android:id "@+id/open_website_button" android:layout_width "wrap_content" android:layout_height "wrap_content" android:layout_marginBottom "24dp" android:text "@string/button_uri" android:onClick "openWebsite" - Add a second EditText and a Button for the Open Website function.
Use the same attributes as those in the previous step, but modify these attributes as noted below:
Attribute (EditText) Value (EditText) android:id "@+id/location_edittext" android:text "@string/edittext_loc" Attribute (Button) Value (Button) android:id "@+id/open_location_button" android:text "@string/button_loc" android:onClick "openLocation" Add a third EditText and a Button for the Share This function. Make these changes:
Attribute (EditText) Value (EditText) android:id "@+id/share_edittext" android:text "@string/edittext_share" Attribute (Button) Value (Button) android:id "@+id/share_text_button" android:text "@string/button_share" android:onClick "shareText"
Solution code:
Depending on your version of Android Studio, your code will look something like the following.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.android.implicitintents.MainActivity"
android:orientation="vertical">
<EditText
android:id="@+id/website_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/edittext_uri" />
<Button
android:id="@+id/open_website_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:onClick="openWebsite"
android:text="@string/button_uri" />
<EditText
android:id="@+id/location_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/edittext_loc" />
<Button
android:id="@+id/open_location_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:onClick="openLocation"
android:text="@string/button_loc" />
<EditText
android:id="@+id/share_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/edittext_share" />
<Button
android:id="@+id/share_text_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:onClick="shareText"
android:text="@string/button_share" />
</LinearLayout>
Task 2. Implement "open website"
In this task you'll implement the on-click handler method for the first button in the layout ("Open Website.") This action uses an implicit intent to send the given URI to an activity that can handle that Implicit Intent (such as a web browser).
2.1 Define the openWebsite method
- Open MainActivity.java.
- Add a private variable at the top of the class to hold the EditText object for the web site URI.
private EditText mWebsiteEditText;
- In the onCreate() method, use findViewById() to get a reference to the EditText instance and assign it to that private variable:
mWebsiteEditText = (EditText) findViewById(R.id.website_edittext);
- Create a new method called openWebsite(), with this signature:
public void openWebsite(View view) { }
- Get the string value of the EditText:
String url = mWebsiteEditText.getText().toString();
- Encode and parse that string into a Uri object:
Uri webpage = Uri.parse(url);
Create a new Intent with Intent.ACTION_VIEW as the action and the URI as the data:
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
This intent constructor is different from the one you used to create an explicit intent. In your previous constructor, you specified the current context and a specific component (activity class) to send the intent. In this constructor you specify an action and the data for that action. Actions are defined by the Intent class and can include ACTION_VIEW (to view the given data), ACTION_EDIT (to edit the given data), or ACTION_DIAL (to dial a phone number). In this case the action is ACTION_VIEW because we want to open and view the web page specified by the URI in the webpage variable.
Use the resolveActivity() and the Android package manager to find an activity that can handle your implicit intent. Check to make sure the that request resolved successfully.
if (intent.resolveActivity(getPackageManager()) != null) { }
This request that matches your intent action and data with the intent filters for installed applications on the device to make sure there is at least one activity that can handle your requests.
- Inside the if-statement, call startActivity() to send the intent.
startActivity(intent);
- Add an else block to print a log message if the intent could not be resolved.
} else { Log.d("ImplicitIntents", "Can't handle this!"); }
Solution code (not the entire class):
public void openWebsite(View view) {
// Get the URL text.
String url = mWebsiteEditText.getText().toString();
// Parse the URI and create the intent.
Uri webpage = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
// Find an activity to hand the intent and start that activity.
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Log.d("ImplicitIntents", "Can't handle this intent!");
}
}
Task 3. Implement "open location"
In this task you'll implement the on-click handler method for the second button in the UI ("Open Location.") This method is almost identical to the openWebsite() method. The difference is the use of a geo URI to indicate a map location. You can use a geo URI with latitude and longitude, or use a query string for a general location. In this example we've used the latter.
3.1 Define the openLocation method
- Open MainActivity.java (java/com.example.android.implicitintents/MainActivity).
- Add a private variable at the top of the class to hold the EditText object for the location URI.
private EditText mLocationEditText;
- In the onCreate() method, use findViewByID() to get a reference to the EditText instance and assign it to that private variable:
mLocationEditText = (EditText) findViewById(R.id.location_edittext);
- Create a new method called openLocation to use as the onClick method for the Open Location button. Use the same method signature as openWebsite().
- Get the string value of the mLocationEditText EditText.
String loc = mLocationEditText.getText().toString();
- Parse that string into a Uri object with a geo search query:
Uri addressUri = Uri.parse("geo:0,0?q=" + loc);
- Create a new Intent with Intent.ACTION_VIEW as the action and loc as the data.
Intent intent = new Intent(Intent.ACTION_VIEW, addressUri);
- Resolve the intent and check to make sure the intent resolved successfully. If so, startActivity(), otherwise log an error message.
if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } else { Log.d("ImplicitIntents", "Can't handle this intent!"); }
Solution code (not the entire class):
public void openLocation(View view) {
// Get the string indicating a location. Input is not validated; it is
// passed to the location handler intact.
String loc = mLocationEditText.getText().toString();
// Parse the location and create the intent.
Uri addressUri = Uri.parse("geo:0,0?q=" + loc);
Intent intent = new Intent(Intent.ACTION_VIEW, addressUri);
// Find an activity to handle the intent, and start that activity.
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Log.d("ImplicitIntents", "Can't handle this intent!");
}
}
Task 4. Implement share this text
Sharing actions are an easy way for users to share items in your app with social networks and other apps. Although you could build a share action in your own app using implicit intents, Android provides the ShareCompat.IntentBuilder helper class to make implementing sharing easy. You can use ShareCompat.IntentBuilder to build an intent and launch a chooser to let the user choose the destination app for sharing.
In this final task we'll implement sharing a bit of text in a text edit with the ShareCompat.IntentBuilder class.
4.1 Implement the shareText method
- Open MainActivity.java.
- Add a private variable at the top of the class to hold the EditText object for the web site URI.
private EditText mShareTextEditText;
- In the onCreate() method, use findViewById() to get a reference to the EditText instance and assign it to that private variable:
mShareTextEditText = (EditText) findViewById(R.id.share_edittext);
- Create a new method called shareThis() to use as the onClick method for the Share This Text button. Use the same method signature as openWebsite().
- Get the string value of the
mShareTextEditText
EditText.String txt = mShareTextEditText.getText().toString();
- Define the mime type of the text to share:
String mimeType = "text/plain";
Call ShareCompat.IntentBuilder with these methods:
ShareCompat.IntentBuilder .from(this) .setType(mimeType) .setChooserTitle("Share this text with: ") .setText(txt) .startChooser();
This call to ShareCompat.IntentBuilder uses these methods:
Method Description from() The activity that launches this share intent (this). setType() The MIME type of the item to be shared. setChooserTitle() The title that appears on the system app chooser. setText() The actual text to be shared startChooser() Show the system app chooser and send the intent. </tr> </table> This format, with all the builder's setter methods strung together in one statement, is an easy shorthand way to create and launch the intent. You can add any of the additional methods to this list.
Solution code (not the entire class):
public void shareText(View view) {
String txt = mShareTextEditText.getText().toString();
String mimeType = "text/plain";
ShareCompat.IntentBuilder
.from(this)
.setType(mimeType)
.setChooserTitle("Share this text with: ")
.setText(txt)
.startChooser();
}
Task 5. Receive implicit intents
So far, you've created apps that use both explicit and implicit intents in order to launch some other app's activity. In this task we'll look at the problem from the other way around: allowing an activity in your app to respond to implicit intents sent from some other app.
Activities in your app can always be activated from inside or outside your app with explicit intents. To allow an activity to receive implicit intents, you define an intent filter in your manifest to indicate which implicit intents your activity is interested in handling.
To match your request with a specific app installed on the device, the Android system matches your implicit intent with an activity whose intent filters indicate that they can perform that action. If there are multiple apps installed that match, the user is presented with an app chooser that lets them select which app they want to use to handle that intent.
When an app on the device sends an implicit intent, the Android system matches that intent's action and data with available activities that include the right intent filters. If your activity's intent filters match the intent, your activity can either handle the intent itself (if it is the only matching activity), or (if there are multiple matches) an app chooser appears to allow the user to pick which app they'd prefer to execute that action.
In this task you'll create a very simple app that receives implicit intents to open the URI for a web page. When activated by an implicit intent, that app displays the requested URI as a string in a TextView.
5.1 Create the project and layout
- Start Android Studio and create a new Android Studio project.
- Call your application "Implicit Intents Receiver."
- Choose Empty Activity for the project template.
- Accept the default activity name (MainActivity). Click Next.
- Make sure the Generate Layout file box is checked. Click Finish.
- Open
res/layout/activity_main.xml
. Change the existing ("Hello World") TextView these attributes:
Attribute Value android:id "@+id/text_uri_message" android:layout_width wrap_content android:layout_height wrap_content android:textSize "18sp" android:textStyle "bold" - Delete the
android:text
attribute. There's no text in this TextView by default, but you'll add the URI from the intent in onCreate().
5.2 Modify the Android manifest to add an intent filter
- Open
manifests/AndroidManifest.xml
. Note that the main activity already has this intent filter:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
This intent filter, which is part of the default project manifest, indicates that this activity is the main entry point for your app (it has an intent action of "android.intent.action.MAIN"), and that this activity should appear as a top-level item in the launcher (its category is
"android.intent.category.LAUNCHER")
Add a second
<intent-filter>
tag inside<activity>
, and include these elements :<action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" android:host="developer.android.com" />
These lines define an intent filter for the activity, that is, the kind of intents that the activity can handle. This intent filter declares these elements:
Filter type Value Matches action "android.intent.action.VIEW" All intents with view actions. category "android.intent.category.DEFAULT" All implicit intents. This category must be included for your activity to receive any implicit intents. category "android.intent.category.BROWSABLE" Requests for browsable links from web pages, email, or other sources. data android:scheme="http" android:host="developer.android.com" URIs that contain a scheme of http AND a host name of developer.android.com. Note that the data filter has a restriction on both the kind of links it will accept and the hostname for those URIs. If you'd prefer your receiver to be able to accept any links, you can leave the
<data>
element out altogether.
Solution code
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.implicitintentsreceiver">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="developer.android.com" />
</intent-filter>
</activity>
</application>
</manifest>
5.3 Process the intent
In the onCreate() method for your activity, you process the incoming intent for any data or extras it includes. In this case, the incoming implicit intent has the URI stored in the Intent data.
- Open
MainActivity.java
. - In the
onCreate()
method, get the incoming intent that was used to activate the activity:Intent intent = getIntent();
- Get the intent data. Intent data is always a URI object:
Uri uri = intent.getData();
- Check to make sure the
uri
variable is not null. If that check passes, create a string from that URI object:if (uri != null) { String uri_string = "URI: " + uri.toString(); }
- Inside that same if block, get the text view for the message:
TextView textView = (TextView) findViewById(R.id.text_uri_message);
- Also inside the if-block, set the text of that TextView to the URI:
textView.setText(uri_string);
Run the receiver app.
Running the app on its own shows a blank activity with no text. This is because the activity was activated from the system launcher, and not with an intent from another app.
Run the ImplicitIntents app, and click Open Website with the default URI.
An app chooser appears asking if you want to use the default browser or the ImplicitIntentsReceiver app. Choose "Just Once" for the receiver app. The ImplicitIntentsReceiver app launches and the message shows the URI from the original request.
Tap the back button and enter a different URI. Click Open Website.
The receiver app has a very restrictive intent filter that matches only exact URI protocol (http) and host (developer.android.com). Any other URI opens in the default web browser.
Solution code
Android Studio project: ImplicitIntents
Coding challenge
Challenge: In the last section's challenge you created a shopping list app builder with two activities: one to display the list, and one to pick an item. Add an EditText and a Button to the shopping list activity to locate a particular store on a map.
Summary
- Implicit intents allow you to activate an activity if you know the action, but not the specific app or activity that will handle that action.
- Activities that can receive implicit intents must define intent filters in their Android manifest that match one or more intent actions and categories.
- The Android system matches the content of an implicit intent and the intent filters of all available activities to determine which activity to activate. If there is more than one available activity, the system provides a chooser so the user can pick one.
- The ShareCompat.IntentBuilder class makes it easy to build implicit intents for sharing data to social media or email.
Related concept
The related concept documentation is in Android Developer Fundamentals: Concepts.
Learn more
- Activity (API Guide)
- Activity (API Reference)
- Intents and Intent Filters (API Guide)
- Intent (API Reference)
- Uri
- Google Maps Intents
- ShareCompat.IntentBuilder (API Reference)