Refactored so that the widget is now with the app, not the provider

 - CalendarProvider2 now broadcasts a PROVIDER_CHANGED intent to notify the
   widget and any listeners of changes
 - CalendarProvider2 will also batch any update notifications over a
   configurable timeout, right now at 3 seconds, in order to prevent a storm
   of intents.

Change-Id: I884c2fff505be39f6a17b389e002b7bd07976141
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d9926c2..8b384df 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -46,6 +46,10 @@
                 android:readPermission="android.permission.READ_CALENDAR"
                 android:writePermission="android.permission.WRITE_CALENDAR" />
 
+        <!-- This is used to keep the provider alive long enough to send update
+             intent broadcasts. -->
+        <service android:name=".EmptyService" />
+
         <activity android:name="CalendarContentProviderTests" android:label="Calendar Content Provider">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -57,21 +61,7 @@
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
-        <receiver android:name=".CalendarAppWidgetProvider" android:label="@string/gadget_title">
-            <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
-                <action android:name="com.android.calendar.APPWIDGET_UPDATE" />
-            </intent-filter>
-            <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
-        </receiver>
-        <receiver android:name=".TimeChangeReceiver" android:enabled="false">
-            <intent-filter>
-                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
-                <action android:name="android.intent.action.TIME_SET" />
-                <action android:name="android.intent.action.DATE_CHANGED" />
-            </intent-filter>
-        </receiver>
-        <service android:name=".CalendarAppWidgetService" />
+
         <activity android:name="CalendarDebug" android:label="@string/calendar_info">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/res/anim/slide_in.xml b/res/anim/slide_in.xml
deleted file mode 100644
index 72fc56b..0000000
--- a/res/anim/slide_in.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
-    <translate
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromYDelta="100%p" android:toYDelta="0"
-        android:duration="@integer/slide_transition_duration"/>
-    <alpha
-        android:interpolator="@android:anim/accelerate_interpolator"
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:duration="@integer/slide_transition_duration" />
-</set>
\ No newline at end of file
diff --git a/res/anim/slide_out.xml b/res/anim/slide_out.xml
deleted file mode 100644
index 52681a7..0000000
--- a/res/anim/slide_out.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
-    <translate
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromYDelta="0" android:toYDelta="-100%p"
-        android:duration="@integer/slide_transition_duration"/>
-    <alpha
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="1.0" android:toAlpha="0"
-        android:duration="@integer/slide_transition_duration" />
-</set>
\ No newline at end of file
diff --git a/res/drawable-hdpi/appwidget_bg.9.png b/res/drawable-hdpi/appwidget_bg.9.png
deleted file mode 100644
index d9af8fb..0000000
--- a/res/drawable-hdpi/appwidget_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_bg_focus.9.png b/res/drawable-hdpi/appwidget_bg_focus.9.png
deleted file mode 100644
index ee098af..0000000
--- a/res/drawable-hdpi/appwidget_bg_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_bg_press.9.png b/res/drawable-hdpi/appwidget_bg_press.9.png
deleted file mode 100644
index 03ca2a1..0000000
--- a/res/drawable-hdpi/appwidget_bg_press.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/appwidget_calendar_bgtop_blue.9.png b/res/drawable-hdpi/appwidget_calendar_bgtop_blue.9.png
deleted file mode 100644
index c55f808..0000000
--- a/res/drawable-hdpi/appwidget_calendar_bgtop_blue.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/white_list_rule_cal.9.png b/res/drawable-hdpi/white_list_rule_cal.9.png
deleted file mode 100755
index a4d54cf..0000000
--- a/res/drawable-hdpi/white_list_rule_cal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appwidget_bg.9.png b/res/drawable-mdpi/appwidget_bg.9.png
deleted file mode 100644
index 8049191..0000000
--- a/res/drawable-mdpi/appwidget_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appwidget_bg_focus.9.png b/res/drawable-mdpi/appwidget_bg_focus.9.png
deleted file mode 100644
index f4bbb08..0000000
--- a/res/drawable-mdpi/appwidget_bg_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appwidget_bg_press.9.png b/res/drawable-mdpi/appwidget_bg_press.9.png
deleted file mode 100644
index d060b77..0000000
--- a/res/drawable-mdpi/appwidget_bg_press.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/appwidget_calendar_bgtop_blue.9.png b/res/drawable-mdpi/appwidget_calendar_bgtop_blue.9.png
deleted file mode 100644
index af0f466..0000000
--- a/res/drawable-mdpi/appwidget_calendar_bgtop_blue.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/white_list_rule_cal.9.png b/res/drawable-mdpi/white_list_rule_cal.9.png
deleted file mode 100644
index 11cb7ab..0000000
--- a/res/drawable-mdpi/white_list_rule_cal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/appwidget_background.xml b/res/drawable/appwidget_background.xml
deleted file mode 100644
index abbb9e6..0000000
--- a/res/drawable/appwidget_background.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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_window_focused="false" android:drawable="@drawable/appwidget_bg" />
-    <item android:state_pressed="true" android:drawable="@drawable/appwidget_bg_press" />
-    <item android:state_focused="true" android:drawable="@drawable/appwidget_bg_focus" />
-    <item android:drawable="@drawable/appwidget_bg" />
-</selector>
diff --git a/res/layout-land/agenda_appwidget.xml b/res/layout-land/agenda_appwidget.xml
deleted file mode 100644
index 6d314c0..0000000
--- a/res/layout-land/agenda_appwidget.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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:id="@+id/agenda_appwidget"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="@drawable/appwidget_background"
-    android:focusable="true"
-    android:clickable="true">
-
-    <!-- Header -->
-    <LinearLayout
-        android:id="@+id/header"
-        android:layout_width="match_parent"
-        android:layout_height="40dip"
-        android:orientation="horizontal"
-        android:background="@drawable/appwidget_calendar_bgtop_blue">
-
-        <TextView
-            android:id="@+id/day_of_week"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:layout_marginLeft="7dip"
-            android:layout_marginRight="7dip"
-            android:layout_marginBottom="5dip"
-            android:textColor="@color/appwidget_date"
-            android:textSize="18sp"
-            android:gravity="left|bottom"
-            android:singleLine="true" />
-
-        <TextView
-            android:id="@+id/day_of_month"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="7dip"
-            android:layout_marginRight="7dip"
-            android:layout_marginBottom="5dip"
-            android:gravity="right|bottom"
-            android:textColor="@color/appwidget_date"
-            android:textSize="20sp"
-            android:textStyle="bold"
-            android:singleLine="true" />
-    </LinearLayout>
-
-    <!-- No Event -->
-    <TextView
-        android:id="@+id/no_events"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="10dip"
-        android:padding="7dip"
-        android:gravity="center"
-        android:textSize="14sp"
-        android:textStyle="bold"
-        android:textColor="@color/appwidget_no_events"
-        android:text="@string/gadget_no_events" />
-
-    <!-- Event #1 -->
-    <TextView
-        android:id="@+id/when1"
-        style="@style/TextAppearance.WidgetWhen" />
-
-    <TextView
-        android:id="@+id/where1"
-        style="@style/TextAppearance.WidgetWhere" />
-
-    <TextView
-        android:id="@+id/title1"
-        android:layout_marginBottom="-3dip"
-        style="@style/TextAppearance.WidgetTitle" />
-
-    <!-- Conflict banner -->
-    <TextView
-        android:id="@+id/conflict_landscape"
-        style="@style/TextAppearance.WidgetConflict" />
-
-    <!-- These fields are not visible in landscape mode but required to avoid exceptions -->
-    <TextView
-        android:id="@+id/when2"
-        android:visibility="gone"
-        android:layout_width="0dp"
-        android:layout_height="0dp" />
-    <TextView
-        android:id="@+id/where2"
-        android:visibility="gone"
-        android:layout_width="0dp"
-        android:layout_height="0dp" />
-    <TextView
-        android:id="@+id/title2"
-        android:visibility="gone"
-        android:layout_width="0dp"
-        android:layout_height="0dp" />
-
-    <TextView
-        android:id="@+id/conflict_portrait"
-        android:visibility="gone"
-        android:layout_width="0dp"
-        android:layout_height="0dp" />
-
-</LinearLayout>
diff --git a/res/layout/agenda_appwidget.xml b/res/layout/agenda_appwidget.xml
deleted file mode 100644
index 826eac9..0000000
--- a/res/layout/agenda_appwidget.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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:id="@+id/agenda_appwidget"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="@drawable/appwidget_background"
-    android:focusable="true"
-    android:clickable="true">
-
-    <!-- Header -->
-    <LinearLayout
-        android:id="@+id/header"
-        android:layout_width="match_parent"
-        android:layout_height="40dip"
-        android:orientation="horizontal"
-        android:background="@drawable/appwidget_calendar_bgtop_blue">
-
-        <TextView
-            android:id="@+id/day_of_week"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:layout_marginLeft="7dip"
-            android:layout_marginRight="7dip"
-            android:layout_marginBottom="5dip"
-            android:textColor="@color/appwidget_date"
-            android:textSize="18sp"
-            android:gravity="left|bottom"
-            android:singleLine="true" />
-
-        <TextView
-            android:id="@+id/day_of_month"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="7dip"
-            android:layout_marginRight="7dip"
-            android:layout_marginBottom="5dip"
-            android:gravity="right|bottom"
-            android:textColor="@color/appwidget_date"
-            android:textSize="20sp"
-            android:textStyle="bold"
-            android:singleLine="true" />
-    </LinearLayout>
-
-    <!-- Container to show only a single page -->
-    <FrameLayout
-        android:id="@+id/single_page"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-    </FrameLayout>
-
-    <!-- Flipper for event pages -->
-    <ViewFlipper
-        android:id="@+id/page_flipper"
-        android:autoStart="true"
-        android:flipInterval="@integer/flip_interval"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:inAnimation="@anim/slide_in"
-        android:outAnimation="@anim/slide_out"
-        android:animateFirstView="false">
-    </ViewFlipper>
-
-    <!-- No Event -->
-    <TextView
-        android:id="@+id/no_events"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="10dip"
-        android:padding="7dip"
-        android:gravity="center"
-        android:textSize="14sp"
-        android:textStyle="bold"
-        android:textColor="@color/appwidget_no_events"
-        android:text="@string/gadget_no_events" />
-
-
-
-</LinearLayout>
diff --git a/res/layout/page_appwidget.xml b/res/layout/page_appwidget.xml
deleted file mode 100644
index eabd43a..0000000
--- a/res/layout/page_appwidget.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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:id="@+id/page_appwidget"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:addStatesFromChildren="true">
-
-     <!-- Event #1 -->
-    <TextView
-        android:id="@+id/when1"
-        style="@style/TextAppearance.WidgetWhen" />
-
-    <TextView
-        android:id="@+id/where1"
-        style="@style/TextAppearance.WidgetWhere" />
-
-    <TextView
-        android:id="@+id/title1"
-        android:layout_marginBottom="6dip"
-        style="@style/TextAppearance.WidgetTitle" />
-
-    <!-- Event #2 -->
-    <TextView
-        android:id="@+id/when2"
-        style="@style/TextAppearance.WidgetWhen" />
-
-    <TextView
-        android:id="@+id/where2"
-        style="@style/TextAppearance.WidgetWhere" />
-
-    <TextView
-        android:id="@+id/title2"
-        android:layout_marginBottom="6dip"
-        style="@style/TextAppearance.WidgetTitle" />
-
-    <!-- Page count -->
-    <TextView
-        android:id="@+id/page_count"
-        android:gravity="right|bottom"
-        android:layout_gravity="bottom"
-        android:layout_weight="1"
-        style="@style/TextAppearance.WidgetPageCount">
-    </TextView>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
deleted file mode 100644
index 2cdfb99..0000000
--- a/res/values-land/dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<resources>
-    <dimen name="appwidget_width">206dip</dimen>
-    <dimen name="appwidget_height">143dip</dimen>
-</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
deleted file mode 100644
index ac4c3f1..0000000
--- a/res/values/colors.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<resources>
-    <color name="appwidget_date">#ff000000</color>
-
-    <color name="appwidget_when">#ff666666</color>
-    <color name="appwidget_title">#ff000000</color>
-    <color name="appwidget_where">#ff666666</color>
-    <color name="appwidget_conflict">#ff000000</color>
-
-    <color name="appwidget_no_events">#bb000000</color>
-    <color name="appwidget_page_count">#ff666666</color>
-</resources>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
deleted file mode 100644
index ff94dd5..0000000
--- a/res/values/config.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<resources>
-    <integer name="flip_interval">5000</integer>
-    <integer name="slide_transition_duration">600</integer>
-</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4765299..b993ef4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -19,35 +19,9 @@
     <!-- Title for the Calendar Storage process. -->
     <string name="calendar_storage">Calendar Storage</string>
 
