From 604e4ed4814169f85312a1ca788b81c819b9f049 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 13 Dec 2011 18:24:34 -0800 Subject: doc change: Android U lessons for audio and battery Change-Id: I98d83f8458e3ad62be28d09b85fe099916d1b73d --- docs/html/training/managing-audio/audio-focus.jd | 183 +++++++++++++++++++++ docs/html/training/managing-audio/audio-output.jd | 88 ++++++++++ docs/html/training/managing-audio/index.jd | 62 +++++++ .../training/managing-audio/volume-playback.jd | 156 ++++++++++++++++++ .../monitoring-device-state/battery-monitoring.jd | 156 ++++++++++++++++++ .../connectivity-monitoring.jd | 90 ++++++++++ .../monitoring-device-state/docking-monitoring.jd | 90 ++++++++++ .../html/training/monitoring-device-state/index.jd | 63 +++++++ .../monitoring-device-state/manifest-receivers.jd | 64 +++++++ 9 files changed, 952 insertions(+) create mode 100644 docs/html/training/managing-audio/audio-focus.jd create mode 100644 docs/html/training/managing-audio/audio-output.jd create mode 100644 docs/html/training/managing-audio/index.jd create mode 100644 docs/html/training/managing-audio/volume-playback.jd create mode 100644 docs/html/training/monitoring-device-state/battery-monitoring.jd create mode 100644 docs/html/training/monitoring-device-state/connectivity-monitoring.jd create mode 100644 docs/html/training/monitoring-device-state/docking-monitoring.jd create mode 100644 docs/html/training/monitoring-device-state/index.jd create mode 100644 docs/html/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html/training/managing-audio/audio-focus.jd b/docs/html/training/managing-audio/audio-focus.jd new file mode 100644 index 000000000000..07a4465f2bed --- /dev/null +++ b/docs/html/training/managing-audio/audio-focus.jd @@ -0,0 +1,183 @@ +page.title=Managing Audio Focus +parent.title=Managing Audio Playback and Focus +parent.link=index.html + +trainingnavtop=true +previous.title=Controlling Your App's Volume and Playback +previous.link=volume-playback.html +next.title=Dealing with Audio Output Hardware +next.link=audio-output.html + +@jd:body + + +
+
+ +

This lesson teaches you to

+
    +
  1. Request the Audio Focus
  2. +
  3. Handle the Loss of Audio Focus
  4. +
  5. Duck!
  6. +
+ + +

You should also read

+ + +
+
+ + +

With multiple apps potentially playing audio it's important to think about how they should +interact. To avoid every music app playing at the same time, Android uses audio focus to moderate +audio playback—only apps that hold the audio focus should play audio.

+ +

Before your app starts playing audio it should request—and receive—the audio focus. +Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that +happens.

+ + +

Request the Audio Focus

+ +

Before your app starts playing any audio, it should hold the audio focus for the stream +it will be using. This is done with a call to {@link android.media.AudioManager#requestAudioFocus +requestAudioFocus()} which returns +{@link android.media.AudioManager#AUDIOFOCUS_REQUEST_GRANTED} if your request is successful.

+ +

You must specify which stream you're using and whether you expect to require transient or +permanent audio focus. Request transient focus when you expect to play audio for only a short time +(for example when playing navigation instructions). Request permanent audio focus when you +plan to play audio for the foreseeable future (for example, when playing music).

+ +

The following snippet requests permanent audio focus on the music audio stream. You should +request the audio focus immediately before you begin playback, such as when the user presses +play or the background music for the next game level begins.

+ +
+AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
+...
+
+// Request audio focus for playback
+int result = am.requestAudioFocus(afChangeListener,
+                                 // Use the music stream.
+                                 AudioManager.STREAM_MUSIC,
+                                 // Request permanent focus.
+                                 AudioManager.AUDIOFOCUS_GAIN);
+   
+if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+    am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
+    // Start playback.
+}
+
+ +

