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;
- }
-}