-    <!-- Title for calendar gadget when displayed in list of all other gadgets -->
-    <string name="gadget_title">Calendar</string>
-
-    <!-- Title of event when no explicit title is specified by the user -->
-    <string name="no_title_label">(No title)</string>
-
     <!-- Temporary Calendar name to use before we have the calendar name from the server -->
     <string name="calendar_default_name">Default</string>
 
-    <!-- Shown in gadget when additional events are available for display, but no room remaining -->
-    <plurals name="gadget_more_events">
-        <!-- additional events message for 1 event -->
-        <item quantity="one">1 more event</item>
-        <!-- additional events message for multiple events -->
-        <item quantity="other"><xliff:g id="number">%d</xliff:g> more events</item>
-    </plurals>
-
-    <!-- Caption to show on gadget when there are no upcoming calendar events -->
-    <string name="gadget_no_events">No upcoming calendar events</string>
-
-    <!-- Text to show on gadget when an event starts on the next day -->
-    <string name="tomorrow">Tomorrow</string>
-
-    <!-- Text to show on gadget when an event is currently in progress -->
-    <string name="in_progress">in progress</string>
-
-    <!-- Text to show on gadget when an all-day event is in progress -->
-    <string name="today">Today</string>
-
     <!-- Caption on secret calendar info -->
     <string name="calendar_info">Calendar info</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
