summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/html/design/building-blocks/progress.jd6
-rw-r--r--docs/html/design/building-blocks/tabs.jd2
-rw-r--r--docs/html/design/design_toc.cs1
-rw-r--r--docs/html/design/downloads/index.jd2
-rw-r--r--docs/html/design/get-started/principles.jd34
-rw-r--r--docs/html/design/media/app_structure_market.pngbin163420 -> 155446 bytes
-rw-r--r--docs/html/design/media/app_structure_music_lndscp.pngbin166650 -> 165178 bytes
-rw-r--r--docs/html/design/media/app_structure_scrolltabs.pngbin80451 -> 81574 bytes
-rw-r--r--docs/html/design/media/app_structure_shortcut_on_item.pngbin206816 -> 206869 bytes
-rw-r--r--docs/html/design/media/iconography_launcher_example.pngbin185426 -> 181075 bytes
-rw-r--r--docs/html/design/media/iconography_overview.pngbin241867 -> 250035 bytes
-rw-r--r--docs/html/design/media/migrating_intents.pngbin0 -> 177057 bytes
-rw-r--r--docs/html/design/media/misc_full_galaxynexus_blank_land_span13.pngbin64431 -> 0 bytes
-rw-r--r--docs/html/design/media/misc_full_galaxynexus_blank_port_span5.pngbin39994 -> 0 bytes
-rw-r--r--docs/html/design/media/misc_full_galaxynexus_blank_port_span9.pngbin75920 -> 0 bytes
-rwxr-xr-xdocs/html/design/media/navigation_between_apps_back.pngbin0 -> 91787 bytes
-rwxr-xr-xdocs/html/design/media/navigation_between_apps_inward.pngbin0 -> 89944 bytes
-rwxr-xr-xdocs/html/design/media/navigation_between_apps_up.pngbin0 -> 100374 bytes
-rwxr-xr-x[-rw-r--r--]docs/html/design/media/navigation_between_siblings_market1.pngbin28009 -> 27882 bytes
-rwxr-xr-x[-rw-r--r--]docs/html/design/media/navigation_between_siblings_market2.pngbin35764 -> 34622 bytes
-rw-r--r--docs/html/design/media/navigation_from_outside_up.pngbin22713 -> 0 bytes
-rw-r--r--docs/html/design/media/navigation_indirect_notification.pngbin0 -> 42108 bytes
-rw-r--r--docs/html/design/media/navigation_popup_notification.pngbin0 -> 27734 bytes
-rw-r--r--docs/html/design/media/navigation_up_vs_back_gmail.pngbin92227 -> 93258 bytes
-rw-r--r--docs/html/design/media/navigation_with_back_and_up.pngbin10812 -> 12322 bytes
-rw-r--r--docs/html/design/media/progress_activity.pngbin28719 -> 25446 bytes
-rw-r--r--docs/html/design/media/progress_download.pngbin157564 -> 154955 bytes
-rw-r--r--docs/html/design/media/settings_checkbox.pngbin0 -> 97372 bytes
-rw-r--r--docs/html/design/media/settings_date_time.pngbin0 -> 149291 bytes
-rw-r--r--docs/html/design/media/settings_dependency.pngbin0 -> 184476 bytes
-rw-r--r--docs/html/design/media/settings_flowchart.pngbin0 -> 125645 bytes
-rw-r--r--docs/html/design/media/settings_grouping.pngbin0 -> 73868 bytes
-rw-r--r--docs/html/design/media/settings_individual_on_off.pngbin0 -> 223658 bytes
-rw-r--r--docs/html/design/media/settings_list_subscreen.pngbin0 -> 107236 bytes
-rw-r--r--docs/html/design/media/settings_main.pngbin0 -> 234013 bytes
-rw-r--r--docs/html/design/media/settings_master_on_off.pngbin0 -> 139574 bytes
-rw-r--r--docs/html/design/media/settings_master_on_off_2.pngbin0 -> 170058 bytes
-rw-r--r--docs/html/design/media/settings_multiple_choice.pngbin0 -> 144772 bytes
-rw-r--r--docs/html/design/media/settings_overflow.pngbin0 -> 67730 bytes
-rw-r--r--docs/html/design/media/settings_slider.pngbin0 -> 123323 bytes
-rw-r--r--docs/html/design/media/settings_subscreen_navigation.pngbin0 -> 125305 bytes
-rw-r--r--docs/html/design/media/tabs_scrolly.mp4bin94983 -> 96516 bytes
-rw-r--r--docs/html/design/media/tabs_scrolly.ogvbin239873 -> 236605 bytes
-rw-r--r--docs/html/design/media/tabs_scrolly.webmbin102539 -> 101642 bytes
-rw-r--r--docs/html/design/media/ui_overview_all_apps.pngbin59859 -> 56933 bytes
-rw-r--r--docs/html/design/patterns/actionbar.jd2
-rw-r--r--docs/html/design/patterns/app-structure.jd8
-rw-r--r--docs/html/design/patterns/navigation.jd194
-rw-r--r--docs/html/design/patterns/pure-android.jd30
-rw-r--r--docs/html/design/patterns/settings.jd689
-rw-r--r--docs/html/design/style/writing.jd112
-rw-r--r--docs/html/guide/developing/testing/testing_otheride.jd2
-rw-r--r--docs/html/resources/resources_toc.cs25
-rw-r--r--docs/html/shareables/training/BitmapFun.zipbin0 -> 315657 bytes
-rw-r--r--docs/html/shareables/training/LocationAware.zipbin36317 -> 36310 bytes
-rw-r--r--docs/html/training/displaying-bitmaps/cache-bitmap.jd337
-rw-r--r--docs/html/training/displaying-bitmaps/display-bitmap.jd400
-rw-r--r--docs/html/training/displaying-bitmaps/index.jd78
-rw-r--r--docs/html/training/displaying-bitmaps/load-bitmap.jd165
-rw-r--r--docs/html/training/displaying-bitmaps/process-bitmap.jd239
60 files changed, 2162 insertions, 164 deletions
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index dc3ded18bad6..b18853871fce 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -42,9 +42,9 @@ available space.</p>
<li class="value-1"><h4>Activity bar (shown with the Holo Dark theme)</h4>
<p>
-An indeterminate activity bar is used at the start of an application download because Google Play hasn't
-been able to contact the server yet, and it's not possible to determine how long it will take for
-the download to begin.
+An indeterminate activity bar is used at the start of an application download because the Play Store
+app hasn't been able to contact the server yet, and it's not possible to determine how long it will
+take for the download to begin.
</p>
</li>
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 2c854d3c9f91..19ed1c3ff135 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -25,7 +25,7 @@ to the next/previous view, swipe left or right.</p>
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions">&nbsp;</div>
</div>
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 19b58d979665..6dd8d610418f 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -35,6 +35,7 @@
<li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
</ul>
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 618c44b8fddf..67dfd7916ffe 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -39,7 +39,7 @@ available.</p>
<p>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
- <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe&reg; Photoshop&reg; Sources</a>
+ <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120302.zip">Adobe&reg; Photoshop&reg; Sources</a>
</p>
</div>
diff --git a/docs/html/design/get-started/principles.jd b/docs/html/design/get-started/principles.jd
index 8f5b44642ea7..0b7147b8a83c 100644
--- a/docs/html/design/get-started/principles.jd
+++ b/docs/html/design/get-started/principles.jd
@@ -10,7 +10,7 @@ with purpose.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Delight me in surprising ways</h4>
+<h4 id="delight-me">Delight me in surprising ways</h4>
<p>A beautiful surface, a carefully-placed animation, or a well-timed sound effect is a joy to
experience. Subtle effects contribute to a feeling of effortlessness and a sense that a powerful
force is at hand.</p>
@@ -28,7 +28,7 @@ force is at hand.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Real objects are more fun than buttons and menus</h4>
+<h4 id="real-objects-more-fun">Real objects are more fun than buttons and menus</h4>
<p>Allow people to directly touch and manipulate objects in your app. It reduces the cognitive effort
needed to perform a task while making it more emotionally satisfying.</p>
@@ -45,7 +45,7 @@ needed to perform a task while making it more emotionally satisfying.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Let me make it mine</h4>
+<h4 id="make-it-mine">Let me make it mine</h4>
<p>People love to add personal touches because it helps them feel at home and in control. Provide
sensible, beautiful defaults, but also consider fun, optional customizations that don't hinder
primary tasks.</p>
@@ -63,7 +63,7 @@ primary tasks.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Get to know me</h4>
+<h4 id="get-to-know-me">Get to know me</h4>
<p>Learn peoples' preferences over time. Rather than asking them to make the same choices over and
over, place previous choices within easy reach.</p>
@@ -80,7 +80,7 @@ over, place previous choices within easy reach.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Keep it brief</h4>
+<h4 id="keep-it-brief">Keep it brief</h4>
<p>Use short phrases with simple words. People are likely to skip sentences if they're long.</p>
</div>
@@ -96,7 +96,7 @@ over, place previous choices within easy reach.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Pictures are faster than words</h4>
+<h4 id="pictures-faster-than-words">Pictures are faster than words</h4>
<p>Consider using pictures to explain ideas. They get people's attention and can be much more efficient
than words.</p>
@@ -113,7 +113,7 @@ than words.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Decide for me but let me have the final say</h4>
+<h4 id="decide-for-me">Decide for me but let me have the final say</h4>
<p>Take your best guess and act rather than asking first. Too many choices and decisions make people
unhappy. Just in case you get it wrong, allow for 'undo'.</p>
@@ -130,7 +130,7 @@ unhappy. Just in case you get it wrong, allow for 'undo'.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only show what I need when I need it</h4>
+<h4 id="only-show-when-i-need-it">Only show what I need when I need it</h4>
<p>People get overwhelmed when they see too much at once. Break tasks and information into small,
digestible chunks. Hide options that aren't essential at the moment, and teach people as they go.</p>
@@ -147,7 +147,7 @@ digestible chunks. Hide options that aren't essential at the moment, and teach p
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>I should always know where I am</h4>
+<h4 id="always-know-where-i-am">I should always know where I am</h4>
<p>Give people confidence that they know their way around. Make places in your app look distinct and
use transitions to show relationships among screens. Provide feedback on tasks in progress.</p>
@@ -164,7 +164,7 @@ use transitions to show relationships among screens. Provide feedback on tasks i
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Never lose my stuff</h4>
+<h4 id="never-lose-my-stuff">Never lose my stuff</h4>
<p>Save what people took time to create and let them access it from anywhere. Remember settings,
personal touches, and creations across phones, tablets, and computers. It makes upgrading the
easiest thing in the world.</p>
@@ -182,7 +182,7 @@ easiest thing in the world.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>If it looks the same, it should act the same</h4>
+<h4 id="looks-same-should-act-same">If it looks the same, it should act the same</h4>
<p>Help people discern functional differences by making them visually distinct rather than subtle.
Avoid modes, which are places that look similar but act differently on the same input.</p>
@@ -199,7 +199,7 @@ Avoid modes, which are places that look similar but act differently on the same
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only interrupt me if it's important</h4>
+<h4 id="interrupt-only-if-important">Only interrupt me if it's important</h4>
<p>Like a good personal assistant, shield people from unimportant minutiae. People want to stay
focused, and unless it's critical and time-sensitive, an interruption can be taxing and frustrating.</p>
@@ -216,7 +216,7 @@ focused, and unless it's critical and time-sensitive, an interruption can be tax
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Give me tricks that work everywhere</h4>
+<h4 id="give-me-tricks">Give me tricks that work everywhere</h4>
<p>People feel great when they figure things out for themselves. Make your app easier to learn by
leveraging visual patterns and muscle memory from other Android apps. For example, the swipe gesture
may be a good navigational shortcut.</p>
@@ -234,7 +234,7 @@ may be a good navigational shortcut.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>It's not my fault</h4>
+<h4 id="its-not-my-fault">It's not my fault</h4>
<p>Be gentle in how you prompt people to make corrections. They want to feel smart when they use your
app. If something goes wrong, give clear recovery instructions but spare them the technical details.
If you can fix it behind the scenes, even better.</p>
@@ -252,7 +252,7 @@ If you can fix it behind the scenes, even better.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Sprinkle encouragement</h4>
+<h4 id="sprinkle-encouragement">Sprinkle encouragement</h4>
<p>Break complex tasks into smaller steps that can be easily accomplished. Give feedback on actions,
even if it's just a subtle glow.</p>
@@ -269,7 +269,7 @@ even if it's just a subtle glow.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Do the heavy lifting for me</h4>
+<h4 id="do-heavy-lifting-for-me">Do the heavy lifting for me</h4>
<p>Make novices feel like experts by enabling them to do things they never thought they could. For
example, shortcuts that combine multiple photo effects can make amateur photographs look amazing in
only a few steps.</p>
@@ -287,7 +287,7 @@ only a few steps.</p>
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Make important things fast</h4>
+<h4 id="make-important-things-fast">Make important things fast</h4>
<p>Not all actions are equal. Decide what's most important in your app and make it easy to find and
fast to use, like the shutter button in a camera, or the pause button in a music player.</p>
diff --git a/docs/html/design/media/app_structure_market.png b/docs/html/design/media/app_structure_market.png
index 3b0b786a6b9e..5aa595e61307 100644
--- a/docs/html/design/media/app_structure_market.png
+++ b/docs/html/design/media/app_structure_market.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_music_lndscp.png b/docs/html/design/media/app_structure_music_lndscp.png
index 0dd400c25a45..67354de8e162 100644
--- a/docs/html/design/media/app_structure_music_lndscp.png
+++ b/docs/html/design/media/app_structure_music_lndscp.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_scrolltabs.png b/docs/html/design/media/app_structure_scrolltabs.png
index ef4fca4b7140..ea742c2d7fb6 100644
--- a/docs/html/design/media/app_structure_scrolltabs.png
+++ b/docs/html/design/media/app_structure_scrolltabs.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_shortcut_on_item.png b/docs/html/design/media/app_structure_shortcut_on_item.png
index 3874e1d4b14f..1341f1f31d15 100644
--- a/docs/html/design/media/app_structure_shortcut_on_item.png
+++ b/docs/html/design/media/app_structure_shortcut_on_item.png
Binary files differ
diff --git a/docs/html/design/media/iconography_launcher_example.png b/docs/html/design/media/iconography_launcher_example.png
index a5db53ee42af..0cce7ef90e2e 100644
--- a/docs/html/design/media/iconography_launcher_example.png
+++ b/docs/html/design/media/iconography_launcher_example.png
Binary files differ
diff --git a/docs/html/design/media/iconography_overview.png b/docs/html/design/media/iconography_overview.png
index 688c1b5a7832..56cd4098072b 100644
--- a/docs/html/design/media/iconography_overview.png
+++ b/docs/html/design/media/iconography_overview.png
Binary files differ
diff --git a/docs/html/design/media/migrating_intents.png b/docs/html/design/media/migrating_intents.png
new file mode 100644
index 000000000000..65fc1a57b061
--- /dev/null
+++ b/docs/html/design/media/migrating_intents.png
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png b/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
deleted file mode 100644
index bab6aca5af3b..000000000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
deleted file mode 100644
index bdccc2f2324c..000000000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
deleted file mode 100644
index 5e0135b22ba3..000000000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_back.png b/docs/html/design/media/navigation_between_apps_back.png
new file mode 100755
index 000000000000..ded5d0ac6633
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_back.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_inward.png b/docs/html/design/media/navigation_between_apps_inward.png
new file mode 100755
index 000000000000..1f5e401585ec
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_inward.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_up.png b/docs/html/design/media/navigation_between_apps_up.png
new file mode 100755
index 000000000000..f192c88e53b6
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_up.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market1.png b/docs/html/design/media/navigation_between_siblings_market1.png
index c3148f8d8548..8f2b3dcf70c6 100644..100755
--- a/docs/html/design/media/navigation_between_siblings_market1.png
+++ b/docs/html/design/media/navigation_between_siblings_market1.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market2.png b/docs/html/design/media/navigation_between_siblings_market2.png
index 208be4786a07..33b654ced3a0 100644..100755
--- a/docs/html/design/media/navigation_between_siblings_market2.png
+++ b/docs/html/design/media/navigation_between_siblings_market2.png
Binary files differ
diff --git a/docs/html/design/media/navigation_from_outside_up.png b/docs/html/design/media/navigation_from_outside_up.png
deleted file mode 100644
index eaa3cdbcda9e..000000000000
--- a/docs/html/design/media/navigation_from_outside_up.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_indirect_notification.png b/docs/html/design/media/navigation_indirect_notification.png
new file mode 100644
index 000000000000..6f99267f2057
--- /dev/null
+++ b/docs/html/design/media/navigation_indirect_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_popup_notification.png b/docs/html/design/media/navigation_popup_notification.png
new file mode 100644
index 000000000000..a0a3ee7059c5
--- /dev/null
+++ b/docs/html/design/media/navigation_popup_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_up_vs_back_gmail.png b/docs/html/design/media/navigation_up_vs_back_gmail.png
index 71e6484a92d6..ff7adfe23980 100644
--- a/docs/html/design/media/navigation_up_vs_back_gmail.png
+++ b/docs/html/design/media/navigation_up_vs_back_gmail.png
Binary files differ
diff --git a/docs/html/design/media/navigation_with_back_and_up.png b/docs/html/design/media/navigation_with_back_and_up.png
index 4fb6dcede3eb..544022044634 100644
--- a/docs/html/design/media/navigation_with_back_and_up.png
+++ b/docs/html/design/media/navigation_with_back_and_up.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity.png b/docs/html/design/media/progress_activity.png
index 51210b4bc5bc..32cf1f528f0a 100644
--- a/docs/html/design/media/progress_activity.png
+++ b/docs/html/design/media/progress_activity.png
Binary files differ
diff --git a/docs/html/design/media/progress_download.png b/docs/html/design/media/progress_download.png
index f567f74a899c..ab6bf58b4794 100644
--- a/docs/html/design/media/progress_download.png
+++ b/docs/html/design/media/progress_download.png
Binary files differ
diff --git a/docs/html/design/media/settings_checkbox.png b/docs/html/design/media/settings_checkbox.png
new file mode 100644
index 000000000000..6615bfbeda25
--- /dev/null
+++ b/docs/html/design/media/settings_checkbox.png
Binary files differ
diff --git a/docs/html/design/media/settings_date_time.png b/docs/html/design/media/settings_date_time.png
new file mode 100644
index 000000000000..8df92d4a13cb
--- /dev/null
+++ b/docs/html/design/media/settings_date_time.png
Binary files differ
diff --git a/docs/html/design/media/settings_dependency.png b/docs/html/design/media/settings_dependency.png
new file mode 100644
index 000000000000..4821c61bac6d
--- /dev/null
+++ b/docs/html/design/media/settings_dependency.png
Binary files differ
diff --git a/docs/html/design/media/settings_flowchart.png b/docs/html/design/media/settings_flowchart.png
new file mode 100644
index 000000000000..7e8623c9ffbc
--- /dev/null
+++ b/docs/html/design/media/settings_flowchart.png
Binary files differ
diff --git a/docs/html/design/media/settings_grouping.png b/docs/html/design/media/settings_grouping.png
new file mode 100644
index 000000000000..d271ea80884b
--- /dev/null
+++ b/docs/html/design/media/settings_grouping.png
Binary files differ
diff --git a/docs/html/design/media/settings_individual_on_off.png b/docs/html/design/media/settings_individual_on_off.png
new file mode 100644
index 000000000000..03bea0b8d442
--- /dev/null
+++ b/docs/html/design/media/settings_individual_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_list_subscreen.png b/docs/html/design/media/settings_list_subscreen.png
new file mode 100644
index 000000000000..385aa6abc29b
--- /dev/null
+++ b/docs/html/design/media/settings_list_subscreen.png
Binary files differ
diff --git a/docs/html/design/media/settings_main.png b/docs/html/design/media/settings_main.png
new file mode 100644
index 000000000000..f42a358f4829
--- /dev/null
+++ b/docs/html/design/media/settings_main.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off.png b/docs/html/design/media/settings_master_on_off.png
new file mode 100644
index 000000000000..e46bb9739771
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off_2.png b/docs/html/design/media/settings_master_on_off_2.png
new file mode 100644
index 000000000000..ab4e992da6cc
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off_2.png
Binary files differ
diff --git a/docs/html/design/media/settings_multiple_choice.png b/docs/html/design/media/settings_multiple_choice.png
new file mode 100644
index 000000000000..9b285664ff1e
--- /dev/null
+++ b/docs/html/design/media/settings_multiple_choice.png
Binary files differ
diff --git a/docs/html/design/media/settings_overflow.png b/docs/html/design/media/settings_overflow.png
new file mode 100644
index 000000000000..9000bec98ddb
--- /dev/null
+++ b/docs/html/design/media/settings_overflow.png
Binary files differ
diff --git a/docs/html/design/media/settings_slider.png b/docs/html/design/media/settings_slider.png
new file mode 100644
index 000000000000..51545c8d6bd6
--- /dev/null
+++ b/docs/html/design/media/settings_slider.png
Binary files differ
diff --git a/docs/html/design/media/settings_subscreen_navigation.png b/docs/html/design/media/settings_subscreen_navigation.png
new file mode 100644
index 000000000000..2ab0b96652e8
--- /dev/null
+++ b/docs/html/design/media/settings_subscreen_navigation.png
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.mp4 b/docs/html/design/media/tabs_scrolly.mp4
index 4329243d3624..dc9e9c6cf048 100644
--- a/docs/html/design/media/tabs_scrolly.mp4
+++ b/docs/html/design/media/tabs_scrolly.mp4
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.ogv b/docs/html/design/media/tabs_scrolly.ogv
index 345e57a78929..3e484f97a3a8 100644
--- a/docs/html/design/media/tabs_scrolly.ogv
+++ b/docs/html/design/media/tabs_scrolly.ogv
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.webm b/docs/html/design/media/tabs_scrolly.webm
index 17e368e7fc0e..e9d371d2cb77 100644
--- a/docs/html/design/media/tabs_scrolly.webm
+++ b/docs/html/design/media/tabs_scrolly.webm
Binary files differ
diff --git a/docs/html/design/media/ui_overview_all_apps.png b/docs/html/design/media/ui_overview_all_apps.png
index 467f5ad1d8f9..17e7ece70433 100644
--- a/docs/html/design/media/ui_overview_all_apps.png
+++ b/docs/html/design/media/ui_overview_all_apps.png
Binary files differ
diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 9e3f48c2d9f5..2226fec3b262 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -176,7 +176,7 @@ themselves.</p>
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions">&nbsp;</div>
</div>
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index b54b12ff3262..e2398ed17206 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -7,7 +7,7 @@ page.title=Application Structure
single screen</li>
<li>Apps such as Phone whose main purpose is to switch between different activities without deeper
navigation</li>
-<li>Apps such as Gmail or Google Play that combine a broad set of data views with deep navigation</li>
+<li>Apps such as Gmail or the Play Store that combine a broad set of data views with deep navigation</li>
</ul>
<p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
<h2 id="general-structure">General Structure</h2>
@@ -60,7 +60,7 @@ layouts that are visually engaging and appropriate for the data type and screen
<img src="{@docRoot}design/media/app_structure_market.png">
<div class="figure-caption">
- The Google Play app's start screen primarily allows navigation into the stores for Apps, Music, Books,
+ The Play Store app's start screen primarily allows navigation into the stores for Apps, Music, Books,
Movies and Games. It is also enriched with tailored recommendations and promotions that
surface content of interest to the user. Search is readily available from the action bar.
</div>
@@ -145,8 +145,8 @@ minimize navigational effort. Rule of thumb: no more than 5&ndash;7 tabs.</p>
<img src="{@docRoot}design/media/app_structure_scrolltabs.png">
<div class="figure-caption">
- Google Play uses tabs to simultaneously show category choice and content. To navigate between
- categories, users can swipe left/right on the content.
+ The Play Store app uses tabs to simultaneously show category choice and content. To navigate
+ between categories, users can swipe left/right on the content.
</div>
</div>
diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd
index d35cd8298eb0..7e288aeaad5b 100644
--- a/docs/html/design/patterns/navigation.jd
+++ b/docs/html/design/patterns/navigation.jd
@@ -13,32 +13,36 @@ the <em>Up</em> button, consisting of the app icon and a left-point caret.</p>
<h2 id="up-vs-back">Up vs. Back</h2>
-<p>The Up button is used to navigate within an application based on the hierarchical relationships
+<p>The Up button is used to navigate within an app based on the hierarchical relationships
between screens. For instance, if screen A displays a list of items, and selecting an item leads to
screen B (which presents that item in more detail), then screen B should offer an Up button that
returns to screen A.</p>
-<p>If a screen is the topmost one in an app (i.e. the home of the app), it should not present an Up
+<p>If a screen is the topmost one in an app (that is, the app's home), it should not present an Up
button.</p>
-<p>The system Back key is used to navigate based on the history of screens the user has recently seen,
-in reverse chronological order&mdash;in effect, the temporal relationships between screens.</p>
+
+<p>The system Back button is used to navigate, in reverse chronological order, through the history
+of screens the user has recently worked with. It is generally based on the temporal relationships
+between screens, rather than the app's hierarchy.</p>
+
<p>When the previously viewed screen is also the hierarchical parent of the current screen, pressing
-the Back key will have the same result as pressing an Up button -- this is a common occurrence.
-However, unlike the Up button, which ensures the user remains within your app, the Back key can
-return the user to the Home screen, or even to a different application.</p>
+the Back button has the same result as pressing an Up button&mdash;this is a common
+occurrence. However, unlike the Up button, which ensures the user remains within your app, the Back
+button can return the user to the Home screen, or even to a different app.</p>
<img src="{@docRoot}design/media/navigation_up_vs_back_gmail.png">
-<p>The Back key also supports a few behaviors not directly tied to screen-to-screen navigation:</p>
+<p>The Back button also supports a few behaviors not directly tied to screen-to-screen navigation:
+</p>
<ul>
-<li>Back dismisses floating windows (dialogs, popups)</li>
-<li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
-<li>Back hides the onscreen keyboard (IME)</li>
+<li>Dismisses floating windows (dialogs, popups)</li>
+<li>Dismisses contextual action bars, and removes the highlight from the selected items</li>
+<li>Hides the onscreen keyboard (IME)</li>
</ul>
<h2 id="within-app">Navigation Within Your App</h2>
<h4>Navigating to screens with multiple entry points</h4>
<p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
-from multiple entry points&mdash;e.g., a settings screen which can be navigated to from any screen
+from multiple entry points&mdash;such as a settings screen that can be reached from any other screen
in your app. In this case, the Up button should choose to return to the referring screen, behaving
identically to Back.</p>
<h4>Changing view within a screen</h4>
@@ -50,7 +54,7 @@ in the same place within the app's hierarchy, and no new navigation history is c
<li>Switching views using a dropdown (aka collapsed tabs)</li>
<li>Filtering a list</li>
<li>Sorting a list</li>
-<li>Changing display characteristics (e.g. zooming)</li>
+<li>Changing display characteristics (such as zooming)</li>
</ul>
<h4>Navigating between sibling screens</h4>
<p>When your app supports navigation from a list of items to a detail view of one of those items, it's
@@ -61,56 +65,140 @@ navigation does not change the behavior of Up or Back.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_gmail.png">
-<p>However, a notable exception to this occurs when browsing between "related" detail views not tied
-together by the referring list&mdash;for example, when browsing on Google Play between apps from
+<p>However, a notable exception to this occurs when browsing between related detail views not tied
+together by the referring list&mdash;for example, when browsing in the Play Store between apps from
the same developer, or albums by the same artist. In these cases, following each link does create
-history, causing the Back button to step through each screen of related content which has been
-viewed. Up should continue to bypass these related screens and navigate to the most recently viewed
-container screen.</p>
+history, causing the Back button to step through each previously viewed screen. Up should continue
+to bypass these related screens and navigate to the most recently viewed container screen.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market1.png">
<p>You have the ability to make the Up behavior even smarter based on your knowledge of detail
-view. If we extend our Google Play sample from above, imagine the user has navigated from the last Book
-viewed to the details for the Movie adaptation. In that case, Up can return to a container (Movies)
-which the user had not previously navigated through.</p>
+view. Extending the Play Store example from above, imagine the user has navigated from the last
+Book viewed to the details for the Movie adaptation. In that case, Up can return to a container
+(Movies) which the user hasn't previously navigated through.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market2.png">
-<h2 id="from-outside">Navigation From Outside Your App</h2>
+<h2 id="into-your-app">Navigation into Your App via Home Screen Widgets and Notifications</h2>
+
+<p>You can use Home screen widgets or notifications to help your users navigate directly to screens
+deep within your app's hierarchy. For example, Gmail's Inbox widget and new message notification can
+both bypass the Inbox screen, taking the user directly to a conversation view.</p>
+
+<p>For both of these cases, handle the Up button as follows:</p>
-<p>There are two categories of navigation from outside your app to screens deep within the app's
-hierarchy:</p>
<ul>
-<li>App-to-app navigation, such as via intent completion.</li>
-<li>System-to-app navigation, such as via notifications and home screen widgets.</li>
+<li><em>If the destination screen is typically reached from one particular screen within your
+app</em>, Up should navigate to that screen.</li>
+<li><em>Otherwise</em>, Up should navigate to the topmost ("Home") screen of your app.</li>
</ul>
-<p>Gmail provides examples of each of these. For app-to-app navigation, a "Share" intent goes directly
-to the compose screen. For system-to-app navigation, both a new message notification and a home
-screen widget can bypass the Inbox screen, taking the user directly to a conversation view.</p>
-<h4>App-to-app navigation</h4>
-<p>When navigating deep into your app's hierarchy directly from another app via an intent, Back will
-return to the referring app.</p>
-<p>The Up button is handled as follows:
-- If the destination screen is typically reached from one particular screen within your app, Up
- should navigate to that screen.
-- Otherwise, Up should navigate to the topmost ("Home") screen of your app.</p>
-<p>For example, after choosing to share a book being viewed on Google Play, the user navigates directly to
-Gmail's compose screen. From there, Up returns to the Inbox (which happens to be both the
-typical referrer to compose, as well as the topmost screen of the app), while Back returns to
-Google Play.</p>
-
-<img src="{@docRoot}design/media/navigation_from_outside_up.png">
-
-<h4>System-to-app navigation</h4>
-<p>If your app was reached via the system mechanisms of notifications or home screen widgets, Up
-behaves as described for app-to-app navigation, above.</p>
-<p>For the Back key, you should make navigation more predictably by inserting into the task's back
-stack the complete upward navigation path to the app's topmost screen. This way, a user who has
-forgotten how they entered your app can safely navigate to the app's topmost screen before exiting
-it.</p>
-<p>For example, Gmail's Home screen widget has a button for diving directly to its compose screen.
-After following that path, the Back key first returns to the Inbox, and from there continues to
-Home.</p>
+
+<p>In the case of the Back button, you should make navigation more predictable by inserting into the
+task's back stack the complete upward navigation path to the app's topmost screen. This allows users
+who've forgotten how they entered your app to navigate to the app's topmost screen before
+exiting.</p>
+
+<p>As an example, Gmail's Home screen widget has a button for diving directly to its compose
+screen. Up or Back from the compose screen would take the user to the Inbox, and from there the
+Back button continues to Home.</p>
<img src="{@docRoot}design/media/navigation_from_outside_back.png">
+
+<h4>Indirect notifications</h4>
+
+<p>When your app needs to present information about multiple events simultaneously, it can use a
+single notification that directs the user to an interstitial screen. This screen summarizes these
+events, and provides paths for the user to dive deeply into the app. Notifications of this style are
+called <em>indirect notifications</em>.</p>
+
+<p>Unlike standard (direct) notifications, pressing Back from an indirect notification's
+interstitial screen returns the user to the point the notification was triggered from&mdash;no
+additional screens are inserted into the back stack. Once the user proceeds into the app from its
+interstitial screen, Up and Back behave as for standard notifications, as described above:
+navigating within the app rather than returning to the interstitial.</p>
+
+<p>For example, suppose a user in Gmail receives an indirect notification from Calendar. Touching
+this notification opens the interstitial screen, which displays reminders for several different
+events. Touching Back from the interstitial returns the user to Gmail. Touching on a particular
+event takes the user away from the interstitial and into the full Calendar app to display details of
+the event. From the event details, Up and Back navigate to the top-level view of Calendar.</p>
+
+<img src="{@docRoot}design/media/navigation_indirect_notification.png">
+
+<h4>Pop-up notifications</h4>
+
+<p><em>Pop-up notifications</em> bypass the notification drawer, instead appearing directly in
+front of the user. They are rarely used, and <strong>should be reserved for occasions where a timely
+response is required and the interruption of the user's context is necessary</strong>. For example,
+Talk uses this style to alert the user of an invitation from a friend to join a video chat, as this
+invitation will automatically expire after a few seconds.</p>
+
+<p>In terms of navigation behavior, pop-up notifications closely follow the behavior of an indirect
+notification's interstitial screen. Back dismisses the pop-up notification. If the user navigates
+from the pop-up into the notifying app, Up and Back follow the rules for standard notifications,
+navigating within the app.</p>
+
+<img src="{@docRoot}design/media/navigation_popup_notification.png">
+
+<h2 id="between-apps">Navigation Between Apps</h2>
+
+<p>One of the fundamental strengths of the Android system is the ability for apps to activate each
+other, giving the user the ability to navigate directly from one app into another. For example, an
+app that needs to capture a photo can activate the Camera app, which will return the photo
+to the referring app. This is a tremendous benefit to both the developer, who can easily leverage
+code from other apps, and the user, who enjoys a consistent experience for commonly performed
+actions.</p>
+
+<p>To understand app-to-app navigation, it's important to understand the Android framework behavior
+discussed below.</p>
+
+<h4>Activities, tasks, and intents</h4>
+
+<p>In Android, an <strong>activity</strong> is an application component that defines a screen of
+information and all of the associated actions the user can perform. Your app is a collection of
+activities, consisting of both the activities you create and those you re-use from other apps.</p>
+
+<p>A <strong>task</strong> is the sequence of activities a user follows to accomplish a goal. A
+single task can make use of activities from just one app, or may draw on activities from a number
+of different apps.</p>
+
+<p>An <strong>intent</strong> is a mechanism for one app to signal it would like another
+app's assistance in performing an action. An app's activities can indicate which intents
+they can respond to. For common intents such as "Share", the user may have many apps installed
+that can fulfill that request.</p>
+
+<h4>Example: navigating between apps to support sharing</h4>
+
+<p>To understand how activities, tasks, and intents work together, consider how one app allows users
+to share content by using another app. For example, launching the Play Store app from Home begins
+new Task A (see figure below). After navigating through the Play Store and touching a promoted book
+to see its details, the user remains in the same task, extending it by adding activities. Triggering
+the Share action prompts the user with a dialog listing each of the activities (from different apps)
+which have registered to handle the Share intent.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_inward.png">
+
+<p>When the user elects to share via Gmail, Gmail's compose activity is added as a continuation of
+Task A&mdash;no new task is created. If Gmail had its own task running in the background, it would
+be unaffected.</p>
+
+<p>From the compose activity, sending the message or touching the Back button returns the user to
+the book details activity. Subsequent touches of Back continue to navigate back through the Play
+Store, ultimately arriving at Home.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_back.png">
+
+<p>However, by touching Up from the compose activity, the user indicates a desire to remain within
+Gmail. Gmail's conversation list activity appears, and a new Task B is created for it. New tasks are
+always rooted to Home, so touching Back from the conversation list returns there.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_up.png">
+
+<p>Task A persists in the background, and the user may return to it later (for example, via the
+Recents screen). If Gmail already had its own task running in the background, it would be replaced
+with Task B&mdash;the prior context is abandoned in favor of the user's new goal.</p>
+
+<p>When your app registers to handle intents with an activity deep within the app's hierarchy,
+refer to <a href="#into-your-app">Navigation into Your App via Home Screen Widgets and
+Notifications</a> for guidance on how to specify Up navigation.</p>
diff --git a/docs/html/design/patterns/pure-android.jd b/docs/html/design/patterns/pure-android.jd
index 8ed1347db35c..77813c0d9fda 100644
--- a/docs/html/design/patterns/pure-android.jd
+++ b/docs/html/design/patterns/pure-android.jd
@@ -48,7 +48,8 @@ conventions of a different platform.</p>
document or deleting.</p>
<p>As you are migrating your app to Android, please swap out platform-specific icons with their Android
counterparts.</p>
-<p>You can find a wide variety of icons for use in your app in the Android SDK.</p>
+<p>You can find a wide variety of icons for use in your app on the
+<a href="{@docRoot}design/downloads/index.html">Downloads</a> page.</p>
</div>
<div class="layout-content-col span-8">
@@ -89,6 +90,33 @@ platform and to avoid confusion between actions and view switching on Android.</
<div class="layout-content-row">
<div class="layout-content-col span-5">
+<h4>Don't hardcode links to other apps</h4>
+<p>In some cases you might want your app to take advantage of another app's feature set. For
+example, you may want to share the content that your app created via a social network or messaging
+app, or view the content of a weblink in a browser. Don't use hard-coded, explicit links to
+particular apps to achieve this. Instead, use Android's intent API to launch an activity chooser
+which lists all applications that are set up to handle the particular request. This lets the user
+complete the task with their preferred app. For sharing in particular, consider using the <em>Share
+Action Provider</em> in your action bar to provide faster access to the user's most recently used
+sharing target.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/migrating_intents.png">
+ <div class="figure-caption">
+ Link to other apps with the activity chooser or use the <em>Share Action Provider</em> in the
+ action bar.
+ </div>
+
+ </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5">
+
<h4>Don't use labeled back buttons on action bars</h4>
<p>Other platforms use an explicit back button with label to allow the user to navigate up the
application's hierarchy. Instead, Android uses the main action bar's app icon for hierarchical
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
new file mode 100644
index 000000000000..3b28b849319f
--- /dev/null
+++ b/docs/html/design/patterns/settings.jd
@@ -0,0 +1,689 @@
+page.title=Settings
+@jd:body
+
+<p>Settings is a place in your app where users indicate their preferences for how your app should
+behave. This benefits users because:</p>
+
+<ul>
+<li>You don't need to interrupt them with the same questions over and over when certain situations
+arise. The settings predetermine what will always happen in those situations (see design
+principle: <a href="{@docRoot}design/get-started/principles.html#decide-for-me">Decide for me but
+let me have the final say</a>).</li>
+<li>You help them feel at home and in control (see design principle:
+<a href="{@docRoot}design/get-started/principles.html#make-it-mine">Let me make it mine</a>).</li>
+</ul>
+
+<h2 id="flow-structure">Flow and Structure</h2>
+
+<h4 id="settings-access">Provide access to Settings in the action overflow</h4>
+
+<p>Settings is given low prominence in the UI because it's not frequently needed. Even if there's
+room in the <a href="{@docRoot}design/patterns/actionbar.html">action bar</a>, never make Settings
+an action button. Always keep it in the action overflow and label it "Settings". Place it below
+all other items except "Help".</p>
+
+<img src="{@docRoot}design/media/settings_overflow.png">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4 id="what-to-make-a-setting">Avoid the temptation to make everything a setting</h4>
+
+<p>Because Settings is a few navigational steps away, no matter how many items you have, they'll
+never clutter up the core part of your UI. This may seem like good news, but it also poses a
+challenge.</p>
+
+<p>Settings can be a tempting place to keep a lot of stuff&mdash;like a hall closet where things
+get stashed when you tidy up before company comes over. It's not a place where you spend lots of
+time, so it's easy to rationalize and ignore its cluttered condition. But when users visit
+Settings&mdash;however infrequently&mdash;they'll have the same expectations for the experience as
+they do everywhere else in your app. More settings means more choices to make, and too many are
+overwhelming.</p>
+
+<p>So don't punt on the difficult product decisions and debates that can bring on the urge to
+"just make it a setting". For each control you're considering adding to Settings, make sure it
+meets the bar:</p>
+
+<img src="{@docRoot}design/media/settings_flowchart.png">
+
+<div class="vspace size-3">&nbsp;</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5 with-callouts">
+
+<h4 id="group-settings">If you still have lots of settings, group related settings together</h4>
+
+<p>The number of items an average human can hold in short-term memory is 7&plusmn;2. If you
+present a list of 10 or more settings (even after applying the criteria above), users will have
+more difficulty scanning, comprehending, and processing them.</p>
+
+<p>You can remedy this by dividing some or all of the settings into groups, effectively turning
+one long list into multiple shorter lists. A group of related settings can be presented in one of
+two ways:</p>
+
+<ol>
+<li><h4>Under a section divider</h4></li>
+<li><h4>In a separate subscreen</h4></li>
+</ol>
+
+<p>You can use one or both these grouping techniques to organize your app's settings.</p>
+
+<p>For example, in the main screen of the Android Settings app, each item in the list navigates
+to a subscreen of related settings. In addition, the items themselves are grouped under section
+dividers.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/settings_grouping.png">
+
+ </div>
+</div>
+
+<p>Grouping settings is not an exact science, but here's some advice for how to approach it, based
+on the total number of settings in your app.</p>
+
+<div class="vspace size-1">&nbsp;</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>7 or fewer</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Don't group them at all. It won't benefit users and will seem like overkill.</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>8 to 10</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Try grouping related settings under 1 or 2 section dividers. If you have any "singletons"
+(settings that don't relate to any other settings and can't be grouped under your section
+dividers), treat them as follows:</p>
+
+<ul>
+<li>If they include some of your most important settings, list them at the top without a section
+divider.</li>
+<li>Otherwise, list them at the bottom with a section divider called "OTHER", in order of
+importance.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>11 to 15</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Same advice as above, but try 2 to 4 section dividers.</p>
+
+<p>Also, try the following to reduce the list:</p>
+
+<ul>
+<li>If 2 or more of the settings are mainly for power users, move them out of your main Settings
+screen and into an "Advanced" subscreen. Place an item in the action overflow called "Advanced" to
+navigate to it.</li>
+<li>Look for "doubles": two settings that relate to one another, but not to any other settings.
+Try to combine them into one setting, using the design patterns described later in this section.
+For example, you might be able to redesign two related checkbox settings into one multiple choice
+setting.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>16 or more</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>If you have any instances of 4 or more related settings, group them under a subscreen. Then use
+the advice suggested above for the reduced list size.</p>
+
+ </div>
+</div>
+
+
+<h2 id="patterns">Design Patterns</h2>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Checkbox</h4>
+<p>Use this pattern for a setting that is either selected or not selected.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_checkbox.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Multiple choice</h4>
+<p>Use this pattern for a setting that needs to present a discrete set of options, from which the
+user can choose only one.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_multiple_choice.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Slider</h4>
+<p>Use this pattern for a setting where the range of values are not discrete and fall along a
+continuum.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_slider.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Date/time</h4>
+<p>Use this pattern for a setting that needs to collect a date and/or time from the user.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_date_time.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Subscreen navigation</h4>
+<p>Use this pattern for navigating to a subscreen or sequence of subscreens that guide the user
+through a more complex setup process.</p>
+<ul>
+<li>If navigating to a single subscreen, use the same title in both the subscreen and the label
+navigating to it.</li>
+<li>If navigating to a sequence of subscreens (as in this example), use a title that describes the
+first step in the sequence.</li>
+</ul>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_subscreen_navigation.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>List subscreen</h4>
+<p>Use this pattern for a setting or category of settings that contains a list of equivalent items.
+</p>
+<p>The label provides the name of the item, and secondary text may be used for status. (In this
+example, status is reinforced with an icon to the right of the label.) Any actions associated with
+the list appear in the action bar rather than the list itself.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_list_subscreen.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Master on/off switch</h4>
+<p>Use this pattern for a category of settings that need a mechanism for turning on or off as a
+whole.</p>
+<p>An on/off switch is placed as the first item in the action bar of a subscreen. When the switch
+is turned off, the items in the list disappear, replaced by text that describes why the list is
+empty. If any actions require the switch to be on, they become disabled.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<p>You can also echo the master on/off switch in the menu item that leads to the subscreen.
+However, you should only do this in cases where users rarely need to access the subscreen once
+it's initially set up and more often just want to toggle the switch.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off_2.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Individual on/off switch</h4>
+<p>Use this pattern for an individual setting that requires a more elaborate description than can
+be provided in checkbox form.</p>
+<p>The on/off switch only appears in the subscreen so that users aren't able to toggle it without
+also being exposed to the descriptive text. Secondary text appears below the setting label to
+reflect the current selection.</p>
+<p>In this example, Android Beam is on by default. Since users might not know what this setting
+does, we made the status more descriptive than just "On".</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_individual_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Dependency</h4>
+<p>Use this pattern for a setting that changes availability based on the value of another setting.
+</p>
+<p>The disabled setting appears below its dependency, without any indentation. If the setting
+includes a status line, it says "Unavailable", and if the reason isn't obvious, a brief
+explanation is included in the status.</p>
+<p>If a given setting is a dependency to 3 or more settings, consider using a subscreen with a
+master on/off switch so that your main settings screen isn't cluttered by lots of disabled items.
+</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_dependency.png">
+
+ </div>
+</div>
+
+<h2 id="defaults">Defaults</h2>
+
+<p>Take great care in choosing default values for each of your settings. Because settings
+determine app behavior, your choices will contribute to users' first impressions of your app. Even
+though users can change settings, they'll expect the initial states to be sensible. The following
+questions (when applicable) may help inform your decisions:</p>
+
+<ul>
+<li>Which choice would most users be likely to choose on their own if there were no default?</li>
+<li>Which choice is the most neutral or middle-of-the-road?</li>
+<li>Which choice is the least risky, controversial, or over-the-top?</li>
+<li>Which choice uses the least amount of battery or mobile data?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#never-lose-my-stuff">Never lose my stuff</a>?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#interrupt-only-if-important">Only interrupt
+me if it's important</a>?
+</li>
+</ul>
+
+<h2 id="writing">Writing Guidelines</h2>
+
+<h4>Label clearly and concisely</h4>
+
+<p>Writing a good label for a setting can be challenging because space is very limited. You only
+get one line, and it's incredibly short on the smallest of devices. Follow these guidelines to
+make your labels brief, meaningful, and scannable:</p>
+
+<ul>
+<li>Write each label in sentence case (i.e. only the first word and proper nouns are capitalized).
+</li>
+<li>Don't start a label with an instructional verb like "Set", "Change", "Edit", "Modify",
+"Manage", "Use", "Select", or "Choose". Users already understand that they can do these things to
+settings.</li>
+<li>Likewise, don't end a label with a word like "setting" or "settings". It's already implied.
+</li>
+<li>If the setting is part of a grouping, don't repeat the word(s) used in the section divider or
+subscreen title.</li>
+<li>Avoid starting a label with a negative word like "Don't" or "Never". For example, "Don't
+allow" could be rephrased to "Block".</li>
+<li>Steer clear of technical jargon as much as possible, unless it's a term widely understood by
+your target users. Use common verbs and nouns to convey the setting's purpose rather than its
+underlying technology.</li>
+<li>Don't refer to the user. For example, for a setting allowing the user to turn notifications on
+or off, label it "Notifications" instead of "Notify me".</li>
+</ul>
+
+<p>Once you've decided on labels for your settings, be sure to preview them on an
+<a href="{@docRoot}design/style/metrics-grids.html">LDPI handset</a> in portrait to make sure
+they'll fit everywhere.</p>
+
+<h4>Secondary text below is for status, not description&hellip;</h4>
+
+<p>Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe
+it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status.
+</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+</div>
+
+<p>Status in secondary text has the following benefits:</p>
+<ul>
+<li>Users can see at a glance what the current value of a setting is without having to navigate
+any further.</li>
+<li>It applies the design principle
+<a href="{@docRoot}design/get-started/principles.html#keep-it-brief">Keep it brief</a>, which
+users greatly appreciate.</li>
+</ul>
+
+<h4>&hellip;unless it's a checkbox setting</h4>
+<p>There's one important exception to the using secondary text for status: checkbox settings.
+Here, use secondary text for description, not status. Status below a checkbox is unnecessary
+because the checkbox already indicates it. The reason why it's appropriate to have a description
+below a checkbox setting is because&mdash;unlike other controls&mdash;it doesn't display a dialog
+or navigate to another screen where additional information can be provided.</p>
+
+<p>That said, if a checkbox setting's label is clear enough on its own, there's no need to also
+provide a description. Only include one if necessary.</p>
+
+<p>Follow these guidelines to write checkbox setting descriptions:</p>
+<ul>
+<li>Keep it to one sentence and don't use ending punctuation.</li>
+<li>Convey what happens when the setting is checked, phrased in the form of a command. Example:
+"Allow data exchange", not "Allows data exchange".</li>
+<li>Avoid repetition by choosing words that don't already appear in the label.</li>
+<li>Don't refer to the user unless it's necessary for understanding the setting.</li>
+<li>If you must refer to the user, do so in the second person ("you") rather than the first person
+("I"). Android speaks to users, not on behalf of them.</li>
+</ul>
+
+<h4>Writing examples</h4>
+
+<p>The following are examples of changes we made to labels and secondary text in the Settings app
+in Ice Cream Sandwich.</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Use tactile feedback
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Vibrate on touch
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting, we eliminated the throwaway word "Use" and rephrased the label to be
+more direct and understandable.</p>
+
+ </div>
+
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this multiple choice setting, we changed the label to a friendlier term and also replaced
+the description with status. We put some descriptive words around the selected value, "10
+minutes", because on its own, the meaning could be misinterpreted as "sleep for 10 minutes".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Change screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Change or disable pattern, PIN, or password security
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Pattern
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>This setting navigates to a a sequence of subscreens that allow users to choose a type of
+screen lock and then set it up. We eliminated the throwaway word "Change" in the label, and
+replaced the description with the current type of screen lock set up by the user. If the user
+hasn't set up a screen lock, the secondary text says "None".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Use Near Field Communication to read and exchange tags
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Allow data exchange when the phone touches another device
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting&mdash;although it's technical jargon&mdash;we kept the "NFC" label
+because: (1) we couldn't find a clear, concise alternative, and (2) user familiarity with the
+acronym is expected to increase dramatically in the next couple of years.</p>
+<p>We did, however, rewrite the description. It's far less technical than before and does a better
+job of conveying how and why you'd use NFC. We didn't include what the acronym stands for because
+it doesn't mean anything to most users and would have taken up a lot of space.</p>
+
+ </div>
+</div>
+
+<h2 id="checklist">Checklist</h2>
+<ul>
+<li><p>Make sure each item in Settings meets the criteria for belonging there.</p></li>
+<li><p>If you have more than 7 items, explore ways to group related settings.</p></li>
+<li><p>Use design patterns wherever applicable so users don't face a learning curve.</p></li>
+<li><p>Choose defaults that are safe, neutral, and fit the majority of users.</p></li>
+<li><p>Give each setting a clear, concise label and use secondary text appropriately.</p></li>
+</ul> \ No newline at end of file
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
index 80fd03e66745..919ea7aa1fdf 100644
--- a/docs/html/design/style/writing.jd
+++ b/docs/html/design/style/writing.jd
@@ -1,58 +1,6 @@
page.title=Writing Style
@jd:body
-<style>
-
-/* UI tables */
-
-.ui_table {
- width: 100%;
- background: #282828;
- color: #fff;
- border-radius: 2px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.25);
- border-collapse: separate;
-}
-
-.ui_table th,
-.ui_table td {
- padding: 5px 10px;
-}
-
-.ui_table thead th {
- font-weight: 600;
-}
-
-.ui_table tfoot td {
- border-top: 1px solid #494949;
- border-right: 1px solid #494949;
- text-align: center;
-}
-
-.ui_table tfoot td:last-child {
- border-right: 0;
-}
-
-.list_item_margins {
- margin-left: 30px !important;
-}
-
-.example_label {
- margin-bottom: 10px;
- padding-left: 20px;
- background: transparent none no-repeat scroll 0px 3px;
-}
-
-.example_label.bad {
- background-image: url({@docRoot}assets/design/ico_wrong.png);
-}
-
-.example_label.good {
- background-image: url({@docRoot}assets/design/ico_good.png);
-}
-
-</style>
-
<p>When choosing words for your app:</p>
<ol>
<li>
@@ -90,20 +38,20 @@ page.title=Writing Style
<ol><li class="value-1"><strong>Keep it brief.</strong> From the setup wizard:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Too formal</div>
+ <div class="do-dont-label bad">Too formal</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Consult the documentation that came with your phone for further instructions.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Read the instructions that came with your phone.
</td></tr></tbody></table>
@@ -115,11 +63,11 @@ page.title=Writing Style
<ol><li class="value-2"><strong>Keep it simple.</strong> From the Location settings screen:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing</div>
+ <div class="do-dont-label bad">Confusing</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -139,9 +87,9 @@ page.title=Writing Style
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
@@ -167,12 +115,12 @@ page.title=Writing Style
crashes:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing and annoying&mdash;"Sorry" just rubs salt in the
+ <div class="do-dont-label bad">Confusing and annoying&mdash;"Sorry" just rubs salt in the
wound.</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th colspan="3">
@@ -200,9 +148,9 @@ crashes:</ol>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
+ <div class="do-dont-label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th colspan="3">
@@ -234,20 +182,20 @@ crashes:</ol>
<ol><li class="value-4"><strong>Put the most important thing first.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Top news last</div>
+ <div class="do-dont-label bad">Top news last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
77 other people +1'd this, including Larry Page.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Top news first</div>
+ <div class="do-dont-label good">Top news first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Larry Page and 77 others +1'd this.
</td></tr></tbody></table>
@@ -255,20 +203,20 @@ crashes:</ol>
</div>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Task last</div>
+ <div class="do-dont-label bad">Task last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
Touch Next to complete setup using a Wi-Fi connection.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Task first</div>
+ <div class="do-dont-label good">Task first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
To finish setup using Wi-Fi, touch Next.
</td></tr></tbody></table>
@@ -280,11 +228,11 @@ crashes:</ol>
<ol><li class="value-5"><strong>Describe only what's necessary, and no more.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">From a Setup Wizard screen</div>
+ <div class="do-dont-label bad">From a Setup Wizard screen</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -306,9 +254,9 @@ crashes:</ol>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">From a Setup Wizard screen</div>
+ <div class="do-dont-label good">From a Setup Wizard screen</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
diff --git a/docs/html/guide/developing/testing/testing_otheride.jd b/docs/html/guide/developing/testing/testing_otheride.jd
index 93af97941d9b..7745ae775f41 100644
--- a/docs/html/guide/developing/testing/testing_otheride.jd
+++ b/docs/html/guide/developing/testing/testing_otheride.jd
@@ -209,7 +209,7 @@ $ android create test-project -m ../HelloAndroid -n HelloAndroidTest -p HelloAnd
<p>
To update a test project with the <code>android</code> tool, enter:
</p>
-<pre>android update-test-project -m &lt;main_path&gt; -p &lt;test_path&gt;</pre>
+<pre>android update test-project -m &lt;main_path&gt; -p &lt;test_path&gt;</pre>
<table>
<tr>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index bbbe6fb119fa..5297c23c82cd 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -299,6 +299,31 @@ class="new">&nbsp;new!</span></span>
</li>
</ul>
</li>
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/displaying-bitmaps/index.html">
+ <span class="en">Displaying Bitmaps Efficiently<span class="new">&nbsp;new!</span></span>
+ </a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/load-bitmap.html">
+ <span class="en">Loading Large Bitmaps Efficiently</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/process-bitmap.html">
+ <span class="en">Processing Bitmaps Off the UI Thread</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/cache-bitmap.html">
+ <span class="en">Caching Bitmaps</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/display-bitmap.html">
+ <span class="en">Displaying Bitmaps in Your UI</span>
+ </a>
+ </li>
+ </ul>
+ </li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/accessibility/index.html">
diff --git a/docs/html/shareables/training/BitmapFun.zip b/docs/html/shareables/training/BitmapFun.zip
new file mode 100644
index 000000000000..e7e71f9f85a0
--- /dev/null
+++ b/docs/html/shareables/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/shareables/training/LocationAware.zip b/docs/html/shareables/training/LocationAware.zip
index e1926faa6930..46970cdc174c 100644
--- a/docs/html/shareables/training/LocationAware.zip
+++ b/docs/html/shareables/training/LocationAware.zip
Binary files differ
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
new file mode 100644
index 000000000000..94abe2186561
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -0,0 +1,337 @@
+page.title=Caching Bitmaps
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Displaying Bitmaps in Your UI
+next.link=display-bitmap.html
+previous.title=Processing Bitmaps Off the UI Thread
+previous.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#memory-cache">Use a Memory Cache</a></li>
+ <li><a href="#disk-cache">Use a Disk Cache</a></li>
+ <li><a href="#config-changes">Handle Configuration Changes</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Loading a single bitmap into your user interface (UI) is straightforward, however things get more
+complicated if you need to load a larger set of images at once. In many cases (such as with
+components like {@link android.widget.ListView}, {@link android.widget.GridView} or {@link
+android.support.v4.view.ViewPager }), the total number of images on-screen combined with images that
+might soon scroll onto the screen are essentially unlimited.</p>
+
+<p>Memory usage is kept down with components like this by recycling the child views as they move
+off-screen. The garbage collector also frees up your loaded bitmaps, assuming you don't keep any
+long lived references. This is all good and well, but in order to keep a fluid and fast-loading UI
+you want to avoid continually processing these images each time they come back on-screen. A memory
+and disk cache can often help here, allowing components to quickly reload processed images.</p>
+
+<p>This lesson walks you through using a memory and disk bitmap cache to improve the responsiveness
+and fluidity of your UI when loading multiple bitmaps.</p>
+
+<h2 id="memory-cache">Use a Memory Cache</h2>
+
+<p>A memory cache offers fast access to bitmaps at the cost of taking up valuable application
+memory. The {@link android.util.LruCache} class (also available in the <a
+href="{@docRoot}reference/android/support/v4/util/LruCache.html">Support Library</a> for use back
+to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently
+referenced objects in a strong referenced {@link java.util.LinkedHashMap} and evicting the least
+recently used member before the cache exceeds its designated size.</p>
+
+<p class="note"><strong>Note:</strong> In the past, a popular memory cache implementation was a
+{@link java.lang.ref.SoftReference} or {@link java.lang.ref.WeakReference} bitmap cache, however
+this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more
+aggressive with collecting soft/weak references which makes them fairly ineffective. In addition,
+prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which
+is not released in a predictable manner, potentially causing an application to briefly exceed its
+memory limits and crash.</p>
+
+<p>In order to choose a suitable size for a {@link android.util.LruCache}, a number of factors
+should be taken into consideration, for example:</p>
+
+<ul>
+ <li>How memory intensive is the rest of your activity and/or application?</li>
+ <li>How many images will be on-screen at once? How many need to be available ready to come
+ on-screen?</li>
+ <li>What is the screen size and density of the device? An extra high density screen (xhdpi) device
+ like <a href="http://www.android.com/devices/detail/galaxy-nexus">Galaxy Nexus</a> will need a
+ larger cache to hold the same number of images in memory compared to a device like <a
+ href="http://www.android.com/devices/detail/nexus-s">Nexus S</a> (hdpi).</li>
+ <li>What dimensions and configuration are the bitmaps and therefore how much memory will each take
+ up?</li>
+ <li>How frequently will the images be accessed? Will some be accessed more frequently than others?
+ If so, perhaps you may want to keep certain items always in memory or even have multiple {@link
+ android.util.LruCache} objects for different groups of bitmaps.</li>
+ <li>Can you balance quality against quantity? Sometimes it can be more useful to store a larger
+ number of lower quality bitmaps, potentially loading a higher quality version in another
+ background task.</li>
+</ul>
+
+<p>There is no specific size or formula that suits all applications, it's up to you to analyze your
+usage and come up with a suitable solution. A cache that is too small causes additional overhead with
+no benefit, a cache that is too large can once again cause {@code java.lang.OutOfMemory} exceptions
+and leave the rest of your app little memory to work with.</p>
+
+<p>Here’s an example of setting up a {@link android.util.LruCache} for bitmaps:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Get memory class of this device, exceeding this amount will throw an
+ // OutOfMemory exception.
+ final int memClass = ((ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE)).getMemoryClass();
+
+ // Use 1/8th of the available memory for this memory cache.
+ final int cacheSize = 1024 * 1024 * memClass / 8;
+
+ mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ &#64;Override
+ protected int sizeOf(String key, Bitmap bitmap) {
+ // The cache size will be measured in bytes rather than number of items.
+ return bitmap.getByteCount();
+ }
+ };
+ ...
+}
+
+public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+}
+
+public Bitmap getBitmapFromMemCache(String key) {
+ return mMemoryCache.get(key);
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> In this example, one eighth of the application memory is
+allocated for our cache. On a normal/hdpi device this is a minimum of around 4MB (32/8). A full
+screen {@link android.widget.GridView} filled with images on a device with 800x480 resolution would
+use around 1.5MB (800*480*4 bytes), so this would cache a minimum of around 2.5 pages of images in
+memory.</p>
+
+<p>When loading a bitmap into an {@link android.widget.ImageView}, the {@link android.util.LruCache}
+is checked first. If an entry is found, it is used immediately to update the {@link
+android.widget.ImageView}, otherwise a background thread is spawned to process the image:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ final String imageKey = String.valueOf(resId);
+
+ final Bitmap bitmap = getBitmapFromMemCache(imageKey);
+ if (bitmap != null) {
+ mImageView.setImageBitmap(bitmap);
+ } else {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+}
+</pre>
+
+<p>The <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> also needs to be
+updated to add entries to the memory cache:</p>
+
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+ // Decode image in background.
+ &#64;Override
+ protected Bitmap doInBackground(Integer... params) {
+ final Bitmap bitmap = decodeSampledBitmapFromResource(
+ getResources(), params[0], 100, 100));
+ addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
+ return bitmap;
+ }
+ ...
+}
+</pre>
+
+<h2 id="disk-cache">Use a Disk Cache</h2>
+
+<p>A memory cache is useful in speeding up access to recently viewed bitmaps, however you cannot
+rely on images being available in this cache. Components like {@link android.widget.GridView} with
+larger datasets can easily fill up a memory cache. Your application could be interrupted by another
+task like a phone call, and while in the background it might be killed and the memory cache
+destroyed. Once the user resumes, your application it has to process each image again.</p>
+
+<p>A disk cache can be used in these cases to persist processed bitmaps and help decrease loading
+times where images are no longer available in a memory cache. Of course, fetching images from disk
+is slower than loading from memory and should be done in a background thread, as disk read times can
+be unpredictable.</p>
+
+<p class="note"><strong>Note:</strong> A {@link android.content.ContentProvider} might be a more
+appropriate place to store cached images if they are accessed more frequently, for example in an
+image gallery application.</p>
+
+<p>Included in the sample code of this class is a basic {@code DiskLruCache} implementation.
+However, a more robust and recommended {@code DiskLruCache} solution is included in the Android 4.0
+source code ({@code libcore/luni/src/main/java/libcore/io/DiskLruCache.java}). Back-porting this
+class for use on previous Android releases should be fairly straightforward (a <a
+href="http://www.google.com/search?q=disklrucache">quick search</a> shows others who have already
+implemented this solution).</p>
+
+<p>Here’s updated example code that uses the simple {@code DiskLruCache} included in the sample
+application of this class:</p>
+
+<pre>
+private DiskLruCache mDiskCache;
+private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
+private static final String DISK_CACHE_SUBDIR = "thumbnails";
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Initialize memory cache
+ ...
+ File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
+ mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
+ ...
+}
+
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+ // Decode image in background.
+ &#64;Override
+ protected Bitmap doInBackground(Integer... params) {
+ final String imageKey = String.valueOf(params[0]);
+
+ // Check disk cache in background thread
+ Bitmap bitmap = getBitmapFromDiskCache(imageKey);
+
+ if (bitmap == null) { // Not found in disk cache
+ // Process as normal
+ final Bitmap bitmap = decodeSampledBitmapFromResource(
+ getResources(), params[0], 100, 100));
+ }
+
+ // Add final bitmap to caches
+ addBitmapToCache(String.valueOf(imageKey, bitmap);
+
+ return bitmap;
+ }
+ ...
+}
+
+public void addBitmapToCache(String key, Bitmap bitmap) {
+ // Add to memory cache as before
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+
+ // Also add to disk cache
+ if (!mDiskCache.containsKey(key)) {
+ mDiskCache.put(key, bitmap);
+ }
+}
+
+public Bitmap getBitmapFromDiskCache(String key) {
+ return mDiskCache.get(key);
+}
+
+// Creates a unique subdirectory of the designated app cache directory. Tries to use external
+// but if not mounted, falls back on internal storage.
+public static File getCacheDir(Context context, String uniqueName) {
+ // Check if media is mounted or storage is built-in, if so, try and use external cache dir
+ // otherwise use internal cache dir
+ final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
+ || !Environment.isExternalStorageRemovable() ?
+ context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
+
+ return new File(cachePath + File.separator + uniqueName);
+}
+</pre>
+
+<p>While the memory cache is checked in the UI thread, the disk cache is checked in the background
+thread. Disk operations should never take place on the UI thread. When image processing is
+complete, the final bitmap is added to both the memory and disk cache for future use.</p>
+
+<h2 id="config-changes">Handle Configuration Changes</h2>
+
+<p>Runtime configuration changes, such as a screen orientation change, cause Android to destroy and
+restart the running activity with the new configuration (For more information about this behavior,
+see <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>).
+You want to avoid having to process all your images again so the user has a smooth and fast
+experience when a configuration change occurs.</p>
+
+<p>Luckily, you have a nice memory cache of bitmaps that you built in the <a
+href="#memory-cache">Use a Memory Cache</a> section. This cache can be passed through to the new
+activity instance using a {@link android.app.Fragment} which is preserved by calling {@link
+android.app.Fragment#setRetainInstance setRetainInstance(true)}). After the activity has been
+recreated, this retained {@link android.app.Fragment} is reattached and you gain access to the
+existing cache object, allowing images to be quickly fetched and re-populated into the {@link
+android.widget.ImageView} objects.</p>
+
+<p>Here’s an example of retaining a {@link android.util.LruCache} object across configuration
+changes using a {@link android.app.Fragment}:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ RetainFragment mRetainFragment =
+ RetainFragment.findOrCreateRetainFragment(getFragmentManager());
+ mMemoryCache = RetainFragment.mRetainedCache;
+ if (mMemoryCache == null) {
+ mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ ... // Initialize cache here as usual
+ }
+ mRetainFragment.mRetainedCache = mMemoryCache;
+ }
+ ...
+}
+
+class RetainFragment extends Fragment {
+ private static final String TAG = "RetainFragment";
+ public LruCache<String, Bitmap> mRetainedCache;
+
+ public RetainFragment() {}
+
+ public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
+ RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
+ if (fragment == null) {
+ fragment = new RetainFragment();
+ }
+ return fragment;
+ }
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ <strong>setRetainInstance(true);</strong>
+ }
+}
+</pre>
+
+<p>To test this out, try rotating a device both with and without retaining the {@link
+android.app.Fragment}. You should notice little to no lag as the images populate the activity almost
+instantly from memory when you retain the cache. Any images not found in the memory cache are
+hopefully available in the disk cache, if not, they are processed as usual.</p>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
new file mode 100644
index 000000000000..7a93313cb998
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -0,0 +1,400 @@
+page.title=Displaying Bitmaps in Your UI
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Caching Bitmaps
+previous.link=cache-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#viewpager">Load Bitmaps into a ViewPager Implementation</a></li>
+ <li><a href="#gridview">Load Bitmaps into a GridView Implementation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}design/patterns/swipe-views.html">Android Design: Swipe Views</a></li>
+ <li><a href="{@docRoot}design/building-blocks/grid-lists.html">Android Design: Grid Lists</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p></p>
+
+<p>This lesson brings together everything from previous lessons, showing you how to load multiple
+bitmaps into {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+components using a background thread and bitmap cache, while dealing with concurrency and
+configuration changes.</p>
+
+<h2 id="viewpager">Load Bitmaps into a ViewPager Implementation</h2>
+
+<p>The <a href="{@docRoot}design/patterns/swipe-views.html">swipe view pattern</a> is an excellent
+way to navigate the detail view of an image gallery. You can implement this pattern using a {@link
+android.support.v4.view.ViewPager} component backed by a {@link
+android.support.v4.view.PagerAdapter}. However, a more suitable backing adapter is the subclass
+{@link android.support.v4.app.FragmentStatePagerAdapter} which automatically destroys and saves
+state of the {@link android.app.Fragment Fragments} in the {@link android.support.v4.view.ViewPager}
+as they disappear off-screen, keeping memory usage down.</p>
+
+<p class="note"><strong>Note:</strong> If you have a smaller number of images and are confident they
+all fit within the application memory limit, then using a regular {@link
+android.support.v4.view.PagerAdapter} or {@link android.support.v4.app.FragmentPagerAdapter} might
+be more appropriate.</p>
+
+<p>Here’s an implementation of a {@link android.support.v4.view.ViewPager} with {@link
+android.widget.ImageView} children. The main activity holds the {@link
+android.support.v4.view.ViewPager} and the adapter:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ public static final String EXTRA_IMAGE = "extra_image";
+
+ private ImagePagerAdapter mAdapter;
+ private ViewPager mPager;
+
+ // A static dataset to back the ViewPager adapter
+ public final static Integer[] imageResIds = new Integer[] {
+ R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+ R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+ R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
+
+ mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPager.setAdapter(mAdapter);
+ }
+
+ public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
+ private final int mSize;
+
+ public ImagePagerAdapter(FragmentManager fm, int size) {
+ super(fm);
+ mSize = size;
+ }
+
+ &#64;Override
+ public int getCount() {
+ return mSize;
+ }
+
+ &#64;Override
+ public Fragment getItem(int position) {
+ return ImageDetailFragment.newInstance(position);
+ }
+ }
+}
+</pre>
+
+<p>The details {@link android.app.Fragment} holds the {@link android.widget.ImageView} children:</p>
+
+<pre>
+public class ImageDetailFragment extends Fragment {
+ private static final String IMAGE_DATA_EXTRA = "resId";
+ private int mImageNum;
+ private ImageView mImageView;
+
+ static ImageDetailFragment newInstance(int imageNum) {
+ final ImageDetailFragment f = new ImageDetailFragment();
+ final Bundle args = new Bundle();
+ args.putInt(IMAGE_DATA_EXTRA, imageNum);
+ f.setArguments(args);
+ return f;
+ }
+
+ // Empty constructor, required as per Fragment docs
+ public ImageDetailFragment() {}
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
+ }
+
+ &#64;Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // image_detail_fragment.xml contains just an ImageView
+ final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
+ mImageView = (ImageView) v.findViewById(R.id.imageView);
+ return v;
+ }
+
+ &#64;Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final int resId = ImageDetailActivity.imageResIds[mImageNum];
+ <strong>mImageView.setImageResource(resId);</strong> // Load image into ImageView
+ }
+}
+</pre>
+
+<p>Hopefully you noticed the issue with this implementation; The images are being read from
+resources on the UI thread which can lead to an application hanging and being force closed. Using an
+{@link android.os.AsyncTask} as described in the <a href="process-bitmap.html">Processing Bitmaps Off
+the UI Thread</a> lesson, it’s straightforward to move image loading and processing to a background
+thread:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ ...
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+
+ ... // include <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> class
+}
+
+public class ImageDetailFragment extends Fragment {
+ ...
+
+ &#64;Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (ImageDetailActivity.class.isInstance(getActivity())) {
+ final int resId = ImageDetailActivity.imageResIds[mImageNum];
+ // Call out to ImageDetailActivity to load the bitmap in a background thread
+ ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
+ }
+ }
+}
+</pre>
+
+<p>Any additional processing (such as resizing or fetching images from the network) can take place
+in the <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> without affecting
+responsiveness of the main UI. If the background thread is doing more than just loading an image
+directly from disk, it can also be beneficial to add a memory and/or disk cache as described in the
+lesson <a href="cache-bitmap.html#memory-cache">Caching Bitmaps</a>. Here's the additional
+modifications for a memory cache:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ ...
+ private LruCache<String, Bitmap> mMemoryCache;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ ...
+ // initialize LruCache as per <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+ }
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ final String imageKey = String.valueOf(resId);
+
+ final Bitmap bitmap = mMemoryCache.get(imageKey);
+ if (bitmap != null) {
+ mImageView.setImageBitmap(bitmap);
+ } else {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+ }
+
+ ... // include updated BitmapWorkerTask from <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+}
+</pre>
+
+<p>Putting all these pieces together gives you a responsive {@link
+android.support.v4.view.ViewPager} implementation with minimal image loading latency and the ability
+to do as much or as little background processing on your images as needed.</p>
+
+<h2 id="gridview">Load Bitmaps into a GridView Implementation</h2>
+
+<p>The <a href="{@docRoot}design/building-blocks/grid-lists.html">grid list building block</a> is
+useful for showing image data sets and can be implemented using a {@link android.widget.GridView}
+component in which many images can be on-screen at any one time and many more need to be ready to
+appear if the user scrolls up or down. When implementing this type of control, you must ensure the
+UI remains fluid, memory usage remains under control and concurrency is handled correctly (due to
+the way {@link android.widget.GridView} recycles its children views).</p>
+
+<p>To start with, here is a standard {@link android.widget.GridView} implementation with {@link
+android.widget.ImageView} children placed inside a {@link android.app.Fragment}:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+ private ImageAdapter mAdapter;
+
+ // A static dataset to back the GridView adapter
+ public final static Integer[] imageResIds = new Integer[] {
+ R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+ R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+ R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+ // Empty constructor as per Fragment docs
+ public ImageGridFragment() {}
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAdapter = new ImageAdapter(getActivity());
+ }
+
+ &#64;Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
+ final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setOnItemClickListener(this);
+ return v;
+ }
+
+ &#64;Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
+ i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
+ startActivity(i);
+ }
+
+ private class ImageAdapter extends BaseAdapter {
+ private final Context mContext;
+
+ public ImageAdapter(Context context) {
+ super();
+ mContext = context;
+ }
+
+ &#64;Override
+ public int getCount() {
+ return imageResIds.length;
+ }
+
+ &#64;Override
+ public Object getItem(int position) {
+ return imageResIds[position];
+ }
+
+ &#64;Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ &#64;Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ ImageView imageView;
+ if (convertView == null) { // if it's not recycled, initialize some attributes
+ imageView = new ImageView(mContext);
+ imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ imageView.setLayoutParams(new GridView.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ } else {
+ imageView = (ImageView) convertView;
+ }
+ <strong>imageView.setImageResource(imageResIds[position]);</strong> // Load image into ImageView
+ return imageView;
+ }
+ }
+}
+</pre>
+
+<p>Once again, the problem with this implementation is that the image is being set in the UI thread.
+While this may work for small, simple images (due to system resource loading and caching), if any
+additional processing needs to be done, your UI grinds to a halt.</p>
+
+<p>The same asynchronous processing and caching methods from the previous section can be implemented
+here. However, you also need to wary of concurrency issues as the {@link android.widget.GridView}
+recycles its children views. To handle this, use the techniques discussed in the <a
+href="process-bitmap#concurrency">Processing Bitmaps Off the UI Thread</a> lesson. Here is the updated
+solution:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+ ...
+
+ private class ImageAdapter extends BaseAdapter {
+ ...
+
+ &#64;Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ ...
+ <strong>loadBitmap(imageResIds[position], imageView)</strong>
+ return imageView;
+ }
+ }
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ if (cancelPotentialWork(resId, imageView)) {
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable =
+ new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+ imageView.setImageDrawable(asyncDrawable);
+ task.execute(resId);
+ }
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference =
+ new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
+
+ public static boolean cancelPotentialWork(int data, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final int bitmapData = bitmapWorkerTask.data;
+ if (bitmapData != data) {
+ // Cancel previous task
+ bitmapWorkerTask.cancel(true);
+ } else {
+ // The same work is already in progress
+ return false;
+ }
+ }
+ // No task associated with the ImageView, or an existing task was cancelled
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ ... // include updated <a href="process-bitmap.html#BitmapWorkerTaskUpdated">{@code BitmapWorkerTask}</a> class
+</pre>
+
+<p class="note"><strong>Note:</strong> The same code can easily be adapted to work with {@link
+android.widget.ListView} as well.</p>
+
+<p>This implementation allows for flexibility in how the images are processed and loaded without
+impeding the smoothness of the UI. In the background task you can load images from the network or
+resize large digital camera photos and the images appear as the tasks finish processing.</p>
+
+<p>For a full example of this and other concepts discussed in this lesson, please see the included
+sample application.</p>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
new file mode 100644
index 000000000000..6755c24495ae
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -0,0 +1,78 @@
+page.title=Displaying Bitmaps Efficiently
+
+trainingnavtop=true
+startpage=true
+next.title=Loading Large Bitmaps Efficiently
+next.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 2.1 (API Level 7) or higher</li>
+ <li><a href="{@docRoot}sdk/compatibility-library.html">Support Library</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class covers some common techniques for processing and loading {@link
+android.graphics.Bitmap} objects in a way that keeps your user interface (UI) components responsive
+and avoids exceeding your application memory limit. If you're not careful, bitmaps can quickly
+consume your available memory budget leading to an application crash due to the dreaded
+exception:<br />{@code java.lang.OutofMemoryError: bitmap size exceeds VM budget}.</p>
+
+<p>There are a number of reasons why loading bitmaps in your Android application is tricky:</p>
+
+<ul>
+ <li>Mobile devices typically have constrained system resources. Android devices can have as little
+ as 16MB of memory available to a single application. The <a
+ href="http://source.android.com/compatibility/downloads.html">Android Compatibility Definition
+ Document</a> (CDD), <i>Section 3.7. Virtual Machine Compatibility</i> gives the required minimum
+ application memory for various screen sizes and densities. Applications should be optimized to
+ perform under this minimum memory limit. However, keep in mind many devices are configured with
+ higher limits.</li>
+ <li>Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the
+ camera on the <a href="http://www.google.com/nexus/">Galaxy Nexus</a> takes photos up to 2592x1936
+ pixels (5 megapixels). If the bitmap configuration used is {@link
+ android.graphics.Bitmap.Config ARGB_8888} (the default from the Android 2.3 onward) then loading
+ this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the
+ per-app limit on some devices.</li>
+ <li>Android app UI’s frequently require several bitmaps to be loaded at once. Components such as
+ {@link android.widget.ListView}, {@link android.widget.GridView} and {@link
+ android.support.v4.view.ViewPager} commonly include multiple bitmaps on-screen at once with many
+ more potentially off-screen ready to show at the flick of a finger.</li>
+</ul>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="load-bitmap.html">Loading Large Bitmaps Efficiently</a></b></dt>
+ <dd>This lesson walks you through decoding large bitmaps without exceeding the per application
+ memory limit.</dd>
+
+ <dt><b><a href="process-bitmap.html">Processing Bitmaps Off the UI Thread</a></b></dt>
+ <dd>Bitmap processing (resizing, downloading from a remote source, etc.) should never take place
+ on the main UI thread. This lesson walks you through processing bitmaps in a background thread
+ using {@link android.os.AsyncTask} and explains how to handle concurrency issues.</dd>
+
+ <dt><b><a href="cache-bitmap.html">Caching Bitmaps</a></b></dt>
+ <dd>This lesson walks you through using a memory and disk bitmap cache to improve the
+ responsiveness and fluidity of your UI when loading multiple bitmaps.</dd>
+
+ <dt><b><a href="display-bitmap.html">Displaying Bitmaps in Your UI</a></b></dt>
+ <dd>This lesson brings everything together, showing you how to load multiple bitmaps into
+ components like {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+ using a background thread and bitmap cache.</dd>
+
+</dl> \ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
new file mode 100644
index 000000000000..c0a5709d41cc
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -0,0 +1,165 @@
+page.title=Loading Large Bitmaps Efficiently
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Processing Bitmaps Off the UI Thread
+next.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li>
+ <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Images come in all shapes and sizes. In many cases they are larger than required for a typical
+application user interface (UI). For example, the system Gallery application displays photos taken
+using your Android devices's camera which are typically much higher resolution than the screen
+density of your device.</p>
+
+<p>Given that you are working with limited memory, ideally you only want to load a lower resolution
+version in memory. The lower resolution version should match the size of the UI component that
+displays it. An image with a higher resolution does not provide any visible benefit, but still takes
+up precious memory and incurs additional performance overhead due to additional on the fly
+scaling.</p>
+
+<p>This lesson walks you through decoding large bitmaps without exceeding the per application
+memory limit by loading a smaller subsampled version in memory.</p>
+
+<h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2>
+
+<p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+decodeByteArray()}, {@link
+android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options)
+decodeFile()}, {@link
+android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options)
+decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose
+the most appropriate decode method based on your image data source. These methods attempt to
+allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory}
+exception. Each type of decode method has additional signatures that let you specify decoding
+options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding
+avoids memory allocation, returning {@code null} for the bitmap object but setting {@link
+android.graphics.BitmapFactory.Options#outWidth}, {@link
+android.graphics.BitmapFactory.Options#outHeight} and {@link
+android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the
+dimensions and type of the image data prior to construction (and memory allocation) of the
+bitmap.</p>
+
+<pre>
+BitmapFactory.Options options = new BitmapFactory.Options();
+options.inJustDecodeBounds = true;
+BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
+int imageHeight = options.outHeight;
+int imageWidth = options.outWidth;
+String imageType = options.outMimeType;
+</pre>
+
+<p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before
+decoding it, unless you absolutely trust the source to provide you with predictably sized image data
+that comfortably fits within the available memory.</p>
+
+<h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2>
+
+<p>Now that the image dimensions are known, they can be used to decide if the full image should be
+loaded into memory or if a subsampled version should be loaded instead. Here are some factors to
+consider:</p>
+
+<ul>
+ <li>Estimated memory usage of loading the full image in memory.</li>
+ <li>Amount of memory you are willing to commit to loading this image given any other memory
+ requirements of your application.</li>
+ <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image
+ is to be loaded into.</li>
+ <li>Screen size and density of the current device.</li>
+</ul>
+
+<p>For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be
+displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p>
+
+<p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link
+android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link
+android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that
+is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
+bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
+image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Here’s
+a method to calculate a the sample size value based on a target width and height:</p>
+
+<pre>
+public static int calculateInSampleSize(
+ BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ if (width > height) {
+ inSampleSize = Math.round((float)height / (float)reqHeight);
+ } else {
+ inSampleSize = Math.round((float)width / (float)reqWidth);
+ }
+ }
+ return inSampleSize;
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Using powers of 2 for {@link
+android.graphics.BitmapFactory.Options#inSampleSize} values is faster and more efficient for the
+decoder. However, if you plan to cache the resized versions in memory or on disk, it’s usually still
+worth decoding to the most appropriate image dimensions to save space.</p>
+
+<p>To use this method, first decode with {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options
+through and then decode again using the new {@link
+android.graphics.BitmapFactory.Options#inSampleSize} value and {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p>
+
+<a name="decodeSampledBitmapFromResource"></a>
+<pre>
+public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
+ int reqWidth, int reqHeight) {
+
+ // First decode with inJustDecodeBounds=true to check dimensions
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeResource(res, resId, options);
+
+ // Calculate inSampleSize
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+ // Decode bitmap with inSampleSize set
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeResource(res, resId, options);
+}
+</pre>
+
+<p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link
+android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example
+code:</p>
+
+<pre>
+mImageView.setImageBitmap(
+ decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
+</pre>
+
+<p>You can follow a similar process to decode bitmaps from other sources, by substituting the
+appropriate {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} method as needed.</p> \ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
new file mode 100644
index 000000000000..c1450b4a8850
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -0,0 +1,239 @@
+page.title=Processing Bitmaps Off the UI Thread
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Caching Bitmaps
+next.link=cache-bitmap.html
+previous.title=Loading Large Bitmaps Efficiently
+previous.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#async-task">Use an AsyncTask</a></li>
+ <li><a href="#concurrency">Handle Concurrency</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a></li>
+ <li><a
+ href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+ for Performance</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>The {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} methods, discussed in the <a href="load-bitmap.html">Load Large Bitmaps
+Efficiently</a> lesson, should not be executed on the main UI thread if the source data is read from
+disk or a network location (or really any source other than memory). The time this data takes to
+load is unpredictable and depends on a variety of factors (speed of reading from disk or network,
+size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags
+your application as non-responsive and the user has the option of closing it (see <a
+href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a> for
+more information).</p>
+
+<p>This lesson walks you through processing bitmaps in a background thread using
+{@link android.os.AsyncTask} and shows you how to handle concurrency issues.</p>
+
+<h2 id="async-task">Use an AsyncTask</h2>
+
+<p>The {@link android.os.AsyncTask} class provides an easy way to execute some work in a background
+thread and publish the results back on the UI thread. To use it, create a subclass and override the
+provided methods. Here’s an example of loading a large image into an {@link
+android.widget.ImageView} using {@link android.os.AsyncTask} and <a
+href="load-bitmap.html#decodeSampledBitmapFromResource">{@code
+decodeSampledBitmapFromResource()}</a>: </p>
+
+<a name="BitmapWorkerTask"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private int data = 0;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ // Use a WeakReference to ensure the ImageView can be garbage collected
+ imageViewReference = new WeakReference<ImageView>(imageView);
+ }
+
+ // Decode image in background.
+ &#64;Override
+ protected Bitmap doInBackground(Integer... params) {
+ data = params[0];
+ return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
+ }
+
+ // Once complete, see if ImageView is still around and set bitmap.
+ &#64;Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (imageViewReference != null && bitmap != null) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+ }
+}
+</pre>
+
+<p>The {@link java.lang.ref.WeakReference} to the {@link android.widget.ImageView} ensures that the
+{@link android.os.AsyncTask} does not prevent the {@link android.widget.ImageView} and anything it
+references from being garbage collected. There’s no guarantee the {@link android.widget.ImageView}
+is still around when the task finishes, so you must also check the reference in {@link
+android.os.AsyncTask#onPostExecute(Result) onPostExecute()}. The {@link android.widget.ImageView}
+may no longer exist, if for example, the user navigates away from the activity or if a
+configuration change happens before the task finishes.</p>
+
+<p>To start loading the bitmap asynchronously, simply create a new task and execute it:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ task.execute(resId);
+}
+</pre>
+
+<h2 id="concurrency">Handle Concurrency</h2>
+
+<p>Common view components such as {@link android.widget.ListView} and {@link
+android.widget.GridView} introduce another issue when used in conjunction with the {@link
+android.os.AsyncTask} as demonstrated in the previous section. In order to be efficient with memory,
+these components recycle child views as the user scrolls. If each child view triggers an {@link
+android.os.AsyncTask}, there is no guarantee that when it completes, the associated view has not
+already been recycled for use in another child view. Furthermore, there is no guarantee that the
+order in which asynchronous tasks are started is the order that they complete.</p>
+
+<p>The blog post <a
+href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+for Performance</a> further discusses dealing with concurrency, and offers a solution where the
+{@link android.widget.ImageView} stores a reference to the most recent {@link android.os.AsyncTask}
+which can later be checked when the task completes. Using a similar method, the {@link
+android.os.AsyncTask} from the previous section can be extended to follow a similar pattern.</p>
+
+<p>Create a dedicated {@link android.graphics.drawable.Drawable} subclass to store a reference
+back to the worker task. In this case, a {@link android.graphics.drawable.BitmapDrawable} is used so
+that a placeholder image can be displayed in the {@link android.widget.ImageView} while the task
+completes:</p>
+
+<a name="AsyncDrawable"></a>
+<pre>
+static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference =
+ new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+}
+</pre>
+
+<p>Before executing the <a href="#BitmapWorkerTask">{@code BitmapWorkerTask}</a>, you create an <a
+href="#AsyncDrawable">{@code AsyncDrawable}</a> and bind it to the target {@link
+android.widget.ImageView}:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ if (cancelPotentialWork(resId, imageView)) {
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable =
+ new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+ imageView.setImageDrawable(asyncDrawable);
+ task.execute(resId);
+ }
+}
+</pre>
+
+<p>The {@code cancelPotentialWork} method referenced in the code sample above checks if another
+running task is already associated with the {@link android.widget.ImageView}. If so, it attempts to
+cancel the previous task by calling {@link android.os.AsyncTask#cancel cancel()}. In a small number
+of cases, the new task data matches the existing task and nothing further needs to happen. Here is
+the implementation of {@code cancelPotentialWork}:</p>
+
+<pre>
+public static boolean cancelPotentialWork(int data, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final int bitmapData = bitmapWorkerTask.data;
+ if (bitmapData != data) {
+ // Cancel previous task
+ bitmapWorkerTask.cancel(true);
+ } else {
+ // The same work is already in progress
+ return false;
+ }
+ }
+ // No task associated with the ImageView, or an existing task was cancelled
+ return true;
+}
+</pre>
+
+<p>A helper method, {@code getBitmapWorkerTask()}, is used above to retrieve the task associated
+with a particular {@link android.widget.ImageView}:</p>
+
+<pre>
+private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+}
+</pre>
+
+<p>The last step is updating {@code onPostExecute()} in <a href="#BitmapWorkerTask">{@code
+BitmapWorkerTask}</a> so that it checks if the task is cancelled and if the current task matches the
+one associated with the {@link android.widget.ImageView}:</p>
+
+<a name="BitmapWorkerTaskUpdated"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+
+ &#64;Override
+ protected void onPostExecute(Bitmap bitmap) {
+ <strong>if (isCancelled()) {
+ bitmap = null;
+ }</strong>
+
+ if (imageViewReference != null && bitmap != null) {
+ final ImageView imageView = imageViewReference.get();
+ <strong>final BitmapWorkerTask bitmapWorkerTask =
+ getBitmapWorkerTask(imageView);</strong>
+ if (<strong>this == bitmapWorkerTask &&</strong> imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+ }
+}
+</pre>
+
+<p>This implementation is now suitable for use in {@link android.widget.ListView} and {@link
+android.widget.GridView} components as well as any other components that recycle their child
+views. Simply call {@code loadBitmap} where you normally set an image to your {@link
+android.widget.ImageView}. For example, in a {@link android.widget.GridView} implementation this
+would be in the {@link android.widget.Adapter#getView getView()} method of the backing adapter.</p> \ No newline at end of file