Move over resources and files for the widget.

This renames/ moves over styles and drawables for the widget
as well as xml files and classes.

Doesn't do much right now except display a unified email
widget in the widgets selection area and then say "app not
installed" when you drop it. But its a start!
Change-Id: Ie2759ce0adf520bd65222b50ddc8ab14c6659a37
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3d27dbb..95fc926 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -45,6 +45,24 @@
         <activity
            android:name=".ui.MailActivity"
            android:uiOptions="splitActionBarWhenNarrow" />
+        <activity android:name="LabelSynchronizationActivity"
+            android:theme="@android:style/Theme.Holo.Light.Dialog" />
+
+        <activity android:name="CreateShortcutActivity"
+            android:theme="@style/ShortcutWidgetTheme"
+            android:label="@string/activity_folder_selection" />
+
+        <activity android:name="FolderSelectionActivity"
+            android:theme="@style/ShortcutWidgetTheme"
+            android:label="@string/activity_folder_selection" />
+
+        <activity android:name="MailboxSelectionActivity"
+            android:theme="@style/InvisibleShortcutWidgetTheme"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
+            </intent-filter>
+        </activity>
 
         <provider
             android:authorities="com.android.mail.mockprovider"
@@ -87,6 +105,22 @@
         <service android:name=".providers.protos.boot.GmailAccountService"/>
         <service android:name=".providers.protos.boot.EmailAccountService"/>
         <service android:name=".compose.EmptyService"/>
+
+        <!-- Widget -->
+        <receiver android:name=".widget.WidgetProvider"  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.google.android.gm.intent.ACTION_NOTIFY_DATASET_CHANGED" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_info" />
+        </receiver>
+        <service android:name=".widget.WidgetService"
+                 android:permission="android.permission.BIND_REMOTEVIEWS"
+                 android:exported="false" />
+
     </application>
 
 </manifest>
diff --git a/res/drawable-hdpi/gradient_bg_email_widget_holo.9.png b/res/drawable-hdpi/gradient_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-hdpi/gradient_bg_email_widget_holo.9.png
rename to res/drawable-hdpi/gradient_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/header_bg_email_widget_holo.9.png b/res/drawable-hdpi/header_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-hdpi/header_bg_email_widget_holo.9.png
rename to res/drawable-hdpi/header_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_div_top_btm_email_widget_holo.9.png b/res/drawable-hdpi/list_div_top_btm_widget_holo.9.png
similarity index 100%
rename from res/drawable-hdpi/list_div_top_btm_email_widget_holo.9.png
rename to res/drawable-hdpi/list_div_top_btm_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/gradient_bg_email_widget_holo.9.png b/res/drawable-mdpi/gradient_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-mdpi/gradient_bg_email_widget_holo.9.png
rename to res/drawable-mdpi/gradient_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/header_bg_email_widget_holo.9.png b/res/drawable-mdpi/header_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-mdpi/header_bg_email_widget_holo.9.png
rename to res/drawable-mdpi/header_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_div_top_btm_email_widget_holo.9.png b/res/drawable-mdpi/list_div_top_btm_widget_holo.9.png
similarity index 100%
rename from res/drawable-mdpi/list_div_top_btm_email_widget_holo.9.png
rename to res/drawable-mdpi/list_div_top_btm_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-nodpi/googlemail_widget_preview.png b/res/drawable-nodpi/googlemail_widget_preview.png
deleted file mode 100644
index fac94b0..0000000
--- a/res/drawable-nodpi/googlemail_widget_preview.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-nodpi/mail_widget_preview.png b/res/drawable-nodpi/mail_widget_preview.png
new file mode 100644
index 0000000..f04b42a
--- /dev/null
+++ b/res/drawable-nodpi/mail_widget_preview.png
Binary files differ
diff --git a/res/drawable-xhdpi/gradient_bg_email_widget_holo.9.png b/res/drawable-xhdpi/gradient_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-xhdpi/gradient_bg_email_widget_holo.9.png
rename to res/drawable-xhdpi/gradient_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/header_bg_email_widget_holo.9.png b/res/drawable-xhdpi/header_bg_widget_holo.9.png
similarity index 100%
rename from res/drawable-xhdpi/header_bg_email_widget_holo.9.png
rename to res/drawable-xhdpi/header_bg_widget_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_div_top_btm_email_widget_holo.9.png b/res/drawable-xhdpi/list_div_top_btm_widget_holo.9.png
similarity index 100%
rename from res/drawable-xhdpi/list_div_top_btm_email_widget_holo.9.png
rename to res/drawable-xhdpi/list_div_top_btm_widget_holo.9.png
Binary files differ
diff --git a/res/drawable/widget_conversation_read_selector.xml b/res/drawable/widget_conversation_read_selector.xml
new file mode 100644
index 0000000..78eb9c7
--- /dev/null
+++ b/res/drawable/widget_conversation_read_selector.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@drawable/list_pressed_holo" />
+    <item android:state_focused="true"
+          android:drawable="@drawable/list_focused_holo" />
+    <item android:state_selected="true"
+          android:drawable="@drawable/list_focused_holo" />
+    <item android:drawable="@drawable/list_read_holo" />
+</selector>
diff --git a/res/drawable/widget_conversation_unread_selector.xml b/res/drawable/widget_conversation_unread_selector.xml
new file mode 100644
index 0000000..56f4d16
--- /dev/null
+++ b/res/drawable/widget_conversation_unread_selector.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@drawable/list_pressed_holo" />
+    <item android:state_focused="true"
+          android:drawable="@drawable/list_focused_holo" />
+    <item android:state_selected="true"
+          android:drawable="@drawable/list_focused_holo" />
+    <item android:drawable="@drawable/list_unread_holo" />
+</selector>
diff --git a/res/layout/folder_sync_for_widget_dialog.xml b/res/layout/folder_sync_for_widget_dialog.xml
new file mode 100644
index 0000000..4e17f98
--- /dev/null
+++ b/res/layout/folder_sync_for_widget_dialog.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+      android:fillViewport="true"
+      android:layout_height="0dip"
+      android:layout_weight="1"
+      android:layout_width="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:id="@+id/folder_sync_for_widget_text"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="10dip"
+            android:paddingBottom="20dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:singleLine="false"
+            android:text="@string/folder_sync_for_widget_description" />
+
+        <CheckBox
+            android:id="@+id/folder_sync_for_widget_confirm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/folder_sync_for_widget_text"
+            android:text="@string/folder_sync_for_widget_checkbox"
+            android:checked="true" />
+
+    </RelativeLayout>
+
+</ScrollView>
diff --git a/res/layout/mailbox_item.xml b/res/layout/mailbox_item.xml
new file mode 100644
index 0000000..1ec2156
--- /dev/null
+++ b/res/layout/mailbox_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
+
+    <TextView
+        android:id="@+id/mailbox_name"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
+        android:drawablePadding="10dip" />
+
+</LinearLayout>
diff --git a/res/layout/mailbox_selection_activity.xml b/res/layout/mailbox_selection_activity.xml
new file mode 100644
index 0000000..88fbf92
--- /dev/null
+++ b/res/layout/mailbox_selection_activity.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:minWidth="@dimen/folder_list_popup_width"
+    android:orientation="vertical">
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:cacheColorHint="#00000000"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
+        android:fadingEdgeLength="16dip" />
+
+    <include layout="@layout/one_button_button_bar"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/one_button_button_bar.xml b/res/layout/one_button_button_bar.xml
new file mode 100644
index 0000000..018a177
--- /dev/null
+++ b/res/layout/one_button_button_bar.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:divider="?android:attr/dividerHorizontal"
+    android:showDividers="beginning"
+    android:dividerPadding="16dip"
+    android:background="@null">
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="2dip"
+        android:paddingRight="2dip"
+        android:measureWithLargestChild="true"
+        android:background="@null">
+        <Button
+            android:id="@+id/cancel"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:text="@string/cancel" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/shortcut_name_activity.xml b/res/layout/shortcut_name_activity.xml
new file mode 100644
index 0000000..c9e05c5
--- /dev/null
+++ b/res/layout/shortcut_name_activity.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/ShortcutNameWrapper"
+    android:minWidth="@dimen/folder_list_popup_width"
+    android:orientation="vertical">
+
+    <!-- Shortcut name -->
+    <EditText
+        android:id="@+id/folder_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textAutoCorrect|textCapSentences|textImeMultiLine"
+        android:imeOptions="actionDone"
+        android:layout_marginRight="16dip"
+        android:layout_marginLeft="16dip"
+        android:layout_gravity="center"
+        style="@style/ShortcutNameStyle"/>
+
+    <include layout="@layout/two_button_button_bar"/>
+
+</LinearLayout>
diff --git a/res/layout/two_button_button_bar.xml b/res/layout/two_button_button_bar.xml
new file mode 100644
index 0000000..a98ef19
--- /dev/null
+++ b/res/layout/two_button_button_bar.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:divider="?android:attr/dividerHorizontal"
+    android:showDividers="beginning"
+    android:dividerPadding="16dip"
+    android:background="@null">
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="2dip"
+        android:paddingRight="2dip"
+        android:measureWithLargestChild="true"
+        android:background="@null">
+        <Button
+            android:id="@+id/cancel"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:text="@string/cancel" />
+        <Button
+            android:id="@+id/done"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:text="@string/done" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/widget.xml b/res/layout/widget.xml
new file mode 100644
index 0000000..d0da897
--- /dev/null
+++ b/res/layout/widget.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="@dimen/widget_margin_top"
+    android:layout_marginLeft="@dimen/widget_margin_left"
+    android:layout_marginRight="@dimen/widget_margin_right"
+    android:layout_marginBottom="@dimen/widget_margin_bottom"
+    android:orientation="vertical">
+    <LinearLayout
+        android:id="@+id/widget_header"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:orientation="horizontal"
+        android:paddingLeft="8dip"
+        android:paddingRight="8dip"
+        android:background="@drawable/header_bg_widget_holo"
+        android:gravity="center_vertical">
+        <ImageView
+            android:id="@+id/widget_icon"
+            android:layout_width="32dip"
+            android:layout_height="32dip"
+            android:layout_marginRight="8dip"
+            android:src="@mipmap/ic_launcher_mail" />
+        <LinearLayout
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight = "1"
+            android:layout_marginRight="8dip"
+            android:orientation="vertical">
+            <TextView
+                android:id="@+id/widget_folder"
+                style="@style/WidgetTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:freezesText="true" />
+            <TextView
+                android:id="@+id/widget_account"
+                style="@style/WidgetSubtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:freezesText="true" />
+        </LinearLayout>
+        <!-- Note: We can't set "freezesText" on the widget_unread_count view, as the launcher can
+             restore stale data over updated data. -->
+        <TextView
+            android:id="@+id/widget_unread_count"
+            style="@style/UnreadCount"
+            android:layout_marginRight="12dip"
+            android:textColor="#777777" />
+        <ImageButton
+            android:id="@+id/widget_compose"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_menu_compose_normal_holo_light"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="4dip"
+            android:paddingBottom="4dip" />
+    </LinearLayout>
+    <ListView
+        android:id="@+id/conversation_list"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:cacheColorHint="#00000000"
+        android:background="@drawable/gradient_bg_widget_holo" />
+    <LinearLayout
+        android:id="@+id/widget_configuration"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center"
+        android:background="@drawable/gradient_bg_widget_holo"
+        android:visibility="gone">
+        <ImageView
+            android:src="@mipmap/ic_launcher_mail"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="8dip" />
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/tap_to_configure"
+            android:textColor="@android:color/black"
+            android:textSize="16sp"
+            android:textStyle="bold"/>
+    </LinearLayout>
+    <LinearLayout android:id="@+id/widget_folder_not_synced"
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:paddingLeft="24dip"
+            android:paddingRight="24dip"
+            android:orientation="vertical"
+            android:gravity="center"
+            android:background="@drawable/gradient_bg_widget_holo"
+            android:visibility="gone" >
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dip"
+            android:text="@string/non_synced_folder_description"
+            android:textColor="@android:color/black"
+            android:textSize="16sp" />
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dip"
+            android:paddingBottom="8dip"
+            android:text="@string/tap_to_configure_folder_sync"
+            android:textColor="@android:color/black"
+            android:background="@android:color/darker_gray"
+            android:gravity="center"
+            android:textSize="16sp"
+            android:textStyle="bold"/>
+    </LinearLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/list_div_top_btm_widget_holo" />
+</LinearLayout>
diff --git a/res/layout/widget_conversation.xml b/res/layout/widget_conversation.xml
new file mode 100644
index 0000000..423403b
--- /dev/null
+++ b/res/layout/widget_conversation.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_conversation"
+    android:layout_width="match_parent"
+    android:layout_height="70sp">
+    <!--
+        Remote view doesn't allow changing background so we have to work around
+        by having 2 image views here.
+    -->
+    <ImageView
+        android:id="@+id/widget_unread_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/widget_conversation_unread_selector" />
+    <ImageView
+        android:id="@+id/widget_read_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/widget_conversation_read_selector" />
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingLeft="16dip"
+        android:paddingRight="8dip"
+        android:orientation="vertical">
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:gravity="right" >
+            <ImageView
+                android:id="@+id/widget_folder_0"
+                android:layout_width="32dip"
+                android:layout_height="6sp"
+                android:scaleType="fitXY" />
+            <ImageView
+                android:id="@+id/widget_folder_1"
+                android:layout_width="32dip"
+                android:layout_height="6sp"
+                android:layout_toRightOf="@id/widget_folder_0"
+                android:scaleType="fitXY" />
+            <ImageView
+                android:id="@+id/widget_folder_2"
+                android:layout_width="32dip"
+                android:layout_height="6sp"
+                android:layout_toRightOf="@id/widget_folder_1"
+                android:scaleType="fitXY" />
+        </RelativeLayout>
+        <RelativeLayout
+            android:id="@+id/widget_attachment_and_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="10sp"
+            android:orientation="horizontal" >
+            <!-- Use negative margins to align attachment icon with text -->
+            <ImageView
+                android:id="@+id/widget_attachment"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="-4sp" />
+            <TextView
+                android:id="@+id/widget_date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@id/widget_attachment" />
+        </RelativeLayout>
+        <TextView
+            android:id="@+id/widget_senders"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@id/widget_attachment_and_date"
+            android:layout_marginTop="6sp"
+            android:paddingRight="16dip"
+            android:singleLine="true" />
+        <TextView
+            android:id="@+id/widget_subject"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/widget_senders"
+            android:layout_marginTop="-2sp"
+            android:maxLines="2" />
+    </RelativeLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/widget_loading.xml b/res/layout/widget_loading.xml
new file mode 100644
index 0000000..c0f03e3
--- /dev/null
+++ b/res/layout/widget_loading.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_loading"
+    android:layout_width="match_parent"
+    android:layout_height="64sp"
+    android:background="@drawable/conversation_read_selector">
+    <TextView
+        android:id="@+id/loading_text"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:layout_marginLeft="16dip"
+        android:gravity="center"
+        android:textColor="@android:color/black"
+        android:textSize="14sp"
+        android:textStyle="bold" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/constants.xml b/res/values/constants.xml
index 4229c70..5fadbda 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -41,4 +41,6 @@
     <integer name="conversation_view_weight">1</integer>
     <!-- Max unread count to show for a folder -->
     <integer name="maxUnreadCount">999</integer>