deleted file mode 100644
index c2a1b3a..0000000
--- a/res/values/styles.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2010, 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.
-*/
--->
-<resources>
-    <style name="TextAppearance" parent="android:TextAppearance">
-    </style>
-
-    <style name="TextAppearance.WidgetWhen">
-        <item name="android:visibility">gone</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginLeft">7dip</item>
-        <item name="android:layout_marginRight">7dip</item>
-
-        <item name="android:layout_marginTop">3dip</item>
-        <item name="android:layout_marginBottom">-3dip</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:textColor">@color/appwidget_when</item>
-        <item name="android:singleLine">true</item>
-    </style>
-
-    <style name="TextAppearance.WidgetWhere">
-        <item name="android:visibility">gone</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginLeft">7dip</item>
-        <item name="android:layout_marginRight">7dip</item>
-
-        <item name="android:layout_marginBottom">-3dip</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:textColor">@color/appwidget_where</item>
-        <item name="android:singleLine">true</item>
-    </style>
-
-    <style name="TextAppearance.WidgetTitle">
-        <item name="android:visibility">gone</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginLeft">7dip</item>
-        <item name="android:layout_marginRight">7dip</item>
-
-        <item name="android:textSize">14sp</item>
-        <item name="android:textStyle">bold</item>
-        <item name="android:textColor">@color/appwidget_title</item>
-        <item name="android:maxLines">2</item>
-    </style>
-
-    <style name="TextAppearance.WidgetConflict">
-        <item name="android:visibility">gone</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:paddingLeft">7dip</item>
-        <item name="android:paddingRight">7dip</item>
-
-        <item name="android:layout_marginBottom">4dip</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:singleLine">true</item>
-        <item name="android:textColor">@color/appwidget_conflict</item>
-    </style>
-
-    <style name="TextAppearance.WidgetPageCount">
-        <item name="android:visibility">gone</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginLeft">5dip</item>
-        <item name="android:layout_marginRight">5dip</item>
-
-        <item name="android:layout_marginTop">3dip</item>
-        <item name="android:layout_marginBottom">7dip</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:textColor">@color/appwidget_when</item>
-        <item name="android:singleLine">true</item>
-    </style>
-</resources>
diff --git a/res/xml/appwidget_info.xml b/res/xml/appwidget_info.xml
deleted file mode 100644
index acfe6af..0000000
--- a/res/xml/appwidget_info.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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="146dip"
-    android:minHeight="146dip"
-    android:updatePeriodMillis="0"
-    android:initialLayout="@layout/agenda_appwidget"
-    >
-</appwidget-provider>
diff --git a/src/com/android/providers/calendar/AppWidgetShared.java b/src/com/android/providers/calendar/AppWidgetShared.java
deleted file mode 100644
index b871c30..0000000
--- a/src/com/android/providers/calendar/AppWidgetShared.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2009 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.providers.calendar;
-
-import android.app.Service;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * Set of static variables that are shared between
- * {@link CalendarAppWidgetProvider} and {@link CalendarAppWidgetService} and
- * guarded by {@link #sLock}.
- */
-public class AppWidgetShared {
-    static final String TAG = "AppWidgetShared";
-    static final boolean LOGD = false;
-    
-    static Object sLock = new Object();
-    static WakeLock sWakeLock;
-    static boolean sUpdateRequested = false;
-    static boolean sUpdateRunning = false;
-    
-    /**
-     * {@link System#currentTimeMillis()} at last update request. 
-     */
-    static long sLastRequest = -1;
-
-    private static HashSet<Integer> sAppWidgetIds = new HashSet<Integer>();
-    private static HashSet<Long> sChangedEventIds = new HashSet<Long>();
-    
-    /**
-     * Merge a set of filtering appWidgetIds with those from other pending
-     * requests. If null, then reset the filter to match all.
-     * <p>
-     * Only call this while holding a {@link #sLock} lock.
-     */
-    static void mergeAppWidgetIdsLocked(int[] appWidgetIds) {
-        if (appWidgetIds != null) {
-            for (int appWidgetId : appWidgetIds) {
-                sAppWidgetIds.add(appWidgetId);
-            }
-        } else {
-            sAppWidgetIds.clear();
-        }
-    }
-    
-    /**
-     * Merge a set of filtering changedEventIds with those from other pending
-     * requests. If null, then reset the filter to match all.
-     * <p>
-     * Only call this while holding a {@link #sLock} lock.
-     */
-    static void mergeChangedEventIdsLocked(long[] changedEventIds) {
-        if (changedEventIds != null) {
-            for (long changedEventId : changedEventIds) {
-                sChangedEventIds.add(changedEventId);
-            }
-        } else {
-            sChangedEventIds.clear();
-        }
-    }
-
-    /**
-     * Collect all currently requested appWidgetId filters, returning as single
-     * list. This call also clears the internal list.
-     * <p>
-     * Only call this while holding a {@link #sLock} lock.
-     */
-    static int[] collectAppWidgetIdsLocked() {
-        final int size = sAppWidgetIds.size();
-        int[] array = new int[size];
-        Iterator<Integer> iterator = sAppWidgetIds.iterator();
-        for (int i = 0; i < size; i++) {
-            array[i] = iterator.next();
-        }
-        sAppWidgetIds.clear();
-        return array;
-    }
-
-    /**
-     * Collect all currently requested changedEventId filters, returning as
-     * single list. This call also clears the internal list.
-     * <p>
-     * Only call this while holding a {@link #sLock} lock.
-     */
-    static Set<Long> collectChangedEventIdsLocked() {
-        Set<Long> set = new HashSet<Long>();
-        for (Long value : sChangedEventIds) {
-            set.add(value);
-        }
-        sChangedEventIds.clear();
-        return set;
-    }
-
-    /**
-     * Call this at any point to release any {@link WakeLock} and reset to
-     * default state. Usually called before {@link Service#stopSelf()}.
-     * <p>
-     * Only call this while holding a {@link #sLock} lock.
-     */
-    static void clearLocked() {
-        if (sWakeLock != null && sWakeLock.isHeld()) {
-            if (LOGD) Log.d(TAG, "found held wakelock, so releasing");
-            sWakeLock.release();
-        }
-        sWakeLock = null;
-        
-        sUpdateRequested = false;
-        sUpdateRunning = false;
-        
-        sAppWidgetIds.clear();
-        sChangedEventIds.clear();
-    }
-}
diff --git a/src/com/android/providers/calendar/CalendarAppWidgetProvider.java b/src/com/android/providers/calendar/CalendarAppWidgetProvider.java
deleted file mode 100644
index 928d4bc..0000000
--- a/src/com/android/providers/calendar/CalendarAppWidgetProvider.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2009 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.providers.calendar;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProvider;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-/**
- * Simple widget to show next upcoming calendar event.
- */
-public class CalendarAppWidgetProvider extends AppWidgetProvider {
-    static final String TAG = "CalendarAppWidgetProvider";
-    static final boolean LOGD = false;
-
-    static final String ACTION_CALENDAR_APPWIDGET_UPDATE =
-            "com.android.providers.calendar.APPWIDGET_UPDATE";
-    
-    /**
-     * Threshold to check against when building widget updates. If system clock
-     * has changed less than this amount, we consider ignoring the request.
-     */
-    static final long UPDATE_THRESHOLD = DateUtils.MINUTE_IN_MILLIS;
-
-    /**
-     * Maximum time to hold {@link WakeLock} when performing widget updates.
-     */
-    static final long WAKE_LOCK_TIMEOUT = DateUtils.MINUTE_IN_MILLIS;
-    
-    static final String PACKAGE_THIS_APPWIDGET =
-        "com.android.providers.calendar";
-    static final String CLASS_THIS_APPWIDGET =
-        "com.android.providers.calendar.CalendarAppWidgetProvider";
-
-    private static CalendarAppWidgetProvider sInstance;
-    
-    static synchronized CalendarAppWidgetProvider getInstance() {
-        if (sInstance == null) {
-            sInstance = new CalendarAppWidgetProvider();
-        }
-        return sInstance;
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        // Handle calendar-specific updates ourselves because they might be
-        // coming in without extras, which AppWidgetProvider then blocks.
-        final String action = intent.getAction();
-        if (ACTION_CALENDAR_APPWIDGET_UPDATE.equals(action)) {
-            performUpdate(context, null /* all widgets */,
-                    null /* no eventIds */, false /* don't ignore */);
-        } else {
-            super.onReceive(context, intent);
-        }
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onEnabled(Context context) {
-        // Enable updates for timezone and date changes
-        PackageManager pm = context.getPackageManager();
-        pm.setComponentEnabledSetting(
-                new ComponentName(context, TimeChangeReceiver.class),
-                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onDisabled(Context context) {
-        // Unsubscribe from all AlarmManager updates
-        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        PendingIntent pendingUpdate = getUpdateIntent(context);
-        am.cancel(pendingUpdate);
-
-        // Disable updates for timezone and date changes
-        PackageManager pm = context.getPackageManager();
-        pm.setComponentEnabledSetting(
-                new ComponentName(context, TimeChangeReceiver.class),
-                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
-        performUpdate(context, appWidgetIds, null /* no eventIds */, false /* force */);
-    }
-    
-    /**
-     * Check against {@link AppWidgetManager} if there are any instances of this widget.
-     */
-    private boolean hasInstances(Context context) {
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-        ComponentName thisAppWidget = getComponentName(context);
-        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
-        return (appWidgetIds.length > 0);
-    }
-    
-    /**
-     * Build {@link ComponentName} describing this specific
-     * {@link AppWidgetProvider}
-     */
-    static ComponentName getComponentName(Context context) {
-        return new ComponentName(PACKAGE_THIS_APPWIDGET, CLASS_THIS_APPWIDGET);
-    }
-
-    /**
-     * The {@link CalendarProvider} has been updated, which means we should push
-     * updates to any widgets, if they exist.
-     * 
-     * @param context Context to use when creating widget.
-     * @param changedEventId Specific event known to be changed, otherwise -1.
-     *            If present, we use it to decide if an update is necessary.
-     */
-    void providerUpdated(Context context, long changedEventId) {
-        if (hasInstances(context)) {
-            // Only pass along changedEventId if not -1
-            long[] changedEventIds = null;
-            if (changedEventId != -1) {
-                changedEventIds = new long[] { changedEventId };
-            }
-            
-            performUpdate(context, null /* all widgets */, changedEventIds, false /* force */);
-        }
-    }
-
-    /**
-     * {@link TimeChangeReceiver} has triggered that the time changed.
-     * 
-     * @param context Context to use when creating widget.
-     * @param considerIgnore If true, compare
-     *            {@link AppWidgetShared#sLastRequest} against
-     *            {@link #UPDATE_THRESHOLD} to consider ignoring this update
-     *            request.
-     */
-    void timeUpdated(Context context, boolean considerIgnore) {
-        if (hasInstances(context)) {
-            performUpdate(context, null /* all widgets */, null /* no events */, considerIgnore);
-        }
-    }
-    
-    /**
-     * Process and push out an update for the given appWidgetIds. This call
-     * actually fires an intent to start {@link CalendarAppWidgetService} as a
-     * background service which handles the actual update, to prevent ANR'ing
-     * during database queries.
-     * <p>
-     * This call will acquire a single {@link WakeLock} and set a flag that an
-     * update has been requested.
-     * 
-     * @param context Context to use when acquiring {@link WakeLock} and
-     *            starting {@link CalendarAppWidgetService}.
-     * @param appWidgetIds List of specific appWidgetIds to update, or null for
-     *            all.
-     * @param changedEventIds Specific events known to be changed. If present,
-     *            we use it to decide if an update is necessary.
-     * @param considerIgnore If true, compare
-     *            {@link AppWidgetShared#sLastRequest} against
-     *            {@link #UPDATE_THRESHOLD} to consider ignoring this update
-     *            request.
-     */
-    private void performUpdate(Context context, int[] appWidgetIds,
-            long[] changedEventIds, boolean considerIgnore) {
-        synchronized (AppWidgetShared.sLock) {
-            // Consider ignoring this update request if inside threshold. This
-            // check is inside the lock because we depend on this "now" time.
-            long now = System.currentTimeMillis();
-            if (considerIgnore && AppWidgetShared.sLastRequest != -1) {
-                long delta = Math.abs(now - AppWidgetShared.sLastRequest);
-                if (delta < UPDATE_THRESHOLD) {
-                    if (LOGD) Log.d(TAG, "Ignoring update request because delta=" + delta);
-                    return;
-                }
-            }
-            
-            // We need to update, so make sure we have a valid, held wakelock
-            if (AppWidgetShared.sWakeLock == null ||
-                    !AppWidgetShared.sWakeLock.isHeld()) {
-                if (LOGD) Log.d(TAG, "no held wakelock found, so acquiring new one");
-                PowerManager powerManager = (PowerManager)
-                        context.getSystemService(Context.POWER_SERVICE);
-                AppWidgetShared.sWakeLock =
-                        powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-                AppWidgetShared.sWakeLock.setReferenceCounted(false);
-                AppWidgetShared.sWakeLock.acquire(WAKE_LOCK_TIMEOUT);
-            }
-            
-            if (LOGD) Log.d(TAG, "setting request now=" + now);
-            AppWidgetShared.sLastRequest = now;
-            AppWidgetShared.sUpdateRequested = true;
-            
-            // Apply filters that would limit the scope of this update, or clear
-            // any pending filters if all requested.
-            AppWidgetShared.mergeAppWidgetIdsLocked(appWidgetIds);
-            AppWidgetShared.mergeChangedEventIdsLocked(changedEventIds);
-            
-            // Launch over to service so it can perform update
-            final Intent updateIntent = new Intent(context, CalendarAppWidgetService.class);
-            context.startService(updateIntent);
-        }
-    }
-    
-    /**
-     * Build the {@link PendingIntent} used to trigger an update of all calendar
-     * widgets. Uses {@link #ACTION_CALENDAR_APPWIDGET_UPDATE} to directly target
-     * all widgets instead of using {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}.
-     * 
-     * @param context Context to use when building broadcast.
-     */
-    static PendingIntent getUpdateIntent(Context context) {
-        Intent updateIntent = new Intent(ACTION_CALENDAR_APPWIDGET_UPDATE);
-        updateIntent.setComponent(new ComponentName(context, CalendarAppWidgetProvider.class));
-        return PendingIntent.getBroadcast(context, 0 /* no requestCode */,
-                updateIntent, 0 /* no flags */);
-    }
-}
diff --git a/src/com/android/providers/calendar/CalendarAppWidgetService.java b/src/com/android/providers/calendar/CalendarAppWidgetService.java
deleted file mode 100644
index 29644f6..0000000
--- a/src/com/android/providers/calendar/CalendarAppWidgetService.java
+++ /dev/null
@@ -1,875 +0,0 @@
-/*
- * Copyright (C) 2009 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.providers.calendar;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import com.android.providers.calendar.CalendarAppWidgetService.CalendarAppWidgetModel.EventInfo;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.IBinder;
-import android.provider.Calendar.Attendees;
-import android.provider.Calendar.Calendars;
-import android.provider.Calendar.Instances;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.TimeZone;
-
-
-public class CalendarAppWidgetService extends Service implements Runnable {
-    private static final String TAG = "CalendarAppWidgetService";
-    private static final boolean LOGD = false;
-
-    /* TODO query doesn't handle all-day events properly, we should fix this in
-     * the provider in a manner similar to how it is handled in Event.loadEvents
-     * in the Calendar application.
-     */
-    private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, "
-            + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, "
-            + Instances.END_MINUTE + " ASC LIMIT 10";
-
-    // TODO can't use parameter here because provider is dropping them
-    private static final String EVENT_SELECTION = Calendars.SELECTED + "=1 AND "
-            + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED;
-
-    static final String[] EVENT_PROJECTION = new String[] {
-        Instances.ALL_DAY,
-        Instances.BEGIN,
-        Instances.END,
-        Instances.TITLE,
-        Instances.EVENT_LOCATION,
-        Instances.EVENT_ID,
-    };
-
-    static final int INDEX_ALL_DAY = 0;
-    static final int INDEX_BEGIN = 1;
-    static final int INDEX_END = 2;
-    static final int INDEX_TITLE = 3;
-    static final int INDEX_EVENT_LOCATION = 4;
-    static final int INDEX_EVENT_ID = 5;
-
-    private static final long SEARCH_DURATION = DateUtils.WEEK_IN_MILLIS;
-
-    // If no next-update calculated, or bad trigger time in past, schedule
-    // update about six hours from now.
-    private static final long UPDATE_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6;
-
-    private static final String KEY_DETAIL_VIEW = "DETAIL_VIEW";
-
-    static class CalendarAppWidgetModel {
-        String dayOfWeek;
-        String dayOfMonth;
-        /*
-         * TODO Refactor this so this class is used in the case of "no event"
-         * So for now, this field is always View.GONE
-         */
-        int visibNoEvents;
-
-        EventInfo[] eventInfos;
-
-        public CalendarAppWidgetModel() {
-            this(2);
-        }
-
-        public CalendarAppWidgetModel(int size) {
-            // we round up to the nearest even integer
-            eventInfos = new EventInfo[2 * ((size + 1) / 2)];
-            for (int i = 0; i < eventInfos.length; i++) {
-                eventInfos[i] = new EventInfo();
-            }
-            visibNoEvents = View.GONE;
-        }
-
-        class EventInfo {
-            int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE)
-            String when;
-            int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE)
-            String where;
-            int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE)
-            String title;
-
-            public EventInfo() {
-                visibWhen = View.GONE;
-                visibWhere = View.GONE;
-                visibTitle = View.GONE;
-            }
-
-            @Override
-            public String toString() {
-                StringBuilder builder = new StringBuilder();
-                builder.append("EventInfo [visibTitle=");
-                builder.append(visibTitle);
-                builder.append(", title=");
-                builder.append(title);
-                builder.append(", visibWhen=");
-                builder.append(visibWhen);
-                builder.append(", when=");
-                builder.append(when);
-                builder.append(", visibWhere=");
-                builder.append(visibWhere);
-                builder.append(", where=");
-                builder.append(where);
-                builder.append("]");
-                return builder.toString();
-            }
-
-            @Override
-            public int hashCode() {
-                final int prime = 31;
-                int result = 1;
-                result = prime * result + getOuterType().hashCode();
-                result = prime * result + ((title == null) ? 0 : title.hashCode());
-                result = prime * result + visibTitle;
-                result = prime * result + visibWhen;
-                result = prime * result + visibWhere;
-                result = prime * result + ((when == null) ? 0 : when.hashCode());
-                result = prime * result + ((where == null) ? 0 : where.hashCode());
-                return result;
-            }
-
-            @Override
-            public boolean equals(Object obj) {
-                if (this == obj) {
-                    return true;
-                }
-                if (obj == null) {
-                    return false;
-                }
-                if (getClass() != obj.getClass()) {
-                    return false;
-                }
-                EventInfo other = (EventInfo) obj;
-                if (title == null) {
-                    if (other.title != null) {
-                        return false;
-                    }
-                } else if (!title.equals(other.title)) {
-                    return false;
-                }
-                if (visibTitle != other.visibTitle) {
-                    return false;
-                }
-                if (visibWhen != other.visibWhen) {
-                    return false;
-                }
-                if (visibWhere != other.visibWhere) {
-                    return false;
-                }
-                if (when == null) {
-                    if (other.when != null) {
-                        return false;
-                    }
-                } else if (!when.equals(other.when)) {
-                    return false;
-                }
-                if (where == null) {
-                    if (other.where != null) {
-                        return false;
-                    }
-                } else if (!where.equals(other.where)) {
-                    return false;
-                }
-                return true;
-            }
-
-            private CalendarAppWidgetModel getOuterType() {
-                return CalendarAppWidgetModel.this;
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("\nCalendarAppWidgetModel [eventInfos=");
-            builder.append(Arrays.toString(eventInfos));
-            builder.append(", visibNoEvents=");
-            builder.append(visibNoEvents);
-            builder.append(", dayOfMonth=");
-            builder.append(dayOfMonth);
-            builder.append(", dayOfWeek=");
-            builder.append(dayOfWeek);
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-
-    @Override
-    public void onStart(Intent intent, int startId) {
-        super.onStart(intent, startId);
-
-        // Only start processing thread if not already running
-        synchronized (AppWidgetShared.sLock) {
-            if (!AppWidgetShared.sUpdateRunning) {
-                if (LOGD) Log.d(TAG, "no thread running, so starting new one");
-                AppWidgetShared.sUpdateRunning = true;
-                new Thread(this).start();
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    /**
-     * Thread loop to handle
-     */
-    public void run() {
-        while (true) {
-            long now = -1;
-            int[] appWidgetIds;
-            Set<Long> changedEventIds;
-
-            synchronized (AppWidgetShared.sLock) {
-                // Bail out if no remaining updates
-                if (!AppWidgetShared.sUpdateRequested) {
-                    // Clear current shared state, release wakelock, and stop service
-                    if (LOGD) Log.d(TAG, "no requested update or expired wakelock, bailing");
-                    AppWidgetShared.clearLocked();
-                    stopSelf();
-                    return;
-                }
-
-                // Clear requested flag and collect latest parameters
-                AppWidgetShared.sUpdateRequested = false;
-
-                now = AppWidgetShared.sLastRequest;
-                appWidgetIds = AppWidgetShared.collectAppWidgetIdsLocked();
-                changedEventIds = AppWidgetShared.collectChangedEventIdsLocked();
-            }
-
-            // Process this update
-            if (LOGD) Log.d(TAG, "processing requested update now=" + now);
-            performUpdate(this, appWidgetIds, changedEventIds, now);
-        }
-    }
-
-    /**
-     * Process and push out an update for the given appWidgetIds.
-     *
-     * @param context Context to use when updating widget.
-     * @param appWidgetIds List of appWidgetIds to update, or null for all.
-     * @param changedEventIds Specific events known to be changed, otherwise
-     *            null. If present, we use to decide if an update is necessary.
-     * @param now System clock time to use during this update.
-     */
-    private void performUpdate(Context context, int[] appWidgetIds,
-            Set<Long> changedEventIds, long now) {
-        ContentResolver resolver = context.getContentResolver();
-
-        Cursor cursor = null;
-        RemoteViews views = null;
-        long triggerTime = -1;
-
-        try {
-            cursor = getUpcomingInstancesCursor(resolver, SEARCH_DURATION, now);
-            if (cursor != null) {
-                MarkedEvents events = buildMarkedEvents(cursor, changedEventIds, now);
-
-                boolean shouldUpdate = true;
-                if (changedEventIds.size() > 0) {
-                    shouldUpdate = events.watchFound;
-                }
-
-                if (events.markedIds.isEmpty()) {
-                    views = getAppWidgetNoEvents(context);
-                } else if (shouldUpdate) {
-                    views = getAppWidgetUpdate(context, cursor, events);
-                    triggerTime = calculateUpdateTime(cursor, events);
-                }
-            } else {
-                views = getAppWidgetNoEvents(context);
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-
-        // Bail out early if no update built
-        if (views == null) {
-            if (LOGD) Log.d(TAG, "Didn't build update, possibly because changedEventIds=" +
-                    changedEventIds.toString());
-            return;
-        }
-
-        AppWidgetManager gm = AppWidgetManager.getInstance(context);
-        if (appWidgetIds != null && appWidgetIds.length > 0) {
-            gm.updateAppWidget(appWidgetIds, views);
-        } else {
-            ComponentName thisWidget = CalendarAppWidgetProvider.getComponentName(context);
-            gm.updateAppWidget(thisWidget, views);
-        }
-
-        // Schedule an alarm to wake ourselves up for the next update.  We also cancel
-        // all existing wake-ups because PendingIntents don't match against extras.
-
-        // If no next-update calculated, or bad trigger time in past, schedule
-        // update about six hours from now.
-        if (triggerTime == -1 || triggerTime < now) {
-            if (LOGD) Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now));
-            triggerTime = now + UPDATE_NO_EVENTS;
-        }
-
-        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        PendingIntent pendingUpdate = CalendarAppWidgetProvider.getUpdateIntent(context);
-
-        am.cancel(pendingUpdate);
-        am.set(AlarmManager.RTC, triggerTime, pendingUpdate);
-
-        if (LOGD) Log.d(TAG, "Scheduled next update at " + formatDebugTime(triggerTime, now));
-    }
-
-    /**
-     * Format given time for debugging output.
-     *
-     * @param unixTime Target time to report.
-     * @param now Current system time from {@link System#currentTimeMillis()}
-     *            for calculating time difference.
-     */
-    static private String formatDebugTime(long unixTime, long now) {
-        Time time = new Time();
-        time.set(unixTime);
-
-        long delta = unixTime - now;
-        if (delta > DateUtils.MINUTE_IN_MILLIS) {
-            delta /= DateUtils.MINUTE_IN_MILLIS;
-            return String.format("[%d] %s (%+d mins)", unixTime, time.format("%H:%M:%S"), delta);
-        } else {
-            delta /= DateUtils.SECOND_IN_MILLIS;
-            return String.format("[%d] %s (%+d secs)", unixTime, time.format("%H:%M:%S"), delta);
-        }
-    }
-
-    /**
-     * Convert given UTC time into current local time.
-     *
-     * @param recycle Time object to recycle, otherwise null.
-     * @param utcTime Time to convert, in UTC.
-     */
-    static private long convertUtcToLocal(Time recycle, long utcTime) {
-        if (recycle == null) {
-            recycle = new Time();
-        }
-        recycle.timezone = Time.TIMEZONE_UTC;
-        recycle.set(utcTime);
-        recycle.timezone = TimeZone.getDefault().getID();
-        return recycle.normalize(true);
-    }
-
-    /**
-     * Figure out the next time we should push widget updates, usually the time
-     * calculated by {@link #getEventFlip(Cursor, long, long, boolean)}.
-     *
-     * @param cursor Valid cursor on {@link Instances#CONTENT_URI}
-     * @param events {@link MarkedEvents} parsed from the cursor
-     */
-    private long calculateUpdateTime(Cursor cursor, MarkedEvents events) {
-        long result = -1;
-        if (!events.markedIds.isEmpty()) {
-            cursor.moveToPosition(events.markedIds.get(0));
-            long start = cursor.getLong(INDEX_BEGIN);
-            long end = cursor.getLong(INDEX_END);
-            boolean allDay = cursor.getInt(INDEX_ALL_DAY) != 0;
-
-            // Adjust all-day times into local timezone
-            if (allDay) {
-                final Time recycle = new Time();
-                start = convertUtcToLocal(recycle, start);
-                end = convertUtcToLocal(recycle, end);
-            }
-
-            result = getEventFlip(cursor, start, end, allDay);
-
-            // Make sure an update happens at midnight or earlier
-            long midnight = getNextMidnightTimeMillis();
-            result = Math.min(midnight, result);
-        }
-        return result;
-    }
-
-    private long getNextMidnightTimeMillis() {
-        Time time = new Time();
-        time.setToNow();
-        time.monthDay++;
-        time.hour = 0;
-        time.minute = 0;
-        time.second = 0;
-        long midnight = time.normalize(true);
-        return midnight;
-    }
-
-    /**
-     * Calculate flipping point for the given event; when we should hide this
-     * event and show the next one. This is defined as the end time of the
-     * event.
-     *
-     * @param start Event start time in local timezone.
-     * @param end Event end time in local timezone.
-     */
-    static private long getEventFlip(Cursor cursor, long start, long end, boolean allDay) {
-        return end;
-    }
-
-    /**
-     * Set visibility of various widget components if there are events, or if no
-     * events were found.
-     *
-     * @param views Set of {@link RemoteViews} to apply visibility.
-     * @param noEvents True if no events found, otherwise false.
-     */
-    private void setNoEventsVisible(RemoteViews views, boolean noEvents) {
-        views.setViewVisibility(R.id.no_events, noEvents ? View.VISIBLE : View.GONE);
-        views.setViewVisibility(R.id.page_flipper, View.GONE);
-        views.setViewVisibility(R.id.single_page, View.GONE);
-    }
-
-    /**
-     * Build a set of {@link RemoteViews} that describes how to update any
-     * widget for a specific event instance.
-     *
-     * @param cursor Valid cursor on {@link Instances#CONTENT_URI}
-     * @param events {@link MarkedEvents} parsed from the cursor
-     */
-    private RemoteViews getAppWidgetUpdate(Context context, Cursor cursor, MarkedEvents events) {
-        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.agenda_appwidget);
-        setNoEventsVisible(views, false);
-
-        long currentTime = System.currentTimeMillis();
-        CalendarAppWidgetModel model = buildAppWidgetModel(context, cursor, events, currentTime);
-
-        applyModelToView(context, model, views);
-
-        // Clicking on the widget launches Calendar
-        long startTime = Math.max(currentTime, events.firstTime);
-
-        PendingIntent pendingIntent = getLaunchPendingIntent(context, startTime);
-        views.setOnClickPendingIntent(R.id.agenda_appwidget, pendingIntent);
-
-        return views;
-    }
-
-    private void applyModelToView(Context context, CalendarAppWidgetModel model, RemoteViews views) {
-        views.setTextViewText(R.id.day_of_week, model.dayOfWeek);
-        views.setTextViewText(R.id.day_of_month, model.dayOfMonth);
-        views.setViewVisibility(R.id.no_events, model.visibNoEvents);
-
-        // Make sure we have a clean slate first
-        views.removeAllViews(R.id.page_flipper);
-        views.removeAllViews(R.id.single_page);
-
-        // If we don't have any events, just hide the relevant views and return
-        if (model.visibNoEvents != View.GONE) {
-            views.setViewVisibility(R.id.page_flipper, View.GONE);
-            views.setViewVisibility(R.id.single_page, View.GONE);
-            return;
-        }
-
-        // Luckily, length of this array is guaranteed to be even
-        int pages = model.eventInfos.length / 2;
-
-        // We use a separate container for the case of only one page to prevent
-        // a ViewFlipper from repeatedly animating one view
-        if (pages > 1) {
-            views.setViewVisibility(R.id.page_flipper, View.VISIBLE);
-            views.setViewVisibility(R.id.single_page, View.GONE);
-        } else {
-            views.setViewVisibility(R.id.single_page, View.VISIBLE);
-            views.setViewVisibility(R.id.page_flipper, View.GONE);
-        }
-
-        // Iterate two at a time through the events and populate the views
-        for (int i = 0; i < model.eventInfos.length; i += 2) {
-            RemoteViews pageViews = new RemoteViews(context.getPackageName(),
-                    R.layout.page_appwidget);
-            EventInfo e1 = model.eventInfos[i];
-            EventInfo e2 = model.eventInfos[i + 1];
-
-            updateTextView(pageViews, R.id.when1, e1.visibWhen, e1.when);
-            updateTextView(pageViews, R.id.where1, e1.visibWhere, e1.where);
-            updateTextView(pageViews, R.id.title1, e1.visibTitle, e1.title);
-            updateTextView(pageViews, R.id.when2, e2.visibWhen, e2.when);
-            updateTextView(pageViews, R.id.where2, e2.visibWhere, e2.where);
-            updateTextView(pageViews, R.id.title2, e2.visibTitle, e2.title);
-
-            if (pages > 1) {
-                views.addView(R.id.page_flipper, pageViews);
-                updateTextView(pageViews, R.id.page_count, View.VISIBLE,
-                        makePageCount((i / 2) + 1, pages));
-            } else {
-                views.addView(R.id.single_page, pageViews);
-            }
-        }
-
-    }
-
-    static String makePageCount(int current, int total) {
-        return Integer.toString(current) + " / " + Integer.toString(total);
-    }
-
-    static void updateTextView(RemoteViews views, int id, int visibility, String string) {
-        views.setViewVisibility(id, visibility);
-        if (visibility == View.VISIBLE) {
-            views.setTextViewText(id, string);
-        }
-    }
-
-    static CalendarAppWidgetModel buildAppWidgetModel(Context context, Cursor cursor,
-            MarkedEvents events, long currentTime) {
-        int eventCount = events.markedIds.size();
-        CalendarAppWidgetModel model = new CalendarAppWidgetModel(eventCount);
-        Time time = new Time();
-        time.set(currentTime);
-        time.monthDay++;
-        time.hour = 0;
-        time.minute = 0;
-        time.second = 0;
-        long startOfNextDay = time.normalize(true);
-
-        time.set(currentTime);
-
-        // Calendar header
-        String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, DateUtils.LENGTH_MEDIUM)
-                .toUpperCase();
-
-        model.dayOfWeek = dayOfWeek;
-        model.dayOfMonth = Integer.toString(time.monthDay);
-
-        int i = 0;
-        for (Integer id : events.markedIds) {
-            populateEvent(context, cursor, id, model, time, i, true, startOfNextDay, currentTime);
-            i++;
-        }
-
-        return model;
-    }
-
-    /**
-     * Pulls the information for a single event from the cursor and populates
-     * the corresponding model object with the data.
-     *
-     * @param context a Context to use for accessing resources
-     * @param cursor the cursor to retrieve the data from
-     * @param rowId the ID of the row to retrieve
-     * @param model the model object to populate
-     * @param recycle a Time instance to recycle
-     * @param eventIndex which event index in the model to populate
-     * @param showTitleLocation whether or not to show the title and location
-     * @param startOfNextDay the beginning of the next day
-     * @param currentTime the current time
-     */
-    static private void populateEvent(Context context, Cursor cursor, int rowId,
-            CalendarAppWidgetModel model, Time recycle, int eventIndex,
-            boolean showTitleLocation, long startOfNextDay, long currentTime) {
-        cursor.moveToPosition(rowId);
-
-        // When
-        boolean allDay = cursor.getInt(INDEX_ALL_DAY) != 0;
-        long start = cursor.getLong(INDEX_BEGIN);
-        long end = cursor.getLong(INDEX_END);
-        if (allDay) {
-            start = convertUtcToLocal(recycle, start);
-            end = convertUtcToLocal(recycle, end);
-        }
-
-        boolean eventIsInProgress = start <= currentTime && end > currentTime;
-        boolean eventIsToday = start < startOfNextDay;
-        boolean eventIsTomorrow = !eventIsToday && !eventIsInProgress
-                && (start < (startOfNextDay + DateUtils.DAY_IN_MILLIS));
-
-        // Compute a human-readable string for the start time of the event
-        String whenString;
-        if (eventIsInProgress && allDay) {
-            // All day events for the current day display as just "Today"
-            whenString = context.getString(R.string.today);
-        } else if (eventIsTomorrow && allDay) {
-            // All day events for the next day display as just "Tomorrow"
-            whenString = context.getString(R.string.tomorrow);
-        } else {
-            int flags = DateUtils.FORMAT_ABBREV_ALL;
-            if (allDay) {
-                flags |= DateUtils.FORMAT_UTC;
-            } else {
-                flags |= DateUtils.FORMAT_SHOW_TIME;
-                if (DateFormat.is24HourFormat(context)) {
-                    flags |= DateUtils.FORMAT_24HOUR;
-                }
-            }
-            // Show day of the week if not today or tomorrow
-            if (!eventIsTomorrow && !eventIsToday) {
-                flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
-            }
-            whenString = DateUtils.formatDateRange(context, start, start, flags);
-            if (eventIsTomorrow) {
-                whenString += (", ");
-                whenString += context.getString(R.string.tomorrow);
-            } else if (eventIsInProgress) {
-                whenString += " (";
-                whenString += context.getString(R.string.in_progress);
-                whenString += ")";
-            }
-        }
-
-        model.eventInfos[eventIndex].when = whenString;
-        model.eventInfos[eventIndex].visibWhen = View.VISIBLE;
-
-        if (showTitleLocation) {
-            // What
-            String titleString = cursor.getString(INDEX_TITLE);
-            if (TextUtils.isEmpty(titleString)) {
-                titleString = context.getString(R.string.no_title_label);
-            }
-            model.eventInfos[eventIndex].title = titleString;
-            model.eventInfos[eventIndex].visibTitle = View.VISIBLE;
-
-            // Where
-            String whereString = cursor.getString(INDEX_EVENT_LOCATION);
-            if (!TextUtils.isEmpty(whereString)) {
-                model.eventInfos[eventIndex].visibWhere = View.VISIBLE;
-                model.eventInfos[eventIndex].where = whereString;
-            } else {
-                model.eventInfos[eventIndex].visibWhere = View.GONE;
-            }
-            if (LOGD) Log.d(TAG, " Title:" + titleString + " Where:" + whereString);
-        }
-    }
-
-    /**
-     * Build a set of {@link RemoteViews} that describes an error state.
-     */
-    private RemoteViews getAppWidgetNoEvents(Context context) {
-        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.agenda_appwidget);
-        setNoEventsVisible(views, true);
-
-        // Calendar header
-        Time time = new Time();
-        time.setToNow();
-        String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, DateUtils.LENGTH_MEDIUM)
-                .toUpperCase();
-        views.setTextViewText(R.id.day_of_week, dayOfWeek);
-        views.setTextViewText(R.id.day_of_month, Integer.toString(time.monthDay));
-
-        // Clicking on widget launches the agenda view in Calendar
-        PendingIntent pendingIntent = getLaunchPendingIntent(context, 0);
-        views.setOnClickPendingIntent(R.id.agenda_appwidget, pendingIntent);
-
-        return views;
-    }
-
-    /**
-     * Build a {@link PendingIntent} to launch the Calendar app. This correctly
-     * sets action, category, and flags so that we don't duplicate tasks when
-     * Calendar was also launched from a normal desktop icon.
-     * @param goToTime time that calendar should take the user to
-     */
-    private PendingIntent getLaunchPendingIntent(Context context, long goToTime) {
-        Intent launchIntent = new Intent();
-        String dataString = "content://com.android.calendar/time";
-        launchIntent.setAction(Intent.ACTION_VIEW);
-        launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED |
-                Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        if (goToTime != 0) {
-            launchIntent.putExtra(KEY_DETAIL_VIEW, true);
-            dataString += "/" + goToTime;
-        }
-        Uri data = Uri.parse(dataString);
-        launchIntent.setData(data);
-        return PendingIntent.getActivity(context, 0 /* no requestCode */,
-                launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-
-    static class MarkedEvents {
-
-        /**
-         * The row IDs of all events marked for display
-         */
-        List<Integer> markedIds = new ArrayList<Integer>(10);
-
-        /**
-         * The start time of the first marked event
-         */
-        long firstTime = -1;
-
-        /** The number of events currently in progress */
-        int inProgressCount = 0; // Number of events with same start time as the primary evt.
-
-        /** The start time of the next upcoming event */
-        long primaryTime = -1;
-
-        /**
-         * The number of events that share the same start time as the next
-         * upcoming event
-         */
-        int primaryCount = 0; // Number of events with same start time as the secondary evt.
-
-        /** The start time of the next next upcoming event */
-        long secondaryTime = 1;
-
-        /**
-         * The number of events that share the same start time as the next next
-         * upcoming event.
-         */
-        int secondaryCount = 0;
-
-        boolean watchFound = false;
-    }
-
-    /**
-     * Walk the given instances cursor and build a list of marked events to be
-     * used when updating the widget. This structure is also used to check if
-     * updates are needed.
-     *
-     * @param cursor Valid cursor across {@link Instances#CONTENT_URI}.
-     * @param watchEventIds Specific events to watch for, setting
-     *            {@link MarkedEvents#watchFound} if found during marking.
-     * @param now Current system time to use for this update, possibly from
-     *            {@link System#currentTimeMillis()}
-     */
-    @VisibleForTesting
-    static MarkedEvents buildMarkedEvents(Cursor cursor, Set<Long> watchEventIds, long now) {
-        MarkedEvents events = new MarkedEvents();
-        final Time recycle = new Time();
-
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext()) {
-            int row = cursor.getPosition();
-            long eventId = cursor.getLong(INDEX_EVENT_ID);
-            long start = cursor.getLong(INDEX_BEGIN);
-            long end = cursor.getLong(INDEX_END);
-
-            boolean allDay = cursor.getInt(INDEX_ALL_DAY) != 0;
-
-            if (LOGD) {
-                Log.d(TAG, "Row #" + row + " allDay:" + allDay + " start:" + start + " end:" + end
-                        + " eventId:" + eventId);
-            }
-
-            // Adjust all-day times into local timezone
-            if (allDay) {
-                start = convertUtcToLocal(recycle, start);
-                end = convertUtcToLocal(recycle, end);
-            }
-
-            boolean inProgress = now < end && now > start;
-
-            // Skip events that have already passed their flip times
-            long eventFlip = getEventFlip(cursor, start, end, allDay);
-            if (LOGD) Log.d(TAG, "Calculated flip time " + formatDebugTime(eventFlip, now));
-            if (eventFlip < now) {
-                continue;
-            }
-
-            // Mark if we've encountered the watched event
-            if (watchEventIds != null && watchEventIds.contains(eventId)) {
-                events.watchFound = true;
-            }
-
-            /* Scan through the events with the following logic:
-             *   Rule #1 Show A) all the events that are in progress including
-             *     all day events and B) the next upcoming event and any events
-             *     with the same start time.
-             *
-             *   Rule #2 If there are no events in progress, show A) the next
-             *     upcoming event and B) any events with the same start time.
-             *
-             *   Rule #3 If no events start at the same time at A in rule 2,
-             *     show A) the next upcoming event and B) the following upcoming
-             *     event + any events with the same start time.
-             */
-            if (inProgress) {
-                // events for part A of Rule #1
-                events.markedIds.add(row);
-                events.inProgressCount++;
-                if (events.firstTime == -1) {
-                    events.firstTime = start;
-                }
-            } else {
-                if (events.primaryCount == 0) {
-                    // first upcoming event
-                    events.markedIds.add(row);
-                    events.primaryTime = start;
-                    events.primaryCount++;
-                    if (events.firstTime == -1) {
-                        events.firstTime = start;
-                    }
-                } else if (events.primaryTime == start) {
-                    // any events with same start time as first upcoming event
-                    events.markedIds.add(row);
-                    events.primaryCount++;
-                } else if (events.markedIds.size() == 1) {
-                    // only one upcoming event, so we take the next upcoming
-                    events.markedIds.add(row);
-                    events.secondaryTime = start;
-                    events.secondaryCount++;
-                } else if (events.secondaryCount > 0
-                        && events.secondaryTime == start) {
-                    // any events with same start time as next upcoming
-                    events.markedIds.add(row);
-                    events.secondaryCount++;
-                } else {
-                    // looks like we're done
-                    break;
-                }
-            }
-        }
-        return events;
-    }
-
-    /**
-     * Query across all calendars for upcoming event instances from now until
-     * some time in the future.
-     *
-     * @param resolver {@link ContentResolver} to use when querying
-     *            {@link Instances#CONTENT_URI}.
-     * @param searchDuration Distance into the future to look for event
-     *            instances, in milliseconds.
-     * @param now Current system time to use for this update, possibly from
-     *            {@link System#currentTimeMillis()}.
-     */
-    private Cursor getUpcomingInstancesCursor(ContentResolver resolver,
-            long searchDuration, long now) {
-        // Search for events from now until some time in the future
-        long end = now + searchDuration;
-
-        Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI,
-                String.format("%d/%d", now, end));
-
-        return resolver.query(uri, EVENT_PROJECTION, EVENT_SELECTION, null,
-                EVENT_SORT_ORDER);
-    }
-}
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index 0bdfd46..e76ce35 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -39,6 +39,8 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Debug;
+import android.os.Handler;
+import android.os.Message;
 import android.os.Process;
 import android.pim.EventRecurrence;
 import android.pim.RecurrenceSet;
@@ -253,7 +255,32 @@
 
     private AlarmManager mAlarmManager;
 
-    private CalendarAppWidgetProvider mAppWidgetProvider = CalendarAppWidgetProvider.getInstance();
+    /**
+     * Arbitrary integer that we assign to the messages that we send to this
+     * thread's handler, indicating that these are requests to send an update
+     * notification intent.
+     */
+    private static final int UPDATE_BROADCAST_MSG = 1;
+
+    /**
+     * Any requests to send a PROVIDER_CHANGED intent will be collapsed over
+     * this window, to prevent spamming too many intents at once.
+     */
+    private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS =
+        (int) DateUtils.SECOND_IN_MILLIS;
+
+    private final Handler mBroadcastHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            Context context = CalendarProvider2.this.getContext();
+            if (msg.what == UPDATE_BROADCAST_MSG) {
+                // Broadcast a provider changed intent
+                doSendUpdateNotification();
+                // Stop the service that was protecting us
+                context.stopService(new Intent(context, EmptyService.class));
+            }
+        }
+    };
 
     /**
      * Listens for timezone changes and disk-no-longer-full events
@@ -1610,7 +1637,7 @@
                         }
                         createAttendeeEntry(id, status, owner);
                     }
-                    triggerAppWidgetUpdate(id);
+                    sendUpdateNotification(id);
                 }
                 break;
             case CALENDARS:
@@ -1624,7 +1651,7 @@
                     mDbHelper.scheduleSync(account, false /* two-way sync */, calendarUrl);
                 }
                 id = mDbHelper.calendarsInsert(values);
