Android SDK (phần 8) - Pdf 16

Introducing Notifications

317
As each enhancement is described, you will be provided with a code snippet that can be added to the
Earthquake example to provide user feedback on the severity of each earthquake as it’s detected.
To use the Notification techniques described here without also displaying the status
bar icon, simply cancel the Notification directly after triggering it. This stops the
icon from displaying but doesn’t interrupt the other effects.
Using the Defaults
The simplest and most consistent way to add sound, light, and vibration to your Notifications is to use
the current user default settings. Using the
defaults
property you can combine:

Notification.DEFAULT_LIGHTS

Notification.DEFAULT_SOUND

Notification.DEFAULT_VIBRATE
The following code snippet assigns the default sound and vibration settings to a Notification:
notification.defaults = Notification.DEFAULT_SOUND |
Notification.DEFAULT_VIBRATE;
If you want to use all the default values you can use the
Notification.DEFAULT_ALL
constant.
Making Sounds
Using an audio alert to notify the user of a device event (like incoming calls) is a technique that predates
the mobile, and has stood the test of time. Most native phone events, from incoming calls to new
messages and low battery, are announced by audible ringtones.
Android lets you play any audio file on the phone as a Notification by assigning a location URI to the
sound

to your application to request access to the device vibration using the following code
snippet:
<uses-permission android:name="android.permission.VIBRATE"/>
The following example shows how to modify a Notification to vibrate in a repeating pattern of one
second on and one second off, for five seconds total.
long[] vibrate = new long[] { 1000, 1000, 1000, 1000, 1000 };
notification.vibrate = vibrate;
You can take advantage of this fine-grained control to pass information to your users. In the following
update to the
announceNewQuake
method, the phone is set to vibrate in a pattern based on the power
of the quake. Earthquakes are measured on an exponential scale, so you’ll use the same scale when
creating the vibration pattern.
For a barely perceptible magnitude 1 quake the phone will vibrate for a fraction of a second; for one
of magnitude 10, an earthquake that would split the earth in two, your users will have a head start on
the Apocalypse when their devices vibrate for a full 20 seconds. Most significant quakes fall between
3 and 7 on the Richter scale, or a more reasonable 200-millisecond-to-four-second range of vibration
duration.
double vibrateLength = 100*Math.exp(0.53*quake.getMagnitude());
long[] vibrate = new long[] {100, 100, (long)vibrateLength };
newEarthquakeNotification.vibrate = vibrate;
The current Android Emulator does not visually or audibly indicate that the device
is vibrating.
Flashing the Lights
Notifications also include properties to configure the color and flash frequency of the device’s LED.
Each device may have different limitations with regard to control over the LED.
Where the color you specify is not available, as close an approximation as possible
will be used. When using LEDs to convey information to the user keep this
limitation in mind and avoid making it the only way such information is made
available.

notification.ledOnMS = 1;
notification.flags = notification.flags | Notification.FLAG_SHOW_LIGHTS;
Controlling the color and flash frequency gives you another opportunity to pass additional information
to users.
In the earthquake-monitoring example you can help your users perceive the nuances of an exponential
scale by also using the device’s LED to help convey the magnitude. In the following snippet the color of
the LED depends on the size of the quake, and the frequency of the flashing is inversely related to the
power of the quake:
int color;
if (quake.getMagnitude() < 5.4)
color = Color.GREEN;
else if (quake.getMagnitude() < 6)
color = Color.YELLOW;
else
color = Color.RED;
newEarthquakeNotification.ledARGB = color;
newEarthquakeNotification.ledOffMS = (int)vibrateLength;
newEarthquakeNotification.ledOnMS = (int)vibrateLength;
newEarthquakeNotification.flags = newEarthquakeNotification.flags |
Notification.FLAG_SHOW_LIGHTS;
The current Android Emulator does not visually illustrate the LEDs.
Ongoing and Insistent Notifications
You can configure Notifications as ongoing and/or insistent by setting the
FLAG_INSISTENT
and
FLAG_ONGOING_EVENT
flags.
Notifications flagged as ongoing, as in the following snippet, are used to represent events that are
currently in progress (such as a download in progress or music playing in the background). An ongoing
Notification is a requirement for a foreground Service, as described earlier in this chapter.