Once you've finished playback be sure to call {@link +android.media.AudioManager#abandonAudioFocus abandonAudioFocus()}. This notifies +the system that you no longer require focus and unregisters the associated {@link +android.media.AudioManager.OnAudioFocusChangeListener}. In the case of abandoning transient focus, +this allows any interupted app to continue playback.

+ +
+// Abandon audio focus when playback complete    
+am.abandonAudioFocus(afChangeListener);
+
+ +

When requesting transient audio focus you have an additional option: whether or not you want to +enable "ducking." Normally, when a well-behaved audio app loses audio focus it immediately +silences its playback. By requesting a transient audio focus that allows ducking you tell other +audio apps that it’s acceptable for them to keep playing, provided they lower their volume until the +focus returns to them.

+ +
+// Request audio focus for playback
+int result = am.requestAudioFocus(afChangeListener,
+                             // Use the music stream.
+                             AudioManager.STREAM_MUSIC,
+                             // Request permanent focus.
+                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
+   
+if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+    // Start playback.
+}
+
+ +

Ducking is particularly suitable for apps that use the audio stream intermittently, such as for +audible driving directions.

+ +

Whenever another app requests audio focus as described above, its choice between permanent and +transient (with or without support for ducking) audio focus is received by the listener you +registered when requesting focus.

+ + +

Handle the Loss of Audio Focus

+ +

If your app can request audio focus, it follows that it will in turn lose that focus when another +app requests it. How your app responds to a loss of audio focus depends on the manner of that +loss.

+ +

The {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange +onAudioFocusChange()} callback method of they audio focus change listener you registered when +requesting audio focus receives a parameter that describes the focus change event. Specifically, +the possible focus loss events mirror the focus request types from the previous +section—permanent loss, transient loss, and transient with ducking permitted.

+ +

Generally speaking, a transient (temporary) loss of audio focus should result in your app +silencing it’s audio stream, but otherwise maintaining the same state. You should continue to +monitor changes in audio focus and be prepared to resume playback where it was paused once you’ve +regained the focus.

+ +

If the audio focus loss is permanent, it’s assumed that another application is now being used to +listen to audio and your app should effectively end itself. In practical terms, that means stopping +playback, removing media button listeners—allowing the new audio player to exclusively handle +those events—and abandoning your audio focus. At that point, you would expect a user action +(pressing play in your app) to be required before you resume playing audio.

+ +

In the following code snippet, we pause the playback or our media player object if the audio +loss is transien and resume it when we have regained the focus. If the loss is permanent, it +unregisters our media button event receiver and stops monitoring audio focus changes.

+ +

+OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
+    public void onAudioFocusChange(int focusChange) {
+        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
+            // Pause playback
+        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+            // Resume playback 
+        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+            am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
+            am.abandonAudioFocus(afChangeListener);
+            // Stop playback
+        }
+    }
+};
+
+ +

In the case of a transient loss of audio focus where ducking is permitted, rather than pausing +playback, you can "duck" instead.

+ + +

Duck!

+ +

Ducking is the process of lowering your audio stream output volume to make transient audio from +another app easier to hear without totally disrupting the audio from your own application.

+ +

In the following code snippet lowers the volume on our media player object when we temporarily +lose focus, then returns it to its previous level when we regain focus.

+ +
+OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
+    public void onAudioFocusChange(int focusChange) {
+        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
+            // Lower the volume
+        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+            // Raise it back to normal
+        }
+    }
+};
+
+ +

A loss of audio focus is the most important broadcast to react to, but not the only one. The +system broadcasts a number of intents to alert you to changes in user’s audio experience. +The next lesson demonstrates how to monitor them to improve the user’s overall experience.

diff --git a/docs/html/training/managing-audio/audio-output.jd b/docs/html/training/managing-audio/audio-output.jd new file mode 100644 index 000000000000..d5d7e4b97e0c --- /dev/null +++ b/docs/html/training/managing-audio/audio-output.jd @@ -0,0 +1,88 @@ +page.title=Dealing with Audio Output Hardware +parent.title=Managing Audio Playback and Focus +parent.link=index.html + +trainingnavtop=true +previous.title=Managing Audio Focus +previous.link=audio-focus.html + +@jd:body + + +
+
+ +

