WeatherReporter, the next sample application we’ll build, uses the Yahoo! Weather API to retrieve weather data and then displays the data to the user. This applica- tion can also optionally alert users about severe weather for certain locations, based either on the current loca- tion of the device or on a specified postal code.
Within this project, you’ll see how you can define a custom URI and register it with a matching Intent filter to allow any other application to invoke a weather report through an Intent. Defining and publishing an Intent in this way allows other applications to easily use your application. When the WeatherReporter application is complete, the main screen will look like figure 4.4.
4.2.1 Offering a custom URI
Let’s look more deeply into how to define Intent filters in XML. The manifest for WeatherReporter is shown in the following listing.
Table 4.2 Common Android application Intent, action, and URI combinations
Action URI Description
Intent.ACTION_CALL tel:phone_number Opens the phone application and calls the specified number Intent.ACTION_DIAL tel:phone_number Opens the phone application and
dials (but doesn’t call) the speci- fied number
Intent.ACTION_DIAL voicemail: Opens the phone application and dials (but doesn’t call) the voice- mail number
Intent.ACTION_VIEW geo:latitude,longitude Opens the maps application to the specified latitude and longitude
Intent.ACTION_VIEW geo:0,0?q=street+address Opens the maps application to the specified address
Intent.ACTION_VIEW http://web_address Opens the browser application to the specified URL
Intent.ACTION_VIEW https://web_address Opens the browser application to the specified secure URL
Figure 4.4 The WeatherReporter application, showing the weather forecast for the current location
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.msi.manning.weather">
<application android:icon="@drawable/weather_sun_clouds_120"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black"
android:allowClearUserData="true">
<activity android:name="ReportViewSavedLocations"
android:label="@string/app_name_view_saved_locations" />
<activity android:name="ReportSpecifyLocation"
android:label=
"@string/app_name_specify_location" />
<activity android:name="ReportViewDetail"
android:label="@string/app_name_view_detail">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="weather"
android:host="com.msi.manning" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="weather"
android:host="com.msi.manning" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name=
"android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=
".service.WeatherAlertServiceReceiver">
<intent-filter>
<action android:name=
"android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".service.WeatherAlertService" />
</application>
<uses-permission
android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name=
"android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name=
"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
Listing 4.3 Android manifest file for the WeatherReporter application
B
Define activities
Define receiver
C
Define Service
D
Include necessary E
permissions
In the WeatherReporter manifest, we define three activities B. The first two don’t include an <intent-filter>, so they can only be explicitly invoked from within this application. The ReportViewDetail Activity has multiple <intent-filter> tags defined for it, including one denoting it as the MAIN LAUNCHER and one with the weather://com.msi.manning scheme and authority. Our application supports this custom URI to provide weather access.
You can use any combination of scheme, authority, and path, as shown in listing 4.3, or you can use an explicit MIME type. You’ll find out more about MIME types and how they’re processed in chapter 5, where we’ll look at data sources and use an Android concept known as a ContentProvider.
After we define these activities, we use the <receiver> element in the manifest file to refer to a BroadcastReceiver class C. We’ll examine BroadcastReceiver more closely in section 4.3, but for now know that an <intent-filter> associates this receiver with an Intent—in this case, for the BOOT_COMPLETED action. This filter tells the platform to invoke the WeatherAlertServiceReceiver class after it completes the bootup sequence.
We also define a ServiceD. You’ll see how this Service is built, and how it polls for severe weather alerts in the background, in section 4.3. Finally, our manifest includes a set of required permissions E.
4.2.2 Inspecting a custom URI
With the foundation for our sample application in place via the manifest, Android will launch WeatherReporter when it encounters a request that uses our custom URI. As usual, it’ll invoke the onStart() method of the main Activity WeatherReporter will use. The following listing shows our implementation, where we parse data from the URI and use it to display a weather report.
@Override
public void onStart() { super.onStart();
dbHelper = new DBHelper(this);
deviceZip = WeatherAlertService.deviceLocationZIP;
if ((getIntent().getData() != null)
&& (getIntent().getData().getEncodedQuery() != null)
&& (getIntent().getData().getEncodedQuery().length() > 8)) { String queryString =
getIntent().getData().getEncodedQuery();
reportZip = queryString.substring(4, 9);
useDeviceLocation = false;
} else {
reportZip = deviceZip;
useDeviceLocation = true;
}
savedLocation = dbHelper.get(reportZip);
deviceAlertEnabledLocation =
dbHelper.get(DBHelper.DEVICE_ALERT_ENABLED_ZIP);
Listing 4.4 onStart() method of the ReportViewDetailActivity
Create database helper
B Get device
location postal code
C
if (useDeviceLocation) {
currentCheck.setText(R.string.view_checkbox_current);
if (deviceAlertEnabledLocation != null) { currentCheck.setChecked(true);
} else {
currentCheck.setChecked(false);
} } else {
currentCheck.setText(R.string.view_checkbox_specific);
if (savedLocation != null) {
if (savedLocation.alertenabled == 1) { currentCheck.setChecked(true);
} else {
currentCheck.setChecked(false);
} } }
loadReport(reportZip);
}
You can get the complete ReportViewDetailActivity from the source code down- load for this chapter. In the onStart() method shown in this listing, we focus on pars- ing data from the URI passed in as part of the Intent that invokes the Activity.
First, we establish a database helper object B. This object will be used to query a local SQLite database that stores user-specified location data. We’ll show more about how data is handled, and the details of this helper class, in chapter 5.
In this method, we also obtain the postal code of the current device location from a LocationManager in the WeatherAlertService class C. We want to use the location of the device as the default weather report location. As the user travels with the phone, this location will automatically update. We’ll cover location and Location- Manager in chapter 11.
After obtaining the device location, we move on to the key aspect of obtaining URI data from an Intent. We check whether our Intent provided specific data; if so, we parse the URI passed in to obtain the queryString and embedded postal code to use for the user’s specified location. If this location is present, we use it; if not, we default to the device location postal code.
After determining the postal code to use, we set the status of the check box that indicates whether to enable alerts D. We have two kinds of alerts: one for the device location and another for the user’s specified saved locations.
Finally, we call the loadReport() method, which makes the call to the Yahoo!
Weather API to obtain data; then we use a Handler to send a Message to update the needed UIView elements.
Remember that this Activity registered in the manifest to receive weather://
com.msi.manningIntents. Any application can invoke this Activity without knowing any details other than the URI. This separation of responsibilities enables late binding.
After invocation, we check the URI to see what our caller wanted.
You’ve now seen the manifest and pertinent details of the main Activity class for the WeatherReporter application we’ll build in the next few sections. We’ve also
D
Set status of alert- enabled check box
discussed how Intent and IntentFilter classes work together to wire up calls between components. Next, we’ll look at some of the built-in Android applications that accept external Intent requests. These requests enable you to launch activities by simply passing in the correct URI.