For example, you can use Alarms to implement an alarm
clock application, perform regular network lookups, or
schedule time-consuming or cost-bound operations at ‘‘off-
peak’’ times.
For timing operations that occur only during the lifetime of your applications,
using the
Handler
class in combination with Timers and Threads is a better
approach than using Alarms, as this allows Android better control over system
resources. Alarms provide a mechanism to reduce the lifetime of your applications
by moving scheduled events out of their control.
Alarms in Android remain active while the device is in sleep mode and can optionally be set to wake
the device; however, all Alarms are canceled whenever the device is rebooted.
Alarm operations are handled through the
AlarmManager
, a system Service accessed via
getSystemService
, as shown here:
AlarmManager alarms =
(AlarmManager)getSystemService(Context.ALARM_SERVICE);
To create a new one-shot Alarm, use the
set
method and specify an alarm type, a trigger time, and a
Pending Intent to fire when the Alarm triggers. If the trigger time you specify for the Alarm occurs in
the past, the Alarm will be triggered immediately.
Using Alarms

321
There are four alarm types available. Your selection will determine if the time value passed in the
set

on the Alarm Manager, passing in the Pending Intent you no longer
wish to trigger, as shown in the following snippet:
alarms.cancel(pendingIntent);
In Listing 9-26, two Alarms are set and the first one is subsequently canceled. The first is explicitly set
to a specific time and will wake up the device in order to fire. The second is set to fire 30 minutes after
the device is started, but will not wake the device if it’s sleeping.
LISTING 9-26: Setting and canceling an Alarm
AlarmManager alarms =
(AlarmManager)getSystemService(Context.ALARM_SERVICE);
String MY_RTC_ALARM = "MY_RTC_ALARM";
String ALARM_ACTION = "MY_ELAPSED_ALARM";
PendingIntent rtcIntent =
PendingIntent.getBroadcast(this, 0,
new Intent(MY_RTC_ALARM), 1);
continues
322

CHAPTER 9 WORKING IN THE BACKGROUND
LISTING 9-26 (continued)
PendingIntent elapsedIntent =
PendingIntent.getBroadcast(this, 0,
new Intent(ALARM_ACTION), 1);
// Wakeup and fire intent in 5 hours.
Date t = new Date();
t.setTime(java.lang.System.currentTimeMillis() + 60*1000*5);
alarms.set(AlarmManager.RTC_WAKEUP, t.getTime(), rtcIntent);
// Fire intent in 30 mins if already awake.
alarms.set(AlarmManager.ELAPSED_REALTIME, 30*60*1000, elapsedIntent);
// Cancel the first alarm.
alarms.cancel(rtcIntent);

INTERVAL_HALF_DAY

INTERVAL_DAY
At run time Android will synchronize multiple inexact repeating alarms and trigger them simul-
taneously. This prevents each application from separately waking the device in a similar but
non-overlapping period to perform an update or poll a network data source. By synchronizing these
alarms the system is able to limit the impact of regularly repeating events on battery resources.
Using Alarms

323
LISTING 9-27: Setting repeating alarms
// Fire an intent exactly every hour if already awake.
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
60*60*1000, 60*60*1000, elapsedIntent);
// Wakeup and fire an alarm about every hour
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
60*60*1000, AlarmManager.INTERVAL_DAY,
elapsedIntent);
The battery impact of setting regularly repeating alarms can be significant. It is
good practice to limit your alarm frequency to the slowest acceptable rate, wake
the device only if necessary, and use the inexact repeating alarm whenever possible.
Using Repeating Alarms to Update Earthquakes
In this final modification to the Earthquake example you’ll use Alarms to replace the Timer currently
used to schedule Earthquake network refreshes.
One of the most significant advantages of this approach is that it allows the Service to stop itself when
it has completed a refresh, freeing significant system resources.
1. Start by creating a new
EarthquakeAlarmReceiver
class that extends
BroadcastReceiver