-                triggerAppWidgetUpdate(id);
+                sendUpdateNotification(id);
                 break;
             case ATTENDEES:
                 if (!values.containsKey(Attendees.EVENT_ID)) {
@@ -2285,7 +2312,7 @@
                         result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */);
                     }
                     scheduleNextAlarm(false /* do not remove alarms */);
-                    triggerAppWidgetUpdate(-1 /* changedEventId */);
+                    sendUpdateNotification();
                 } finally {
                     cursor.close();
                     cursor = null;
@@ -2466,7 +2493,7 @@
 
         if (!isBatch) {
             scheduleNextAlarm(false /* do not remove alarms */);
-            triggerAppWidgetUpdate(-1 /* changedEventId */);
+            sendUpdateNotification();
         }
         return result;
     }
@@ -2624,7 +2651,7 @@
 
                 if (result > 0) {
                     // update the widget
-                    triggerAppWidgetUpdate(-1 /* changedEventId */);
+                    sendUpdateNotification();
                 }
 
                 return result;
@@ -2707,7 +2734,7 @@
                         scheduleNextAlarm(false /* do not remove alarms */);
                     }
 
-                    triggerAppWidgetUpdate(id);
+                    sendUpdateNotification(id);
                 }
 
                 return result;
@@ -2886,19 +2913,6 @@
 //        triggerAppWidgetUpdate(-1);
 //    }
 