This lesson teaches you to

+
    +
  1. Check What Hardware is Being Used
  2. +
  3. Handle Changes in the Audio Output Hardware
  4. +
+ + +

You should also read

+ + + +
+
+ +

Users have a number of alternatives when it comes to enjoying the audio from their Android +devices. Most devices have a built-in speaker, headphone jacks for wired headsets, and many also +feature Bluetooth connectivity and support for A2DP audio.

+ + +

Check What Hardware is Being Used

+ +

How your app behaves might be affected by which hardware its output is being routed to.

+ +

You can query the {@link android.media.AudioManager} to determine if the audio is currently +being routed to the device speaker, wired headset, or attached Bluetooth device as shown in the +following snippet:

+ +
+if (isBluetoothA2dpOn()) {
+    // Adjust output for Bluetooth.
+} else if (isSpeakerphoneOn()) {
+    // Adjust output for Speakerphone.
+} else if (isWiredHeadsetOn()) {
+    // Adjust output for headsets
+} else { 
+    // If audio plays and noone can hear it, is it still playing?
+}
+
+ + +

Handle Changes in the Audio Output Hardware

+ +

When a headset is unplugged, or a Bluetooth device disconnected, the audio stream +automatically reroutes to the built in speaker. If you listen to your music at as high a volume as I +do, that can be a noisy surprise.

+ +

Luckily the system broadcasts an {@link android.media.AudioManager#ACTION_AUDIO_BECOMING_NOISY} +intent when this happens. It’s good practice to register a {@link android.content.BroadcastReceiver} +that listens for this intent whenever you’re playing audio. In the case of music players, users +typically expect the playback to be paused—while for games you may choose to significantly +lower the volume.

+ +
+private class NoisyAudioStreamReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
+            // Pause the playback
+        }
+    }
+}
+
+private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+
+private void startPlayback() {
+    registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
+}
+
+private void stopPlayback() {
+    unregisterReceiver(myNoisyAudioStreamReceiver);
+}
+
diff --git a/docs/html/training/managing-audio/index.jd b/docs/html/training/managing-audio/index.jd new file mode 100644 index 000000000000..c7df39be7354 --- /dev/null +++ b/docs/html/training/managing-audio/index.jd @@ -0,0 +1,62 @@ +page.title=Managing Audio Playback and Focus + +trainingnavtop=true +startpage=true +next.title=Controlling Your App's Volume and Playback +next.link=volume-playback.html + +@jd:body + +
+
+ +

Dependencies and prerequisites

+ + +

You should also read

+ + +
+
+ + +

If your app plays audio, it’s important that your users can control the audio in a predictable +manner. To ensure a great user experience, it’s also important that your app manages the audio focus +to ensure multiple apps aren’t playing audio at the same time.

+ +

After this class, you will be able to build apps that respond to hardware audio key presses, +which request audio focus when playing audio, and which respond appropriately to changes in audio +focus caused by the system or other applications.

+ + + + +

Lessons

+ + + +
+
Controlling Your App’s Volume and +Playback
+
Learn how to ensure your users can control the volume of your app using the hardware or +software volume controls and where available the play, stop, pause, skip, and previous media +playback keys.
+ +
Managing Audio Focus
+
With multiple apps potentially playing audio it's important to think about how they should +interact. To avoid every music app playing at the same time, Android uses audio focus to moderate +audio playback. Learn how to request the audio focus, listen for a loss of audio focus, and how to +respond when that happens.
+ +
Dealing with Audio Output Hardware
+
Audio can be played from a number of sources. Learn how to find out where the audio is being +played and how to handle a headset being disconnected during playback.
+
\ No newline at end of file diff --git a/docs/html/training/managing-audio/volume-playback.jd b/docs/html/training/managing-audio/volume-playback.jd new file mode 100644 index 000000000000..7038ddfbd782 --- /dev/null +++ b/docs/html/training/managing-audio/volume-playback.jd @@ -0,0 +1,156 @@ +page.title=Controlling Your App’s Volume and Playback +parent.title=Managing Audio Playback and Focus +parent.link=index.html + +trainingnavtop=true +next.title=Managing Audio Focus +next.link=audio-focus.html + +@jd:body + + +
+ +
+ + + +