<receiver android:name=".EarthquakeAlarmReceiver">
<intent-filter>
<action
android:name="com.paad.earthquake.ACTION_REFRESH_EARTHQUAKE_ALARM"
/>
</intent-filter>
</receiver>
5. Within the EarthquakeService, update the
onCreate
method to get a reference to the
AlarmManager
, and create a new
PendingIntent
that will be fired when the Alarm goes off.
You can also remove the
timerTask
initialization.
AlarmManager alarms;
PendingIntent alarmIntent;
@Override
public void onCreate() {
int icon = R.drawable.icon;
String tickerText = "New Earthquake Detected";
long when = System.currentTimeMillis();
newEarthquakeNotification =
new Notification(icon, tickerText, when);
alarms = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
String ALARM_ACTION;
ALARM_ACTION =
EarthquakeAlarmReceiver.ACTION_REFRESH_EARTHQUAKE_ALARM;

boolean autoUpdate =
prefs.getBoolean(Preferences.PREF_AUTO_UPDATE, false);
Resources r = getResources();
int[] minMagValues = r.getIntArray(R.array.magnitude);
int[] freqValues = r.getIntArray(R.array.update_freq_values);
minimumMagnitude = minMagValues[minMagIndex];
int updateFreq = freqValues[freqIndex];
if (autoUpdate) {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long timeToRefresh = SystemClock.elapsedRealtime() +
updateFreq*60*1000;
alarms.setRepeating(alarmType, timeToRefresh,
updateFreq*60*1000, alarmIntent);
}
else
alarms.cancel(alarmIntent);
refreshEarthquakes();
return Service.START_NOT_STICKY;
};
7. In the
EarthquakeLookupTask
, fill in the
onPostExecute
stub to call
stopSelf
when the back-
ground refresh has completed. Because the asynchronous lookup task is called only from
within
onStartCommand
, and only if not already running, this ensures the Service is never pre-

➤ Ensure that your applications remain responsive by moving time-consuming processing like
network lookups onto worker threads using
AsyncTask
.
➤ Use handlers to synchronize child threads with the main application GUI when performing
operations using visual controls and Toasts.
In Chapter 10 you’ll learn how to integrate your application into the home screen. Starting with
creating dynamic, interactive home screen widgets you’ll move on to creating Live Folders and Live
Wallpapers. Finally you’ll be introduced to the Quick Search Box, and learn how to surface your appli-
cation’s search results to the home screen search widget.
10
Invading the Phone-Top
WHAT’S IN THIS CHAPTER?
➤ Creating home screen Widgets
➤ Implementing Live Folders
➤ Adding search to your applications
➤ Surfacing search results to the Quick Search Box
➤ Creating Live Wallpaper
Widgets, Live Folders, Live Wallpaper, and the Quick Search Box let you own a piece of the
user’s home screen, providing either a window to your application or a stand-alone source of
information directly on the home screen. They’re an exciting innovation for users and develop-
ers, providing the following:
➤ Users get instant access to interesting information without needing to open an application.
➤ Developers get an entry point to their applications directly from the home screen.
A useful widget, Live Folder, or dynamic wallpaper decreases the chance that an application
will be uninstalled, and increases the likelihood of its being used.
With such power comes responsibility. Widgets run constantly as subprocesses of the home-
screen process. You need to be particularly careful when creating widgets to ensure they remain
responsive and don’t drain system resources.
This chapter demonstrates how to create and use App Widgets, Live Folders, and Live Wallpa-

available on Android devices: the search box, power control,
news and weather, and media player.
To add a widget to the home screen, long-press a piece of empty space and select
Widgets. You will be presented with a list of available widgets. Once you’ve added
one you can move it by long-pressing it and dragging it around the screen. Remove
widgets by dragging them into the garbage can icon at the bottom of the screen.
Widgets embedded into the home screen are hosted within the home screen’s process. They will wake
the device based on their update rates to ensure each widget is up to date when it is visible. As a devel-
oper, you need to take extra care when creating your widgets to ensure that the update rate is as low as
possible, and that the code executed within the update method is lightweight.
The following sections show how to create widgets and describe some best practices for performing
updates and adding interaction.
CREATING APP WIDGETS
App Widgets are implemented as
IntentReceivers
.Theyuse
RemoteViews
to update a view hierarchy
hosted within another application process; in most cases that host process is the home screen.
Creating App Widgets

329
To create a widget for your application you need to create three components:
1. A layout resource that defines the UI for the widget
2. An XML definition file that describes the metadata associated with the widget
3. An Intent Receiver that defines and controls the widget
You can create as many widgets as you want for a single application, or have an application that
consists of a single widget. Each widget can use the same size, layout, refresh rate, and update logic, or
they can all use different ones. In many cases it can be useful to offer multiple versions of your widgets
in different sizes.

image resources used to create the native Android widgets shipped with Google Experience devices.
330

CHAPTER 10 INVADING THE PHONE-TOP
App Widgets fully support transparent backgrounds and allow the use of NinePatches and partially
transparent PNG-drawable resources.
Supported Widget Views and Layouts
Because of security and performance considerations there are several restrictions on the layouts and
Views available to you when you’re constructing your widget UI.
In general, the following Views are unavailable for App Widget layouts and will result in a null pointer
error (NPE) if used:
➤ All custom Views
➤ Descendents of the allowed Views

