Refactored so that the widget is now with the app, not the provider
- CalendarAppWidgetReceiver now functions as the broadcast receiver for: * android.intent.action.TIMEZONE_CHANGED * android.intent.action.TIME_SET * android.intent.action.PROVIDER_CHANGED (content://com.android.calendar) - CalendarAppWidgetService has been refactored to use IntentService for serializing widget updates - AppWidgetShared has been preserved for managing wake locks - CalendarAppWidgetModel was pulled out into its own class Change-Id: If7641aba278acc8ad00f14acd289ddbcbb1d5bcf
@ -9,7 +9,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
# library.
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES += android-common
|
||||
LOCAL_STATIC_JAVA_LIBRARIES += android-common guava
|
||||
|
||||
LOCAL_PACKAGE_NAME := Calendar
|
||||
|
||||
|
@ -124,6 +124,29 @@
|
||||
|
||||
<service android:name="DismissAllAlarmsService" />
|
||||
|
||||
<!-- Declarations for the widget -->
|
||||
<receiver android:name="CalendarAppWidgetReceiver" 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>
|
||||
<intent-filter>
|
||||
<data android:scheme="content" android:host="com.android.calendar"/>
|
||||
<action android:name="android.intent.action.PROVIDER_CHANGED"/>
|
||||
</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>
|
||||
|
||||
<service android:name="CalendarAppWidgetService" />
|
||||
|
||||
<activity android:name="CalendarTests" android:label="Calendar Tests">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
27
res/anim/slide_in_fade.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?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>
|
27
res/anim/slide_out_fade.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?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>
|
BIN
res/drawable-hdpi/appwidget_bg.9.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
res/drawable-hdpi/appwidget_bg_focus.9.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
res/drawable-hdpi/appwidget_bg_press.9.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
res/drawable-hdpi/appwidget_calendar_bgtop_blue.9.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-hdpi/white_list_rule_cal.9.png
Executable file
After Width: | Height: | Size: 147 B |
BIN
res/drawable-mdpi/appwidget_bg.9.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-mdpi/appwidget_bg_focus.9.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
res/drawable-mdpi/appwidget_bg_press.9.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
res/drawable-mdpi/appwidget_calendar_bgtop_blue.9.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-mdpi/white_list_rule_cal.9.png
Normal file
After Width: | Height: | Size: 172 B |
22
res/drawable/appwidget_background.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?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>
|
117
res/layout-land/appwidget.xml
Normal file
@ -0,0 +1,117 @@
|
||||
<?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/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>
|
96
res/layout/appwidget.xml
Normal file
@ -0,0 +1,96 @@
|
||||
<?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/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_fade"
|
||||
android:outAnimation="@anim/slide_out_fade"
|
||||
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>
|
62
res/layout/appwidget_page.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?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/appwidget_page"
|
||||
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>
|
20
res/values-land/dimens.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?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>
|
@ -65,5 +65,13 @@
|
||||
<color name="agenda_day_bar_color">#c8c8c8</color>
|
||||
|
||||
<drawable name="agenda_item_declined">#88ffffff</drawable>
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
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.
|
||||
@ -16,4 +16,6 @@
|
||||
|
||||
<resources>
|
||||
<integer name="number_of_hours">10</integer>
|
||||
<integer name="flip_interval">5000</integer>
|
||||
<integer name="slide_transition_duration">600</integer>
|
||||
</resources>
|
||||
|
@ -403,4 +403,30 @@
|
||||
<!-- Dialog title for the Vibrate dialog -->
|
||||
<string name="prefDialogTitle_vibrateWhen">Vibrate</string>
|
||||
|
||||
<!-- Widget -->
|
||||
<skip />
|
||||
<!-- Title for calendar gadget when displayed in list of all other gadgets -->
|
||||
<string name="gadget_title">Calendar</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>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -105,4 +105,72 @@
|
||||
<item name="android:gravity">center_vertical|left</item>
|
||||
</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>
|
||||
|
23
res/xml/appwidget_info.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?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/appwidget"
|
||||
>
|
||||
</appwidget-provider>
|
158
src/com/android/calendar/CalendarAppWidgetModel.java
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.calendar;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
147
src/com/android/calendar/CalendarAppWidgetProvider.java
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.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.text.format.DateUtils;
|
||||
|
||||
/**
|
||||
* 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.calendar.APPWIDGET_UPDATE";
|
||||
|
||||
// TODO Move these to Calendar.java
|
||||
static final String EXTRA_WIDGET_IDS = "com.android.calendar.EXTRA_WIDGET_IDS";
|
||||
static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS";
|
||||
|
||||
/**
|
||||
* {@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 */);
|
||||
} else {
|
||||
super.onReceive(context, intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enable updates for timezone, date, and provider changes
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.setComponentEnabledSetting(
|
||||
new ComponentName(context, CalendarAppWidgetReceiver.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, date, and provider changes
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.setComponentEnabledSetting(
|
||||
new ComponentName(context, CalendarAppWidgetReceiver.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 */);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build {@link ComponentName} describing this specific
|
||||
* {@link AppWidgetProvider}
|
||||
*/
|
||||
static ComponentName getComponentName(Context context) {
|
||||
return new ComponentName(context, CalendarAppWidgetProvider.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param context Context to use when 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.
|
||||
*/
|
||||
private void performUpdate(Context context, int[] appWidgetIds,
|
||||
long[] changedEventIds) {
|
||||
// Launch over to service so it can perform update
|
||||
final Intent updateIntent = new Intent(context, CalendarAppWidgetService.class);
|
||||
|
||||
if (appWidgetIds != null) {
|
||||
updateIntent.putExtra(EXTRA_WIDGET_IDS, appWidgetIds);
|
||||
}
|
||||
if (changedEventIds != null) {
|
||||
updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds);
|
||||
}
|
||||
|
||||
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 */);
|
||||
}
|
||||
}
|
48
src/com/android/calendar/CalendarAppWidgetReceiver.java
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.calendar;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public class CalendarAppWidgetReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "CalendarAppWidgetReceiver";
|
||||
private static final boolean LOGD = false;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// Launch over to service so it can perform update
|
||||
final Intent updateIntent = new Intent(context, CalendarAppWidgetService.class);
|
||||
|
||||
// Copy over the relevant extra fields if they exist
|
||||
if (intent.hasExtra(CalendarAppWidgetProvider.EXTRA_EVENT_IDS)) {
|
||||
int[] data = intent.getIntArrayExtra(CalendarAppWidgetProvider.EXTRA_EVENT_IDS);
|
||||
updateIntent.putExtra(CalendarAppWidgetProvider.EXTRA_EVENT_IDS, data);
|
||||
}
|
||||
|
||||
if (intent.hasExtra(CalendarAppWidgetProvider.EXTRA_WIDGET_IDS)) {
|
||||
int[] data = intent.getIntArrayExtra(CalendarAppWidgetProvider.EXTRA_WIDGET_IDS);
|
||||
updateIntent.putExtra(CalendarAppWidgetProvider.EXTRA_WIDGET_IDS, data);
|
||||
}
|
||||
|
||||
if (LOGD) Log.d(TAG, "Something changed, updating widget");
|
||||
context.startService(updateIntent);
|
||||
}
|
||||
}
|
710
src/com/android/calendar/CalendarAppWidgetService.java
Normal file
@ -0,0 +1,710 @@
|
||||
/*
|
||||
* 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.calendar;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import com.android.calendar.CalendarAppWidgetModel.EventInfo;
|
||||
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IntentService;
|
||||
import android.app.PendingIntent;
|
||||
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.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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
public class CalendarAppWidgetService extends IntentService {
|
||||
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";
|
||||
|
||||
public CalendarAppWidgetService() {
|
||||
super(TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
// These will be null if the extra data doesn't exist
|
||||
int[] widgetIds = intent.getIntArrayExtra(CalendarAppWidgetProvider.EXTRA_WIDGET_IDS);
|
||||
long[] eventIds = null;
|
||||
HashSet<Long> eventIdsSet = null;
|
||||
if (intent.hasExtra(CalendarAppWidgetProvider.EXTRA_EVENT_IDS)) {
|
||||
eventIds = intent.getExtras().getLongArray(CalendarAppWidgetProvider.EXTRA_EVENT_IDS);
|
||||
eventIdsSet = new HashSet<Long>(eventIds.length);
|
||||
for (int i = 0; i < eventIds.length; i++) {
|
||||
eventIdsSet.add(eventIds[i]);
|
||||
}
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
performUpdate(this, widgetIds, eventIdsSet, 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 != null && 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.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.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.appwidget_page);
|
||||
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.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.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);
|
||||
}
|
||||
}
|
505
tests/src/com/android/calendar/CalendarAppWidgetServiceTest.java
Normal file
@ -0,0 +1,505 @@
|
||||
/*
|
||||
**
|
||||
** 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.calendar;
|
||||
|
||||
import com.android.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;
|
||||
}
|
||||
}
|