-    /**
-     * Update any existing widgets with the changed events.
-     *
-     * @param changedEventId Specific event known to be changed, otherwise -1.
-     *            If present, we use it to decide if an update is necessary.
-     */
-    private synchronized void triggerAppWidgetUpdate(long changedEventId) {
-        Context context = getContext();
-        if (context != null) {
-            mAppWidgetProvider.providerUpdated(context, changedEventId);
-        }
-    }
-
     /* Retrieve and cache the alarm manager */
     private AlarmManager getAlarmManager() {
         synchronized(mAlarmLock) {
@@ -3195,6 +3209,58 @@
                 CalendarAlerts.STATE + "=" + CalendarAlerts.SCHEDULED, null /* whereArgs */);
     }
 
+    /**
+     * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent.
+     * This also provides a timeout, so any calls to this method will be batched
+     * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class.
+     */
+    private void sendUpdateNotification() {
+        sendUpdateNotification(-1);
+    }
+
+    /**
+     * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent.
+     * This also provides a timeout, so any calls to this method will be batched
+     * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class.  The
+     * actual sending of the intent is done in
+     * {@link #doSendUpdateNotification()}.
+     *
+     * TODO add support for eventId
+     *
+     * @param the ID of the event that changed, or -1 for no specific event
+     */
+    private void sendUpdateNotification(long eventId) {
+        // Are there any pending broadcast requests?
+        if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) {
+            // Delete any pending requests, before requeuing a fresh one
+            mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG);
+        } else {
+            // No pending requests, start an empty service to prevent the
+            // process from getting killed off.  This will be stopped when the
+            // messaged is handled.
+            final Context context = getContext();
+            context.startService(new Intent(context, EmptyService.class));
+        }
+        Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG);
+        mBroadcastHandler.sendMessageDelayed(msg, UPDATE_BROADCAST_TIMEOUT_MILLIS);
+    }
+
+    /**
+     * This method should not ever be called directly, to prevent sending too
+     * many potentially expensive broadcasts.  Instead, call
+     * {@link #sendUpdateNotification()} instead.
+     *
+     * @see #sendUpdateNotification()
+     */
+    private void doSendUpdateNotification() {
+        Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED,
+                Uri.parse("content://" + Calendar.AUTHORITY + "/"));
+        Log.i(TAG, "Sending notification intent: " + intent);
+        // TODO attach extra information to the intent
+        // intent.putExtra(name, value)
+        getContext().sendBroadcast(intent, null);
+    }
+
     private static String sEventsTable = "Events";
     private static String sAttendeesTable = "Attendees";
     private static String sRemindersTable = "Reminders";