+   <!-- <integer name="widget_refresh_delay_ms">4000</integer>-->
+    <integer name="widget_folder_refresh_delay_ms">500</integer>
 </resources>
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index 9f350a3..540af6f 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -17,7 +17,6 @@
 -->
 
 <resources>
-
     <dimen name="account_dropdown_item_height">48dip</dimen>
     <dimen name="wide_senders_font_size">16sp</dimen>
     <dimen name="wide_subject_font_size">16sp</dimen>
@@ -67,4 +66,11 @@
     <dimen name="folder_list_popup_width">200dip</dimen>
     <dimen name="folder_list_folder_color_width">36dip</dimen>
     <dimen name="folder_list_minimum_height">48dip</dimen>
+    <dimen name="widget_senders_font_size">18sp</dimen>
+    <dimen name="widget_subject_font_size">14sp</dimen>
+    <dimen name="widget_date_font_size">12sp</dimen>
+    <dimen name="widget_margin_top">11dip</dimen>
+    <dimen name="widget_margin_left">17dip</dimen>
+    <dimen name="widget_margin_right">17dip</dimen>
+    <dimen name="widget_margin_bottom">22dip</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a81e9b3..3496bf1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -441,8 +441,6 @@
     <!-- Folders -->
     <!-- Displayed in the middle of the Folders screen when no folders have been defined -->
     <string name="folders_activity_instructions">No folders have been set for this account.</string>
-    <!-- Displayed in widget when unread count > 100. [CHAR LIMIT=4] -->
-    <string name="widget_large_unread_count">%d+</string>
 
     <!-- Sync status errors translate="false" [CHAR LIMIT=100] -->
     <string-array name="sync_status">
@@ -453,4 +451,51 @@
         <item>Storage error</item>
         <item>Internal error</item>
     </string-array>
+
+    <!-- Widget strings -->
+    <!-- Displayed when user adds a new widget. Tapping on the widget in this
+          mode will bring user to the account selection screen [CHAR LIMIT=35] -->
+    <string name="tap_to_configure">Touch to set up</string>
+
+    <!-- Displayed within a widget that was setup for a non-synced label [CHAR LIMIT=35] -->
+    <string name="non_synced_folder_description">To view conversations, sync this folder.</string>
+
+    <!-- Displayed within a widget that was setup for a non-synced folder.  This is the text of the
+         button that will allow the user to configure sync for the folder [CHAR LIMIT=35] -->
+    <string name="tap_to_configure_folder_sync">Sync Folder</string>
+
+    <!-- Displayed in widget when unread count > 100. [CHAR LIMIT=4] -->
+    <string name="widget_large_unread_count">%d+</string>
+
+    <!-- Displayed at the end of the conversation list in the widget. Tapping on this will open the default Inbox. [CHAR LIMIT=35] -->
+    <string name="view_more_conversations">View more conversations</string>
+
+    <!-- Displayed while we load a conversation. [CHAR LIMIT=100] -->
+    <string name="loading_conversation">Loading\u2026</string>
+
+    <!-- Name of the Mailboxes activity [CHAR LIMIT=30] -->
+    <string name="activity_mailbox_selection">Choose account</string>
+
+    <!-- Name of the Folders activity [CHAR LIMIT=30] -->
+    <string name="activity_folder_selection">Choose folder</string>
+
+    <!-- Manange folders mode, sync option: synchronize all conversations with this folder. [CHAR LIMIT=100] -->
+    <string name="sync_all">Sync: All</string>
+    <plurals name="sync_recent">
+        <!-- Manage folders mode, sync option: synchronize conversations in the last day with this folder [CHAR LIMIT=100] -->
+        <item quantity="one">Sync: Last <xliff:g id="count" example="1">%1$d</xliff:g> day</item>
+        <!-- Manage folders mode, sync option: synchronize conversations in the last 2+ day with this folder [CHAR LIMIT=100] -->
+        <item quantity="other">Sync: Last <xliff:g id="count" example="4">%1$d</xliff:g> days</item>
+    </plurals>
+    <!-- Manage folders mode, sync option: don't synchronize any conversation with this folder [CHAR LIMIT=100] -->
+    <string name="sync_none">Sync: None</string>
+
+    <!-- Title for dialog encouraging users enable sync for a currently non-synced folder [CHAR LIMIT=30] -->
+    <string name="folder_sync_for_widget_title">Sync folder</string>
+
+    <!-- Dialog text encouraging users to enable sync for a newly created widget [CHAR LIMIT=400] -->
+    <string name="folder_sync_for_widget_description">To view conversations in the widget, sync this folder.</string>
+
+    <!-- Checkbox text to enable sync for a folder where a widget is in the processes of being added, and sync had been not been enabled [CHAR LIMIT=30] -->
+    <string name="folder_sync_for_widget_checkbox">Sync last <xliff:g id="numSyncDays" example="30">%d</xliff:g> days</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 290dbb7..2d51989 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -313,4 +313,30 @@
         <item name="android:layout_height">@dimen/folder_swatch_height</item>
         <item name="android:layout_width">@dimen/folder_list_folder_color_width</item>
     </style>