EditText
Currently, the layouts available are limited to:

FrameLayout

LinearLayout

RelativeLayout
The Views they contain are restricted to:

AnalogClock

Button

Chronometer


<TextView
android:id="@+id/widget_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Text Goes Here"
/>
</LinearLayout>
Defining Your Widget Settings
Widget definition resources are stored as XML in the res/xml folder of your project. The
appwidget-provider
tag lets you describe the widget metadata that defines the size, layout, and update
rate for your widget using the following attributes:

initialLayout
The layout resource to use in constructing the widget’s user interface.

minWidth / minHeight
Respectively, the minimum width and minimum height of the wid-
get, as described in the previous section.

label
The title used by your widget in the widget-picker.

updatePeriodMillis
The minimum period between widget updates in milliseconds.
Android will wake the device to update your widget at this rate, so you should specify at least
an hour. Ideally your widget shouldn’t use this update technique more than once or twice
daily. More details on this and other update techniques are provided later in this chapter.

configure

IntentReceiver
class directly and
interpreting those broadcast Intents by overriding the
onReceive
method.
The
AppWidgetProvider
class provides a simplified alternative by encapsulating the Intent processing
and presenting you with event handlers for the update, delete, enable, and disable events.
Listing 10-3 shows a simple widget implementation that extends
AppWidgetProvider
and overrides the
onUpdate
method:
LISTING 10-3: App Widget implementation
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
public class MyAppWidget extends AppWidgetProvider {
@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Update the Widget UI.
}
}
Widgets are added to the application manifest much like other Intent Receivers. However, to spec-
ify an Intent Receiver as an App Widget you need to add two additional tags to its manifest node
(Listing 10-4).
➤ An Intent Filter for the

image values, and add click listeners.
This section describes how to create new Remote Views from within and without the
onUpdate
method
of an App Widget Provider. It also demonstrates how to use Remote Views to update widget UI and
add interactivity to your widgets.
Creating Remote Views and Using the App Widget Manager to Apply Them
To create a new Remote Views object you must pass the name of the calling application package, and
the layout resource you plan to manipulate, into the constructor, as shown in Listing 10-5. Later in
this section you’ll learn how to use this Remote Views object to update the Views and layout of your
widget.
LISTING 10-5: Using Remote Views
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.my_remote_layout);
To use Remote Views on widgets, call the static
getInstance
method to return an instance of the App
Widget Manager and use it to find identifiers for each instance of a particular widget class, as in this
continuation of Listing 10-5:
// Get the App Widget Manager.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
// Retrieve the identifiers for each instance of your chosen widget.
ComponentName thisWidget = new ComponentName(context, MyAppWidget.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
When you’ve finished making changes to a Remote Views object, apply those modifications to one or
more widgets by calling the
updateAppWidget
method on the App Widget Manager, passing in either
an individual widget ID or an array of identifiers:
appWidgetManager.updateAppWidget(appWidgetIds, views);

public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
int appWidgetId = appWidgetIds[i];
// Create a Remove View
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.my_widget_layout);
// TODO Update the UI.
// Notify the App Widget Manager to update the widget using
// the modified remote view.
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
Creating App Widgets

335
Using Remote Views to Modify UI
Remote Views expose a variety of methods designed to provide access to the properties and methods
available on Views in order for you to change their appearance.
The most versatile of these is a series of methods that lets you execute a target method name on a
remotely hosted View. These methods support the passing of single-value parameters. Supported
method signatures include a parameter for each primitive type, including Boolean, integer, and float,
plus strings, bitmaps, and URI parameters.
Listing 10-8 shows examples of some of the method signatures supported.
LISTING 10-8: Using a Remote View to modify App Widget UI
// Set the image level for an ImageView.
views.setInt(R.id.widget_image_view, "setImageLevel", 2);
// Show the cursor of a TextView.