@@ -3462,7 +3528,7 @@
         }
 
         // make sure the widget reflects the account changes
-        triggerAppWidgetUpdate(-1 /* changedEventId */);
+        sendUpdateNotification();
     }
 
     /* package */ static boolean readBooleanQueryParameter(Uri uri, String name,
diff --git a/src/com/android/providers/calendar/EmptyService.java b/src/com/android/providers/calendar/EmptyService.java
new file mode 100644
index 0000000..70cf3d4
--- /dev/null
+++ b/src/com/android/providers/calendar/EmptyService.java
@@ -0,0 +1,32 @@
+package com.android.providers.calendar;
+/*
+ * Copyright (C) 2010 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.
+ */
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Background {@link Service} that is used to keep our process alive long enough
+ * for background threads to finish. Started and stopped directly by specific
+ * background tasks when needed.
+ */
+public class EmptyService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/src/com/android/providers/calendar/TimeChangeReceiver.java b/src/com/android/providers/calendar/TimeChangeReceiver.java
deleted file mode 100644
index 5fa4187..0000000
--- a/src/com/android/providers/calendar/TimeChangeReceiver.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2009 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.providers.calendar;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-public class TimeChangeReceiver extends BroadcastReceiver {
-    static final String TAG = "TimeChangeReceiver";
-    static final boolean LOGD = false;
-    
-    CalendarAppWidgetProvider mAppWidgetProvider = CalendarAppWidgetProvider.getInstance();
-    
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        // Pass time-changed notification through to any widgets
-        if (LOGD) Log.d(TAG, "Received time changed action=" + intent.getAction());
-        
-        // Consider ignoring this update request if only TIME_CHANGED
-        boolean considerIgnore = (Intent.ACTION_TIME_CHANGED.equals(intent.getAction()));
-        mAppWidgetProvider.timeUpdated(context, considerIgnore);
-    }
-}
diff --git a/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java b/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java
deleted file mode 100644
index d473cca..0000000
--- a/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
-**
-** Copyright 2010, 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.providers.calendar;
-
-import com.android.providers.calendar.CalendarAppWidgetService.CalendarAppWidgetModel;
-import com.android.providers.calendar.CalendarAppWidgetService.MarkedEvents;
-
-import android.database.MatrixCursor;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.format.DateUtils;
-import android.view.View;
-
-// adb shell am instrument -w -e class com.android.providers.calendar.CalendarAppWidgetServiceTest
-//   com.android.providers.calendar.tests/android.test.InstrumentationTestRunner
-
-public class CalendarAppWidgetServiceTest extends AndroidTestCase {
-    private static final String TAG = "CalendarAppWidgetService";
-
-    final long now = 1262340000000L; // Fri Jan 01 2010 02:00:00 GMT-0800 (PST)
-    final long ONE_MINUTE = 60000;
-    final long ONE_HOUR = 60 * ONE_MINUTE;
-    final long HALF_HOUR = ONE_HOUR / 2;
-    final long TWO_HOURS = ONE_HOUR * 2;
-
-    final String title = "Title";
-    final String location = "Location";
-
-//    TODO Disabled test since this CalendarAppWidgetModel is not used for the no event case
-//
-//    @SmallTest
-//    public void testGetAppWidgetModel_noEvents() throws Exception {
-//        // Input
-//        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-//
-//        // Expected Output
-//        CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
-//        expected.visibNoEvents = View.VISIBLE;
-//
-//        // Test
-//        long now = 1270000000000L;
-//        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-//        CalendarAppWidgetModel actual = CalendarAppWidgetService.getAppWidgetModel(
-//                getTestContext(), cursor, events, now);
-//
-//        assertEquals(expected.toString(), actual.toString());
-//    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_1Event() throws Exception {
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-
-        // Input
-        // allDay, begin, end, title, location, eventId
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title, location, 0));
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[0].visibWhen = View.VISIBLE;
-        expected.eventInfos[0].visibWhere = View.VISIBLE;
-        expected.eventInfos[0].visibTitle = View.VISIBLE;
-        expected.eventInfos[0].when = "3am";
-        expected.eventInfos[0].where = location;
-        expected.eventInfos[0].title = title;
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_2StaggeredEvents() throws Exception {
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-        long tomorrow = now + DateUtils.DAY_IN_MILLIS;
-        long sunday = tomorrow + DateUtils.DAY_IN_MILLIS;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[0].visibWhen = View.VISIBLE;
-        expected.eventInfos[0].visibWhere = View.VISIBLE;
-        expected.eventInfos[0].visibTitle = View.VISIBLE;
-        expected.eventInfos[0].when = "2am, Tomorrow";
-        expected.eventInfos[0].where = location + i;
-        expected.eventInfos[0].title = title + i;
-        ++i;
-        expected.eventInfos[1].visibWhen = View.VISIBLE;
-        expected.eventInfos[1].visibWhere = View.VISIBLE;
-        expected.eventInfos[1].visibTitle = View.VISIBLE;
-        expected.eventInfos[1].when = "2am, Sun";
-        expected.eventInfos[1].where = location + i;
-        expected.eventInfos[1].title = title + i;
-
-        // Input
-        // allDay, begin, end, title, location, eventId
-        i = 0;
-        cursor.addRow(getRow(0, tomorrow, tomorrow + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, sunday, sunday + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-
-        // Secondary test - Add two more afterwards
-        cursor.addRow(getRow(0, sunday + ONE_HOUR, sunday + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, sunday + ONE_HOUR, sunday + TWO_HOURS, title + i, location + i, 0));
-
-        // Test again
-        events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        actual = CalendarAppWidgetService.buildAppWidgetModel(getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_2SameStartTimeEvents() throws Exception {
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[0].visibWhen = View.VISIBLE;
-        expected.eventInfos[0].visibWhere = View.VISIBLE;
-        expected.eventInfos[0].visibTitle = View.VISIBLE;
-        expected.eventInfos[0].when = "3am";
-        expected.eventInfos[0].where = location + i;
-        expected.eventInfos[0].title = title + i;
-        ++i;
-        expected.eventInfos[1].visibWhen = View.VISIBLE;
-        expected.eventInfos[1].visibWhere = View.VISIBLE;
-        expected.eventInfos[1].visibTitle = View.VISIBLE;
-        expected.eventInfos[1].when = "3am";
-        expected.eventInfos[1].where = location + i;
-        expected.eventInfos[1].title = title + i;
-
-
-        // Input
-        // allDay, begin, end, title, location, eventId
-        i = 0;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-
-        // Secondary test - Add two more afterwards
-        cursor.addRow(getRow(0, now + TWO_HOURS, now + TWO_HOURS + 1, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + TWO_HOURS, now + TWO_HOURS + 1, title + i, location + i, 0));
-
-        // Test again
-        events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        actual = CalendarAppWidgetService.buildAppWidgetModel(getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_1EventThen2SameStartTimeEvents() throws Exception {
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(3);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        // Input
-        int i = 0;
-        // allDay, begin, end, title, location, eventId
-        cursor.addRow(getRow(0, now, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        i = 0;
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "2am (in progress)";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_3SameStartTimeEvents() throws Exception {
-        final long now = 1262340000000L; // Fri Jan 01 2010 01:00:00 GMT-0700 (PDT)
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(3);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-
-        // Input
-        // allDay, begin, end, title, location, eventId
-        i = 0;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-
-        // Secondary test - Add one more afterwards
-        cursor.addRow(getRow(0, now + TWO_HOURS, now + TWO_HOURS + 1, title + i, location + i, 0));
-
-        // Test again, nothing should have changed, same expected result
-        events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        actual = CalendarAppWidgetService.buildAppWidgetModel(getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_2InProgress2After() throws Exception {
-        final long now = 1262340000000L + HALF_HOUR; // Fri Jan 01 2010 01:30:00 GMT-0700 (PDT)
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(4);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "2am (in progress)";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "2am (in progress)";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "4:30am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "4:30am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-
-        // Input
-        // allDay, begin, end, title, location, eventId
-        i = 0;
-        cursor.addRow(getRow(0, now - HALF_HOUR, now + HALF_HOUR, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now - HALF_HOUR, now + HALF_HOUR, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + TWO_HOURS, now + 3 * ONE_HOUR, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + TWO_HOURS, now + 4 * ONE_HOUR, title + i, location + i, 0));
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_AllDayEventToday() throws Exception {
-        final long now = 1262340000000L; // Fri Jan 01 2010 01:00:00 GMT-0700 (PDT)
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(2);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "Today";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i = 0;
-        cursor.addRow(getRow(1, 1262304000000L, 1262390400000L, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_AllDayEventTomorrow() throws Exception {
-        final long now = 1262340000000L; // Fri Jan 01 2010 01:00:00 GMT-0700 (PDT)
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(2);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "Tomorrow";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i = 0;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(1, 1262390400000L, 1262476800000L, title + i, location + i, 0));
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    @SmallTest
-    public void testGetAppWidgetModel_AllDayEventLater() throws Exception {
-        final long now = 1262340000000L; // Fri Jan 01 2010 01:00:00 GMT-0700 (PDT)
-        CalendarAppWidgetModel expected = new CalendarAppWidgetModel(2);
-        MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
-
-        int i = 0;
-
-        // Expected Output
-        expected.dayOfMonth = "1";
-        expected.dayOfWeek = "FRI";
-        expected.visibNoEvents = View.GONE;
-
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "3am";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i++;
-        expected.eventInfos[i].visibWhen = View.VISIBLE;
-        expected.eventInfos[i].visibWhere = View.VISIBLE;
-        expected.eventInfos[i].visibTitle = View.VISIBLE;
-        expected.eventInfos[i].when = "Sun";
-        expected.eventInfos[i].where = location + i;
-        expected.eventInfos[i].title = title + i;
-
-        i = 0;
-        cursor.addRow(getRow(0, now + ONE_HOUR, now + TWO_HOURS, title + i, location + i, 0));
-        ++i;
-        cursor.addRow(getRow(1, 1262476800000L, 1262563200000L, title + i, location + i, 0));
-
-        // Test
-        MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
-        CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
-                getContext(), cursor, events, now);
-
-        assertEquals(expected.toString(), actual.toString());
-    }
-
-    private Object[] getRow(int allDay, long begin, long end, String title, String location,
-            long eventId) {
-        Object[] row = new Object[CalendarAppWidgetService.EVENT_PROJECTION.length];
-        row[CalendarAppWidgetService.INDEX_ALL_DAY] = new Integer(allDay);
-        row[CalendarAppWidgetService.INDEX_BEGIN] = new Long(begin);
-        row[CalendarAppWidgetService.INDEX_END] = new Long(end);
-        row[CalendarAppWidgetService.INDEX_TITLE] = new String(title);
-        row[CalendarAppWidgetService.INDEX_EVENT_LOCATION] = new String(location);
-        row[CalendarAppWidgetService.INDEX_EVENT_ID] = new Long(eventId);
-        return row;
-    }
-}