+
+    <!-- Widget styles -->
+    <style name="WidgetTitle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
+        <item name="android:textColor">#333333</item>
+        <item name="android:singleLine">true</item>
+    </style>
+
+    <style name="WidgetSubtitle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle">
+        <item name="android:textColor">#777777</item>
+        <item name="android:singleLine">true</item>
+    </style>
+
+    <style name="ShortcutNameWrapper">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+
+    <style name="ShortcutNameStyle">
+        <item name="android:layout_marginTop">16dip</item>
+    </style>
+
+    <style name="ShortcutWidgetTheme" parent="@android:style/Theme.Holo.Light.DialogWhenLarge"/>
+
+    <style name="InvisibleShortcutWidgetTheme" parent="@style/ShortcutWidgetTheme">
+        <item name="android:windowNoDisplay">true</item>
+    </style>
 </resources>
diff --git a/res/xml-sw600dp/widget_info.xml b/res/xml-sw600dp/widget_info.xml
new file mode 100644
index 0000000..a650e59
--- /dev/null
+++ b/res/xml-sw600dp/widget_info.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="220dp"
+    android:minHeight="220dp"
+    android:updatePeriodMillis="0"
+    android:previewImage="@drawable/mail_widget_preview"
+    android:initialLayout="@layout/widget"
+    android:configure="com.android.mail.ui.MailboxSelectionActivity"
+    android:resizeMode="horizontal|vertical">
+</appwidget-provider>
diff --git a/res/xml/widget_info.xml b/res/xml/widget_info.xml
new file mode 100644
index 0000000..819dc5e
--- /dev/null
+++ b/res/xml/widget_info.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="180dp"
+    android:minHeight="180dp"
+    android:minResizeWidth="180dp"
+    android:minResizeHeight="110dp"
+    android:updatePeriodMillis="0"
+    android:previewImage="@drawable/mail_widget_preview"
+    android:initialLayout="@layout/widget"
+    android:configure="com.android.mail.ui.MailboxSelectionActivity"
+    android:resizeMode="horizontal|vertical">
+</appwidget-provider>
diff --git a/src/com/android/mail/providers/Account.java b/src/com/android/mail/providers/Account.java
index f0573cd..3287632 100644
--- a/src/com/android/mail/providers/Account.java
+++ b/src/com/android/mail/providers/Account.java
@@ -146,6 +146,20 @@
         dest.writeInt(syncStatus);
     }
 
