Fix bug #2589483 and implemented animated pagination in the widget
- Fixed a number of refresh issues in the widget
- Implemented animated pagination for overflowing events
- Updated corresponding unit tests for new behavior
Change-Id: I8883611be476d61d655c4427b8b9ba86092b0cbb
diff --git a/res/anim/slide_in.xml b/res/anim/slide_in.xml
new file mode 100644
index 0000000..72fc56b
--- /dev/null
+++ b/res/anim/slide_in.xml
@@ -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>
\ No newline at end of file
diff --git a/res/anim/slide_out.xml b/res/anim/slide_out.xml
new file mode 100644
index 0000000..52681a7
--- /dev/null
+++ b/res/anim/slide_out.xml
@@ -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>
\ No newline at end of file
diff --git a/res/layout/agenda_appwidget.xml b/res/layout/agenda_appwidget.xml
index 8f4c922..826eac9 100644
--- a/res/layout/agenda_appwidget.xml
+++ b/res/layout/agenda_appwidget.xml
@@ -59,6 +59,25 @@
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"
@@ -72,43 +91,6 @@
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="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" />
-
- <!-- Conflict banner -->
- <TextView
- android:id="@+id/conflict_portrait"
- android:textStyle="bold"
- style="@style/TextAppearance.WidgetConflict" />
-
- <TextView
- android:id="@+id/conflict_landscape"
- android:visibility="gone"
- android:layout_width="0dp"
- android:layout_height="0dp" />
</LinearLayout>
diff --git a/res/layout/page_appwidget.xml b/res/layout/page_appwidget.xml
new file mode 100644
index 0000000..eabd43a
--- /dev/null
+++ b/res/layout/page_appwidget.xml
@@ -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/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/colors.xml b/res/values/colors.xml
index ea98617..ac4c3f1 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -23,5 +23,5 @@
<color name="appwidget_conflict">#ff000000</color>
<color name="appwidget_no_events">#bb000000</color>
-</resources>
-
+ <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
new file mode 100644
index 0000000..ff94dd5
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,19 @@
+<?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 9435360..4765299 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -39,9 +39,15 @@
<!-- 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 events starts on the next day -->
+ <!-- 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
index 9799b12..c2a1b3a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -72,4 +72,18 @@
<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/src/com/android/providers/calendar/CalendarAppWidgetService.java b/src/com/android/providers/calendar/CalendarAppWidgetService.java
index f5e426b..29644f6 100644
--- a/src/com/android/providers/calendar/CalendarAppWidgetService.java
+++ b/src/com/android/providers/calendar/CalendarAppWidgetService.java
@@ -18,6 +18,8 @@
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;
@@ -40,7 +42,9 @@
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;
@@ -49,6 +53,10 @@
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";
@@ -92,19 +100,17 @@
EventInfo[] eventInfos;
- int visibConflictPortrait; // Visibility value for conflictPortrait textview
- String conflictPortrait;
- int visibConflictLandscape; // Visibility value for conflictLandscape textview
- String conflictLandscape;
-
public CalendarAppWidgetModel() {
- eventInfos = new EventInfo[2];
- eventInfos[0] = new EventInfo();
- eventInfos[1] = new EventInfo();
+ 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;
- visibConflictPortrait = View.GONE;
- visibConflictLandscape = View.GONE;
}
class EventInfo {
@@ -209,14 +215,6 @@
StringBuilder builder = new StringBuilder();
builder.append("\nCalendarAppWidgetModel [eventInfos=");
builder.append(Arrays.toString(eventInfos));
- builder.append(", visibConflictLandscape=");
- builder.append(visibConflictLandscape);
- builder.append(", conflictLandscape=");
- builder.append(conflictLandscape);
- builder.append(", visibConflictPortrait=");
- builder.append(visibConflictPortrait);
- builder.append(", conflictPortrait=");
- builder.append(conflictPortrait);
builder.append(", visibNoEvents=");
builder.append(visibNoEvents);
builder.append(", dayOfMonth=");
@@ -307,7 +305,7 @@
shouldUpdate = events.watchFound;
}
- if (events.primaryCount == 0) {
+ if (events.markedIds.isEmpty()) {
views = getAppWidgetNoEvents(context);
} else if (shouldUpdate) {
views = getAppWidgetUpdate(context, cursor, events);
@@ -402,8 +400,8 @@
*/
private long calculateUpdateTime(Cursor cursor, MarkedEvents events) {
long result = -1;
- if (events.primaryRow != -1) {
- cursor.moveToPosition(events.primaryRow);
+ 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;
@@ -437,15 +435,14 @@
/**
* Calculate flipping point for the given event; when we should hide this
- * event and show the next one. This is 15 minutes into the event or half
- * way into the event whichever is earlier.
+ * 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) {
- long duration = end - start;
- return start + Math.min(DateUtils.MINUTE_IN_MILLIS * 15, duration / 2);
+ return end;
}
/**
@@ -457,15 +454,8 @@
*/
private void setNoEventsVisible(RemoteViews views, boolean noEvents) {
views.setViewVisibility(R.id.no_events, noEvents ? View.VISIBLE : View.GONE);
-
- views.setViewVisibility(R.id.when1, View.GONE);
- views.setViewVisibility(R.id.where1, View.GONE);
- views.setViewVisibility(R.id.title1, View.GONE);
- views.setViewVisibility(R.id.when2, View.GONE);
- views.setViewVisibility(R.id.where2, View.GONE);
- views.setViewVisibility(R.id.title2, View.GONE);
- views.setViewVisibility(R.id.conflict_landscape, View.GONE);
- views.setViewVisibility(R.id.conflict_portrait, View.GONE);
+ views.setViewVisibility(R.id.page_flipper, View.GONE);
+ views.setViewVisibility(R.id.single_page, View.GONE);
}
/**
@@ -482,15 +472,10 @@
long currentTime = System.currentTimeMillis();
CalendarAppWidgetModel model = buildAppWidgetModel(context, cursor, events, currentTime);
- applyModelToView(model, views);
+ applyModelToView(context, model, views);
// Clicking on the widget launches Calendar
- long startTime;
- if (events.primaryAllDay) {
- startTime = currentTime;
- } else {
- startTime = events.primaryTime;
- }
+ long startTime = Math.max(currentTime, events.firstTime);
PendingIntent pendingIntent = getLaunchPendingIntent(context, startTime);
views.setOnClickPendingIntent(R.id.agenda_appwidget, pendingIntent);
@@ -498,29 +483,62 @@
return views;
}
- private void applyModelToView(CalendarAppWidgetModel model, RemoteViews 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);
- if (model.visibNoEvents == View.GONE) {
- updateTextView(views, R.id.when1, model.eventInfos[0].visibWhen,
- model.eventInfos[0].when);
- updateTextView(views, R.id.where1, model.eventInfos[0].visibWhere,
- model.eventInfos[0].where);
- updateTextView(views, R.id.title1, model.eventInfos[0].visibTitle,
- model.eventInfos[0].title);
- updateTextView(views, R.id.when2, model.eventInfos[1].visibWhen,
- model.eventInfos[1].when);
- updateTextView(views, R.id.where2, model.eventInfos[1].visibWhere,
- model.eventInfos[1].where);
- updateTextView(views, R.id.title2, model.eventInfos[1].visibTitle,
- model.eventInfos[1].title);
- updateTextView(views, R.id.conflict_portrait, model.visibConflictPortrait,
- model.conflictPortrait);
- updateTextView(views, R.id.conflict_landscape, model.visibConflictLandscape,
- model.conflictLandscape);
+ // 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) {
@@ -532,7 +550,8 @@
static CalendarAppWidgetModel buildAppWidgetModel(Context context, Cursor cursor,
MarkedEvents events, long currentTime) {
- CalendarAppWidgetModel model = new CalendarAppWidgetModel();
+ int eventCount = events.markedIds.size();
+ CalendarAppWidgetModel model = new CalendarAppWidgetModel(eventCount);
Time time = new Time();
time.set(currentTime);
time.monthDay++;
@@ -550,99 +569,81 @@
model.dayOfWeek = dayOfWeek;
model.dayOfMonth = Integer.toString(time.monthDay);
- // Fill primary event details
- cursor.moveToPosition(events.primaryRow);
- boolean allDay = cursor.getInt(INDEX_ALL_DAY) != 0;
- populateEvent(context, cursor, model, time, 0, true, startOfNextDay);
-
- // Conflicts
- int conflictCountLandscape = events.primaryCount - 1;
- if (conflictCountLandscape > 0) {
- model.conflictLandscape = context.getResources().getQuantityString(
- R.plurals.gadget_more_events, conflictCountLandscape, conflictCountLandscape);
- model.visibConflictLandscape = View.VISIBLE;
- } else {
- model.visibConflictLandscape = View.GONE;
+ int i = 0;
+ for (Integer id : events.markedIds) {
+ populateEvent(context, cursor, id, model, time, i, true, startOfNextDay, currentTime);
+ i++;
}
- int conflictCountPortrait = 0;
- if (events.primaryCount > 2) {
- conflictCountPortrait = events.primaryCount - 1;
- } else if (events.primaryCount == 1 && events.secondaryCount > 1
- && cursor.moveToPosition(events.secondaryRow)) {
- // Show conflict string for the secondary time slot if there is only one event
- // in the primary time slot.
- populateEvent(context, cursor, model, time, 1, false, startOfNextDay);
- conflictCountPortrait = events.secondaryCount;
- }
-
- if (conflictCountPortrait != 0) {
- model.conflictPortrait = context.getResources().getQuantityString(
- R.plurals.gadget_more_events, conflictCountPortrait, conflictCountPortrait);
- model.visibConflictPortrait = View.VISIBLE;
- } else {
- model.visibConflictPortrait = View.GONE;
-
- // Fill secondary event details
- int secondaryRow = -1;
- if (events.primaryCount == 2) {
- secondaryRow = events.primaryConflictRow;
- } else if (events.primaryCount == 1) {
- secondaryRow = events.secondaryRow;
- }
-
- if (secondaryRow != -1 && cursor.moveToPosition(secondaryRow)) {
- populateEvent(context, cursor, model, time, 1, true, startOfNextDay);
- }
- }
return model;
}
- static private void populateEvent(Context context, Cursor cursor, CalendarAppWidgetModel model,
- Time recycle, int eventIndex, boolean showTitleLocation, long startOfNextDay) {
+ /**
+ * 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
+ boolean eventIsTomorrow = !eventIsToday && !eventIsInProgress
&& (start < (startOfNextDay + DateUtils.DAY_IN_MILLIS));
- String whenString = "";
-
- if (!(allDay && eventIsTomorrow)) {
+ // 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;
- if (!eventIsTomorrow) {
- flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
- }
} else {
flags |= DateUtils.FORMAT_SHOW_TIME;
-
if (DateFormat.is24HourFormat(context)) {
flags |= DateUtils.FORMAT_24HOUR;
}
-
- // Show day or week if different from today
- if (!eventIsTomorrow && !eventIsToday) {
- flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
- }
+ }
+ // 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 += ")";
+ }
}
- if (eventIsTomorrow) {
- if (!allDay) {
- whenString += (", ");
- }
- whenString += context.getString(R.string.tomorrow);
- }
model.eventInfos[eventIndex].when = whenString;
model.eventInfos[eventIndex].visibWhen = View.VISIBLE;
@@ -713,14 +714,38 @@
}
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;
- int primaryRow = -1;
- int primaryConflictRow = -1;
- int primaryCount = 0; // Number of events with same start time as the primary evt.
- boolean primaryAllDay = false;
- long secondaryTime = -1;
- int secondaryRow = -1;
- int secondaryCount = 0; // Number of events with same start time as the secondary evt.
+
+ /**
+ * 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;
}
@@ -746,6 +771,7 @@
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) {
@@ -759,6 +785,8 @@
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));
@@ -771,29 +799,52 @@
events.watchFound = true;
}
- if (events.primaryRow == -1) {
- // Found first event
- events.primaryRow = row;
- events.primaryTime = start;
- events.primaryAllDay = allDay;
- events.primaryCount = 1;
- } else if (events.primaryTime == start) {
- // Found conflicting primary event
- if (events.primaryConflictRow == -1) {
- events.primaryConflictRow = row;
+ /* 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;
}
- events.primaryCount += 1;
- } else if (events.secondaryRow == -1) {
- // Found second event
- events.secondaryRow = row;
- events.secondaryTime = start;
- events.secondaryCount = 1;
- } else if (events.secondaryTime == start) {
- // Found conflicting secondary event
- events.secondaryCount += 1;
} else {
- // Nothing interesting about this event, so bail out
- break;
+ 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;
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index d65d437..2d5e0cc 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -1607,6 +1607,7 @@
mDbHelper.scheduleSync(account, false /* two-way sync */, calendarUrl);
}
id = mDbHelper.calendarsInsert(values);
+ triggerAppWidgetUpdate(id);
break;
case ATTENDEES:
if (!values.containsKey(Attendees.EVENT_ID)) {
@@ -2604,6 +2605,11 @@
int result = mDb.update("Calendars", values, "_id=?",
new String[] {String.valueOf(id)});
+ if (result > 0) {
+ // update the widget
+ triggerAppWidgetUpdate(-1 /* changedEventId */);
+ }
+
return result;
}
case EVENTS:
@@ -2682,9 +2688,11 @@
Log.d(TAG, "updateInternal() changing event");
}
scheduleNextAlarm(false /* do not remove alarms */);
- triggerAppWidgetUpdate(id);
}
+
+ triggerAppWidgetUpdate(id);
}
+
return result;
}
case ATTENDEES_ID: {
@@ -3431,6 +3439,9 @@
} finally {
mDb.endTransaction();
}
+
+ // make sure the widget reflects the account changes
+ triggerAppWidgetUpdate(-1 /* changedEventId */);
}
/* package */ static boolean readBooleanQueryParameter(Uri uri, String name,
diff --git a/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java b/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java
index bef87a5..d473cca 100644
--- a/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java
+++ b/tests/src/com/android/providers/calendar/CalendarAppWidgetServiceTest.java
@@ -33,8 +33,11 @@
private static final String TAG = "CalendarAppWidgetService";
final long now = 1262340000000L; // Fri Jan 01 2010 02:00:00 GMT-0800 (PST)
- final long ONE_HOUR = 3600000;
- final long TWO_HOURS = 7200000;
+ 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";
@@ -60,8 +63,9 @@
@SmallTest
public void testGetAppWidgetModel_1Event() throws Exception {
- MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
+ MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
+
// Input
// allDay, begin, end, title, location, eventId
@@ -81,15 +85,15 @@
// Test
MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
- getTestContext(), cursor, events, now);
+ getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
@SmallTest
public void testGetAppWidgetModel_2StaggeredEvents() throws Exception {
- MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
+ MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
int i = 0;
long tomorrow = now + DateUtils.DAY_IN_MILLIS;
@@ -124,7 +128,7 @@
// Test
MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
- getTestContext(), cursor, events, now);
+ getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -135,15 +139,15 @@
// Test again
events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- actual = CalendarAppWidgetService.buildAppWidgetModel(getTestContext(), cursor, events, now);
+ actual = CalendarAppWidgetService.buildAppWidgetModel(getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
@SmallTest
public void testGetAppWidgetModel_2SameStartTimeEvents() throws Exception {
- MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
+ MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
int i = 0;
// Expected Output
@@ -164,9 +168,6 @@
expected.eventInfos[1].where = location + i;
expected.eventInfos[1].title = title + i;
- expected.conflictPortrait = null;
- expected.conflictLandscape = "1 more event";
- expected.visibConflictLandscape = View.VISIBLE;
// Input
// allDay, begin, end, title, location, eventId
@@ -179,7 +180,7 @@
// Test
MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
- getTestContext(), cursor, events, now);
+ getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -190,15 +191,15 @@
// Test again
events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- actual = CalendarAppWidgetService.buildAppWidgetModel(getTestContext(), cursor, events, 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);
- CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
// Input
int i = 0;
@@ -214,24 +215,31 @@
expected.dayOfWeek = "FRI";
i = 0;
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";
- expected.eventInfos[0].where = location + i;
- expected.eventInfos[0].title = title + i;
-
- expected.eventInfos[1].visibWhen = View.VISIBLE;
- expected.eventInfos[1].when = "3am";
-
- expected.visibConflictPortrait = View.VISIBLE;
- expected.conflictPortrait = "2 more events";
- expected.conflictLandscape = null;
+ 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(
- getTestContext(), cursor, events, now);
+ getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
@@ -239,8 +247,8 @@
@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);
- CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
int i = 0;
@@ -248,17 +256,29 @@
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;
+ 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;
- expected.visibConflictPortrait = View.VISIBLE;
- expected.visibConflictLandscape = View.VISIBLE;
- expected.conflictPortrait = "2 more events";
- expected.conflictLandscape = "2 more events";
+ 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
@@ -273,18 +293,201 @@
// Test
MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
- getTestContext(), cursor, events, now);
+ 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;
+ // Secondary test - Add one more afterwards
cursor.addRow(getRow(0, now + TWO_HOURS, now + TWO_HOURS + 1, title + i, location + i, 0));
- // Test again
+ // Test again, nothing should have changed, same expected result
events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- actual = CalendarAppWidgetService.buildAppWidgetModel(getTestContext(), cursor, events, 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());
}