Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
2,54 MB
Nội dung
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 property, as shown in the following snippet: notification.sound = ringURI; To use your own custom audio, push the file onto your device, or include it as a raw resource, as described in Chapter 11. The following snippet can be added to the announceNewQuake method within the Earthquake Service from the earlier example. It adds an audio component to the earthquake Notification, ringing the default notification ringtone if a significant earthquake (one with a magnitude greater than 6) occurs. if (quake.getMagnitude() > 6) { Uri ringURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); newEarthquakeNotification.sound = ringURI; } Vibrating the Phone You can use the phone’s vibration function to execute a vibration pattern specific to your Notification. Android lets you control the pattern of a vibration; you can use vibration to convey information as well as to get the user’s attention. 318 ❘ CHAPTER 9 WORKING IN THE BACKGROUND To set a vibration pattern, assign an array of longs to the Notification’s vibrate property. Construct the array so that values representing the length of time (in milliseconds) to vibrate alternate with values representing the length of time to pause. Before you can use vibration in your application, you need to be granted permission. Add a uses-permission 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. The ledARGB property can be used to set the LED’s color, while the ledOffMS and ledOnMS properties let you set the frequency and pattern of the flashing LED. You can turn the LED on by setting the ledOnMS property to 1 and the ledOffMS property to 0 , or turn it off by setting both properties to 0 . Introducing Notifications ❘ 319 Once you have configured the LED settings you must also add the FLAG_SHOW_LIGHTS flag to the Noti- fication’s flags property. The following code snippet shows how to turn on the red device LED: notification.ledARGB = Color.RED; notification.ledOffMS = 0; 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. notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT; Ongoing events are separated from regular Notifications within the extended Notification drawer, as shown in Figure 9-6. Insistent Notifications repeat their audio, vibration, and light settings continuously until canceled. These Notifications are typically used for events that require immediate and timely attention — such as an incoming call or the ringing of an alarm clock. 320 ❘ CHAPTER 9 WORKING IN THE BACKGROUND The following code snippet shows how to set a Notification as insistent: notification.flags = notification.flags | Notification.FLAG_INSISTENT; USING ALARMS FIGURE 9-6 Alarms are an application-independent means of firing Intents at predetermined times and intervals. Alarms are set outside the scope of your applications, so they can be used to trigger application events or actions even after your application has been closed. They can be particularly powerful in combination with Broadcast Receivers, enabling you to set Alarms that fire broadcast Intents, start Services, or even open Activities, without the applications’ needing to be open or running until they’re required. As such, Alarms are an extremely effective means of reducing your application’s resource requirements, particularly when running in the background, by enabling you to stop Services and eliminate timers while maintaining the ability to perform scheduled actions. 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 method represents a specific time or an elapsed wait: ➤ RTC_WAKEUP Wake the device from sleep to fire the Pending Intent at the clock time specified. ➤ RTC Fire the Pending Intent at the time specified, but do not wake the device. ➤ ELAPSED_REALTIME Fire the Pending Intent based on the amount of time elapsed since the device was booted, but do not wake the device. The elapsed time includes any period of time the device was asleep. Note that the time elapsed is calculated based on when the device was last booted. ➤ ELAPSED_REALTIME_WAKEUP After a specified length of time has passed since device boot, wake the device from sleep and fire the Pending Intent. The Alarm-creation process is shown in Listing 9-25. LISTING 9-25: Creating an Alarm int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP; long timeOrLengthofWait = 10000; String ALARM_ACTION = "ALARM_ACTION"; Intent intentToFire = new Intent(ALARM_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0); alarms.set(alarmType, timeOrLengthofWait, pendingIntent); When the Alarm goes off, the Pending Intent you specified will be broadcast. Setting a second Alarm using the same Pending Intent replaces the preexisting Alarm. To cancel an Alarm, call cancel 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); Setting Repeating Alarms The Alarm Manager lets you set repeating alarms for situations requiring regularly scheduled events. Repeating alarms work in exactly the same way as the one-shot alarms described earlier, but will continue to trigger at a specified interval until canceled. Because alarms are set outside your Application context they are perfect for scheduling regular updates or data lookups so that they don’t require a Service to be constantly running in the background. To set a repeating alarm, use the setRepeating or setInexactRepeating method on the Alarm Man- ager, as shown in Listing 9-27. Both support an alarm type, an initial trigger time, and a Pending Intent to fire when the alarm triggers (as described in the previous section). Use setRepeating when you need fine-grained control over the exact interval of your repeating alarm. The interval value passed in to this method lets you specify an exact interval for your alarm, down to the millisecond. The setInexactRepeating method is a powerful technique for reducing the battery drain associated with waking the device on a regular schedule to perform updates. Rather than specifying an exact interval, this method accepts one of the following Alarm Manager constants: ➤ INTERVAL_FIFTEEN_MINUTES ➤ INTERVAL_HALF_HOUR ➤ INTERVAL_HOUR ➤ 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 . package com.paad.earthquake; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class EarthquakeAlarmReceiver extends BroadcastReceiver { } 2. Override the onReceive method to explicitly start the EarthquakeService . @Override public void onReceive(Context context, Intent intent) { Intent startIntent = new Intent(context, EarthquakeService.class); context.startService(startIntent); } 3. Create a new public static String to define the action that will be used to trigger the Broadcast Receiver. public static final String ACTION_REFRESH_EARTHQUAKE_ALARM = "com.paad.earthquake.ACTION_REFRESH_EARTHQUAKE_ALARM"; 4. Add the new EarthquakeAlarmReceiver to the manifest, including an <intent-filter> tag that listens for the action defined in Step 3. 324 ❘ CHAPTER 9 WORKING IN THE BACKGROUND <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; Intent intentToFire = new Intent(ALARM_ACTION); alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0); } 6. Modify the onStartCommand method to set a repeating Alarm rather than use a Timer to schedule the refreshes (if automated updates are enabled). Setting a new Intent with the same action will automatically cancel any previous Alarms. Take this opportunity to modify the return result. Rather than setting the Service to sticky, return Service.START_NOT_STICKY . In Step 7 you will stop the Service when the background refresh is complete; the use of alarms guarantees that another refresh will occur at the spec- ified update frequency, so there’s no need for the system to restart the Service if it is killed mid-refresh. @Override public int onStartCommand(Intent intent, int flags, int startId) { SharedPreferences prefs = getSharedPreferences(Preferences.USER_PREFERENCE, Activity.MODE_PRIVATE); int minMagIndex = prefs.getInt(Preferences.PREF_MIN_MAG, 0); if (minMagIndex < 0) minMagIndex = 0; Summary ❘ 325 int freqIndex = prefs.getInt(Preferences.PREF_UPDATE_FREQ, 0); if (freqIndex < 0) freqIndex = 0; 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- maturely terminated. @Override protected void onPostExecute(Void result) { stopSelf(); } 8. Remove the updateTimer instance variable and the Timer Task instance doRefresh . All code snippets in this example are part of the Chapter 9 Earthquake 4 project, available for download at Wrox.com. SUMMARY Background Services are one of the most compelling reasons to develop applications on the Android platform, but using them introduces several complexities to your applications. In this chapter you learned how to use these invisible application components to perform processing while your applica- tions are running in the background. 326 ❘ CHAPTER 9 WORKING IN THE BACKGROUND You were introduced to Toasts, transient message boxes that let you display information to users without stealing focus or interrupting their workflow. YouusedtheNotificationManagertosendalertstoyourusersfromwithinServicesandActivities, using customized LEDs, vibration patterns, and audio files to convey detailed event information. You learned how (and when) to create ongoing Notifications and how to customize their extended status window Layouts. Using Alarms, you were able to schedule one-off and repeating events that broadcast Intents or started Services. This chapter also demonstrated how to: ➤ Bind a Service to an Activity to make use of a more detailed, structured interface than the simple Intent extras. ➤ 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. [...]... xmlns :android= "http://schemas .android. com/apk/res /android" android: orientation="horizontal" android: layout_width="fill_parent" android: layout_height="fill_parent" android: padding="10sp"> Creating App Widgets ❘ 331 ... package name, as shown here: CREATING AN EARTHQUAKE... 10-12: Setting the App Widget minimum update rate 338 ❘ CHAPTER 10 INVADING THE PHONE-TOP Setting this value will cause... Intent broacasts within App Widgets ... Filter for the android. appwidget.action.APPWIDGET_UPDATE action ➤ A reference to the metadata XML resource that describes your widget LISTING 10-4: App Widget manifest node 332 ❘ CHAPTER 10 INVADING THE PHONE-TOP Creating Your Widget Intent Receiver... you created in Step 1 for the initial layout 4 Add your widget to the application manifest, including a reference to... manifest entry: 346 ❘ CHAPTER 10 INVADING THE PHONE-TOP . encoding="utf-8"?> <LinearLayout xmlns :android= "http://schemas .android. com/apk/res /android& quot; android: orientation="horizontal" android: layout_width="fill_parent" android: layout_height="fill_parent" android: padding="10sp"> Creating. encoding="utf-8"?> <appwidget-provider xmlns :android= "http://schemas .android. com/apk/res /android& quot; android: initialLayout="@layout/my_widget_layout" android: minWidth="146dp" android: minHeight="146dp" android: label="My. Widgets ❘ 331 <ImageView android: id="@+id/widget_image" android: layout_width="wrap_content" android: layout_height="wrap_content" android: src="@drawable/icon" /> <TextView android: id="@+id/widget_text" android: layout_width="fill_parent" android: layout_height="fill_parent" android: text="Text