A good user experience is a predictable one. If your app plays media it’s important that your +users can control the volume of your app using the hardware or software volume controls of their +device, bluetooth headset, or headphones.

+ +

Similarly, where appropriate and available, the play, stop, pause, skip, and previous media +playback keys should perform their respective actions on the audio stream used by your app.

+ + +

Identify Which Audio Stream to Use

+ +

The first step to creating a predictable audio experience is understanding which audio stream +your app will use.

+ +

Android maintains a separate audio stream for playing music, alarms, notifications, the incoming +call ringer, system sounds, in-call volume, and DTMF tones. This is done primarily to allow users to +control the volume of each stream independently.

+ +

Most of these streams are restricted to system events, so unless your app is a replacement alarm +clock, you’ll almost certainly be playing your audio using the {@link +android.media.AudioManager#STREAM_MUSIC} stream.

+ + +

Use Hardware Volume Keys to Control Your App’s Audio Volume

+ +

By default, pressing the volume controls modify the volume of the active audio stream. If your +app isn't currently playing anything, hitting the volume keys adjusts the ringer volume.

+ +

If you've got a game or music app, then chances are good that when the user hits the volume keys +they want to control the volume of the game or music, even if they’re currently between songs or +there’s no music in the current game location.

+ +

You may be tempted to try and listen for volume key presses and modify the volume of your +audio stream that way. Resist the urge. Android provides the handy {@link +android.app.Activity#setVolumeControlStream setVolumeControlStream()} method to direct volume key +presses to the audio stream you specify.

+ +

Having identified the audio stream your application +will be using, you should set it as the volume stream target. You should make this call early in +your app’s lifecycle—because you only need to call it once during the activity lifecycle, you +should typically call it within the {@code onCreate()} method (of the {@link +android.app.Activity} or {@link android.app.Fragment} that controls +your media). This ensures that whenever your app is visible, the +volume controls function as the user expects.

+ +

+setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ + +

From this point onwards, pressing the volume keys on the device affect the audio stream you +specify (in this case “music”) whenever the target activity or fragment is visible.

+ + +

Use Hardware Playback Control Keys to Control Your App’s Audio +Playback

+ +

Media playback buttons such as play, pause, stop, skip, and previous are available on some +handsets and many connected or wireless headsets. Whenever a user presses one of these hardware +keys, the system broadcasts an intent with the {@link android.content.Intent#ACTION_MEDIA_BUTTON} +action.

+ +

To respond to media button clicks, you need to register a {@link +android.content.BroadcastReceiver} in your manifest that listens for this action broadcast as shown +below.

+ +
+<receiver android:name=".RemoteControlReceiver">
+    <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON" />
+    </intent-filter>
+</receiver>
+
+ +

The receiver implementation itself needs to extract which key was pressed to cause the broadcast. +The {@link android.content.Intent} includes this under the {@link +android.content.Intent#EXTRA_KEY_EVENT} key, while the {@link android.view.KeyEvent} class includes +a list {@code KEYCODE_MEDIA_*} static constants that represents each of the possible media +buttons, such as {@link android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} and {@link +android.view.KeyEvent#KEYCODE_MEDIA_NEXT}.

+ +

The following snippet shows how to extract the media button pressed and affects the media playback accordingly.

+ +
+public class RemoteControlReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
+            KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+            if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
+                // Handle key press.
+            }
+        }
+    }
+}
+
+ +

Because multiple applications might want to listen for media button presses, you must +also programmatically control when your app should receive media button press events.

+ +

The following code can be used within your app to register and de-register your media button +event receiver using the {@link android.media.AudioManager}. When registered, your broadcast +receiver is the exclusive receiver of all media button broadcasts.

