Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 1 | page.title=App Widgets |
Joe Fernandez | 33baa5a | 2013-11-14 11:41:19 -0800 | [diff] [blame] | 2 | page.tags=home,AppWidgetProvider |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 3 | @jd:body |
| 4 | |
| 5 | <div id="qv-wrapper"> |
Scott Main | 5765525 | 2012-11-13 00:44:17 -0800 | [diff] [blame] | 6 | <div id="qv"> |
Scott Main | ec80d7f | 2010-09-24 16:17:27 -0700 | [diff] [blame] | 7 | |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 8 | <h2>In this document</h2> |
| 9 | <ol> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 10 | <li><a href="#Basics">The Basics</a></li> |
| 11 | <li><a href="#Manifest">Declaring an App Widget in the Manifest</a></li> |
| 12 | <li><a href="#MetaData">Adding the AppWidgetProviderInfo Metadata</a></li> |
| 13 | <li><a href="#CreatingLayout">Creating the App Widget Layout</a></li> |
| 14 | <li><a href="#AppWidgetProvider">Using the AppWidgetProvider Class</a> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 15 | <ol> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 16 | <li><a href="#ProviderBroadcasts">Receiving App Widget broadcast |
| 17 | Intents</a></li> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 18 | </ol> |
| 19 | </li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 20 | <li><a href="#Configuring">Creating an App Widget Configuration |
| 21 | Activity</a> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 22 | <ol> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 23 | <li><a href="#UpdatingFromTheConfiguration">Updating the App Widget |
| 24 | from |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 25 | the Configuration Activity</a></li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 26 | </ol> |
| 27 | </li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 28 | <li><a href="#preview">Setting a Preview Image</a></li> |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 29 | <li><a href="#lockscreen">Enabling App Widgets on the Lockscreen</a> |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 30 | <ol> |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 31 | <li><a href="#lockscreen-sizing">Sizing guidelines</a></li> |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 32 | </ol> |
| 33 | </li> |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 34 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 35 | <li><a href="#collections">Using App Widgets with Collections</a> |
| 36 | <ol> |
| 37 | <li><a href="#collection_sample">Sample application</a></li> |
| 38 | <li><a href="#implementing_collections">Implementing app widgets with |
| 39 | collections |
| 40 | </a></li> |
| 41 | <li><a href="#fresh">Keeping Collection Data Fresh</a></li> |
| 42 | </ol> |
| 43 | </li> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 44 | </ol> |
| 45 | |
Scott Main | ec80d7f | 2010-09-24 16:17:27 -0700 | [diff] [blame] | 46 | <h2>Key classes</h2> |
| 47 | <ol> |
| 48 | <li>{@link android.appwidget.AppWidgetProvider}</li> |
| 49 | <li>{@link android.appwidget.AppWidgetProviderInfo}</li> |
| 50 | <li>{@link android.appwidget.AppWidgetManager}</li> |
| 51 | </ol> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 52 | </div> |
| 53 | </div> |
| 54 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 55 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 56 | <p>App Widgets are miniature application views that can be embedded in other |
| 57 | applications |
| 58 | (such as the Home screen) and receive periodic updates. These views are |
| 59 | referred |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 60 | to as Widgets in the user interface, |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 61 | and you can publish one with an App Widget provider. An application component |
| 62 | that is |
| 63 | able to hold other App Widgets is called an App Widget host. The screenshot |
| 64 | below shows |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 65 | the Music App Widget.</p> |
| 66 | |
&& repo sync -j8 | 1019a9c | 2012-11-14 18:26:07 -0800 | [diff] [blame] | 67 | <img src="{@docRoot}images/appwidgets/appwidget.png" alt="" /> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 68 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 69 | <p>This document describes how to publish an App Widget using an App Widget |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 70 | provider. For a discussion of creating your own {@link android.appwidget.AppWidgetHost} |
| 71 | to host app widgets, see <a href="{@docRoot}guide/topics/appwidgets/host.html"> |
kmccormick | 20ec5c5 | 2013-05-31 12:28:05 -0700 | [diff] [blame] | 72 | App Widget Host</a>.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 73 | |
Scott Main | 5765525 | 2012-11-13 00:44:17 -0800 | [diff] [blame] | 74 | <div class="note design"> |
| 75 | <p><strong>Widget Design</strong></p> |
| 76 | <p>For information about how to design your app widget, read the <a |
| 77 | href="{@docRoot}design/patterns/widgets.html">Widgets</a> design guide.</p> |
| 78 | </div> |
| 79 | |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 80 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 81 | <h2 id="Basics">The Basics</h2> |
| 82 | |
| 83 | <p>To create an App Widget, you need the following:</p> |
| 84 | |
| 85 | <dl> |
| 86 | <dt>{@link android.appwidget.AppWidgetProviderInfo} object</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 87 | <dd>Describes the metadata for an App Widget, such as the App Widget's layout, |
| 88 | update frequency, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 89 | and the AppWidgetProvider class. This should be defined in XML.</dd> |
| 90 | <dt>{@link android.appwidget.AppWidgetProvider} class implementation</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 91 | <dd>Defines the basic methods that allow you to programmatically interface |
| 92 | with the App Widget, |
| 93 | based on broadcast events. Through it, you will receive broadcasts when the |
| 94 | App Widget is updated, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 95 | enabled, disabled and deleted.</dd> |
| 96 | <dt>View layout</dt> |
| 97 | <dd>Defines the initial layout for the App Widget, defined in XML.</dd> |
| 98 | </dl> |
| 99 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 100 | <p>Additionally, you can implement an App Widget configuration Activity. This is |
| 101 | an optional |
| 102 | {@link android.app.Activity} that launches when the user adds your App Widget |
| 103 | and allows him or her |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 104 | to modify App Widget settings at create-time.</p> |
| 105 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 106 | <p>The following sections describe how to set up each of these components.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 107 | |
| 108 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 109 | <h2 id="Manifest">Declaring an App Widget in the Manifest</h2> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 110 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 111 | <p>First, declare the {@link android.appwidget.AppWidgetProvider} class in your |
| 112 | application's |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 113 | <code>AndroidManifest.xml</code> file. For example:</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 114 | |
Scott Main | 14a3662 | 2012-09-26 11:22:09 -0700 | [diff] [blame] | 115 | <pre style="clear:right"> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 116 | <receiver android:name="ExampleAppWidgetProvider" > |
| 117 | <intent-filter> |
| 118 | <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> |
| 119 | </intent-filter> |
| 120 | <meta-data android:name="android.appwidget.provider" |
| 121 | android:resource="@xml/example_appwidget_info" /> |
| 122 | </receiver> |
| 123 | </pre> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 124 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 125 | <p>The <code><receiver></code> element requires the |
| 126 | <code>android:name</code> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 127 | attribute, which specifies the {@link android.appwidget.AppWidgetProvider} used |
| 128 | by the App Widget.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 129 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 130 | <p>The <code><intent-filter></code> element must include an |
| 131 | <code><action></code> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 132 | element with the <code>android:name</code> attribute. This attribute specifies |
| 133 | that the {@link android.appwidget.AppWidgetProvider} accepts the {@link |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 134 | android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE |
| 135 | ACTION_APPWIDGET_UPDATE} broadcast. |
| 136 | This is the only broadcast that you must explicitly declare. The {@link |
| 137 | android.appwidget.AppWidgetManager} |
| 138 | automatically sends all other App Widget broadcasts to the AppWidgetProvider as |
| 139 | necessary.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 140 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 141 | <p>The <code><meta-data></code> element specifies the |
| 142 | {@link android.appwidget.AppWidgetProviderInfo} resource and requires the |
| 143 | following attributes:</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 144 | <ul> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 145 | <li><code>android:name</code> - Specifies the metadata name. Use |
| 146 | <code>android.appwidget.provider</code> |
| 147 | to identify the data as the {@link android.appwidget.AppWidgetProviderInfo} |
| 148 | descriptor.</li> |
| 149 | <li><code>android:resource</code> - Specifies the {@link |
| 150 | android.appwidget.AppWidgetProviderInfo} |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 151 | resource location.</li> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 152 | </ul> |
| 153 | |
| 154 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 155 | <h2 id="MetaData">Adding the AppWidgetProviderInfo Metadata</h2> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 156 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 157 | <p>The {@link android.appwidget.AppWidgetProviderInfo} defines the essential |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 158 | qualities of an App Widget, such as its minimum layout dimensions, its initial |
| 159 | layout resource, |
| 160 | how often to update the App Widget, and (optionally) a configuration Activity to |
| 161 | launch at create-time. |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 162 | Define the AppWidgetProviderInfo object in an XML resource using a single |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 163 | <code><appwidget-provider></code> element and save it in the project's |
| 164 | <code>res/xml/</code> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 165 | folder.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 166 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 167 | <p>For example:</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 168 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 169 | <pre> |
| 170 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 171 | android:minWidth="40dp" |
| 172 | android:minHeight="40dp" |
Scott Main | 33e0f00 | 2009-12-03 11:33:25 -0800 | [diff] [blame] | 173 | android:updatePeriodMillis="86400000" |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 174 | android:previewImage="@drawable/preview" |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 175 | android:initialLayout="@layout/example_appwidget" |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 176 | android:configure="com.example.android.ExampleAppWidgetConfigure" |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 177 | android:resizeMode="horizontal|vertical" |
| 178 | android:widgetCategory="home_screen|keyguard" |
| 179 | android:initialKeyguardLayout="@layout/example_keyguard"> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 180 | </appwidget-provider> |
| 181 | </pre> |
| 182 | |
| 183 | <p>Here's a summary of the <code><appwidget-provider></code> attributes:</p> |
| 184 | <ul> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 185 | <li>The values for the <code>minWidth</code> and <code>minHeight</code> |
Roman Nurik | 2b43f3f | 2011-12-22 13:58:53 -0800 | [diff] [blame] | 186 | attributes specify the minimum amount of space the App Widget consumes |
| 187 | <em>by default</em>. The default Home screen positions App Widgets in its |
| 188 | window based on a grid of cells that have a defined height and width. If |
| 189 | the values for an App Widget's minimum width or height don't match the |
| 190 | dimensions of the cells, then the App Widget dimensions round |
| 191 | <em>up</em> to the nearest cell size. |
| 192 | <p>See the <a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html#anatomy_determining_size"> |
| 193 | App Widget Design Guidelines</a> for more information on sizing your App |
| 194 | Widgets.</p> |
| 195 | |
| 196 | <p class="note"><strong>Note:</strong> To make your app widget portable |
| 197 | across devices, your app widget's minimum size should never be larger |
| 198 | than 4 x 4 cells.</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 199 | </li> |
Roman Nurik | 2b43f3f | 2011-12-22 13:58:53 -0800 | [diff] [blame] | 200 | |
| 201 | <li>The <code>minResizeWidth</code> and <code>minResizeHeight</code> attributes |
| 202 | specify the App Widget's absolute minimum size. These values should specify |
| 203 | the size below which the App Widget would be illegible or otherwise unusable. |
| 204 | Using these attributes allows the user to resize the widget to a size that |
| 205 | may be smaller than the default widget size defined by the |
| 206 | <code>minWidth</code> and <code>minHeight</code> attributes. |
| 207 | Introduced in Android 3.1. |
| 208 | |
| 209 | <p>See the <a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html#anatomy_determining_size"> |
| 210 | App Widget Design Guidelines</a> for more information on sizing your App |
| 211 | Widgets.</p> |
| 212 | </li> |
| 213 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 214 | <li>The <code>updatePeriodMillis</code> attribute defines how often the App |
| 215 | Widget framework should request an update from the {@link |
| 216 | android.appwidget.AppWidgetProvider} by calling the |
| 217 | {@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,android.appwidget.AppWidgetManager,int[]) onUpdate()} |
| 218 | callback method. The actual update |
| 219 | is not guaranteed to occur exactly on time with this value and we suggest |
| 220 | updating as infrequently as possible—perhaps no more than once an hour to |
| 221 | conserve the battery. You might also allow the user to adjust the frequency in a |
| 222 | configuration—some people might want a stock ticker to update every 15 |
| 223 | minutes, or maybe only four times a day. |
| 224 | <p class="note"><strong>Note:</strong> If the device is asleep when it |
| 225 | is time for an update |
| 226 | (as defined by <code>updatePeriodMillis</code>), then the device will |
| 227 | wake up in order |
| 228 | to perform the update. If you don't update more than once per hour, this |
| 229 | probably won't |
| 230 | cause significant problems for the battery life. If, however, you need |
| 231 | to update more |
| 232 | frequently and/or you do not need to update while the device is asleep, |
| 233 | then you can instead |
| 234 | perform updates based on an alarm that will not wake the device. To do |
| 235 | so, set an alarm with |
| 236 | an Intent that your AppWidgetProvider receives, using the {@link |
| 237 | android.app.AlarmManager}. |
| 238 | Set the alarm type to either {@link |
| 239 | android.app.AlarmManager#ELAPSED_REALTIME} or |
Scott Main | e518606 | 2009-07-15 16:52:31 -0700 | [diff] [blame] | 240 | {@link android.app.AlarmManager#RTC}, which will only |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 241 | deliver the alarm when the device is awake. Then set |
| 242 | <code>updatePeriodMillis</code> to |
Scott Main | e518606 | 2009-07-15 16:52:31 -0700 | [diff] [blame] | 243 | zero (<code>"0"</code>).</p> |
| 244 | </li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 245 | <li>The <code>initialLayout</code> attribute points to the layout resource |
| 246 | that defines the |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 247 | App Widget layout.</li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 248 | <li>The <code>configure</code> attribute defines the {@link |
| 249 | android.app.Activity} to launch when |
| 250 | the user adds the App Widget, in order for him or her to configure App |
| 251 | Widget properties. This is optional |
| 252 | (read <a href="#Configuring">Creating an App Widget Configuration |
| 253 | Activity</a> below).</li> |
| 254 | |
| 255 | <li>The <code>previewImage</code> attribute specifies a preview of what the |
| 256 | app widget will look like after it's configured, which the user sees when |
| 257 | selecting the app widget. If not supplied, the user instead sees your |
| 258 | application's launcher icon. This field corresponds to the |
| 259 | <code>android:previewImage</code> attribute in the <code><receiver></code> |
| 260 | element in the <code>AndroidManifest.xml</code> file. For more discussion of |
| 261 | using <code>previewImage</code>, see <a href="#preview">Setting a Preview |
| 262 | Image</a>. Introduced in Android 3.0.</li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 263 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 264 | <li>The <code>autoAdvanceViewId</code> attribute specifies the view ID of the |
| 265 | app widget subview that should be auto-advanced by the widget's host. Introduced in Android 3.0.</li> |
| 266 | |
| 267 | <li>The <code>resizeMode</code> attribute specifies the rules by which a widget |
| 268 | can be resized. You use this attribute to make homescreen widgets |
| 269 | resizeable—horizontally, vertically, or on both axes. Users touch-hold a |
| 270 | widget to show its resize handles, then drag the horizontal and/or vertical |
| 271 | handles to change the size on the layout grid. Values for the |
| 272 | <code>resizeMode</code> attribute include "horizontal", "vertical", and "none". |
| 273 | To declare a widget as resizeable horizontally and vertically, supply the value |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 274 | "horizontal|vertical". Introduced in Android 3.1.</li> |
| 275 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 276 | <li>The <code>minResizeHeight</code> attribute specifies the minimum height (in dps) to which |
| 277 | the widget can be resized. This field has no effect if it is greater than {@code minHeight} or if |
| 278 | vertical resizing isn't enabled (see <code>resizeMode</code>). Introduced in Android 4.0.</li> |
| 279 | |
| 280 | <li>The <code> minResizeWidth </code> attribute specifies the minimum width (in dps) to which |
| 281 | the widget can be resized. This field has no effect if it is greater than {@code minWidth} or if |
| 282 | horizontal resizing isn't enabled (see <code>resizeMode</code>). Introduced in Android 4.0.</li> |
| 283 | |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 284 | <li>The <code>widgetCategory</code> attribute declares whether your App Widget can be displayed on the home screen, |
| 285 | the lock screen (keyguard), or both. Values for this attribute include "home_screen" and "keyguard". A widget that |
| 286 | is displayed on both needs to ensure that it follows the design guidelines for both widget classes. For more |
| 287 | information, see <a href="#lockscreen">Enabling App Widgets on the Lockscreen</a>. The default value is "home_screen". Introduced in Android 4.2. |
| 288 | </li> |
| 289 | |
| 290 | <li>The <code>initialKeyguardLayout</code> attribute points to the layout resource |
| 291 | that defines the lock screen App Widget layout. This works the same way as the |
| 292 | {@link android.appwidget.AppWidgetProviderInfo#initialLayout android:initialLayout}, |
| 293 | in that it provides a layout that can appear immediately until your app widget is initialized and able to update |
| 294 | the layout. Introduced in Android 4.2.</li> |
| 295 | |
| 296 | </ul> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 297 | |
| 298 | <p>See the {@link android.appwidget.AppWidgetProviderInfo} class for more |
| 299 | information on the |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 300 | attributes accepted by the <code><appwidget-provider></code> element.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 301 | |
| 302 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 303 | <h2 id="CreatingLayout">Creating the App Widget Layout</h2> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 304 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 305 | <p>You must define an initial layout for your App Widget in XML and save it in |
| 306 | the project's |
| 307 | <code>res/layout/</code> directory. You can design your App Widget using the |
| 308 | View objects listed |
| 309 | below, but before you begin designing your App Widget, please read and |
| 310 | understand the |
| 311 | <a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget |
| 312 | Design |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 313 | Guidelines</a>.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 314 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 315 | <p>Creating the App Widget layout is simple if you're |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 316 | familiar with <a |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 317 | href="{@docRoot}guide/topics/ui/declaring-layout.html">Layouts</a>. |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 318 | However, you must be aware that App Widget layouts are based on {@link |
| 319 | android.widget.RemoteViews}, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 320 | which do not support every kind of layout or view widget.</p> |
| 321 | |
| 322 | <p>A RemoteViews object (and, consequently, an App Widget) can support the |
| 323 | following layout classes:</p> |
| 324 | |
| 325 | <ul class="nolist"> |
| 326 | <li>{@link android.widget.FrameLayout}</li> |
| 327 | <li>{@link android.widget.LinearLayout}</li> |
| 328 | <li>{@link android.widget.RelativeLayout}</li> |
Katie McCormick | 73ec048 | 2012-08-27 15:43:21 -0700 | [diff] [blame] | 329 | <li>{@link android.widget.GridLayout}</li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 330 | </ul> |
| 331 | |
| 332 | <p>And the following widget classes:</p> |
| 333 | <ul class="nolist"> |
| 334 | <li>{@link android.widget.AnalogClock}</li> |
| 335 | <li>{@link android.widget.Button}</li> |
| 336 | <li>{@link android.widget.Chronometer}</li> |
| 337 | <li>{@link android.widget.ImageButton}</li> |
| 338 | <li>{@link android.widget.ImageView}</li> |
| 339 | <li>{@link android.widget.ProgressBar}</li> |
| 340 | <li>{@link android.widget.TextView}</li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 341 | <li>{@link android.widget.ViewFlipper}</li> |
Katie McCormick | f99c887 | 2011-11-01 16:33:03 -0700 | [diff] [blame] | 342 | <li>{@link android.widget.ListView}</li> |
| 343 | <li>{@link android.widget.GridView}</li> |
| 344 | <li>{@link android.widget.StackView}</li> |
| 345 | <li>{@link android.widget.AdapterViewFlipper}</li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 346 | </ul> |
| 347 | |
| 348 | <p>Descendants of these classes are not supported.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 349 | |
Katie McCormick | 73ec048 | 2012-08-27 15:43:21 -0700 | [diff] [blame] | 350 | <p>RemoteViews also supports {@link android.view.ViewStub}, which is an invisible, zero-sized View you can use |
| 351 | to lazily inflate layout resources at runtime.</p> |
| 352 | |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 353 | |
Roman Nurik | 2b15c00 | 2011-09-27 18:28:46 -0700 | [diff] [blame] | 354 | <h3 id="AddingMargins">Adding margins to App Widgets</h3> |
| 355 | |
| 356 | <p>Widgets should not generally extend to screen edges and should not visually be flush with other widgets, so you should add margins on all sides around your widget frame.</p> |
| 357 | |
| 358 | <p>As of Android 4.0, app widgets are automatically given padding between the widget frame and the app widget's bounding box to provide better alignment with other widgets and icons on the user's home screen. To take advantage of this strongly recommended behavior, set your application's <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">targetSdkVersion</a> to 14 or greater.</p> |
| 359 | |
| 360 | <p>It's easy to write a single layout that has custom margins applied for earlier versions of the platform, and has no extra margins for Android 4.0 and greater:</p> |
| 361 | |
| 362 | <ol> |
| 363 | <li>Set your application's <code>targetSdkVersion</code> to 14 or greater.</li> |
| 364 | <li>Create a layout such as the one below, that references a <a href="{@docRoot}guide/topics/resources/more-resources.html#Dimension">dimension resource</a> for its margins: |
| 365 | |
| 366 | <pre> |
| 367 | <FrameLayout |
| 368 | android:layout_width="match_parent" |
| 369 | android:layout_height="match_parent" |
Roman Nurik | 061cf70 | 2011-11-29 10:02:19 -0800 | [diff] [blame] | 370 | <strong>android:padding="@dimen/widget_margin"></strong> |
Roman Nurik | 2b15c00 | 2011-09-27 18:28:46 -0700 | [diff] [blame] | 371 | |
| 372 | <LinearLayout |
| 373 | android:layout_width="match_parent" |
| 374 | android:layout_height="match_parent" |
| 375 | android:orientation="horizontal" |
| 376 | android:background="@drawable/my_widget_background"> |
| 377 | … |
| 378 | </LinearLayout> |
| 379 | |
| 380 | </FrameLayout> |
| 381 | </pre> |
| 382 | |
| 383 | </li> |
| 384 | <li>Create two dimensions resources, one in <code>res/values/</code> to provide the pre-Android 4.0 custom margins, and one in <code>res/values-v14/</code> to provide no extra padding for Android 4.0 widgets: |
| 385 | |
| 386 | <p><strong>res/values/dimens.xml</strong>:<br> |
Roman Nurik | 061cf70 | 2011-11-29 10:02:19 -0800 | [diff] [blame] | 387 | <pre><dimen name="widget_margin">8dp</dimen></pre></p> |
Roman Nurik | 2b15c00 | 2011-09-27 18:28:46 -0700 | [diff] [blame] | 388 | |
| 389 | <p><strong>res/values-v14/dimens.xml</strong>:<br> |
| 390 | <pre><dimen name="widget_margin">0dp</dimen></pre></p> |
| 391 | </li> |
| 392 | </ol> |
| 393 | |
| 394 | <p>Another option is to simply build extra margins into your <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">nine-patch</a> background assets by default, and provide different nine-patches with no margins for API level 14 or later.</p> |
| 395 | |
| 396 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 397 | <h2 id="AppWidgetProvider">Using the AppWidgetProvider Class</h2> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 398 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 399 | <div class="sidebox-wrapper"> |
Scott Main | f54574a | 2010-03-24 17:18:01 -0700 | [diff] [blame] | 400 | <div class="sidebox"> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 401 | <p>You must declare your AppWidgetProvider class implementation as a |
| 402 | broadcast receiver |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 403 | using the <code><receiver></code> element in the AndroidManifest (see |
| 404 | <a href="#Manifest">Declaring an App Widget in the Manifest</a> above).</p> |
| 405 | </div> |
| 406 | </div> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 407 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 408 | <p>The {@link android.appwidget.AppWidgetProvider} class extends |
| 409 | BroadcastReceiver as a convenience |
| 410 | class to handle the App Widget broadcasts. The AppWidgetProvider receives only |
| 411 | the event broadcasts that |
| 412 | are relevant to the App Widget, such as when the App Widget is updated, deleted, |
| 413 | enabled, and disabled. |
| 414 | When these broadcast events occur, the AppWidgetProvider receives the following |
| 415 | method calls:</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 416 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 417 | <dl> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 418 | <dt> |
| 419 | {@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,android.appwidget.AppWidgetManager,int[]) onUpdate()} |
| 420 | </dt> |
| 421 | <dd>This is called to update the App Widget at intervals defined by the |
| 422 | <code>updatePeriodMillis</code> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 423 | attribute in the AppWidgetProviderInfo (see <a href="#MetaData">Adding the |
| 424 | AppWidgetProviderInfo Metadata</a> above). This method is also called |
| 425 | when the user adds the App Widget, so it should perform the essential setup, |
| 426 | such as define event handlers for Views and start a temporary |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 427 | {@link android.app.Service}, if necessary. However, if you have declared a |
| 428 | configuration |
| 429 | Activity, <strong>this method is not called</strong> when the user adds the |
| 430 | App Widget, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 431 | but is called for the subsequent updates. It is the responsibility of the |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 432 | configuration Activity to perform the first update when configuration is |
| 433 | done. |
| 434 | (See <a href="#Configuring">Creating an App Widget Configuration |
| 435 | Activity</a> below.)</dd> |
Katie McCormick | 73ec048 | 2012-08-27 15:43:21 -0700 | [diff] [blame] | 436 | |
| 437 | <dt> |
| 438 | {@link android.appwidget.AppWidgetProvider#onAppWidgetOptionsChanged onAppWidgetOptionsChanged()} |
| 439 | </dt> |
| 440 | <dd> |
| 441 | This is called when the widget is first placed and any time the widget is resized. You can use this callback to show or hide content based on the widget's size ranges. You get the size ranges by calling {@link android.appwidget.AppWidgetManager#getAppWidgetOptions getAppWidgetOptions()}, which returns a {@link android.os.Bundle} that includes the following:<br /><br /> |
| 442 | <ul> |
| 443 | <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MIN_WIDTH}—Contains |
| 444 | the lower bound on the current width, in dp units, of a widget instance.</li> |
| 445 | <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MIN_HEIGHT}—Contains |
| 446 | the lower bound on the current height, in dp units, of a widget instance.</li> |
| 447 | <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MAX_WIDTH}—Contains |
| 448 | the upper bound on the current width, in dp units, of a widget instance.</li> |
| 449 | <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MAX_HEIGHT}—Contains |
| 450 | the upper bound on the current width, in dp units, of a widget instance.</li> |
| 451 | </ul> |
| 452 | |
| 453 | This callback was introduced in API Level 16 (Android 4.1). If you implement this callback, make sure that your app doesn't depend on it since it won't be called on older devices. |
| 454 | </dd> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 455 | <dt>{@link android.appwidget.AppWidgetProvider#onDeleted(Context,int[])}</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 456 | <dd>This is called every time an App Widget is deleted from the App Widget |
| 457 | host.</dd> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 458 | <dt>{@link android.appwidget.AppWidgetProvider#onEnabled(Context)}</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 459 | <dd>This is called when an instance the App Widget is created for the first |
| 460 | time. For example, if the user |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 461 | adds two instances of your App Widget, this is only called the first time. |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 462 | If you need to open a new database or perform other setup that only needs to |
| 463 | occur once |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 464 | for all App Widget instances, then this is a good place to do it.</dd> |
| 465 | <dt>{@link android.appwidget.AppWidgetProvider#onDisabled(Context)}</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 466 | <dd>This is called when the last instance of your App Widget is deleted from |
| 467 | the App Widget host. |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 468 | This is where you should clean up any work done in |
| 469 | {@link android.appwidget.AppWidgetProvider#onEnabled(Context)}, |
| 470 | such as delete a temporary database.</dd> |
| 471 | <dt>{@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)}</dt> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 472 | <dd>This is called for every broadcast and before each of the above callback |
| 473 | methods. |
| 474 | You normally don't need to implement this method because the default |
| 475 | AppWidgetProvider |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 476 | implementation filters all App Widget broadcasts and calls the above |
| 477 | methods as appropriate.</dd> |
| 478 | </dl> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 479 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 480 | <p>The most important AppWidgetProvider callback is |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 481 | {@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()} |
| 482 | because it is called when |
| 483 | each App Widget is added to a host (unless you use a configuration Activity). If |
| 484 | your App Widget accepts any user interaction events, then you need to register |
| 485 | the event handlers in this callback. If your App Widget doesn't create temporary |
| 486 | files or databases, or perform other work that requires clean-up, then |
| 487 | {@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()} |
| 488 | may be the only callback |
| 489 | method you need to define. For example, if you want an App Widget with a button |
| 490 | that launches an Activity when clicked, you could use the following |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 491 | implementation of AppWidgetProvider:</p> |
| 492 | |
| 493 | <pre> |
| 494 | public class ExampleAppWidgetProvider extends AppWidgetProvider { |
| 495 | |
| 496 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
| 497 | final int N = appWidgetIds.length; |
| 498 | |
| 499 | // Perform this loop procedure for each App Widget that belongs to this provider |
| 500 | for (int i=0; i<N; i++) { |
| 501 | int appWidgetId = appWidgetIds[i]; |
| 502 | |
| 503 | // Create an Intent to launch ExampleActivity |
| 504 | Intent intent = new Intent(context, ExampleActivity.class); |
| 505 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); |
| 506 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 507 | // Get the layout for the App Widget and attach an on-click listener |
| 508 | // to the button |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 509 | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout); |
| 510 | views.setOnClickPendingIntent(R.id.button, pendingIntent); |
| 511 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 512 | // Tell the AppWidgetManager to perform an update on the current app widget |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 513 | appWidgetManager.updateAppWidget(appWidgetId, views); |
| 514 | } |
| 515 | } |
| 516 | } |
| 517 | </pre> |
| 518 | |
| 519 | <p>This AppWidgetProvider defines only the |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 520 | {@link |
| 521 | android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()} |
| 522 | method for the purpose of |
| 523 | defining a {@link android.app.PendingIntent} that launches an {@link |
| 524 | android.app.Activity} and attaching it to the App Widget's button with {@link |
| 525 | android.widget.RemoteViews#setOnClickPendingIntent(int,PendingIntent)}. Notice |
| 526 | that it includes a loop that iterates through each entry in |
| 527 | <code>appWidgetIds</code>, which is an array of IDs that identify each App |
| 528 | Widget created by this provider. In this way, if the user creates more than one |
| 529 | instance of the App Widget, then they are all updated simultaneously. However, |
| 530 | only one <code>updatePeriodMillis</code> schedule will be managed for all |
| 531 | instances of the App Widget. For example, if the update schedule is defined to |
| 532 | be every two hours, and a second instance of the App Widget is added one hour |
| 533 | after the first one, then they will both be updated on the period defined by the |
| 534 | first one and the second update period will be ignored (they'll both be updated |
| 535 | every two hours, not every hour).</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 536 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 537 | <p class="note"><strong>Note:</strong> Because {@link |
| 538 | android.appwidget.AppWidgetProvider} is an extension of {@link |
| 539 | android.content.BroadcastReceiver}, your process is not guaranteed to keep |
| 540 | running after the callback methods return (see {@link |
| 541 | android.content.BroadcastReceiver} for information about the broadcast |
| 542 | lifecycle). If your App Widget setup process can take several seconds (perhaps |
| 543 | while performing web requests) and you require that your process continues, |
| 544 | consider starting a {@link android.app.Service} in the |
| 545 | {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()} |
| 546 | method. From within the Service, you can perform your own updates |
| 547 | to the App Widget without worrying about the AppWidgetProvider closing down due |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 548 | to an <a href="{@docRoot}guide/practices/responsiveness.html">Application |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 549 | Not Responding</a> (ANR) error. See the <a |
Scott Main | cd1b08e | 2011-12-27 16:22:27 -0800 | [diff] [blame] | 550 | href="http://code.google.com/p/wiktionary-android/source/browse/trunk/Wiktionary/src/com/example/android/wiktionary/WordWidget.java">Wiktionary sample's AppWidgetProvider</a> for an example of an App Widget running a {@link |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 551 | android.app.Service}.</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 552 | |
| 553 | <p>Also see the <a |
Scott Main | cd1b08e | 2011-12-27 16:22:27 -0800 | [diff] [blame] | 554 | href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.html">ExampleAppWidgetProvider.java</a> |
| 555 | sample class.</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 556 | |
| 557 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 558 | <h3 id="ProviderBroadcasts">Receiving App Widget broadcast Intents</h3> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 559 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 560 | <p>{@link android.appwidget.AppWidgetProvider} is just a convenience class. If |
| 561 | you would like |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 562 | to receive the App Widget broadcasts directly, you can implement your own |
| 563 | {@link android.content.BroadcastReceiver} or override the |
| 564 | {@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)} callback. |
Katie McCormick | 73ec048 | 2012-08-27 15:43:21 -0700 | [diff] [blame] | 565 | The Intents you need to care about are as follows:</p> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 566 | <ul> |
| 567 | <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE}</li> |
| 568 | <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DELETED}</li> |
| 569 | <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_ENABLED}</li> |
| 570 | <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DISABLED}</li> |
Katie McCormick | 73ec048 | 2012-08-27 15:43:21 -0700 | [diff] [blame] | 571 | <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_OPTIONS_CHANGED}</li> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 572 | </ul> |
| 573 | |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 574 | |
| 575 | |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 576 | <h2 id="Configuring">Creating an App Widget Configuration Activity</h2> |
Scott Main | 620986a | 2009-04-22 18:58:13 -0700 | [diff] [blame] | 577 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 578 | <p>If you would like the user to configure settings when he or she adds a new |
| 579 | App Widget, |
| 580 | you can create an App Widget configuration Activity. This {@link |
| 581 | android.app.Activity} |
| 582 | will be automatically launched by the App Widget host and allows the user to |
| 583 | configure |
| 584 | available settings for the App Widget at create-time, such as the App Widget |
| 585 | color, size, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 586 | update period or other functionality settings.</p> |
| 587 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 588 | <p>The configuration Activity should be declared as a normal Activity in the |
| 589 | Android manifest file. |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 590 | However, it will be launched by the App Widget host with the {@link |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 591 | android.appwidget.AppWidgetManager#ACTION_APPWIDGET_CONFIGURE |
| 592 | ACTION_APPWIDGET_CONFIGURE} action, |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 593 | so the Activity needs to accept this Intent. For example:</p> |
| 594 | |
| 595 | <pre> |
| 596 | <activity android:name=".ExampleAppWidgetConfigure"> |
| 597 | <intent-filter> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 598 | <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 599 | </intent-filter> |
| 600 | </activity> |
| 601 | </pre> |
| 602 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 603 | <p>Also, the Activity must be declared in the AppWidgetProviderInfo XML file, |
| 604 | with the |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 605 | <code>android:configure</code> attribute (see <a href="#MetaData">Adding |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 606 | the AppWidgetProviderInfo Metadata</a> above). For example, the configuration |
| 607 | Activity |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 608 | can be declared like this:</p> |
| 609 | |
| 610 | <pre> |
| 611 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" |
| 612 | ... |
| 613 | android:configure="com.example.android.ExampleAppWidgetConfigure" |
| 614 | ... > |
| 615 | </appwidget-provider> |
| 616 | </pre> |
| 617 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 618 | <p>Notice that the Activity is declared with a fully-qualified namespace, |
| 619 | because |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 620 | it will be referenced from outside your package scope.</p> |
| 621 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 622 | <p>That's all you need to get started with a configuration Activity. Now all you |
| 623 | need is the actual |
| 624 | Activity. There are, however, two important things to remember when you |
| 625 | implement the Activity:</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 626 | <ul> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 627 | <li>The App Widget host calls the configuration Activity and the configuration |
| 628 | Activity should always |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 629 | return a result. The result should include the App Widget ID |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 630 | passed by the Intent that launched the Activity (saved in the Intent extras |
| 631 | as |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 632 | {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID}).</li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 633 | <li>The |
| 634 | {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()} |
| 635 | method <strong>will not be called</strong> when the App Widget |
| 636 | is created |
| 637 | (the system will not send the ACTION_APPWIDGET_UPDATE broadcast when a |
| 638 | configuration Activity |
| 639 | is launched). It is the responsibility of the configuration Activity to |
| 640 | request an update from the |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 641 | AppWidgetManager when the App Widget is first created. However, |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 642 | {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()} |
| 643 | will be called for subsequent updates—it is only skipped |
| 644 | the first time.</li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 645 | </ul> |
| 646 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 647 | <p>See the code snippets in the following section for an example of how to |
| 648 | return a result |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 649 | from the configuration and update the App Widget.</p> |
| 650 | |
| 651 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 652 | <h3 id="UpdatingFromTheConfiguration">Updating the App Widget from the |
| 653 | configuration Activity</h3> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 654 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 655 | <p>When an App Widget uses a configuration Activity, it is the responsibility of |
| 656 | the Activity |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 657 | to update the App Widget when configuration is complete. |
| 658 | You can do so by requesting an update directly from the |
| 659 | {@link android.appwidget.AppWidgetManager}.</p> |
| 660 | |
| 661 | <p>Here's a summary of the procedure to properly update the App Widget and close |
| 662 | the configuration Activity:</p> |
| 663 | |
| 664 | <ol> |
| 665 | <li>First, get the App Widget ID from the Intent that launched the Activity: |
| 666 | <pre> |
| 667 | Intent intent = getIntent(); |
| 668 | Bundle extras = intent.getExtras(); |
| 669 | if (extras != null) { |
| 670 | mAppWidgetId = extras.getInt( |
| 671 | AppWidgetManager.EXTRA_APPWIDGET_ID, |
| 672 | AppWidgetManager.INVALID_APPWIDGET_ID); |
| 673 | } |
| 674 | </pre> |
| 675 | </li> |
| 676 | <li>Perform your App Widget configuration.</li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 677 | <li>When the configuration is complete, get an instance of the |
| 678 | AppWidgetManager by calling |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 679 | {@link android.appwidget.AppWidgetManager#getInstance(Context)}: |
| 680 | <pre> |
| 681 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); |
| 682 | </pre> |
| 683 | </li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 684 | <li>Update the App Widget with a {@link android.widget.RemoteViews} layout by |
| 685 | calling |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 686 | {@link android.appwidget.AppWidgetManager#updateAppWidget(int,RemoteViews)}: |
| 687 | <pre> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 688 | RemoteViews views = new RemoteViews(context.getPackageName(), |
| 689 | R.layout.example_appwidget); |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 690 | appWidgetManager.updateAppWidget(mAppWidgetId, views); |
| 691 | </pre> |
| 692 | </li> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 693 | <li>Finally, create the return Intent, set it with the Activity result, and |
| 694 | finish the Activity:</li> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 695 | <pre> |
| 696 | Intent resultValue = new Intent(); |
| 697 | resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); |
| 698 | setResult(RESULT_OK, resultValue); |
| 699 | finish(); |
| 700 | </pre> |
| 701 | </li> |
| 702 | </ol> |
| 703 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 704 | <p class="note"><strong>Tip:</strong> When your configuration Activity first |
| 705 | opens, set |
| 706 | the Activity result to RESULT_CANCELED. This way, if the user backs-out of the |
| 707 | Activity before |
| 708 | reaching the end, the App Widget host is notified that the configuration was |
| 709 | cancelled and the |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 710 | App Widget will not be added.</p> |
| 711 | |
| 712 | <p>See the <a |
Scott Main | cd1b08e | 2011-12-27 16:22:27 -0800 | [diff] [blame] | 713 | href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.html">ExampleAppWidgetConfigure.java</a> |
| 714 | sample class in ApiDemos for an example.</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 715 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 716 | <h2 id="preview">Setting a Preview Image</h2> |
| 717 | |
| 718 | <p>Android 3.0 introduces the {@link |
| 719 | |
| 720 | |
| 721 | android.appwidget.AppWidgetProviderInfo#previewImage} field, which specifies a |
| 722 | preview of what the app widget looks like. This preview is shown to the user from the |
| 723 | widget picker. If this field is not supplied, the app widget's icon is used for |
| 724 | the preview.</p> |
| 725 | |
| 726 | <p>This is how you specify this setting in XML:</p> |
| 727 | |
| 728 | <pre><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" |
| 729 | ... |
| 730 | android:previewImage="@drawable/preview"> |
| 731 | </appwidget-provider></pre> |
| 732 | |
| 733 | <p>To help create a preview image for your app widget (to specify in the {@link |
| 734 | android.appwidget.AppWidgetProviderInfo#previewImage} field), the Android |
| 735 | emulator includes an application called "Widget Preview." To create a |
| 736 | preview image, launch this application, select the app widget for your |
| 737 | application and set it up how you'd like your preview image to appear, then save |
| 738 | it and place it in your application's drawable resources.</p> |
| 739 | |
Katie McCormick | 7edb7a5 | 2012-10-24 10:58:18 -0700 | [diff] [blame] | 740 | <h2 id="lockscreen">Enabling App Widgets on the Lockscreen</h2> |
| 741 | |
| 742 | <p>Android 4.2 introduces the ability for users to add widgets to the lock screen. To indicate that your app widget is available for use on the lock screen, declare the {@link android.appwidget.AppWidgetProviderInfo#widgetCategory android:widgetCategory} attribute in the XML file that specifies your {@link android.appwidget.AppWidgetProviderInfo}. This attribute supports two values: "home_screen" and "keyguard". An app widget can declare support for one or both.</p> |
| 743 | |
| 744 | <p>By default, every app widget supports placement on the Home screen, so "home_screen" is the default value for the |
| 745 | {@link android.appwidget.AppWidgetProviderInfo#widgetCategory android:widgetCategory} attribute. If you want your app widget to be available for the lock screen, add the "keyguard" value:</p> |
| 746 | <pre> |
| 747 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" |
| 748 | ... |
| 749 | android:widgetCategory="keyguard|home_screen"> |
| 750 | </appwidget-provider> |
| 751 | </pre> |
| 752 | |
| 753 | <p>If you declare a widget to be displayable on both keyguard (lockscreen) and home, it's likely that you'll want to customize the widget depending on where it is displayed. For example, you might create a separate layout file for keyguard vs. home. The next step is to detect the widget category at runtime and respond accordingly. |
| 754 | |
| 755 | You can detect whether your widget is on the lockscreen or home screen by calling |
| 756 | {@link android.appwidget.AppWidgetManager#getAppWidgetOptions getAppWidgetOptions()} |
| 757 | to get the widget's options as a {@link android.os.Bundle}. The returned bundle will include the key |
| 758 | {@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_HOST_CATEGORY}, whose value will be one of {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or |
| 759 | {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}. This value is determined by the host into which the widget is bound. In the {@link android.appwidget.AppWidgetProvider}, you can then check the widget's category, for example:</p> |
| 760 | |
| 761 | <pre> |
| 762 | AppWidgetManager appWidgetManager; |
| 763 | int widgetId; |
| 764 | Bundle myOptions = appWidgetManager.getAppWidgetOptions (widgetId); |
| 765 | |
| 766 | // Get the value of OPTION_APPWIDGET_HOST_CATEGORY |
| 767 | int category = myOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1); |
| 768 | |
| 769 | // If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget |
| 770 | boolean isKeyguard = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD; |
| 771 | </pre> |
| 772 | |
| 773 | <p>Once you know the widget's category, you can optionally load a different base layout, set different properties, and so on. For example:</p> |
| 774 | |
| 775 | <pre> |
| 776 | int baseLayout = isKeyguard ? R.layout.keyguard_widget_layout : R.layout.widget_layout; |
| 777 | </pre> |
| 778 | |
| 779 | |
| 780 | <p>You should also specify an initial layout for your app widget when on the lock screen with the |
| 781 | {@link android.appwidget.AppWidgetProviderInfo#initialKeyguardLayout android:initialKeyguardLayout} attribute. This works the same way as the |
| 782 | {@link android.appwidget.AppWidgetProviderInfo#initialLayout android:initialLayout}, in that it provides a layout that can appear immediately until your app widget is initialized and able to update the layout.</p> |
| 783 | |
| 784 | <h3 id="lockscreen-sizing">Sizing guidelines</h3> |
| 785 | |
| 786 | <p>When a widget is hosted on the lockscreen, the framework ignores the {@code minWidth}, {@code minHeight}, {@code minResizeWidth}, and {@code minResizeHeight} fields. If a widget is also a home screen widget, these parameters are still needed as they're still used on home, but they will be ignored for purposes of the lockscreen.</p> |
| 787 | |
| 788 | <p>The width of a lockscreen widget always fills the provided space. For the height of a lockscreen widget, you have the following options:</p> |
| 789 | |
| 790 | <ul> |
| 791 | <li>If the widget does not mark itself as vertically resizable ({@code android:resizeMode="vertical"}), then the widget height will always be "small": |
| 792 | <ul> |
| 793 | <li>On a phone in portrait mode, "small" is defined as the space remaining when an unlock UI is being displayed.</li> |
| 794 | <li>On tablets and landscape phones, "small" is set on a per-device basis.</li> |
| 795 | </ul> |
| 796 | </li> |
| 797 | <li>If the widget marks itself as vertically resizable, then the widget height shows up as "small" on portrait phones displaying an unlock UI. In all other cases, the widget sizes to fill the available height.</li> |
| 798 | </ul> |
| 799 | |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 800 | <h2 id="collections">Using App Widgets with Collections</h2> |
| 801 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 802 | <p>Android 3.0 introduces app widgets with collections. These kinds of App |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 803 | Widgets use the {@link android.widget.RemoteViewsService} to display collections |
| 804 | that are backed by remote data, such as from a <a |
| 805 | href="{@docRoot}guide/topics/providers/content-providers.html">content |
| 806 | provider</a>. The data provided by the {@link android.widget.RemoteViewsService} |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 807 | is presented in the app widget using one of the following view types, which |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 808 | we’ll refer to as “collection views:”</p> |
| 809 | |
| 810 | <dl> |
| 811 | <dt>{@link android.widget.ListView}</dt> |
| 812 | <dd>A view that shows items in a |
| 813 | vertically scrolling |
| 814 | list. For an example, see the Gmail app widget. </dd> |
| 815 | <dt>{@link android.widget.GridView}</dt> |
| 816 | <dd>A view that shows items in |
| 817 | two-dimensional scrolling grid. For an example, see the Bookmarks app |
| 818 | widget.</dd> |
| 819 | <dt>{@link android.widget.StackView}</dt> |
| 820 | <dd>A |
| 821 | stacked card view (kind of like a rolodex), where the user can flick the front |
| 822 | card up/down to see the previous/next card, respectively. Examples include |
| 823 | the YouTube and Books app widgets. </dd> |
| 824 | <dt>{@link android.widget.AdapterViewFlipper}</dt> |
| 825 | <dd>An adapter-backed simple |
| 826 | {@link |
| 827 | android.widget.ViewAnimator} that animates between two or more views. Only one |
| 828 | child is shown at a time. </dd> |
| 829 | </dl> |
| 830 | |
| 831 | <p>As stated above, these collection views display collections backed by remote |
| 832 | data. This means that they use an {@link android.widget.Adapter} to bind their |
| 833 | user interface to their data. An {@link android.widget.Adapter} binds individual |
| 834 | items from a set of data into individual {@link android.view.View} objects. |
| 835 | Because these collection views are backed by adapters, the Android framework |
| 836 | must include extra architecture to support their use in app widgets. In the |
| 837 | context of an app widget, the {@link android.widget.Adapter} is replaced by a |
| 838 | {@link android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, |
| 839 | which is simply a thin wrapper around the {@link android.widget.Adapter} |
| 840 | interface. |
| 841 | When |
| 842 | requested for a specific item in the collection, the {@link |
| 843 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} creates |
| 844 | and returns the item for the collection as a {@link android.widget.RemoteViews} |
| 845 | object. |
| 846 | In order to include a collection view in your app widget, you |
| 847 | must implement {@link android.widget.RemoteViewsService} and {@link |
| 848 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}.</p> |
| 849 | |
| 850 | <p> {@link android.widget.RemoteViewsService} is a service that allows a remote |
| 851 | adapter to request {@link |
| 852 | android.widget.RemoteViews} objects. {@link |
| 853 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} is an |
| 854 | interface for an adapter between a collection view (such as {@link |
| 855 | android.widget.ListView}, {@link android.widget.GridView}, and so on) and the |
| 856 | underlying data for that view. From the <a |
| 857 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 858 | sample</a>, here is an example of the boilerplate code you use to implement |
| 859 | this service and interface: |
| 860 | </p> |
| 861 | |
| 862 | <pre> |
| 863 | public class StackWidgetService extends RemoteViewsService { |
| 864 | @Override |
| 865 | public RemoteViewsFactory onGetViewFactory(Intent intent) { |
| 866 | return new StackRemoteViewsFactory(this.getApplicationContext(), intent); |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { |
| 871 | |
| 872 | //... include adapter-like methods here. See the StackView Widget sample. |
| 873 | |
| 874 | } |
| 875 | </pre> |
| 876 | |
| 877 | <h3 id="collection_sample">Sample application</h3> |
| 878 | |
| 879 | <p>The code excerpts in this section are drawn from the <a |
| 880 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 881 | sample</a>:</p> |
| 882 | |
| 883 | <p> |
&& repo sync -j8 | 1019a9c | 2012-11-14 18:26:07 -0800 | [diff] [blame] | 884 | <img src="{@docRoot}images/appwidgets/StackWidget.png" alt="" /> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 885 | </p> |
| 886 | |
| 887 | <p>This sample consists of a stack of 10 views, which display the values |
| 888 | <code>"0!"</code> through <code>"9!"</code> The sample |
| 889 | app widget has these primary behaviors:</p> |
| 890 | |
| 891 | <ul> |
| 892 | |
| 893 | <li>The user can vertically fling the top view in the |
| 894 | app widget to display the next or previous view. This is a built-in StackView |
| 895 | behavior.</li> |
| 896 | |
| 897 | <li>Without any user interaction, the app widget automatically advances |
| 898 | through |
| 899 | its views in sequence, like a slide show. This is due to the setting |
| 900 | <code>android:autoAdvanceViewId="@id/stack_view"</code> in the |
| 901 | <code>res/xml/stackwidgetinfo.xml</code> file. This setting applies to the view |
| 902 | ID, |
| 903 | which in this case is the view ID of the stack view.</li> |
| 904 | |
| 905 | <li>If the user touches the top view, the app widget displays the {@link |
| 906 | android.widget.Toast} message "Touched view <em>n</em>," where |
| 907 | <em>n</em> is the index (position) of the touched view. For more discussion of |
| 908 | how this is implemented, see |
| 909 | <a href="#behavior">Adding behavior to individual items</a>.</li> |
| 910 | |
| 911 | </ul> |
| 912 | <h3 id="implementing_collections">Implementing app widgets with collections</h3> |
| 913 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 914 | <p>To implement an app widget with collections, you follow the same basic steps |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 915 | you would use to implement any app widget. The following sections describe the |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 916 | additional steps you need to perform to implement an app widget with |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 917 | collections.</p> |
| 918 | |
| 919 | <h4>Manifest for app widgets with collections</h4> |
| 920 | |
| 921 | <p> In addition to the requirements listed in <a href="#Manifest">Declaring an |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 922 | app widget in the Manifest</a>, to make it possible for app widgets with |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 923 | collections to bind to your {@link android.widget.RemoteViewsService}, you must |
| 924 | declare the service in your manifest file with the permission {@link |
| 925 | android.Manifest.permission#BIND_REMOTEVIEWS}. This prevents other applications |
| 926 | from freely accessing your app widget's data. For example, when creating an App |
| 927 | Widget that uses {@link android.widget.RemoteViewsService} to populate a |
| 928 | collection view, the manifest entry may look like this:</p> |
| 929 | |
| 930 | <pre><service android:name="MyWidgetService" |
| 931 | ... |
| 932 | android:permission="android.permission.BIND_REMOTEVIEWS" /></pre> |
| 933 | |
| 934 | <p>The line <code>android:name="MyWidgetService"</code> |
| 935 | refers to your subclass of {@link android.widget.RemoteViewsService}. </p> |
| 936 | |
| 937 | <h4>Layout for app widgets with collections</h4> |
| 938 | |
| 939 | <p>The main requirement for your app widget layout XML file is that it |
| 940 | include one of the collection views: {@link android.widget.ListView}, |
| 941 | {@link android.widget.GridView}, {@link android.widget.StackView}, or |
| 942 | {@link android.widget.AdapterViewFlipper}. Here is the |
| 943 | <code>widget_layout.xml</code> for |
| 944 | the <a href="{@docRoot}resources/samples/StackWidget/index.html">StackView |
| 945 | Widget sample</a>:</p> |
| 946 | |
| 947 | <pre><?xml version="1.0" encoding="utf-8"?> |
| 948 | |
| 949 | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 950 | android:layout_width="match_parent" |
| 951 | android:layout_height="match_parent"> |
| 952 | <StackView xmlns:android="http://schemas.android.com/apk/res/android" |
| 953 | android:id="@+id/stack_view" |
| 954 | android:layout_width="match_parent" |
| 955 | android:layout_height="match_parent" |
| 956 | android:gravity="center" |
| 957 | android:loopViews="true" /> |
| 958 | <TextView xmlns:android="http://schemas.android.com/apk/res/android" |
| 959 | android:id="@+id/empty_view" |
| 960 | android:layout_width="match_parent" |
| 961 | android:layout_height="match_parent" |
| 962 | android:gravity="center" |
| 963 | android:background="@drawable/widget_item_background" |
| 964 | android:textColor="#ffffff" |
| 965 | android:textStyle="bold" |
| 966 | android:text="@string/empty_view_text" |
| 967 | android:textSize="20sp" /> |
| 968 | </FrameLayout></pre> |
| 969 | |
| 970 | <p> Note that empty views must be siblings of the collection view for which the |
| 971 | empty view represents empty state. </p> |
| 972 | |
| 973 | <p>In addition to the layout file for your entire app widget, you must create |
| 974 | another layout file that defines the layout for each item in the collection (for |
| 975 | example, a layout for each book in a collection of books). For example, the <a |
| 976 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 977 | sample</a> only has one layout file, <code>widget_item.xml</code>, since all |
| 978 | items use the same layout. But the <a |
| 979 | href="{@docRoot}resources/samples/WeatherListWidget/index.html"> |
| 980 | WeatherListWidget sample</a> has two layout files: |
| 981 | <code>dark_widget_item.xml</code> and <code>light_widget_item.xml</code>.</p> |
| 982 | |
| 983 | |
| 984 | |
| 985 | <h4 id="AppWidgetProvider-collections">AppWidgetProvider class for app widgets with collections</h4> |
| 986 | |
| 987 | <p>As with a regular app widget, the bulk of your code in your {@link |
| 988 | android.appwidget.AppWidgetProvider} subclass typically goes in {@link |
| 989 | android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, |
| 990 | android.appwidget.AppWidgetManager, int[]) onUpdate()}. The major difference in |
| 991 | your implementation for {@link |
| 992 | android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, |
| 993 | android.appwidget.AppWidgetManager, int[]) onUpdate()} when creating an app |
| 994 | widget with collections is that you must call {@link |
| 995 | android.widget.RemoteViews#setRemoteAdapter setRemoteAdapter()}. This tells the |
| 996 | collection view where to get its data. The {@link |
| 997 | android.widget.RemoteViewsService} can then return your implementation of {@link |
| 998 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, and |
| 999 | the widget can serve up the appropriate data. When you call this method, you |
| 1000 | must pass an intent that points to your implementation of {@link |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 1001 | android.widget.RemoteViewsService} and the app widget ID that specifies the app |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1002 | widget to update.</p> |
| 1003 | |
| 1004 | |
| 1005 | <p>For example, here's how the StackView Widget sample implements the {@link |
| 1006 | android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, |
| 1007 | android.appwidget.AppWidgetManager, int[]) onUpdate()} callback method to set |
| 1008 | the {@link |
| 1009 | android.widget.RemoteViewsService} as the remote adapter for the app widget |
| 1010 | collection:</p> |
| 1011 | |
| 1012 | <pre>public void onUpdate(Context context, AppWidgetManager appWidgetManager, |
| 1013 | int[] appWidgetIds) { |
| 1014 | // update each of the app widgets with the remote adapter |
| 1015 | for (int i = 0; i < appWidgetIds.length; ++i) { |
| 1016 | |
| 1017 | // Set up the intent that starts the StackViewService, which will |
| 1018 | // provide the views for this collection. |
| 1019 | Intent intent = new Intent(context, StackWidgetService.class); |
| 1020 | // Add the app widget ID to the intent extras. |
| 1021 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); |
| 1022 | intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 1023 | // Instantiate the RemoteViews object for the app widget layout. |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1024 | RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); |
| 1025 | // Set up the RemoteViews object to use a RemoteViews adapter. |
| 1026 | // This adapter connects |
| 1027 | // to a RemoteViewsService through the specified intent. |
| 1028 | // This is how you populate the data. |
| 1029 | rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent); |
| 1030 | |
| 1031 | // The empty view is displayed when the collection has no items. |
| 1032 | // It should be in the same layout used to instantiate the RemoteViews |
| 1033 | // object above. |
| 1034 | rv.setEmptyView(R.id.stack_view, R.id.empty_view); |
| 1035 | |
| 1036 | // |
| 1037 | // Do additional processing specific to this app widget... |
| 1038 | // |
| 1039 | |
| 1040 | appWidgetManager.updateAppWidget(appWidgetIds[i], rv); |
| 1041 | } |
| 1042 | super.onUpdate(context, appWidgetManager, appWidgetIds); |
| 1043 | }</pre> |
| 1044 | |
| 1045 | <h4>RemoteViewsService class</h4> |
| 1046 | |
| 1047 | <div class="sidebox-wrapper"> |
| 1048 | <div class="sidebox"> |
| 1049 | <h3>Persisting data</h3> |
| 1050 | <p>You can’t rely on a single instance of your service, or any data it |
| 1051 | contains, to persist. You should therefore not store any data in your {@link |
| 1052 | android.widget.RemoteViewsService} (unless it is static). If you want your |
| 1053 | app widget’s data to persist, the best approach is to use a {@link |
| 1054 | android.content.ContentProvider} whose data persists beyond the process |
| 1055 | lifecycle.</p> </div> |
| 1056 | </div> |
| 1057 | |
| 1058 | <p>As described above, your {@link android.widget.RemoteViewsService} subclass |
| 1059 | provides the {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1060 | RemoteViewsFactory} used to populate the remote collection view.</p> |
| 1061 | |
| 1062 | <p>Specifically, you need to |
| 1063 | perform these steps:</p> |
| 1064 | |
| 1065 | <ol> |
| 1066 | <li>Subclass {@link android.widget.RemoteViewsService}. {@link |
| 1067 | android.widget.RemoteViewsService} is the service through which |
| 1068 | a remote adapter can request {@link android.widget.RemoteViews}. </li> |
| 1069 | |
| 1070 | <li>In your {@link android.widget.RemoteViewsService} subclass, include a |
| 1071 | class that implements the {@link |
| 1072 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} |
| 1073 | interface. {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1074 | RemoteViewsFactory} is an interface for an adapter between a remote collection |
| 1075 | view (such as {@link android.widget.ListView}, {@link android.widget.GridView}, |
| 1076 | and so on) and the underlying data for that view. Your implementation is |
| 1077 | responsible for making a {@link android.widget.RemoteViews} object for each |
| 1078 | item in the data set. This interface is a thin wrapper around {@link |
| 1079 | android.widget.Adapter}.</li> |
| 1080 | </ol> |
| 1081 | |
| 1082 | <p>The primary contents of the {@link android.widget.RemoteViewsService} |
| 1083 | implementation is its {@link |
| 1084 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, |
| 1085 | described below.</p> |
| 1086 | |
| 1087 | <h4>RemoteViewsFactory interface</h4> |
| 1088 | |
| 1089 | <p>Your custom class that implements the {@link |
| 1090 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} |
| 1091 | interface provides the app widget with the data for the items in its collection. |
| 1092 | To |
| 1093 | do this, it combines your app widget item XML layout file with a source of data. |
| 1094 | This source of data could be anything from a database to a simple array. In the |
| 1095 | <a href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 1096 | sample</a>, the data source is an array of <code>WidgetItems</code>. The {@link |
| 1097 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} |
| 1098 | functions as an adapter to glue the data to the remote collection view.</p> |
| 1099 | |
| 1100 | <p>The two most important methods you need to implement for your |
| 1101 | |
| 1102 | {@link android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} |
| 1103 | subclass are |
| 1104 | {@link android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() |
| 1105 | onCreate()} and |
| 1106 | {@link android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int) |
| 1107 | getViewAt()} |
| 1108 | .</p> |
| 1109 | |
| 1110 | <p>The system calls {@link |
| 1111 | android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()} when |
| 1112 | creating your factory for the first time. This is where you set up any |
| 1113 | connections and/or cursors to your data source. For example, the <a |
| 1114 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 1115 | sample</a> uses {@link |
| 1116 | android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()} to |
| 1117 | initialize an array of <code>WidgetItem</code> objects. When your app widget is |
| 1118 | active, the system accesses these objects using their index position in the |
| 1119 | array and the text they contain is displayed </p> |
| 1120 | |
kmccormick | 76dfc02 | 2013-04-03 12:41:12 -0700 | [diff] [blame] | 1121 | <p>Here is an excerpt from the <a |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1122 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget</a> |
| 1123 | sample's |
| 1124 | {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1125 | RemoteViewsFactory} implementation that shows portions of the {@link |
| 1126 | android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()} |
| 1127 | method:</p> |
| 1128 | |
| 1129 | <pre>class StackRemoteViewsFactory implements |
| 1130 | RemoteViewsService.RemoteViewsFactory { |
| 1131 | private static final int mCount = 10; |
| 1132 | private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>(); |
| 1133 | private Context mContext; |
| 1134 | private int mAppWidgetId; |
| 1135 | |
| 1136 | public StackRemoteViewsFactory(Context context, Intent intent) { |
| 1137 | mContext = context; |
| 1138 | mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, |
| 1139 | AppWidgetManager.INVALID_APPWIDGET_ID); |
| 1140 | } |
| 1141 | |
| 1142 | public void onCreate() { |
| 1143 | // In onCreate() you setup any connections / cursors to your data source. Heavy lifting, |
| 1144 | // for example downloading or creating content etc, should be deferred to onDataSetChanged() |
| 1145 | // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. |
| 1146 | for (int i = 0; i < mCount; i++) { |
| 1147 | mWidgetItems.add(new WidgetItem(i + "!")); |
| 1148 | } |
| 1149 | ... |
| 1150 | } |
| 1151 | ...</pre> |
| 1152 | |
| 1153 | <p>The {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1154 | RemoteViewsFactory} method {@link |
| 1155 | android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int) getViewAt()} |
| 1156 | returns a {@link android.widget.RemoteViews} object corresponding to the data at |
| 1157 | the specified <code>position</code> in the data set. Here is an excerpt from |
| 1158 | the <a |
| 1159 | href="http://developer.android.com/resources/samples/StackWidget/index.html"> |
| 1160 | StackView Widget</a> sample's {@link |
| 1161 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} |
| 1162 | implementation:</p> |
| 1163 | |
| 1164 | <pre>public RemoteViews getViewAt(int position) { |
| 1165 | |
| 1166 | // Construct a remote views item based on the app widget item XML file, |
| 1167 | // and set the text based on the position. |
| 1168 | RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); |
| 1169 | rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text); |
| 1170 | |
| 1171 | ... |
| 1172 | // Return the remote views object. |
| 1173 | return rv; |
| 1174 | }</pre> |
| 1175 | |
| 1176 | <h4 id="behavior">Adding behavior to individual items</h4> |
| 1177 | |
| 1178 | <p>The above sections show you how to bind your data to your app widget |
| 1179 | collection. But what if you want to add dynamic behavior to the individual items |
| 1180 | in your collection view?</p> |
| 1181 | |
| 1182 | <p> As described in <a href="#AppWidgetProvider">Using the AppWidgetProvider |
| 1183 | Class</a>, you normally use {@link |
| 1184 | android.widget.RemoteViews#setOnClickPendingIntent(int, |
| 1185 | android.app.PendingIntent) setOnClickPendingIntent()} to set an object's click |
| 1186 | behavior—such as to cause a button to launch an {@link |
| 1187 | android.app.Activity}. But this approach is not allowed for child views in an |
| 1188 | individual collection item (to clarify, you could use {@link |
| 1189 | android.widget.RemoteViews#setOnClickPendingIntent(int, |
| 1190 | android.app.PendingIntent) setOnClickPendingIntent()} to set up a global button |
| 1191 | in the Gmail app widget that launches the app, for example, but not on the |
| 1192 | individual list items). Instead, to add click behavior to individual items in a |
| 1193 | collection, you use {@link |
| 1194 | android.widget.RemoteViews#setOnClickFillInIntent(int, android.content.Intent) |
| 1195 | setOnClickFillInIntent()}. This entails setting up up a pending intent template |
| 1196 | for your collection view, and then setting a fill-in intent on each item in the |
| 1197 | collection via your {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1198 | RemoteViewsFactory}.</p> |
| 1199 | <p>This section uses the <a |
| 1200 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 1201 | sample</a> to describe how to add behavior to individual items. In the <a |
| 1202 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 1203 | sample</a>, if the user touches the top view, the app widget displays the {@link |
| 1204 | android.widget.Toast} message "Touched view <em>n</em>," where |
| 1205 | <em>n</em> is the index (position) of the touched view. This is how it |
| 1206 | works:</p> |
| 1207 | |
| 1208 | <ul> |
| 1209 | <li>The <code>StackWidgetProvider</code> (an {@link |
| 1210 | android.appwidget.AppWidgetProvider} subclass) creates a pending intent that has |
| 1211 | a custom action called <code>TOAST_ACTION</code>.</li> |
| 1212 | <li>When the user touches a view, the intent is fired and it broadcasts |
| 1213 | <code>TOAST_ACTION</code>.</li> |
| 1214 | |
| 1215 | <li>This broadcast is intercepted by the <code>StackWidgetProvider</code>'s |
| 1216 | {@link android.appwidget.AppWidgetProvider#onReceive(android.content.Context, |
| 1217 | android.content.Intent) onReceive()} method, and the app widget displays the |
| 1218 | {@link |
| 1219 | android.widget.Toast} message for the touched view. The data for the collection |
| 1220 | items is provided by the {@link |
| 1221 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, via |
| 1222 | the {@link android.widget.RemoteViewsService}.</li> |
| 1223 | </ul> |
| 1224 | |
| 1225 | <p class="note"><strong>Note:</strong> The <a |
| 1226 | href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget |
| 1227 | sample</a> uses a broadcast, but typically an app widget would simply launch an |
| 1228 | activity in a scenario like this one.</p> |
| 1229 | |
| 1230 | <h5>Setting up the pending intent template</h5> |
| 1231 | |
| 1232 | <p>The <code>StackWidgetProvider</code> ({@link |
| 1233 | android.appwidget.AppWidgetProvider} subclass) sets up a pending intent. |
| 1234 | Individuals items of a collection cannot set up their own pending intents. |
| 1235 | Instead, the collection as a whole sets up a pending intent template, and the |
| 1236 | individual items set a fill-in intent to create unique behavior on an |
| 1237 | item-by-item |
| 1238 | basis.</p> |
| 1239 | |
| 1240 | <p>This class also receives the broadcast that is sent when the user touches a |
| 1241 | view. It processes this event in its {@link |
| 1242 | android.appwidget.AppWidgetProvider#onReceive(android.content.Context, |
| 1243 | android.content.Intent) onReceive()} method. If the intent's action is |
| 1244 | <code>TOAST_ACTION</code>, the app widget displays a {@link |
| 1245 | android.widget.Toast} |
| 1246 | message for the current view.</p> |
| 1247 | |
| 1248 | <pre>public class StackWidgetProvider extends AppWidgetProvider { |
| 1249 | public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION"; |
| 1250 | public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM"; |
| 1251 | |
| 1252 | ... |
| 1253 | |
| 1254 | // Called when the BroadcastReceiver receives an Intent broadcast. |
| 1255 | // Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget |
| 1256 | // displays a Toast message for the current item. |
| 1257 | @Override |
| 1258 | public void onReceive(Context context, Intent intent) { |
| 1259 | AppWidgetManager mgr = AppWidgetManager.getInstance(context); |
| 1260 | if (intent.getAction().equals(TOAST_ACTION)) { |
| 1261 | int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, |
| 1262 | AppWidgetManager.INVALID_APPWIDGET_ID); |
| 1263 | int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0); |
| 1264 | Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show(); |
| 1265 | } |
| 1266 | super.onReceive(context, intent); |
| 1267 | } |
| 1268 | |
| 1269 | @Override |
| 1270 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
| 1271 | // update each of the app widgets with the remote adapter |
| 1272 | for (int i = 0; i < appWidgetIds.length; ++i) { |
| 1273 | |
| 1274 | // Sets up the intent that points to the StackViewService that will |
| 1275 | // provide the views for this collection. |
| 1276 | Intent intent = new Intent(context, StackWidgetService.class); |
| 1277 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); |
| 1278 | // When intents are compared, the extras are ignored, so we need to embed the extras |
| 1279 | // into the data so that the extras will not be ignored. |
| 1280 | intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); |
| 1281 | RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); |
| 1282 | rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent); |
| 1283 | |
| 1284 | // The empty view is displayed when the collection has no items. It should be a sibling |
| 1285 | // of the collection view. |
| 1286 | rv.setEmptyView(R.id.stack_view, R.id.empty_view); |
| 1287 | |
| 1288 | // This section makes it possible for items to have individualized behavior. |
| 1289 | // It does this by setting up a pending intent template. Individuals items of a collection |
| 1290 | // cannot set up their own pending intents. Instead, the collection as a whole sets |
| 1291 | // up a pending intent template, and the individual items set a fillInIntent |
| 1292 | // to create unique behavior on an item-by-item basis. |
| 1293 | Intent toastIntent = new Intent(context, StackWidgetProvider.class); |
| 1294 | // Set the action for the intent. |
| 1295 | // When the user touches a particular view, it will have the effect of |
| 1296 | // broadcasting TOAST_ACTION. |
| 1297 | toastIntent.setAction(StackWidgetProvider.TOAST_ACTION); |
| 1298 | toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); |
| 1299 | intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); |
| 1300 | PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent, |
| 1301 | PendingIntent.FLAG_UPDATE_CURRENT); |
| 1302 | rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent); |
| 1303 | |
| 1304 | appWidgetManager.updateAppWidget(appWidgetIds[i], rv); |
| 1305 | } |
| 1306 | super.onUpdate(context, appWidgetManager, appWidgetIds); |
| 1307 | } |
| 1308 | }</pre> |
| 1309 | |
| 1310 | <h5><strong>Setting the fill-in Intent</strong></h5> |
| 1311 | |
| 1312 | <p>Your {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1313 | RemoteViewsFactory} must set a fill-in intent on each item in the collection. |
| 1314 | This makes it possible to distinguish the individual on-click action of a given |
| 1315 | item. The fill-in intent is then combined with the {@link |
| 1316 | android.app.PendingIntent} template in order to determine the final intent that |
| 1317 | will be executed when the item is clicked. </p> |
| 1318 | |
| 1319 | <pre> |
| 1320 | public class StackWidgetService extends RemoteViewsService { |
| 1321 | @Override |
| 1322 | public RemoteViewsFactory onGetViewFactory(Intent intent) { |
| 1323 | return new StackRemoteViewsFactory(this.getApplicationContext(), intent); |
| 1324 | } |
| 1325 | } |
| 1326 | |
| 1327 | class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { |
| 1328 | private static final int mCount = 10; |
| 1329 | private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>(); |
| 1330 | private Context mContext; |
| 1331 | private int mAppWidgetId; |
| 1332 | |
| 1333 | public StackRemoteViewsFactory(Context context, Intent intent) { |
| 1334 | mContext = context; |
| 1335 | mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, |
| 1336 | AppWidgetManager.INVALID_APPWIDGET_ID); |
| 1337 | } |
| 1338 | |
| 1339 | // Initialize the data set. |
| 1340 | public void onCreate() { |
| 1341 | // In onCreate() you set up any connections / cursors to your data source. Heavy lifting, |
| 1342 | // for example downloading or creating content etc, should be deferred to onDataSetChanged() |
| 1343 | // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. |
| 1344 | for (int i = 0; i < mCount; i++) { |
| 1345 | mWidgetItems.add(new WidgetItem(i + "!")); |
| 1346 | } |
| 1347 | ... |
| 1348 | } |
| 1349 | ... |
| 1350 | |
| 1351 | // Given the position (index) of a WidgetItem in the array, use the item's text value in |
| 1352 | // combination with the app widget item XML file to construct a RemoteViews object. |
| 1353 | public RemoteViews getViewAt(int position) { |
| 1354 | // position will always range from 0 to getCount() - 1. |
| 1355 | |
| 1356 | // Construct a RemoteViews item based on the app widget item XML file, and set the |
| 1357 | // text based on the position. |
| 1358 | RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); |
| 1359 | rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text); |
| 1360 | |
| 1361 | // Next, set a fill-intent, which will be used to fill in the pending intent template |
| 1362 | // that is set on the collection view in StackWidgetProvider. |
| 1363 | Bundle extras = new Bundle(); |
| 1364 | extras.putInt(StackWidgetProvider.EXTRA_ITEM, position); |
| 1365 | Intent fillInIntent = new Intent(); |
| 1366 | fillInIntent.putExtras(extras); |
| 1367 | // Make it possible to distinguish the individual on-click |
| 1368 | // action of a given item |
| 1369 | rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent); |
| 1370 | |
| 1371 | ... |
| 1372 | |
| 1373 | // Return the RemoteViews object. |
| 1374 | return rv; |
| 1375 | } |
| 1376 | ... |
| 1377 | }</pre> |
| 1378 | |
| 1379 | <h3 id="fresh">Keeping Collection Data Fresh</h3> |
| 1380 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 1381 | <p>The following figure illustrates the flow that occurs in an app widget that |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1382 | uses |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 1383 | collections when updates occur. It shows how the app widget code interacts with |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1384 | the {@link android.widget.RemoteViewsService.RemoteViewsFactory |
| 1385 | RemoteViewsFactory}, and how you can trigger updates:</p> |
| 1386 | |
&& repo sync -j8 | 1019a9c | 2012-11-14 18:26:07 -0800 | [diff] [blame] | 1387 | <img src="{@docRoot}images/appwidgets/appwidget_collections.png" alt="" /> |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1388 | |
kmccormick | 25072c7 | 2013-03-01 16:13:21 -0800 | [diff] [blame] | 1389 | <p>One feature of app widgets that use collections is the ability to provide |
Katie McCormick | 566489d | 2011-03-28 17:08:08 -0700 | [diff] [blame] | 1390 | users with up-to-date content. For example, consider the Android 3.0 Gmail |
| 1391 | app widget, which provides users with a snapshot of their inbox. To make this |
| 1392 | possible, you need to be able to trigger your {@link |
| 1393 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} and |
| 1394 | collection view to fetch and display new data. You achieve this with the {@link |
| 1395 | android.appwidget.AppWidgetManager} call {@link |
| 1396 | android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int, int) |
| 1397 | notifyAppWidgetViewDataChanged()}. This call results in a callback to your |
| 1398 | <code>RemoteViewsFactory</code>’s {@link |
| 1399 | android.widget.RemoteViewsService.RemoteViewsFactory#onDataSetChanged() |
| 1400 | onDataSetChanged()} method, which gives you the opportunity to fetch any new |
| 1401 | data. Note that you can perform |
| 1402 | processing-intensive operations synchronously within the {@link |
| 1403 | android.widget.RemoteViewsService.RemoteViewsFactory#onDataSetChanged() |
| 1404 | onDataSetChanged()} callback. You are guaranteed that this call will be |
| 1405 | completed before the metadata or view data is fetched from the {@link |
| 1406 | android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}. In |
| 1407 | addition, you can perform processing-intensive operations within the {@link |
| 1408 | android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int) getViewAt()} |
| 1409 | method. If this call takes a long time, the loading view (specified by the |
| 1410 | <code>RemoteViewsFactory</code>’s {@link |
| 1411 | android.widget.RemoteViewsService.RemoteViewsFactory#getLoadingView()} method) |
| 1412 | will be displayed in the corresponding position of the collection view until it |
| 1413 | returns.</p> |
Scott Main | 04c72b4 | 2009-05-13 16:48:13 -0700 | [diff] [blame] | 1414 | |
| 1415 | |