CHAPTER 10 INVADING THE PHONE-TOP
Because they run within the home-screen process, the widgets themselves inherit its permissions. As a
result of these security implications widget interactivity is carefully controlled.
Widget interaction is generally limited to two possibilities:
➤ Adding a click listener to one or more views within the layout
➤ Changing the UI based on selection changes
It’s notable that there is no supported technique for entering text directly into an App Widget.
If you need text input from your widget, best practice is to add a click listener that displays an Activity
to accept the user data when a portion of the widget is clicked.
One popular alternative is to use Image Views designed to look like Edit Text
controls. By means of Selection State Drawables they can appear to gain focus.
When the Image View is clicked, a partially transparent Activity is launched to
accept the user input.
Using a Click Listener
The most powerful technique for adding interactivity to your widget is through the use of the
setOnClickPendingIntent
method on a Remote Views object.
This lets you specify a Pending Intent that will be fired when the user clicks on the specified widget
View. Pending Intents (described in more detail in Chapter 5) can contain Intents used to start Activities
or Services or broadcast Intents.
Listing 10-10 demonstrates a broadcast Intent assigned to a Text View element within a widget
layout:
LISTING 10-10: Adding a Click Listener to an App Widget
Intent intent = new Intent("com.paad.ACTION_WIDGET_CLICK");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
views.setOnClickPendingIntent(R.id.my_text_view, pendingIntent);
Using this technique you can add click handlers to one or more of the Views used within your widget,
which means you can add support for multiple actions.
For example, the standard media player widget assigns different broadcast Intents to several buttons,
providing playback control through the play, pause, and next buttons.

The following sections describe several techniques for managing your widget refresh intervals.
Using the Minimum Update Rate
The simplest, but potentially most resource-intensive, technique is to set the minimum update rate for
a widget in the XML definition file, as shown in Listing 10-12, where the widget is updated once every
hour:
LISTING 10-12: Setting the App Widget minimum update rate
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/my_widget_layout"
android:minWidth="146dp"
android:minHeight="146dp"
android:label="My App Widget"
android:updatePeriodMillis="3600000"
/>
338

CHAPTER 10 INVADING THE PHONE-TOP
Setting this value will cause the device to broadcast an Intent requesting an update of your widget at
the rate specified.
The host device will wake up to complete these updates, meaning they are
completed even when the device is on standby. This has the potential to be a
significant resource drain, so it’s very important to consider the implications of
your update rate.
This technique should be used to define the absolute minimum rate at which your widget must be
updated to remain useful. Generally the minimum expected update rate should be at least an hour,
ideally not more than once or twice a day.
If your device requires more frequent updates, consider using one of the techniques described in the
following sections to dynamically perform updates using either an event/Intent-driven model or a more
efficient scheduled model using Alarms.

@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (FORCE_WIDGET_UPDATE.equals(intent.getAction())) {
// TODO Update widget UI.
}
}
To trigger an update of your widget at any point in your application, you can broadcast an Intent using
this action:
context.sendBroadcast(new Intent(FORCE_WIDGET_UPDATE));
This technique is particularly useful for reacting to system, user, or application events — like a data
refresh, or a user action such as clicking buttons on the widget itself. You can also register for system
event broadcasts such as changes to network connectivity, battery level, or screen brightness.
Using Alarms
Alarms provide a middle-ground alternative to the polling and Intent-based techniques described so
far.
Alarms, covered in detail in Chapter 9, provide a flexible way to schedule regular events within your
application. Using alarms you can poll at regular intervals, using an Intent to trigger your updates.
Using Alarms to refresh your widgets is similar to using the Intent-driven model described earlier. Add
a new Intent Filter to the manifest entry for your widget and override its
onReceive
method to identify
the Intent that triggered it. Within your application, use the Alarm Manager to create an Alarm that
fires an Intent with the registered action.
Alarms have an advantage over the minimum refresh rate, thanks to their flexibility.
Like the widgets’ refresh rate, Alarms also have the ability to wake the device when they fire — making
it equally important to take care to minimize battery use.
Alternatively, by using the
RTC
or

In some cases an App Widget will be significantly more useful if the user is given the opportunity to
customize the data it displays and how the data is displayed. This is particularly important given that
multiple instances of the same widget can be added to the home screen.
An App Widget configuration Activity is an Activity that is launched immediately when a widget is
added to the home screen. It can be any Activity within your application, provided it has an Intent
Filter for the
APPWIDGET_CONFIGURE
action, as shown here:
<activity android:name=". MyWidgetConfigurationActivity">
<intent-filter>
<action android:name="android.apwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
It must also return a result Intent that includes an extra that describes the App Widget ID of the wid-
get it is configuring using the
EXTRA_APPWIDGET_ID
constant. This extra is included in the Intent that
launches the Activity.
Intent result = new Intent();
result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, result);
finish();
Creating an Earthquake Widget

341
To assign a completed configuration Activity to a widget you must add it to the widget settings file us-
ing the configure tag. The activity must be specified by its fully qualified package name, as shown here:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#F111"
android:padding="5sp">
<TextView
android:id="@+id/widget_magnitude"
android:layout_width="wrap_content"


Nhờ tải bản gốc
Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status