+    /**
+     * Get the settings associated with this account.
+     * TODO: this method is just a stand-in.
+     */
+    public Cursor getSettings() {
+        return null;
+    }
+
+    public Folder getAccountInbox() {
+        // TODO: (mindyp) fill in with call to settings or reading of account settings
+        // to get the default inbox for this account.
+        return null;
+    }
+
     @SuppressWarnings("hiding")
     public static final Creator<Account> CREATOR = new Creator<Account>() {
         @Override
diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java
index 0a4f5ab..83ccbb2 100644
--- a/src/com/android/mail/ui/FolderListFragment.java
+++ b/src/com/android/mail/ui/FolderListFragment.java
@@ -59,6 +59,9 @@
     private FolderChangeListener mListener;
 
     private static final int FOLDER_LOADER_ID = 0;
+
+    public static final int MODE_DEFAULT = 0;
+    public static final int MODE_PICK = 1;
     /**
      * Hidden constructor.
      */
@@ -73,6 +76,15 @@
      * to display conversation list context.
      */
     public static FolderListFragment newInstance(FolderChangeListener listener, String uri) {
+        return newInstance(listener, uri, MODE_DEFAULT);
+    }
+
+    /**
+     * Creates a new instance of {@link ConversationListFragment}, initialized
+     * to display conversation list context.
+     */
+    public static FolderListFragment newInstance(FolderChangeListener listener, String uri,
+            int mode) {
         FolderListFragment fragment = new FolderListFragment(listener, uri);
         return fragment;
     }
diff --git a/src/com/android/mail/ui/FolderSelectionActivity.java b/src/com/android/mail/ui/FolderSelectionActivity.java
new file mode 100644
index 0000000..4621201
--- /dev/null
+++ b/src/com/android/mail/ui/FolderSelectionActivity.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.ui;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.appwidget.AppWidgetManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.DragEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.mail.R;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
+import com.android.mail.widget.WidgetProvider;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * This activity displays the list of available folders for the current account.
+ */
+public class FolderSelectionActivity extends Activity implements OnClickListener,
+        DialogInterface.OnClickListener, FolderChangeListener {
+    public static final String EXTRA_ACCOUNT_SHORTCUT = "account-shortcut";
+
+    private static final String LOG_TAG = new LogUtils().getLogTag();
+
+    private Account mAccount;
+    private Folder mSelectedFolder;
+    private boolean mConfigureShortcut;
+    private boolean mConfigureWidget;
+    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.folders_activity);
+
+        final Intent intent = getIntent();
+        final String action = intent.getAction();
+        mConfigureShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(action);
+        mConfigureWidget = AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action);
+        if (!mConfigureShortcut && !mConfigureWidget) {
+            LogUtils.wtf(LOG_TAG, "unexpected intent: %s", intent);
+        }
+        if (mConfigureShortcut || mConfigureWidget) {
+            ActionBar actionBar = getActionBar();
+            if (actionBar != null) {
+                actionBar.setIcon(R.mipmap.ic_launcher_shortcut_folder);
+            }
+        }
+
+        if (mConfigureWidget) {
+            mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                    AppWidgetManager.INVALID_APPWIDGET_ID);
+            if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+                LogUtils.wtf(LOG_TAG, "invalid widgetId");
+            }
+        }
+
+        mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT_SHORTCUT);
+        Button cancelButton = (Button) findViewById(R.id.cancel);
+        cancelButton.setVisibility(View.VISIBLE);
+        cancelButton.setOnClickListener(this);
+
+        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
+        Fragment fragment = FolderListFragment.newInstance(this, mAccount.folderListUri,
+                FolderListFragment.MODE_PICK);
+        fragmentTransaction.replace(R.id.folders_pane, fragment);
+        fragmentTransaction.commitAllowingStateLoss();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // TODO: (mindyp) Make sure we're operating on the same account as
+        // before. If the user switched accounts, switch back.
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.cancel:
+                doCancel();
+                break;
+        }
+    }
+
+    private void doCancel() {
+        setResult(RESULT_CANCELED);
+        finish();
+    }
+
+    @Override
+    public Dialog onCreateDialog(int id, Bundle bundle) {
+        Dialog dialog = null;
+        switch (id) {
+            case R.layout.folder_sync_for_widget_dialog:
+                dialog = new SyncForWidgetDialog(this, mAccount, mSelectedFolder, this);
+                break;
+        }
+        return dialog == null ? super.onCreateDialog(id, bundle) : dialog;
+    }
+
+    /**
+     * Create a widget for the specified account and label
+     */
+    private void createWidget() {
+        WidgetProvider.updateWidget(this, mAppWidgetId, mAccount, mSelectedFolder);
+        Intent result = new Intent();
+        result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+        setResult(RESULT_OK, result);
+        finish();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            // The only dialog that is
+            createWidget();
+        } else {
+            doCancel();
+        }
+    }
+
+    @Override
+    public void onFolderChanged(Folder folder) {
+        if (!folder.equals(mSelectedFolder)) {
+            mSelectedFolder = folder;
+            Intent resultIntent = new Intent();
+
+            if (mConfigureShortcut) {
+                /*
+                 * Create the shortcut Intent based on it with the additional
+                 * information that we have in this activity: name of the
+                 * account, calculate the human readable name of the label and
+                 * use it as the shortcut name, etc...
+                 */
+                final Intent clickIntent = Utils.createViewConversationIntent(this, mAccount,
+                        mSelectedFolder, UIProvider.INVALID_CONVERSATION_ID);
+                resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, clickIntent);
+                resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+                        Intent.ShortcutIconResource.fromContext(this,
+                                R.mipmap.ic_launcher_shortcut_folder));
+
+                CharSequence humanLabelName = mSelectedFolder.name;
+
+                resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, humanLabelName);
+
+                // Now ask the user what name they want for this shortcut. Pass
+                // the
+                // shortcut intent that we just created, the user can modify the
+                // label in
+                // ShortcutNameActivity.
+                final Intent shortcutNameIntent = new Intent(this, ShortcutNameActivity.class);
+                shortcutNameIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY
+                        | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+                shortcutNameIntent.putExtra(ShortcutNameActivity.EXTRA_LABEL_CLICK_INTENT,
+                        resultIntent);
+                shortcutNameIntent.putExtra(ShortcutNameActivity.EXTRA_SHORTCUT_NAME,
+                        humanLabelName);
+
+                startActivity(shortcutNameIntent);
+                finish();
+            } else if (mConfigureWidget) {
+                // Check to see if the widget is set to be synchronized
+                final Cursor settings = mAccount.getSettings();
+                final Set<String> synchronizedLabelsSet = Sets.newHashSet();
+
+                // Add all of the synchronized labels to the set
+                // TODO: (mindyp) deal with labels.
+                // synchronizedLabelsSet.addAll(settings.getLabelsIncluded());
+                // synchronizedLabelsSet.addAll(settings.getLabelsPartial());
+
+                if (!synchronizedLabelsSet.contains(mSelectedFolder.name)) {
+                    // Display a dialog offering to enable sync for this label
+                    showDialog(R.layout.folder_sync_for_widget_dialog);
+                } else {
+                    createWidget();
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/mail/ui/MailboxSelectionActivity.java b/src/com/android/mail/ui/MailboxSelectionActivity.java
new file mode 100644
index 0000000..09174d8
--- /dev/null
+++ b/src/com/android/mail/ui/MailboxSelectionActivity.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.mail.ui;
+
+import com.android.mail.R;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountCacheProvider;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import android.app.ActionBar;
+import android.app.ListActivity;
+import android.appwidget.AppWidgetManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An activity that shows the list of all the available accounts and return the
+ * one selected in onResult().
+ */
+public class MailboxSelectionActivity extends ListActivity implements OnClickListener {
+
+    // Used to save our instance state
+    private static final String CREATE_SHORTCUT_KEY = "createShortcut";
+    private static final String CREATE_WIDGET_KEY = "createWidget";
+    private static final String WIDGET_ID_KEY = "widgetId";
+    private static final String WAITING_FOR_ADD_ACCOUNT_RESULT_KEY = "waitingForAddAccountResult";
+
+    private static final String ACCOUNT = "name";
+    private static final String[] COLUMN_NAMES = { ACCOUNT };
+    protected static final String LOG_TAG = new LogUtils().getLogTag();
+    private final int[] VIEW_IDS = { R.id.mailbox_name };
+    private boolean mCreateShortcut = false;
+    private boolean mConfigureWidget = false;
+    private SimpleAdapter mAdapter;
+    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+    // Boolean to indicate that we are waiting for the result from an add account
+    // operation.  This boolean is necessary, as there is no guarantee on whether the
+    // AccountManager callback or onResume will be called first.
+    boolean mWaitingForAddAccountResult = false;
+
+    // Can only do certain actions if the Activity is resumed (e.g. setVisible)
+    private boolean mResumed = false;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.mailbox_selection_activity);
+        if (icicle != null) {
+            restoreState(icicle);
+        } else {
+            if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
+                mCreateShortcut = true;
+            }
+            mAppWidgetId = getIntent().getIntExtra(
+                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+            if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                mConfigureWidget = true;
+            }
+        }
+        // We set the default title to "Gmail" or "Google Mail" for consistency
+        // in Task Switcher. If this is for create shortcut or configure widget,
+        // we should set the title to "Select account".
+        if (mCreateShortcut || mConfigureWidget) {
+            setTitle(getResources().getString(R.string.activity_mailbox_selection));
+            ActionBar actionBar = getActionBar();
+            if (actionBar != null) {
+                actionBar.setIcon(R.mipmap.ic_launcher_shortcut_folder);
+            }
+        }
+        ((Button) findViewById(R.id.cancel)).setOnClickListener(this);
+
+        // Initially, assume that the main view is invisible.  It will be made visible,
+        // if we display the account list
+        setVisible(false);
+        setResult(RESULT_CANCELED);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+
+        icicle.putBoolean(CREATE_SHORTCUT_KEY, mCreateShortcut);
+        icicle.putBoolean(CREATE_WIDGET_KEY, mConfigureWidget);
+        if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+            icicle.putInt(WIDGET_ID_KEY, mAppWidgetId);
+        }
+        icicle.putBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY, mWaitingForAddAccountResult);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        // Initially set up the state, with cached data, unless we are configuring
+        // a widget, where we want to wait for the real data
+        if (!mConfigureWidget) {
+            setupWithCachedAccounts();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mResumed = true;
+        // Only fetch the accounts, if we are not handling a response from the
+        // launched child activity.
+        if (!mWaitingForAddAccountResult) {
+            setupWithAccounts();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mResumed = false;
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent);
+    }
+
+    /**
+     * Restores the activity state from a bundle
+     */
+    private void restoreState(Bundle icicle) {
+        if (icicle.containsKey(CREATE_SHORTCUT_KEY)) {
+            mCreateShortcut = icicle.getBoolean(CREATE_SHORTCUT_KEY);
+        }
+        if (icicle.containsKey(CREATE_WIDGET_KEY)) {
+            mConfigureWidget = icicle.getBoolean(CREATE_WIDGET_KEY);
+        }
+        if (icicle.containsKey(WIDGET_ID_KEY)) {
+            mAppWidgetId = icicle.getInt(WIDGET_ID_KEY);
+        }
+        if (icicle.containsKey(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY)) {
+            mWaitingForAddAccountResult = icicle.getBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY);
+        }
+    }
+
+    private void setupWithAccounts() {
+        final ContentResolver resolver = getContentResolver();
+        AsyncTask<Void, Void, Void> getAccounts = new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                Cursor cursor = resolver.query(AccountCacheProvider.getAccountsUri(),
+                        UIProvider.ACCOUNTS_PROJECTION, null, null, null);
+                Account[] accounts = new Account[0];
+                if (cursor != null) {
+                    accounts = new Account[cursor.getCount()];
+                    int i = 0;
+                    while (cursor.moveToNext()) {
+                        accounts[i] = new Account(cursor);
+                        i++;
+                    }
+                }
+                completeSetupWithAccounts(accounts);
+                return null;
+            }
+
+        }.execute();
+    }
+
+    private void setupWithCachedAccounts() {
+        // TODO: (mindyp) have the AccountCacheProvider cache accounts.
+    }
+
+    private void completeSetupWithAccounts(Account[] accounts) {
+        // TODO: Cache the latest set of accounts
+        // AccountCacheProvider.cacheAccountList(this, false /* synced */,
+        // accounts);
+
+        // Populate the map
+        final List<Map<String, Object>> listData = Lists.newArrayList();
+        for (int i = 0; i < accounts.length; i++) {
+            Map<String, Object> m = Maps.newHashMap();
+            final String account = accounts[i].name;
+            m.put(ACCOUNT, account);
+
+            listData.add(m);
+        }
+        updateAccountList(listData);
+    }
+
+    private void updateAccountList(final List<Map<String, Object>> accountData) {
+        boolean displayAccountList = true;
+        // Configuring a widget or shortcut.
+        if (mConfigureWidget || mCreateShortcut) {
+            if (accountData.size() == 0) {
+                // No account found, show Add Account screen, for both the widget or
+                // shortcut creation process
+                // TODO: (mindyp) allow for adding of account.
+                // No reason to display the account list
+                displayAccountList = false;
+
+                // Indicate that we need to handle the response from the add account action
+                // This allows us to process the results that we get in the AddAccountCallback
+                mWaitingForAddAccountResult = true;
+            } else if (mConfigureWidget && accountData.size() == 1) {
+                // When configuring a widget, if there is only one account, automatically
+                // choose that account.
+                selectAccount((String) accountData.get(0).get(ACCOUNT));
+                // No reason to display the account list
+                displayAccountList = false;
+            }
+        }
+
+        if (displayAccountList) {
+            // We are about to display the list, make this activity visible
+            // But only if the Activity is not paused!
+            if (mResumed) {
+                setVisible(true);
+            }
+
+            mAdapter = new SimpleAdapter(
+                    this, accountData, R.layout.mailbox_item, COLUMN_NAMES, VIEW_IDS) {
+                @Override
+                public View getView(int position, View convertView, ViewGroup parent) {
+                    View v = super.getView(position, convertView, parent);
+                    TextView accountView = (TextView) v.findViewById(R.id.mailbox_name);
+
+                    Map<String, Object> data = accountData.get(position);
+                    String account = (String) data.get(ACCOUNT);
+                    accountView.setText(account);
+
+                    // Set the tag here so we can get the account name in
+                    // OnListItemClick()
+                    v.setTag(account);
+
+                    return v;
+                }
+            };
+            setListAdapter(mAdapter);
+        }
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (v != null) {
+            String selectedAccount = (String) v.getTag();
+            selectAccount(selectedAccount);
+        }
+    }
+
+    private void selectAccount(String account) {
+        if (mCreateShortcut || mConfigureWidget) {
+            // Invoked for a shortcut creation
+            final Intent intent = new Intent(this, FolderSelectionActivity.class);
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            intent.setAction(mCreateShortcut ?
+                    Intent.ACTION_CREATE_SHORTCUT : AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+            if (mConfigureWidget) {
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+            }
+            intent.putExtra(FolderSelectionActivity.EXTRA_ACCOUNT_SHORTCUT, account);
+            startActivity(intent);
+            finish();
+        } else {
+            // TODO: (mindyp) handle changing the account for this shortcut.
+            finish();
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.cancel:
+                setResult(RESULT_CANCELED);
+                finish();
+                break;
+        }
+    }
+}
diff --git a/src/com/android/mail/ui/ShortcutNameActivity.java b/src/com/android/mail/ui/ShortcutNameActivity.java
new file mode 100644
index 0000000..d17570d
--- /dev/null
+++ b/src/com/android/mail/ui/ShortcutNameActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.mail.ui;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.mail.R;
+
+/**
+ * This activity prompts the user for a name for the shortcut to their label
+ * This activity could, in the future, be used to allow the user to specify an
+ * icon for the shortcut
+ */
+public class ShortcutNameActivity extends Activity implements OnClickListener,
+        TextView.OnEditorActionListener {
+    /*package*/static final String EXTRA_LABEL_CLICK_INTENT = "extra_label_click_intent";
+
+    /*package*/static final String EXTRA_SHORTCUT_NAME = "extra_shortcut_name";
+
+    private EditText mLabelText;
+    private String mLabelName;
+
+    private Intent mShortcutClickIntent;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.shortcut_name_activity);
+
+        mShortcutClickIntent = (Intent)getIntent().getParcelableExtra(EXTRA_LABEL_CLICK_INTENT);
+        mLabelName = getIntent().getStringExtra(EXTRA_SHORTCUT_NAME);
+
+        mLabelText = (EditText) findViewById(R.id.folder_text);
+        mLabelText.setText(mLabelName);
+        mLabelText.setOnEditorActionListener(this);
+
+        // Set focus to end of input line
+        mLabelText.requestFocus();
+        Editable editableText = mLabelText.getText();
+        Selection.setSelection(editableText, editableText.length());
+
+        findViewById(R.id.done).setOnClickListener(this);
+        findViewById(R.id.cancel).setOnClickListener(this);
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setIcon(R.mipmap.ic_launcher_shortcut_folder);
+        }
+    }
+
+    public void onClick(View v) {
+        final int id = v.getId();
+        if (R.id.done == id) {
+            doCreateShortcut();
+        } else if (R.id.cancel == id) {
+            doCancel();
+        }
+    }
+
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_DONE) {
+            doCreateShortcut();
+            return true;
+        }
+        return false;
+    }
+
+    private void doCreateShortcut() {
+        // Get the name that the user entered
+        CharSequence userShortcutName = mLabelText.getText();
+
+        Intent resultIntent = new Intent();
+        resultIntent.putExtra(EXTRA_LABEL_CLICK_INTENT, mShortcutClickIntent);
+        // Initially set the name of the shortcut to the name of the label.
+        // If the user sets a valid name, the user specified one will be used.
+        resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mLabelName);
+
+        final String shortcutName = userShortcutName.toString();
+        if (TextUtils.getTrimmedLength(shortcutName) > 0) {
+            mShortcutClickIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);
+        }
+
+        setResult(RESULT_OK, mShortcutClickIntent);
+        finish();
+    }
+
+    private void doCancel() {
+        setResult(RESULT_CANCELED);
+        finish();
+    }
+
+}
diff --git a/src/com/android/mail/ui/SyncForWidgetDialog.java b/src/com/android/mail/ui/SyncForWidgetDialog.java
new file mode 100644
index 0000000..f355970
--- /dev/null
+++ b/src/com/android/mail/ui/SyncForWidgetDialog.java
@@ -0,0 +1,95 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.mail.ui;
+
+import android.content.res.Resources;
+import android.view.View;
+
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
+
+import com.android.mail.R;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Folder;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * A dialog for users attempting to create a widget for a non-synced label.
+ */
+public class SyncForWidgetDialog extends AlertDialog
+        implements DialogInterface.OnClickListener {
+
+    private static final long SYNC_DAYS = 30;
+    private final Account mAccount;
+    private final Folder mFolder;
+
+    private final DialogInterface.OnClickListener mConfirmClickListener;
+
+    public SyncForWidgetDialog(Context context, Account account, Folder folder,
+                DialogInterface.OnClickListener confirmWidgetCreationListener) {
+        super(context);
+
+        mAccount = account;
+        mFolder = folder;
+        mConfirmClickListener = confirmWidgetCreationListener;
+        final ContentResolver resolver = context.getContentResolver();
+
+        // Get the current sync window for the specified account
+        final Cursor settings = mAccount.getSettings();
+        // TODO: get sync days from settings.
+        final long syncDays = SYNC_DAYS;
+
+        final Resources res = context.getResources();
+
+        setTitle(R.string.folder_sync_for_widget_title);
+        setIcon(res.getDrawable(R.mipmap.ic_launcher_mail));
+        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
+        setButton(DialogInterface.BUTTON_NEGATIVE,
+                context.getString(android.R.string.cancel), this);
+
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        final View dialogContents = inflater.inflate(R.layout.folder_sync_for_widget_dialog, null);
+        final CheckBox checkboxView =
+                (CheckBox) dialogContents.findViewById(R.id.folder_sync_for_widget_confirm);
+        checkboxView.setText(res.getString(R.string.folder_sync_for_widget_checkbox, syncDays));
+        setView(dialogContents);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            CheckBox confirmView = (CheckBox) findViewById(R.id.folder_sync_for_widget_confirm);
+
+            if (confirmView.isChecked()) {
+
+                AsyncTask<Context, Void, Void> enableLabelSyncTask =
+                        new AsyncTask<Context, Void, Void>() {
+
+                    @Override
+                    protected Void doInBackground(Context... params) {
+                        final Context context = params[0];
+                        final Cursor settings = mAccount.getSettings();
+                        // TODO: (mindyp) enable syncing of folder.
+
+                        return null;
+                    }
+
+                };
+                enableLabelSyncTask.execute(getContext());
+            }
+        }
+
+        // Call the activity the created this widget, to let them know that the user pressed OK
+        mConfirmClickListener.onClick(dialog, which);
+    }
+
+}
diff --git a/src/com/android/mail/utils/DelayedTaskHandler.java b/src/com/android/mail/utils/DelayedTaskHandler.java
new file mode 100644
index 0000000..67c438c
--- /dev/null
+++ b/src/com/android/mail/utils/DelayedTaskHandler.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+
+/**
+ * Class to queue tasks.  This delay the specified task by the specified time, up to the maximum
+ * duration from the time that the first request was received
+ */
+public abstract class DelayedTaskHandler extends Handler {
+    private final int mDelayMs;
+
+    private static final long UNSET_TIME = -1;
+
+    /**
+     * Tracks the time when a task was queued, when there had been no pending task.
+     */
+    private long mLastTaskExecuteTime = UNSET_TIME;
+
+    public DelayedTaskHandler(Looper looper, int defaultDelayMs) {
+        super(looper);
+        mDelayMs = defaultDelayMs;
+    }
+
+    /**
+     * Schedule the task to be run after the default delay.
+     */
+    public void scheduleTask() {
+        final long currentTime = SystemClock.elapsedRealtime();
+        removeMessages(0);
+        if (mLastTaskExecuteTime == UNSET_TIME ||
+                ((mLastTaskExecuteTime + mDelayMs) <  currentTime)) {
+            // If this is the first task that has been queued or if the last task ran more than
+            // long enough ago that we don't want to delay this task, run it now
+            sendEmptyMessage(0);
+        } else {
+            // Otherwise delay this task for the specify delay duration
+            sendEmptyMessageDelayed(0, mDelayMs);
+        }
+    }
+
+    @Override
+    public void dispatchMessage(Message msg) {
+        onTaskExecution();
+        performTask();
+    }
+
+    /**
+     * Called when the task managed by this handler is executed.  This method can also be called
+     * to indicate that the task has been started externally.
+     *
+     * This updates the handler's internal timestamp.
+     */
+    public void onTaskExecution() {
+        mLastTaskExecuteTime = SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * Method to perform the needed task.
+     */
+    protected abstract void performTask();
+}
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 70a6dee..168d6cd 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -17,8 +17,10 @@
 package com.android.mail.utils;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Typeface;
+import android.net.Uri;
 import android.text.Html;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -36,10 +38,13 @@
 import android.webkit.WebView;
 
 import com.android.mail.R;
+import com.android.mail.browse.ConversationCursor;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
 import com.google.common.collect.Maps;
 
 import java.util.Map;
-import java.util.regex.Pattern;
 
 public class Utils {
     /**
@@ -59,6 +64,14 @@
     public static String[] sSenderFragments = new String[8];
 
     public static final String EXTRA_ACCOUNT = "account";
+    /*
+     * Notifies that changes happened. Certain UI components, e.g., widgets, can
+     * register for this {@link Intent} and update accordingly. However, this
+     * can be very broad and is NOT the preferred way of getting notification.
+     */
+    // TODO: UI Provider has this notification URI?
+   public static final String ACTION_NOTIFY_DATASET_CHANGED =
+           "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
 
     /**
      * Sets WebView in a restricted mode suitable for email use.
@@ -324,8 +337,6 @@
         }
         maxChars -= fixedFragmentLength;
 
-        final boolean normalMessagesExist = numMessagesFragment.length() != 0
-                || maxFoundPriority != Integer.MIN_VALUE;
         int maxPriorityToInclude = -1; // inclusive
         int numCharsUsed = numMessagesFragment.length();
         int numSendersUsed = 0;
@@ -535,4 +546,28 @@
         }
         return errors[status];
     }
+
+    /**
+     * @return an intent which, if launched, will display the corresponding conversation
+     */
+    public static Intent createViewConversationIntent(Context context,
+            Account account, Folder folder, long conversationId) {
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        Uri.Builder builder = new Uri.Builder();
+        builder.scheme("content");
+        builder.authority(UIProvider.AUTHORITY);
+        builder.appendEncodedPath("account/" + account);
+        final String intentLabel = folder.name != null ? folder.name
+                : account.getAccountInbox().name;
+        builder.appendPath("label");
+        builder.appendPath(intentLabel);
+
+        if (conversationId != UIProvider.INVALID_CONVERSATION_ID) {
+            builder.appendEncodedPath("conversationId/" + conversationId);
+        }
+        intent.setDataAndType(builder.build(), "application/" + UIProvider.AUTHORITY);
+
+        return intent;
+    }
 }
diff --git a/src/com/android/mail/widget/BaseWidgetProvider.java b/src/com/android/mail/widget/BaseWidgetProvider.java
new file mode 100644
index 0000000..3bd9d0c
--- /dev/null
+++ b/src/com/android/mail/widget/BaseWidgetProvider.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.widget;
+
+import com.android.mail.ConversationListContext;
+import com.android.mail.R;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.AccountCacheProvider;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.ui.MailboxSelectionActivity;
+import com.android.mail.utils.Utils;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+
+import android.accounts.AccountManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.SharedPreferences.Editor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import java.util.Set;
+
+public class BaseWidgetProvider extends AppWidgetProvider {
+    public static final String EXTRA_ACCOUNT = "account";
+    public static final String EXTRA_FOLDER = "folder";
+    public static final String EXTRA_UNREAD = "unread";
+    public static final String WIDGET_ACCOUNT_PREFIX = "widget-account-";
+
+    private static final String ACCOUNT_FOLDER_PREFERENCE_SEPARATOR = " ";
+
+    private static String createWidgetPreferenceValue(Account account, Folder folder) {
+        return account.name + ACCOUNT_FOLDER_PREFERENCE_SEPARATOR + folder.name;
+
+    }
+
+    /**
+     * Persists the information about the specified widget.
+     */
+    static void saveWidgetInformation(Context context, int appWidgetId, Account account,
+            Folder folder) {
+        // TODO: (mindyp) save widget information.
+        /*
+         * Editor editor = Persistence.getPreferences(context).edit();
+         * editor.putString(WidgetProvider.WIDGET_ACCOUNT_PREFIX + appWidgetId,
+         * BaseWidgetProvider.createWidgetPreferenceValue(account, folder));
+         * editor.apply();
+         */
+    }
+
+    /**
+     * Remove preferences when deleting widget
+     */
+    @Override
+    public void onDeleted(Context context, int[] appWidgetIds) {
+        super.onDeleted(context, appWidgetIds);
+
+        // TODO: (mindyp) save widget information.
+        /*
+         * Editor editor = Persistence.getPreferences(context).edit(); for (int
+         * i = 0; i < appWidgetIds.length; ++i) { // Remove the account in the
+         * preference editor.remove(WIDGET_ACCOUNT_PREFIX + appWidgetIds[i]); }
+         * editor.apply();
+         */
+    }
+
+    /**
+     * @return the list ids for the currently configured widgets.
+     */
+    private static int[] getCurrentWidgetIds(Context context) {
+        final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+        final ComponentName mailComponent = new ComponentName(context, WidgetProvider.PROVIDER_NAME);
+        return appWidgetManager.getAppWidgetIds(mailComponent);
+    }
+
+    /**
+     * Catches ACTION_NOTIFY_DATASET_CHANGED intent and update the corresponding
+     * widgets.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        super.onReceive(context, intent);
+
+        // Receive notification for a certain account.
+        if (Utils.ACTION_NOTIFY_DATASET_CHANGED.equals(intent.getAction())) {
+            final String accountToBeUpdated = intent.getExtras().getString(Utils.EXTRA_ACCOUNT);
+            if (accountToBeUpdated == null) {
+                return;
+            }
+
+            final Set<Integer> widgetsToUpdate = Sets.newHashSet();
+
+            for (int id : getCurrentWidgetIds(context)) {
+                // Retrieve the persisted information for this widget from
+                // preferences.
+                // TODO: (mindyp) get widget preferences.
+                /*
+                 * final String accountFolder =
+                 * Persistence.getPreferences(context)
+                 * .getString(WIDGET_ACCOUNT_PREFIX + id, null); // If the
+                 * account matched, update the widget. if (accountFolder !=
+                 * null) { final String[] parsedInfo = TextUtils.split(
+                 * accountFolder, ACCOUNT_FOLDER_PREFERENCE_SEPARATOR); if
+                 * (accountToBeUpdated.equals(parsedInfo[0])) {
+                 * widgetsToUpdate.add(id); } }
+                 */
+            }
+            if (widgetsToUpdate.size() > 0) {
+                final int[] widgets = Ints.toArray(widgetsToUpdate);
+                AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(widgets,
+                        R.id.conversation_list);
+            }
+        }
+    }
+
+    /**
+     * Update all widgets in the list
+     */
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        super.onUpdate(context, appWidgetManager, appWidgetIds);
+
+        // Update each of the widgets with a remote adapter
+        for (int i = 0; i < appWidgetIds.length; ++i) {
+            // Get the account for this widget from preference
+            // TODO: (mindyp) get widget preferences.
+            /*
+             * String accountFolder =
+             * Persistence.getPreferences(context).getString(
+             * WIDGET_ACCOUNT_PREFIX + appWidgetIds[i], null);
+             */
+            String accountFolder = null;
+            String account = null;
+            String folder = null;
+            if (!TextUtils.isEmpty(accountFolder)) {
+                final String[] parsedInfo = TextUtils.split(accountFolder,
+                        ACCOUNT_FOLDER_PREFERENCE_SEPARATOR);
+                if (parsedInfo.length == 2) {
+                    account = parsedInfo[0];
+                    folder = parsedInfo[1];
+                } else {
+                    // TODO: (mindyp) how can we lookup the associated account?
+                    // AccountCacheProvider?
+                    account = accountFolder;
+                    folder = "inbox"; // account.getAccountInbox(context,
+                                      // account);
+                }
+            }
+            // account will be null the first time a widget is created. This is
+            // OK, as
+            // isAccountValid will return false, allowing the widget to be
+            // configured
+            updateWidget(context, appWidgetIds[i], new Account(Parcel.obtain()),
+                    new Folder(Parcel.obtain()));
+        }
+    }
+
+    private static boolean isAccountValid(Context context, Account account) {
+        if (account != null) {
+            Cursor accountCursor = context.getContentResolver().query(
+                    AccountCacheProvider.getAccountsUri(), UIProvider.ACCOUNTS_PROJECTION, null,
+                    null, null);
+            accountCursor.moveToFirst();
+            do {
+                if (account != null && account.equals(new Account(accountCursor)))
+                    return true;
+            } while (accountCursor.moveToNext());
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * Returns if a widget is configured with the given account.
+     */
+    public static boolean isWidgetConfigured(Context context, int appWidgetId, Account account,
+            Folder folder) {
+        if (isAccountValid(context, account)) {
+            // TODO: (mindyp) get widget preferences.
+            /*
+             * final String accountFolder =
+             * Persistence.getPreferences(context).getString(
+             * WIDGET_ACCOUNT_PREFIX + appWidgetId, null);
+             */
+            // Return true if this widget id has been configured and saved.
+            final String accountFolder = null;
+            return accountFolder != null;
+        }
+        return false;
+    }
+
+    /**
+     * Update the widget appWidgetId with the given account and folder
+     */
+    public static void updateWidget(Context context, int appWidgetId, Account account, Folder folder) {
+        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
+        PendingIntent clickIntent;
+        final boolean isAccountValid = isAccountValid(context, account);
+
+        if (!isAccountValid) {
+            // Widget has not been configured yet
+            remoteViews.setViewVisibility(R.id.widget_folder, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_account, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_unread_count, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_compose, View.GONE);
+            remoteViews.setViewVisibility(R.id.conversation_list, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_folder_not_synced, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_configuration, View.VISIBLE);
+
+            final Intent configureIntent = new Intent(context, MailboxSelectionActivity.class);
+            configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+            configureIntent.setData(Uri.parse(configureIntent.toUri(Intent.URI_INTENT_SCHEME)));
+            configureIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+            clickIntent = PendingIntent.getActivity(context, 0, configureIntent,
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+            remoteViews.setOnClickPendingIntent(R.id.widget_configuration, clickIntent);
+        } else {
+            // Set folder to a space here to avoid flicker.
+            configureValidAccountWidget(context, remoteViews, appWidgetId, account, folder, " ");
+
+        }
+        AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, remoteViews);
+    }
+
+    private static boolean isFolderSynchronized(Context context, Account account, Folder folder) {
+        // Determine if the folder has been configured to be synced
+        final ContentResolver resolver = context.getContentResolver();
+
+        // TODO: (mindyp) Get the current sync window for the specified account
+        final Cursor settings = account.getSettings();
+        final Set<String> synchronizedFoldersSet = Sets.newHashSet();
+
+        // TODO: Add all of the synchronized folders to the set
+        // synchronizedFoldersSet.addAll(settings.getFoldersIncluded());
+        // synchronizedFoldersSet.addAll(settings.getFoldersPartial());
+
+        return synchronizedFoldersSet.contains(folder);
+    }
+
+    /**
+     * Modifies the remoteView for the given account and folder.
+     */
+    static void configureValidAccountWidget(Context context, RemoteViews remoteViews,
+            int appWidgetId, Account account, Folder folder, String folderDisplayName) {
+        PendingIntent clickIntent;// Widget is configured, now display the
+                                  // chosen account.
+
+        final boolean isFolderSynchronized = isFolderSynchronized(context, account, folder);
+
+        remoteViews.setViewVisibility(R.id.widget_folder, View.VISIBLE);
+        remoteViews.setTextViewText(R.id.widget_folder, folderDisplayName);
+        remoteViews.setViewVisibility(R.id.widget_account, View.VISIBLE);
+        remoteViews.setTextViewText(R.id.widget_account, account.name);
+        remoteViews.setViewVisibility(R.id.widget_unread_count, View.VISIBLE);
+        remoteViews.setViewVisibility(R.id.widget_compose, View.VISIBLE);
+        if (isFolderSynchronized) {
+            remoteViews.setViewVisibility(R.id.conversation_list, View.VISIBLE);
+            remoteViews.setViewVisibility(R.id.widget_folder_not_synced, View.GONE);
+        } else {
+            remoteViews.setViewVisibility(R.id.conversation_list, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_folder_not_synced, View.VISIBLE);
+
+            // TODO: (mindyp) Configure the remote view so clicks will launch an
+            // activity that
+            // will allow the user to enable sync for this folder.
+            // remoteViews.setOnClickPendingIntent(R.id.widget_folder_not_synced,
+            // clickIntent);
+        }
+        remoteViews.setViewVisibility(R.id.widget_configuration, View.GONE);
+
+        // Launch an intent to avoid ANRs
+        final Intent intent = new Intent(context, WidgetService.class);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+        intent.putExtra(EXTRA_ACCOUNT, account);
+        intent.putExtra(EXTRA_FOLDER, folder);
+        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+        remoteViews.setRemoteAdapter(R.id.conversation_list, intent);
+
+        // Open mail app when click on header
+        final Intent mailIntent = Utils.createViewConversationIntent(context, account, folder,
+                UIProvider.INVALID_CONVERSATION_ID);
+        clickIntent = PendingIntent.getActivity(context, 0, mailIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        remoteViews.setOnClickPendingIntent(R.id.widget_header, clickIntent);
+
+        // On click intent for Compose
+        final Intent composeIntent = new Intent();
+        composeIntent.setAction(Intent.ACTION_SEND);
+        composeIntent.setData(Uri.parse("from://" + UIProvider.AUTHORITY + "/account/" + account));
+        clickIntent = PendingIntent.getActivity(context, 0, composeIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        remoteViews.setOnClickPendingIntent(R.id.widget_compose, clickIntent);
+
+        // On click intent for Conversation
+        final Intent conversationIntent = new Intent();
+        conversationIntent.setAction(Intent.ACTION_VIEW);
+        clickIntent = PendingIntent.getActivity(context, 0, conversationIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        remoteViews.setPendingIntentTemplate(R.id.conversation_list, clickIntent);
+    }
+
+    /**
+     * Updates all of the configured widgets.
+     */
+    public static void updateAllWidgets(Context context) {
+        AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(
+                getCurrentWidgetIds(context), R.id.conversation_list);
+    }
+}
diff --git a/src/com/android/mail/widget/README b/src/com/android/mail/widget/README
deleted file mode 100644
index a3fcb75..0000000
--- a/src/com/android/mail/widget/README
+++ /dev/null
@@ -1 +0,0 @@
-Unified email directories
\ No newline at end of file
diff --git a/src/com/android/mail/widget/WidgetConversationViewBuilder.java b/src/com/android/mail/widget/WidgetConversationViewBuilder.java
new file mode 100644
index 0000000..eefca0c
--- /dev/null
+++ b/src/com/android/mail/widget/WidgetConversationViewBuilder.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.widget;
+
+import com.android.mail.R;
+import com.android.mail.providers.Account;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import java.util.Map;
+
+public class WidgetConversationViewBuilder {
+    // Static font sizes
+    private static int SENDERS_FONT_SIZE;
+    private static int DATE_FONT_SIZE;
+    private static int SUBJECT_FONT_SIZE;
+
+    // Static colors
+    private static int SUBJECT_TEXT_COLOR_READ;
+    private static int SUBJECT_TEXT_COLOR_UNREAD;
+    private static int SENDERS_TEXT_COLOR_READ;
+    private static int SENDERS_TEXT_COLOR_UNREAD;
+    private static int DATE_TEXT_COLOR_READ;
+    private static int DATE_TEXT_COLOR_UNREAD;
+    private static int DRAFT_TEXT_COLOR;
+
+    // Static bitmap
+    private static Bitmap ATTACHMENT;
+
+    private final Context mContext;
+
+    /*
+     * Get font sizes and bitmaps from Resources
+     */
+    public WidgetConversationViewBuilder(Context context, Account account) {
+        mContext = context;
+        Resources res = context.getResources();
+
+        // Initialize font sizes
+        SENDERS_FONT_SIZE = res.getDimensionPixelSize(R.dimen.widget_senders_font_size);
+        DATE_FONT_SIZE = res.getDimensionPixelSize(R.dimen.widget_date_font_size);
+        SUBJECT_FONT_SIZE = res.getDimensionPixelSize(R.dimen.widget_subject_font_size);
+
+        // Initialize colors
+        SUBJECT_TEXT_COLOR_READ = res.getColor(R.color.subject_text_color_read);
+        SUBJECT_TEXT_COLOR_UNREAD = res.getColor(R.color.subject_text_color_unread);
+        SENDERS_TEXT_COLOR_READ = res.getColor(R.color.senders_text_color_read);
+        SENDERS_TEXT_COLOR_UNREAD = res.getColor(R.color.senders_text_color_unread);
+        DATE_TEXT_COLOR_READ = res.getColor(R.color.date_text_color_read);
+        DATE_TEXT_COLOR_UNREAD = res.getColor(R.color.date_text_color_unread);
+        DRAFT_TEXT_COLOR = res.getColor(R.color.drafts);
+
+        // Initialize Bitmap
+        ATTACHMENT = BitmapFactory.decodeResource(res, R.drawable.ic_attachment_holo_light);
+    }
+
+    /*
+     * Add size, color and style to a given text
+     */
+    private CharSequence addStyle(CharSequence text, int size, int color) {
+        SpannableStringBuilder builder = new SpannableStringBuilder(text);
+        builder.setSpan(
+                new AbsoluteSizeSpan(size), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        if (color != 0) {
+            builder.setSpan(new ForegroundColorSpan(color), 0, text.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        return builder;
+    }
+
+    /*
+     * Return the full View
+     */
+    public RemoteViews getStyledView(
+            CharSequence senders, CharSequence status, CharSequence date, CharSequence subject,
+            CharSequence snippet, String folders, boolean hasAttachments) {
+
+        // TODO: (mindyp) determine unread state.
+        final boolean isUnread = true;
+
+        // Add style to senders
+        CharSequence styledSenders = addStyle(senders, SENDERS_FONT_SIZE,
+                isUnread ? SENDERS_TEXT_COLOR_UNREAD : SENDERS_TEXT_COLOR_READ);
+
+        // Add the status indicator
+        if (status.length() > 0) {
+            final SpannableStringBuilder builder = new SpannableStringBuilder(styledSenders);
+
+            if (senders.length() > 0) {
+                // TODO(pwestbro) sender formatting should use resources.  Bug 5354473
+                builder.append(addStyle(", ", SENDERS_FONT_SIZE,
+                        isUnread ? SENDERS_TEXT_COLOR_UNREAD : SENDERS_TEXT_COLOR_READ));
+            }
+
+            final CharSequence styledStatus = addStyle(status, SENDERS_FONT_SIZE, DRAFT_TEXT_COLOR);
+            styledSenders = builder.append(styledStatus);
+        }
+
+        // Add style to date
+        CharSequence styledDate = addStyle(date, DATE_FONT_SIZE, isUnread ? DATE_TEXT_COLOR_UNREAD
+                : DATE_TEXT_COLOR_READ);
+
+        // Add style to subject
+        int subjectColor = isUnread ? SUBJECT_TEXT_COLOR_UNREAD : SUBJECT_TEXT_COLOR_READ;
+        SpannableStringBuilder subjectAndSnippet = new SpannableStringBuilder(mContext.getString(
+                R.string.subject_and_snippet, subject, snippet));
+        if (isUnread) {
+            subjectAndSnippet.setSpan(new StyleSpan(Typeface.BOLD), 0, subject.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        subjectAndSnippet.setSpan(new ForegroundColorSpan(subjectColor), 0, subjectAndSnippet
+                .length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        CharSequence styledSubject = addStyle(subjectAndSnippet, SUBJECT_FONT_SIZE, 0);
+
+        // Paper clip for attachment
+        Bitmap paperclipBitmap = null;
+        if (hasAttachments) {
+            paperclipBitmap = ATTACHMENT;
+        }
+
+        // Inflate and fill out the remote view
+        RemoteViews remoteViews = new RemoteViews(
+                mContext.getPackageName(), R.layout.widget_conversation);
+        remoteViews.setTextViewText(R.id.widget_senders, styledSenders);
+        remoteViews.setTextViewText(R.id.widget_date, styledDate);
+        remoteViews.setTextViewText(R.id.widget_subject, styledSubject);
+        if (paperclipBitmap != null) {
+            remoteViews.setViewVisibility(R.id.widget_attachment, View.VISIBLE);
+            remoteViews.setImageViewBitmap(R.id.widget_attachment, paperclipBitmap);
+        } else {
+            remoteViews.setViewVisibility(R.id.widget_attachment, View.GONE);
+        }
+        if (isUnread) {
+            remoteViews.setViewVisibility(R.id.widget_unread_background, View.VISIBLE);
+            remoteViews.setViewVisibility(R.id.widget_read_background, View.GONE);
+        } else {
+            remoteViews.setViewVisibility(R.id.widget_unread_background, View.GONE);
+            remoteViews.setViewVisibility(R.id.widget_read_background, View.VISIBLE);
+        }
+
+        return remoteViews;
+    }
+}
diff --git a/src/com/android/mail/widget/WidgetProvider.java b/src/com/android/mail/widget/WidgetProvider.java
new file mode 100644
index 0000000..a035545
--- /dev/null
+++ b/src/com/android/mail/widget/WidgetProvider.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.widget;
+
+public class WidgetProvider extends BaseWidgetProvider {
+    public static final String PROVIDER_NAME = "com.android.mail.widget.WidgetProvider";
+}
diff --git a/src/com/android/mail/widget/WidgetService.java b/src/com/android/mail/widget/WidgetService.java
new file mode 100644
index 0000000..ac462ec
--- /dev/null
+++ b/src/com/android/mail/widget/WidgetService.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.mail.widget;
+
+import com.android.mail.R;
+import com.android.mail.browse.ConversationCursor;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Conversation;
+import com.android.mail.providers.Folder;
+import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.DelayedTaskHandler;
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
+
+import android.appwidget.AppWidgetManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Looper;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import java.util.Map;
+
+public class WidgetService extends RemoteViewsService {
+    /**
+     * Lock to avoid race condition between widgets.
+     */
+    private static Object sWidgetLock = new Object();
+
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        return new MailFactory(getApplicationContext(), intent);
+    }
+
+    /**
+     * Remote Views Factory for Mail Widget.
+     */
+    private static class MailFactory
+            implements RemoteViewsService.RemoteViewsFactory, OnLoadCompleteListener<Cursor> {
+        private static final int MAX_CONVERSATIONS_COUNT = 25;
+        private static final int MAX_SENDERS_LENGTH = 25;
+        private static final String LOG_TAG = new LogUtils().getLogTag();
+
+        private final Context mContext;
+        private final int mAppWidgetId;
+        private final Account mAccount;
+        private final Folder mFolder;
+        private final WidgetConversationViewBuilder mWidgetConversationViewBuilder;
+        private Cursor mConversationCursor;
+        private CursorLoader mFolderLoader;
+        private FolderUpdateHandler mFolderUpdateHandler;
+        private int mFolderCount;
+        private boolean mShouldShowViewMore;
+        private boolean mFolderInformationShown = false;
+        private ContentResolver mResolver;
+
+        public MailFactory(Context context, Intent intent) {
+            mContext = context;
+            mAppWidgetId = intent.getIntExtra(
+                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+            mAccount = intent.getParcelableExtra(WidgetProvider.EXTRA_ACCOUNT);
+            final Folder folder = intent.getParcelableExtra(WidgetProvider.EXTRA_FOLDER);
+            mFolder = folder != null ? folder : mAccount.getAccountInbox();
+            mWidgetConversationViewBuilder = new WidgetConversationViewBuilder(mContext, mAccount);
+            mResolver = context.getContentResolver();
+        }
+
+        @Override
+        public void onCreate() {
+            // Save the map between widgetId and account to preference
+            BaseWidgetProvider.saveWidgetInformation(mContext, mAppWidgetId, mAccount, mFolder);
+
+            // If the account of this widget has been removed, we want to update the widget to
+            // "Tap to configure" mode.
+            if (!BaseWidgetProvider.isWidgetConfigured(mContext, mAppWidgetId, mAccount, mFolder)) {
+                BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolder);
+            }
+
+            mConversationCursor = mResolver.query(Uri.parse(mFolder.conversationListUri),
+                    UIProvider.CONVERSATION_PROJECTION, null, null, null);
+
+            mFolderLoader = new CursorLoader(mContext, Uri.parse(mFolder.uri),
+                    UIProvider.FOLDERS_PROJECTION, null, null, null);
+            mFolderLoader.registerListener(0, this);
+            mFolderUpdateHandler = new FolderUpdateHandler(mContext.getResources().getInteger(
+                    R.integer.widget_folder_refresh_delay_ms));
+            mFolderUpdateHandler.scheduleTask();
+
+        }
+
+        @Override
+        public void onDestroy() {
+            synchronized (sWidgetLock) {
+                if (mConversationCursor != null && !mConversationCursor.isClosed()) {
+                    mConversationCursor.close();
+                    mConversationCursor = null;
+                }
+            }
+
+            if (mFolderLoader != null) {
+                mFolderLoader.reset();
+                mFolderLoader = null;
+            }
+        }
+
+        @Override
+        public void onDataSetChanged() {
+            synchronized (sWidgetLock) {
+                // TODO: use loader manager.
+                mConversationCursor.requery();
+            }
+            mFolderUpdateHandler.scheduleTask();
+        }
+
+        /**
+         * Returns the number of items should be shown in the widget list.  This method also updates
+         * the boolean that indicates whether the "show more" item should be shown.
+         * @return the number of items to be displayed in the list.
+         */
+        @Override
+        public int getCount() {
+            synchronized (sWidgetLock) {
+                final int count = getConversationCount();
+                mShouldShowViewMore = count < mConversationCursor.getCount()
+                        || count < mFolderCount;
+                return count + (mShouldShowViewMore ? 1 : 0);
+            }
+        }
+
+        /**
+         * Returns the number of conversations that should be shown in the widget.  This method
+         * doesn't update the boolean that indicates that the "show more" item should be included
+         * in the list.
+         * @return
+         */
+        private int getConversationCount() {
+            synchronized (sWidgetLock) {
+                return Math.min(mConversationCursor.getCount(), MAX_CONVERSATIONS_COUNT);
+            }
+        }
+
+        /**
+         * @return the {@link RemoteViews} for a specific position in the list.
+         */
+        @Override
+        public RemoteViews getViewAt(int position) {
+            synchronized (sWidgetLock) {
+                // "View more conversations" view.
+                if (mConversationCursor == null
+                        || (mShouldShowViewMore && position >= getConversationCount())) {
+                    return getViewMoreConversationsView();
+                }
+
+                if (!mConversationCursor.moveToPosition(position)) {
+                    // If we ever fail to move to a position, return the "View More conversations"
+                    // view.
+                    LogUtils.e(LOG_TAG,
+                            "Failed to move to position %d in the cursor.", position);
+                    return getViewMoreConversationsView();
+                }
+
+                Conversation conversation = Conversation.from(mConversationCursor);
+                // Split the senders and status from the instructions.
+                SpannableStringBuilder senderBuilder = new SpannableStringBuilder();
+                SpannableStringBuilder statusBuilder = new SpannableStringBuilder();
+                Utils.getStyledSenderSnippet(mContext, conversation.senders, senderBuilder,
+                        statusBuilder, MAX_SENDERS_LENGTH, false, false, false);
+
+                // Get styled date.
+                CharSequence date = DateUtils.getRelativeTimeSpanString(
+                        mContext, conversation.dateMs);
+
+                // Load up our remote view.
+                RemoteViews remoteViews = mWidgetConversationViewBuilder.getStyledView(
+                        senderBuilder, statusBuilder, date, filterTag(conversation.subject),
+                        conversation.snippet, conversation.folderList, conversation.hasAttachments);
+
+                // On click intent.
+                remoteViews.setOnClickFillInIntent(R.id.widget_conversation,
+                        Utils.createViewConversationIntent(mContext, mAccount, mFolder,
+                                        conversation.id));
+
+                return remoteViews;
+            }
+        }
+
+        /**
+         * @return the "View more conversations" view.
+         */
+        private RemoteViews getViewMoreConversationsView() {
+            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
+            view.setTextViewText(
+                    R.id.loading_text, mContext.getText(R.string.view_more_conversations));
+            view.setOnClickFillInIntent(R.id.widget_loading,
+                    Utils.createViewConversationIntent(mContext, mAccount, mFolder,
+                            UIProvider.INVALID_CONVERSATION_ID));
+            return view;
+        }
+
+        @Override
+        public RemoteViews getLoadingView() {
+            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
+            view.setTextViewText(
+                    R.id.loading_text, mContext.getText(R.string.loading_conversation));
+            return view;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return false;
+        }
+
+        @Override
+        public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
+            if (!data.moveToFirst()) {
+                return;
+            }
+
+            final int unreadCount = data.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
+            final String folderName = data.getString(UIProvider.FOLDER_NAME_COLUMN);
+            mFolderCount = data.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
+
+            RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget);
+            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+
+            if (!mFolderInformationShown && !TextUtils.isEmpty(folderName)) {
+                // We want to do a full update to the widget at least once, as the widget
+                // manager doesn't cache the state of the remote views when doing a partial
+                // widget update. This causes the label name to be shown as blank if the state
+                // of the widget is restored.
+                BaseWidgetProvider.configureValidAccountWidget(
+                        mContext, remoteViews, mAppWidgetId, mAccount, mFolder, folderName);
+                appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
+                mFolderInformationShown = true;
+            }
+
+            remoteViews.setViewVisibility(R.id.widget_folder, View.VISIBLE);
+            remoteViews.setTextViewText(R.id.widget_folder, folderName);
+            remoteViews.setViewVisibility(R.id.widget_unread_count, View.VISIBLE);
+            remoteViews.setTextViewText(
+                    R.id.widget_unread_count, Utils.getUnreadCountString(mContext, unreadCount));
+
+            appWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
+        }
+
+        /**
+         * If the subject contains the tag of a mailing-list (text surrounded with []), return the
+         * subject with that tag ellipsized, e.g. "[android-gmail-team] Hello" -> "[andr...] Hello"
+         */
+        private static String filterTag(String subject) {
+            String result = subject;
+            if (subject.length() > 0 && subject.charAt(0) == '[') {
+                int end = subject.indexOf(']');
+                if (end > 0) {
+                    String tag = subject.substring(1, end);
+                    result = "[" + Utils.ellipsize(tag, 7) + "]" + subject.substring(end + 1);
+                }
+            }
+
+            return result;
+        }
+
+        /**
+         * A {@link DelayedTaskHandler} to throttle label update to a reasonable rate.
+         */
+        private class FolderUpdateHandler extends DelayedTaskHandler {
+            public FolderUpdateHandler(int refreshDelay) {
+                super(Looper.myLooper(), refreshDelay);
+            }
+
+            @Override
+            protected void performTask() {
+                // Start the loader. The cached data will be returned if present.
+                if (mFolderLoader != null) {
+                    mFolderLoader.startLoading();
+                }
+            }
+        }
+    }
+}