+ +

+AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
+...
+
+// Start listening for button presses
+am.registerMediaButtonEventReceiver(RemoteControlReceiver);
+...
+
+// Stop listening for button presses
+am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
+
+ +

Typically, apps should unregister most of their receivers whenever they become inactive or +invisible (such as during the {@link android.app.Activity#onStop onStop()} callback). However, it’s +not that simple for media playback apps—in fact, responding to media playback buttons is most +important when your application isn’t visible and therefore can’t be controlled by the on-screen +UI.

+ +

A better approach is to register and unregister the media button event receiver when your +application gains and losses the audio focus. This is covered in detail in the next lesson.

diff --git a/docs/html/training/monitoring-device-state/battery-monitoring.jd b/docs/html/training/monitoring-device-state/battery-monitoring.jd new file mode 100644 index 000000000000..a442140aaad8 --- /dev/null +++ b/docs/html/training/monitoring-device-state/battery-monitoring.jd @@ -0,0 +1,156 @@ +page.title=Monitoring the Battery Level and Charging State +parent.title=Monitoring Device State to Optimize Battery Life +parent.link=index.html + +trainingnavtop=true +next.title=Determining and Monitoring the Docking State and Type +next.link=docking-monitoring.html + +@jd:body + +
+ +
+ +

When you're altering the frequency of your background updates to reduce the effect of those +updates on battery life, checking the current battery level and charging state is a good place to +start.

+ +

The battery-life impact of performing application updates depends on the battery level and +charging state of the device. The impact of performing updates while the device is charging over AC +is negligible, so in most cases you can maximize your refresh rate whenever the device is connected +to a wall charger. Conversely, if the device is discharging, reducing your update rate helps +prolong the battery life.

+ +

Similarly, you can check the battery charge level, potentially reducing the frequency of—or +even stopping—your updates when the battery charge is nearly exhausted.

+ + +

Determine the Current Charging State

+ +

Start by determining the current charge status. The {@link android.os.BatteryManager} +broadcasts all battery and charging details in a sticky {@link android.content.Intent} that includes +the charging status.

+ +

Because it's a sticky intent, you don't need to register a {@link +android.content.BroadcastReceiver}—by simply calling {@code registerReceiver} passing in +{@code null} as the receiver as shown in the next snippet, the current battery status intent is +returned. You could pass in an actual {@link android.content.BroadcastReceiver} object here, but +we'll be handling updates in a later section so it's not necessary.

+ +
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+Intent batteryStatus = context.registerReceiver(null, ifilter);
+ +

You can extract both the current charging status and, if the device is being charged, whether +it's charging via USB or AC charger:

+ +

// Are we charging / charged?
+int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
+                     status == BatteryManager.BATTERY_STATUS_FULL;
+
+// How are we charging?
+int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
+boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
+ +

Typically you should maximize the rate of your background updates in the case where the device is +connected to an AC charger, reduce the rate if the charge is over USB, and lower it +further if the battery is discharging.

+ + +

Monitor Changes in Charging State

+ +

The charging status can change as easily as a device can be plugged in, so it's important to +monitor the charging state for changes and alter your refresh rate accordingly.

+ +

The {@link android.os.BatteryManager} broadcasts an action whenever the device is connected or +disconnected from power. It's important to to receive these events even while your app isn't +running—particularly as these events should impact how often you start your app in order to +initiate a background update—so you should register a {@link +android.content.BroadcastReceiver} in your manifest to listen for both events by defining the +{@link android.content.Intent#ACTION_POWER_CONNECTED} and {@link +android.content.Intent#ACTION_POWER_DISCONNECTED} within an intent filter.

+ +
<receiver android:name=".PowerConnectionReceiver">
+  <intent-filter>
+    <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
+    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
+  </intent-filter>
+</receiver>
+ +

Within the associated {@link android.content.BroadcastReceiver} implementation, you can extract +the current charging state and method as described in the previous step.

+ +
public class PowerConnectionReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) { 
+        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
+                            status == BatteryManager.BATTERY_STATUS_FULL;
+    
+        int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+        boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
+        boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
+    }
+}
+ + +

Determine the Current Battery Level

+ +

In some cases it's also useful to determine the current battery level. You may choose to reduce +the rate of your background updates if the battery charge is below a certain level.

+ +

You can find the current battery charge by extracting the current battery level and scale from +the battery status intent as shown here:

+ +
int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+float batteryPct = level / (float)scale;
+ + +

Monitor Significant Changes in Battery Level

+ +

You can't easily continually monitor the battery state, but you don't need to.

+ +

Generally speaking, the impact of constantly monitoring the battery level has a greater +impact on the battery than your app's normal behavior, so it's good practice to only monitor +significant changes in battery level—specifically when the device enters or exits a low +battery state.

+ +

The manifest snippet below is extracted from the intent filter element within a broadcast +receiver. The receiver is triggered whenever the device battery becomes low or exits the low +condition by listening for {@link android.content.Intent#ACTION_BATTERY_LOW} and {@link +android.content.Intent#ACTION_BATTERY_OKAY}.

+ +
<receiver android:name=".BatteryLevelReceiver">
+<intent-filter>
+  <action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
+  <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
+  </intent-filter>
+</receiver>
+ +

It is generally good practice to disable all your background updates when the battery is +critically low. It doesn't matter how fresh your data is if the phone turns itself off before you +can make use of it.

+ +

In many cases, the act of charging a device is coincident with putting it into a dock. The next +lesson shows you how to determine the current dock state and monitor for changes in device +docking.

+ diff --git a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd new file mode 100644 index 000000000000..4c7127905939 --- /dev/null +++ b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd @@ -0,0 +1,90 @@ +page.title=Determining and Monitoring the Connectivity Status +parent.title=Monitoring Device State to Optimize Battery Life +parent.link=index.html + +trainingnavtop=true + +previous.title=Determining and Monitoring the Docking State and Type +previous.link=docking-monitoring.html +next.title=Manipulating Broadcast Receivers On Demand +next.link=manifest-receivers.html + +@jd:body + +
+ +
+ +

Some of the most common uses for repeating alarms and background services is to schedule regular +updates of application data from Internet resources, cache data, or execute long running downloads. +But if you aren't connected to the Internet, or the connection is too slow to complete your +download, why both waking the device to schedule the update at all?

+ +

You can use the {@link android.net.ConnectivityManager} to check that you're actually +connected to the Internet, and if so, what type of connection is in place.

+ + +

Determine if You Have an Internet Connection

+ +

There's no need to schedule an update based on an Internet resource if you aren't connected to +the Internet. The following snippet shows how to use the {@link android.net.ConnectivityManager} +to query the active network and determine if it has Internet connectivity.

+ +
ConnectivityManager cm =
+        (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ 
+NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+boolean isConnected = activeNetwork.isConnectedOrConnecting();
+ + +

Determine the Type of your Internet Connection

+ +

It's also possible to determine the type of Internet connection currently available.

+ +

Device connectivity can be provided by mobile data, WiMAX, Wi-Fi, and ethernet connections. By +querying the type of the active network, as shown below, you can alter your refresh rate based on +the bandwidth available.

+ +
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
+ +

Mobile data costs tend to be significantly higher than Wi-Fi, so in most cases, your app's update +rate should be lower when on mobile connections. Similarly, downloads of significant size should be +suspended until you have a Wi-Fi connection.

+ +

Having disabled your updates, it's important that you listen for changes in connectivity in order +to resume them once an Internet connection has been established.

+ + +

Monitor for Changes in Connectivity

+ +

The {@link android.net.ConnectivityManager} broadcasts the {@link +android.net.ConnectivityManager#CONNECTIVITY_ACTION} ({@code +"android.net.conn.CONNECTIVITY_CHANGE"}) action whenever the connectivity details have changed. You +can register a broadcast receiver in your manifest to listen for these changes and resume (or +suspend) your background updates accordingly.

+ +
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+ +

Changes to a device's connectivity can be very frequent—this broadcast is triggered +every time you move between mobile data and Wi-Fi. As a result, it's good practice to monitor +this broadcast only when you've previously suspended updates or downloads in order to resume them. +It's generally sufficient to simply check for Internet connectivity before beginning an update and, +should there be none, suspend further updates until connectivity is restored.

+ +

This technique requires toggling broadcast receivers you've declard in the manifest, which is +described in the next lesson.

diff --git a/docs/html/training/monitoring-device-state/docking-monitoring.jd b/docs/html/training/monitoring-device-state/docking-monitoring.jd new file mode 100644 index 000000000000..6a4a9a8e1a91 --- /dev/null +++ b/docs/html/training/monitoring-device-state/docking-monitoring.jd @@ -0,0 +1,90 @@ +page.title=Determining and Monitoring the Docking State and Type +parent.title=Monitoring Device State to Optimize Battery Life +parent.link=index.html + +trainingnavtop=true +previous.title= Monitoring the Battery Level and Charging State +previous.link=battery-monitoring.html +next.title= Determining and Monitoring the Connectivity Status +next.link=connectivity-monitoring.html + +@jd:body + +
+ +
+ +

Android devices can be docked into several different kinds of docks. These include car or home +docks and digital versus analog docks. The dock-state is typically closely linked to the charging +state as many docks provide power to docked devices.

+ +

How the dock-state of the phone affects your update rate depends on your app. You may choose +to increase the update frequency of a sports center app when it's in the desktop dock, or disable +your updates completely if the device is car docked. Conversely, you may choose to maximize your +updates while car docked if your background service is updating traffic conditions.

+ +

The dock state is also broadcast as a sticky {@link android.content.Intent}, allowing you to +query if the device is docked or not, and if so, in which kind of dock.

+ + +

Determine the Current Docking State

+ +

The dock-state details are included as an extra in a sticky broadcast of the {@link +android.content.Intent#ACTION_DOCK_EVENT} action. Because it's sticky, you don't need to register a +{@link android.content.BroadcastReceiver}. You can simply call {@link +android.content.Context#registerReceiver registerReceiver()} passing in {@code null} as the +broadcast receiver as shown in the next snippet.

+ +
IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
+Intent dockStatus = context.registerReceiver(null, ifilter);
+ +

You can extract the current docking status from the {@code EXTRA_DOCK_STATE} extra:

+ +

int dockState = battery.getIntExtra(EXTRA_DOCK_STATE, -1);
+boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ + +

Determine the Current Dock Type

+ +

If a device is docked, it can be docked in any one of four different type of dock: +

+ +

Note that the latter two options were only introduced to Android in API level 11, so it's good +practice to check for all three where you are only interested in the type of dock rather than it +being digital or analog specifically:

+ +
boolean isCar = dockState == EXTRA_DOCK_STATE_CAR;
+boolean isDesk = dockState == EXTRA_DOCK_STATE_DESK || 
+                 dockState == EXTRA_DOCK_STATE_LE_DESK ||
+                 dockState == EXTRA_DOCK_STATE_HE_DESK;
+ + +

Monitor for Changes in the Dock State or Type

+ +

Whenever the the device is docked or undocked, the {@link +android.content.Intent#ACTION_DOCK_EVENT} action is broadcast. To monitor changes in the +device's dock-state, simply register a broadcast receiver in your application manifest as shown in +the snippet below:

+ +
<action android:name="android.intent.action.ACTION_DOCK_EVENT"/>
+ +

You can extract the dock type and state within the receiver implementation using the same +techniques described in the previous step.

diff --git a/docs/html/training/monitoring-device-state/index.jd b/docs/html/training/monitoring-device-state/index.jd new file mode 100644 index 000000000000..e92e1e8a3027 --- /dev/null +++ b/docs/html/training/monitoring-device-state/index.jd @@ -0,0 +1,63 @@ +page.title=Monitoring Device State to Optimize Battery Life + +trainingnavtop=true +startpage=true +next.title=Monitoring the Battery Level and Charging State +next.link=battery-monitoring.html + +@jd:body + +
+
+ +

Dependencies and prerequisites

+ + +

You should also read

+ + +
+
+ +

For your app to be a good citizen, it should seek to limit its impact on the battery life of its +host device. After this class you will be able to build apps that monitor modify their functionality +and behavior based on the state of the host device.

+ +

By taking steps such as disabling background service updates when you lose connectivity, or +reducing the rate of such updates when the battery level is low, you can ensure that the impact of +your app on battery life is minimized, without compromising the user experience.

+ +

Lessons

+ + + +
+
Monitoring the Battery Level and Charging State
+
Learn how to alter your app's update rate by determining, and monitoring, the current battery +level and changes in charging state.
+ +
Determining and Monitoring the Docking State and +Type
+
Optimal refresh rates can vary based on how the host device is being used. Learn how to +determine, and monitor, the docking state and type of dock being used to affect your app's +behavior.
+ +
Determining and Monitoring the Connectivity +Status
+
Without Internet connectivity you can't update your app from an online source. Learn how to +check the connectivity status to alter your background update rate. You'll also learn to check for +Wi-Fi or mobile connectivity before beginning high-bandwidth operations.
+ +
Manipulating Broadcast Receivers On Demand
+
Broadcast receivers that you've declared in the manifest can be toggled at runtime to disable +those that aren't necessary due to the current device state. Learn to improve +efficiency by toggling and cascading state change receivers and delay actions until the device is in +a specific state.
+
\ No newline at end of file diff --git a/docs/html/training/monitoring-device-state/manifest-receivers.jd b/docs/html/training/monitoring-device-state/manifest-receivers.jd new file mode 100644 index 000000000000..bf5462a1e7c3 --- /dev/null +++ b/docs/html/training/monitoring-device-state/manifest-receivers.jd @@ -0,0 +1,64 @@ +page.title=Manipulating Broadcast Receivers On Demand +parent.title=Monitoring Device State to Optimize Battery Life +parent.link=index.html + +trainingnavtop=true + +previous.title=Determining and Monitoring the Connectivity Status +previous.link=connectivity-monitoring.html + +@jd:body + +
+
+ +

This lesson teaches you to

+
    +
  1. Toggle and Cascade State Change Receivers to Improve +Efficiency
  2. +
+ + +

You should also read

+ + +
+
+ +

The simplest way to monitor device state changes is to create a {@link +android.content.BroadcastReceiver} for each state you're monitoring and register each of them in +your application manifest. Then within each of these receivers you simply reschedule your recurring +alarms based on the current device state.

+ +

A side-effect of this approach is that your app will wake the device each time any of these +receivers is triggered—potentially much more frequently than required.

+ +

A better approach is to disable or enable the broadcast receivers at runtime. That way you can +use the receivers you declared in the manifest as passive alarms that are triggered by system events +only when necessary.

+ + +

Toggle and Cascade State Change Receivers to Improve Efficiency

+ +

Use can use the {@link android.content.pm.PackageManager} to toggle the enabled state on any +component defined in the manifest, including whichever broadcast receivers you wish to enable or +disable as shown in the snippet below:

+ +
ComponentName receiver = new ComponentName(context, myReceiver.class);
+
+PackageManager pm = context.getPackageManager();
+
+pm.setComponentEnabledSetting(receiver,
+        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+        PackageManager.DONT_KILL_APP)
+ +

Using this technique, if you determine that connectivity has been lost, you can disable all of +your receivers except the connectivity-change receiver. Conversely, once you are connected you can +stop listening for connectivity changes and simply check to see if you're online immediately before +performing an update and rescheduling a recurring update alarm.

+ +

You can use the same technique to delay a download that requires higher bandwidth to complete. +Simply enable a broadcast receiver that listens for connectivity changes and initiates the +download only after you are connected to Wi-Fi.

-- cgit v1.2.3-59-g8ed1b