aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/drawable-mdpi/appwidget_btn_round_plus.pngbin0 -> 644 bytes
-rw-r--r--res/layout-land/appwidget_disabled.xml117
-rw-r--r--res/layout/appwidget.xml37
-rw-r--r--res/layout/appwidget_loading.xml38
-rw-r--r--res/layout/appwidget_no_events.xml38
-rw-r--r--res/layout/appwidget_row.xml (renamed from res/layout/appwidget_page.xml)35
-rw-r--r--src/com/android/calendar/widget/CalendarAppWidgetModel.java9
-rw-r--r--src/com/android/calendar/widget/CalendarAppWidgetProvider.java88
-rw-r--r--src/com/android/calendar/widget/CalendarAppWidgetReceiver.java18
-rw-r--r--src/com/android/calendar/widget/CalendarAppWidgetService.java1070
-rw-r--r--tests/src/com/android/calendar/widget/CalendarAppWidgetServiceTest.java61
12 files changed, 713 insertions, 800 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d32e4097..9e69cdf2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -155,7 +155,7 @@
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
</receiver>
- <service android:name=".widget.CalendarAppWidgetService" />
+ <service android:name=".widget.CalendarAppWidgetService" android:exported="true"/>
<activity android:name="CalendarTests" android:label="Calendar Tests">
<intent-filter>
diff --git a/res/drawable-mdpi/appwidget_btn_round_plus.png b/res/drawable-mdpi/appwidget_btn_round_plus.png
new file mode 100644
index 00000000..27926024
--- /dev/null
+++ b/res/drawable-mdpi/appwidget_btn_round_plus.png
Binary files differ
diff --git a/res/layout-land/appwidget_disabled.xml b/res/layout-land/appwidget_disabled.xml
deleted file mode 100644
index 418ae11c..00000000
--- a/res/layout-land/appwidget_disabled.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/appwidget"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@drawable/appwidget_background"
- android:focusable="true"
- android:clickable="true">
-
- <!-- Header -->
- <LinearLayout
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="40dip"
- android:orientation="horizontal"
- android:background="@drawable/appwidget_calendar_bgtop_blue">
-
- <TextView
- android:id="@+id/day_of_week"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_marginLeft="7dip"
- android:layout_marginRight="7dip"
- android:layout_marginBottom="5dip"
- android:textColor="@color/appwidget_date"
- android:textSize="18sp"
- android:gravity="left|bottom"
- android:singleLine="true" />
-
- <TextView
- android:id="@+id/day_of_month"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginLeft="7dip"
- android:layout_marginRight="7dip"
- android:layout_marginBottom="5dip"
- android:gravity="right|bottom"
- android:textColor="@color/appwidget_date"
- android:textSize="20sp"
- android:textStyle="bold"
- android:singleLine="true" />
- </LinearLayout>
-
- <!-- No Event -->
- <TextView
- android:id="@+id/no_events"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginBottom="10dip"
- android:padding="7dip"
- android:gravity="center"
- android:textSize="14sp"
- android:textStyle="bold"
- android:textColor="@color/appwidget_no_events"
- android:text="@string/gadget_no_events" />
-
- <!-- Event #1 -->
- <TextView
- android:id="@+id/when1"
- style="@style/TextAppearance.WidgetWhen" />
-
- <TextView
- android:id="@+id/where1"
- style="@style/TextAppearance.WidgetWhere" />
-
- <TextView
- android:id="@+id/title1"
- android:layout_marginBottom="-3dip"
- style="@style/TextAppearance.WidgetTitle" />
-
- <!-- Conflict banner -->
- <TextView
- android:id="@+id/conflict_landscape"
- style="@style/TextAppearance.WidgetConflict" />
-
- <!-- These fields are not visible in landscape mode but required to avoid exceptions -->
- <TextView
- android:id="@+id/when2"
- android:visibility="gone"
- android:layout_width="0dp"
- android:layout_height="0dp" />
- <TextView
- android:id="@+id/where2"
- android:visibility="gone"
- android:layout_width="0dp"
- android:layout_height="0dp" />
- <TextView
- android:id="@+id/title2"
- android:visibility="gone"
- android:layout_width="0dp"
- android:layout_height="0dp" />
-
- <TextView
- android:id="@+id/conflict_portrait"
- android:visibility="gone"
- android:layout_width="0dp"
- android:layout_height="0dp" />
-
-</LinearLayout>
diff --git a/res/layout/appwidget.xml b/res/layout/appwidget.xml
index 9432dfaf..7696ae1a 100644
--- a/res/layout/appwidget.xml
+++ b/res/layout/appwidget.xml
@@ -73,38 +73,13 @@
android:background="#0000" />
</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"
+ <!-- Events container -->
+ <ListView
+ android:id="@+id/events_list"
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" />
-
-
+ android:cacheColorHint="#00000000"
+ android:dividerHeight="3dip"
+ android:divider="#0000" />
</LinearLayout>
diff --git a/res/layout/appwidget_loading.xml b/res/layout/appwidget_loading.xml
new file mode 100644
index 00000000..ead5a540
--- /dev/null
+++ b/res/layout/appwidget_loading.xml
@@ -0,0 +1,38 @@
+<?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_loading"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:addStatesFromChildren="true">
+
+ <!-- Loading -->
+ <TextView
+ android:id="@+id/loading"
+ 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/loading" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/appwidget_no_events.xml b/res/layout/appwidget_no_events.xml
new file mode 100644
index 00000000..e7303a64
--- /dev/null
+++ b/res/layout/appwidget_no_events.xml
@@ -0,0 +1,38 @@
+<?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_no_events"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:addStatesFromChildren="true">
+
+ <!-- No events -->
+ <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> \ No newline at end of file
diff --git a/res/layout/appwidget_page.xml b/res/layout/appwidget_row.xml
index 67dbd5b7..95af38ba 100644
--- a/res/layout/appwidget_page.xml
+++ b/res/layout/appwidget_row.xml
@@ -16,46 +16,23 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/appwidget_page"
+ android:id="@+id/appwidget_row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:addStatesFromChildren="true">
+ android:addStatesFromChildren="true"
+ android:focusableInTouchMode="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"
+ android:id="@+id/title"
style="@style/TextAppearance.WidgetTitle" />
- <!-- Event #2 -->
<TextView
- android:id="@+id/when2"
+ android:id="@+id/when"
style="@style/TextAppearance.WidgetWhen" />
<TextView
- android:id="@+id/where2"
+ android:id="@+id/where"
style="@style/TextAppearance.WidgetWhere" />
- <TextView
- android:id="@+id/title2"
- 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/src/com/android/calendar/widget/CalendarAppWidgetModel.java b/src/com/android/calendar/widget/CalendarAppWidgetModel.java
index 5b6bedde..a8b6dabc 100644
--- a/src/com/android/calendar/widget/CalendarAppWidgetModel.java
+++ b/src/com/android/calendar/widget/CalendarAppWidgetModel.java
@@ -32,13 +32,12 @@ class CalendarAppWidgetModel {
EventInfo[] eventInfos;
public CalendarAppWidgetModel() {
- this(2);
+ this(1);
}
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 = new EventInfo[size];
+ for (int i = 0; i < size; i++) {
eventInfos[i] = new EventInfo();
}
visibNoEvents = View.GONE;
@@ -52,6 +51,8 @@ class CalendarAppWidgetModel {
int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE)
String title;
+ long start;
+
public EventInfo() {
visibWhen = View.GONE;
visibWhere = View.GONE;
diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java b/src/com/android/calendar/widget/CalendarAppWidgetProvider.java
index 048d627c..9530355b 100644
--- a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java
+++ b/src/com/android/calendar/widget/CalendarAppWidgetProvider.java
@@ -16,6 +16,9 @@
package com.android.calendar.widget;
+import com.android.calendar.R;
+import com.android.calendar.Utils;
+
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@@ -24,7 +27,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Log;
+import android.widget.RemoteViews;
/**
* Simple widget to show next upcoming calendar event.
@@ -37,7 +44,6 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
"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";
/**
@@ -49,7 +55,9 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
// 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 */,
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ performUpdate(context, appWidgetManager,
+ appWidgetManager.getAppWidgetIds(getComponentName(context)),
null /* no eventIds */);
} else {
super.onReceive(context, intent);
@@ -92,7 +100,7 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
- performUpdate(context, appWidgetIds, null /* no eventIds */);
+ performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */);
}
@@ -116,19 +124,42 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
* @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,
+ private void performUpdate(Context context, AppWidgetManager appWidgetManager, 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);
- }
+ // Launch over to service so it can perform update
+ for (int appWidgetId : appWidgetIds) {
+ if (LOGD) Log.d(TAG, "Building widget update...");
+ Intent updateIntent = new Intent(context, CalendarAppWidgetService.class);
+ updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
if (changedEventIds != null) {
updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds);
}
-
- context.startService(updateIntent);
+ updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME)));
+
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
+ // 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));
+ // Attach to list of events
+ views.setRemoteAdapter(R.id.events_list, updateIntent);
+
+ // Clicking on the widget launches Calendar
+ // TODO fix this exact behavior?
+// long startTime = Math.max(currentTime, events.firstTime);
+ long startTime = System.currentTimeMillis();
+
+ PendingIntent pendingIntent = getLaunchPendingIntent(context, startTime);
+ views.setOnClickPendingIntent(R.id.appwidget, pendingIntent);
+
+ PendingIntent newEventIntent = getNewEventPendingIntent(context);
+ views.setOnClickPendingIntent(R.id.new_event_button, newEventIntent);
+
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
}
/**
@@ -144,4 +175,37 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
return PendingIntent.getBroadcast(context, 0 /* no requestCode */,
updateIntent, 0 /* no flags */);
}
+
+ /**
+ * 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. If the go to time
+ * is 0, then calendar will be launched without a starting time.
+ *
+ * @param goToTime time that calendar should take the user to, or 0 to
+ * indicate no specific start time.
+ */
+ static 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(Utils.INTENT_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);
+ }
+
+ private static PendingIntent getNewEventPendingIntent(Context context) {
+ Intent newEventIntent = new Intent(Intent.ACTION_EDIT);
+ newEventIntent.setType("vnd.android.cursor.item/event");
+ return PendingIntent.getActivity(context, 0, newEventIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
}
diff --git a/src/com/android/calendar/widget/CalendarAppWidgetReceiver.java b/src/com/android/calendar/widget/CalendarAppWidgetReceiver.java
index 13edcb3d..cfb83aa4 100644
--- a/src/com/android/calendar/widget/CalendarAppWidgetReceiver.java
+++ b/src/com/android/calendar/widget/CalendarAppWidgetReceiver.java
@@ -16,6 +16,7 @@
package com.android.calendar.widget;
+import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -29,7 +30,8 @@ public class CalendarAppWidgetReceiver extends BroadcastReceiver {
@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);
+ final Intent updateIntent = new Intent(
+ CalendarAppWidgetProvider.ACTION_CALENDAR_APPWIDGET_UPDATE);
// Copy over the relevant extra fields if they exist
if (intent.hasExtra(CalendarAppWidgetProvider.EXTRA_EVENT_IDS)) {
@@ -37,12 +39,18 @@ public class CalendarAppWidgetReceiver extends BroadcastReceiver {
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 (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
+ int data = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, data);
+ }
+
+ if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)) {
+ int[] data = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, data);
}
if (LOGD) Log.d(TAG, "Something changed, updating widget");
- context.startService(updateIntent);
+ context.sendBroadcast(updateIntent);
}
}
diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.java b/src/com/android/calendar/widget/CalendarAppWidgetService.java
index 7e4a8e6f..249530c5 100644
--- a/src/com/android/calendar/widget/CalendarAppWidgetService.java
+++ b/src/com/android/calendar/widget/CalendarAppWidgetService.java
@@ -16,25 +16,23 @@
package com.android.calendar.widget;
-import com.google.common.annotations.VisibleForTesting;
-
-import com.android.calendar.R;
-import com.android.calendar.Utils;
-import com.android.calendar.widget.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.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.net.Uri;
+import android.os.Handler;
+import android.provider.Calendar;
import android.provider.Calendar.Attendees;
import android.provider.Calendar.Calendars;
+import android.provider.Calendar.Events;
import android.provider.Calendar.Instances;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -43,25 +41,28 @@ import android.text.format.Time;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import com.android.calendar.R;
+import com.android.calendar.Utils;
+import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo;
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 {
+public class CalendarAppWidgetService extends RemoteViewsService {
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 int EVENT_MAX_COUNT = 10;
+
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";
+ + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT;
// TODO can't use parameter here because provider is dropping them
private static final String EVENT_SELECTION = Calendars.SELECTED + "=1 AND "
@@ -89,647 +90,570 @@ public class CalendarAppWidgetService extends IntentService {
// update about six hours from now.
private static final long UPDATE_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6;
- public CalendarAppWidgetService() {
- super(TAG);
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return new CalendarFactory(getApplicationContext(), intent);
}
- @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();
+ protected 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;
- performUpdate(this, widgetIds, eventIdsSet, now);
+ /**
+ * 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;
}
- /**
- * 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;
- }
+ protected static class CalendarFactory implements RemoteViewsService.RemoteViewsFactory {
+
+ private static final String TAG = CalendarFactory.class.getSimpleName();
- if (events.markedIds.isEmpty()) {
- views = getAppWidgetNoEvents(context);
- } else if (shouldUpdate) {
- views = getAppWidgetUpdate(context, cursor, events);
- triggerTime = calculateUpdateTime(cursor, events);
+ private static final boolean LOGD = true;
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)
+ || action.equals(Intent.ACTION_TIME_CHANGED)
+ || action.equals(Intent.ACTION_DATE_CHANGED)
+ || (action.equals(Intent.ACTION_PROVIDER_CHANGED)
+ && intent.getData().equals(Calendar.CONTENT_URI))) {
+ loadData();
}
- } else {
- views = getAppWidgetNoEvents(context);
}
- } finally {
- if (cursor != null) {
- cursor.close();
+ };
+
+ private final ContentObserver mContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ loadData();
}
+ };
+
+ private final int mAppWidgetId;
+
+ private Context mContext;
+
+ private CalendarAppWidgetModel mModel;
+
+ private Cursor mCursor;
+
+ protected CalendarFactory(Context context, Intent intent) {
+ mContext = context;
+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
}
- // 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;
+ @Override
+ public void onCreate() {
+ loadData();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_DATE_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(Intent.ACTION_PROVIDER_CHANGED);
+ mContext.registerReceiver(mIntentReceiver, filter);
+
+ mContext.getContentResolver().registerContentObserver(
+ Events.CONTENT_URI, true, mContentObserver);
}
- 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);
+ @Override
+ public void onDestroy() {
+ mCursor.close();
+ mContext.unregisterReceiver(mIntentReceiver);
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
}
- // 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;
+ @Override
+ public RemoteViews getLoadingView() {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.appwidget_loading);
+ return views;
}
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- PendingIntent pendingUpdate = CalendarAppWidgetProvider.getUpdateIntent(context);
+ @Override
+ public RemoteViews getViewAt(int position) {
+ // we use getCount here so that it doesn't return null when empty
+ if (position < 0 || position >= getCount()) {
+ return null;
+ }
- am.cancel(pendingUpdate);
- am.set(AlarmManager.RTC, triggerTime, pendingUpdate);
+ if (mModel.eventInfos.length > 0) {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.appwidget_row);
- if (LOGD) Log.d(TAG, "Scheduled next update at " + formatDebugTime(triggerTime, now));
- }
+ EventInfo e = mModel.eventInfos[position];
- /**
- * 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);
- }
- }
+ updateTextView(views, R.id.when, e.visibWhen, e.when);
+ updateTextView(views, R.id.where, e.visibWhere, e.where);
+ updateTextView(views, R.id.title, e.visibTitle, e.title);
- /**
- * 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();
+ PendingIntent launchIntent =
+ CalendarAppWidgetProvider.getLaunchPendingIntent(
+ mContext, e.start);
+ views.setOnClickPendingIntent(R.id.appwidget_row, launchIntent);
+ return views;
+ } else {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.appwidget_no_events);
+ PendingIntent launchIntent =
+ CalendarAppWidgetProvider.getLaunchPendingIntent(
+ mContext, 0);
+ views.setOnClickPendingIntent(R.id.appwidget_no_events, launchIntent);
+ return views;
+ }
}
- 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;
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
- // Adjust all-day times into local timezone
- if (allDay) {
- final Time recycle = new Time();
- start = convertUtcToLocal(recycle, start);
- end = convertUtcToLocal(recycle, end);
- }
+ @Override
+ public int getCount() {
+ // if there are no events, we still return 1 to represent the "no
+ // events" view
+ return Math.max(1, mModel.eventInfos.length);
+ }
- result = getEventFlip(cursor, start, end, allDay);
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
- // Make sure an update happens at midnight or earlier
- long midnight = getNextMidnightTimeMillis();
- result = Math.min(midnight, result);
+ @Override
+ public boolean hasStableIds() {
+ return true;
}
- 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;
- }
+ private void loadData() {
+ long now = System.currentTimeMillis();
+ if (LOGD) Log.d(TAG, "Querying for widget events...");
+ if (mCursor != null) {
+ mCursor.close();
+ }
- /**
- * 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;
- }
+ mCursor = getUpcomingInstancesCursor(
+ mContext.getContentResolver(), SEARCH_DURATION, now);
+ MarkedEvents markedEvents = buildMarkedEvents(mCursor, now);
+ mModel = buildAppWidgetModel(mContext, mCursor, markedEvents, now);
+ long triggerTime = calculateUpdateTime(mCursor, markedEvents);
+ // 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;
+ }
- /**
- * 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);
- }
+ AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent pendingUpdate = CalendarAppWidgetProvider.getUpdateIntent(mContext);
- /**
- * 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);
+ am.cancel(pendingUpdate);
+ am.set(AlarmManager.RTC, triggerTime, pendingUpdate);
+ if (LOGD) Log.d(TAG, "Scheduled next update at " + formatDebugTime(triggerTime, now));
+ }
- long currentTime = System.currentTimeMillis();
- CalendarAppWidgetModel model = buildAppWidgetModel(context, cursor, events, currentTime);
+ /**
+ * Query across all calendars for upcoming event instances from now until
+ * some time in the future.
+ *
+ * Widen the time range that we query by one day on each end so that we can
+ * catch all-day events. All-day events are stored starting at midnight in
+ * UTC but should be included in the list of events starting at midnight
+ * local time. This may fetch more events than we actually want, so we
+ * filter them out later.
+ *
+ * @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
- applyModelToView(context, model, views);
+ // Add a day on either side to catch all-day events
+ long begin = now - DateUtils.DAY_IN_MILLIS;
+ long end = now + searchDuration + DateUtils.DAY_IN_MILLIS;
- // Clicking on the widget launches Calendar
- long startTime = Math.max(currentTime, events.firstTime);
+ Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI,
+ String.format("%d/%d", begin, end));
- PendingIntent pendingIntent = getLaunchPendingIntent(context, startTime);
- views.setOnClickPendingIntent(R.id.appwidget, pendingIntent);
+ Cursor cursor = resolver.query(uri, EVENT_PROJECTION,
+ EVENT_SELECTION, null, EVENT_SORT_ORDER);
- PendingIntent newEventIntent = getNewEventPendingIntent(context);
- views.setOnClickPendingIntent(R.id.new_event_button, newEventIntent);
+ // Start managing the cursor ourselves
+ MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor);
+ cursor.close();
- 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;
+ return matrixCursor;
}
- // 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);
- }
+ /**
+ * 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
+ protected static MarkedEvents buildMarkedEvents(Cursor cursor, 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);
+ }
- // 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);
- }
- }
+ // Adjust all-day times into local timezone
+ if (allDay) {
+ start = convertUtcToLocal(recycle, start);
+ end = convertUtcToLocal(recycle, end);
+ }
- }
+ if (end < now) {
+ // we might get some extra events when querying, in order to
+ // deal with all-day events
+ continue;
+ }
- static String makePageCount(int current, int total) {
- return Integer.toString(current) + " / " + Integer.toString(total);
- }
+ boolean inProgress = now < end && now > start;
- static void updateTextView(RemoteViews views, int id, int visibility, String string) {
- views.setViewVisibility(id, visibility);
- if (visibility == View.VISIBLE) {
- views.setTextViewText(id, string);
- }
- }
+ // 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;
+ }
+
+// /* 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;
+// }
+// }
- 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++;
+ events.markedIds.add(row);
+ }
+ return events;
}
- return model;
- }
+ @VisibleForTesting
+ protected 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++;
+ }
- /**
- * 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);
+ return model;
}
- 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;
+ /**
+ * 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);
}
- }
- // 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;
+ result = getEventFlip(cursor, start, end, allDay);
- if (showTitleLocation) {
- // What
- String titleString = cursor.getString(INDEX_TITLE);
- if (TextUtils.isEmpty(titleString)) {
- titleString = context.getString(R.string.no_title_label);
+ // Make sure an update happens at midnight or earlier
+ long midnight = getNextMidnightTimeMillis();
+ result = Math.min(midnight, result);
}
- 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);
+ return result;
}
- }
-
- /**
- * 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(Utils.INTENT_KEY_DETAIL_VIEW, true);
- dataString += "/" + goToTime;
+ private static 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;
}
- Uri data = Uri.parse(dataString);
- launchIntent.setData(data);
- return PendingIntent.getActivity(context, 0 /* no requestCode */,
- launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent getNewEventPendingIntent(Context context) {
- Intent newEventIntent = new Intent(Intent.ACTION_EDIT);
- newEventIntent.setType("vnd.android.cursor.item/event");
- return PendingIntent.getActivity(context, 0, newEventIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- static class MarkedEvents {
/**
- * The row IDs of all events marked for display
+ * 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.
*/
- List<Integer> markedIds = new ArrayList<Integer>(10);
+ 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);
+ }
+ }
/**
- * The start time of the first marked event
+ * Convert given UTC time into current local time.
+ *
+ * @param recycle Time object to recycle, otherwise null.
+ * @param utcTime Time to convert, in UTC.
*/
- 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;
+ 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);
+ }
/**
- * The number of events that share the same start time as the next
- * upcoming event
+ * 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.
*/
- int primaryCount = 0; // Number of events with same start time as the secondary evt.
+ static private long getEventFlip(Cursor cursor, long start, long end, boolean allDay) {
+ return end;
+ }
- /** The start time of the next next upcoming event */
- long secondaryTime = 1;
+ static void updateTextView(RemoteViews views, int id, int visibility, String string) {
+ views.setViewVisibility(id, visibility);
+ if (visibility == View.VISIBLE) {
+ views.setTextViewText(id, string);
+ }
+ }
/**
- * The number of events that share the same start time as the next next
- * upcoming event.
+ * 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
*/
- int secondaryCount = 0;
-
- boolean watchFound = false;
- }
+ 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);
- /**
- * 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);
+ // When
+ boolean allDay = cursor.getInt(INDEX_ALL_DAY) != 0;
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);
}
- if (end < now) {
- // we might get some extra events when querying, in order to
- // deal with all-day events
- continue;
- }
-
- 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;
+ 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);
+ // TODO better i18n formatting
+ if (eventIsTomorrow) {
+ whenString += (", ");
+ whenString += context.getString(R.string.tomorrow);
+ } else if (eventIsInProgress) {
+ whenString += " (";
+ whenString += context.getString(R.string.in_progress);
+ whenString += ")";
+ }
}
- // Mark if we've encountered the watched event
- if (watchEventIds != null && watchEventIds.contains(eventId)) {
- events.watchFound = true;
- }
+ model.eventInfos[eventIndex].start = start;
+ model.eventInfos[eventIndex].when = whenString;
+ model.eventInfos[eventIndex].visibWhen = View.VISIBLE;
- /* 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;
+ if (showTitleLocation) {
+ // What
+ String titleString = cursor.getString(INDEX_TITLE);
+ if (TextUtils.isEmpty(titleString)) {
+ titleString = context.getString(R.string.no_title_label);
}
- } 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++;
+ 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 {
- // looks like we're done
- break;
+ model.eventInfos[eventIndex].visibWhere = View.GONE;
}
+ if (LOGD) Log.d(TAG, " Title:" + titleString + " Where:" + whereString);
}
}
- return events;
- }
-
- /**
- * Query across all calendars for upcoming event instances from now until
- * some time in the future.
- *
- * Widen the time range that we query by one day on each end so that we can
- * catch all-day events. All-day events are stored starting at midnight in
- * UTC but should be included in the list of events starting at midnight
- * local time. This may fetch more events than we actually want, so we
- * filter them out later.
- *
- * @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
-
- // Add a day on either side to catch all-day events
- long begin = now - DateUtils.DAY_IN_MILLIS;
- long end = now + searchDuration + DateUtils.DAY_IN_MILLIS;
-
- Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI,
- String.format("%d/%d", begin, end));
-
- return resolver.query(uri, EVENT_PROJECTION, EVENT_SELECTION, null,
- EVENT_SORT_ORDER);
}
}
diff --git a/tests/src/com/android/calendar/widget/CalendarAppWidgetServiceTest.java b/tests/src/com/android/calendar/widget/CalendarAppWidgetServiceTest.java
index a07d007f..e06e462d 100644
--- a/tests/src/com/android/calendar/widget/CalendarAppWidgetServiceTest.java
+++ b/tests/src/com/android/calendar/widget/CalendarAppWidgetServiceTest.java
@@ -19,6 +19,7 @@ package com.android.calendar.widget;
import com.android.calendar.widget.CalendarAppWidgetModel;
import com.android.calendar.widget.CalendarAppWidgetService;
+import com.android.calendar.widget.CalendarAppWidgetService.CalendarFactory;
import com.android.calendar.widget.CalendarAppWidgetService.MarkedEvents;
import java.util.TimeZone;
@@ -26,6 +27,7 @@ import java.util.TimeZone;
import android.database.MatrixCursor;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.format.DateUtils;
import android.view.View;
@@ -104,16 +106,17 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
expected.eventInfos[0].title = title;
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
- @SmallTest
+ // TODO re-enable this test when our widget behavior is finalized
+ @Suppress @SmallTest
public void testGetAppWidgetModel_2StaggeredEvents() throws Exception {
- CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
+ CalendarAppWidgetModel expected = new CalendarAppWidgetModel(2);
MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
int i = 0;
@@ -147,8 +150,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
++i;
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -159,13 +162,14 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
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);
+ events = CalendarFactory.buildMarkedEvents(cursor, now);
+ actual = CalendarFactory.buildAppWidgetModel(getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
- @SmallTest
+ // TODO re-enable this test when our widget behavior is finalized
+ @Suppress @SmallTest
public void testGetAppWidgetModel_2SameStartTimeEvents() throws Exception {
CalendarAppWidgetModel expected = new CalendarAppWidgetModel();
MatrixCursor cursor = new MatrixCursor(CalendarAppWidgetService.EVENT_PROJECTION, 0);
@@ -199,8 +203,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
++i;
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -211,8 +215,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
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);
+ events = CalendarFactory.buildMarkedEvents(cursor, now);
+ actual = CalendarFactory.buildAppWidgetModel(getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
@@ -258,14 +262,15 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
expected.eventInfos[i].title = title + i;
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
- @SmallTest
+ // TODO re-enable this test when our widget behavior is finalized
+ @Suppress @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);
@@ -312,8 +317,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
++i;
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -322,8 +327,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
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);
+ events = CalendarFactory.buildMarkedEvents(cursor, now);
+ actual = CalendarFactory.buildAppWidgetModel(getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
}
@@ -384,8 +389,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
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(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -424,8 +429,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
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(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -465,8 +470,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
cursor.addRow(getRow(1, 1262390400000L, 1262476800000L, title + i, location + i, 0));
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());
@@ -506,8 +511,8 @@ public class CalendarAppWidgetServiceTest extends AndroidTestCase {
cursor.addRow(getRow(1, 1262476800000L, 1262563200000L, title + i, location + i, 0));
// Test
- MarkedEvents events = CalendarAppWidgetService.buildMarkedEvents(cursor, null, now);
- CalendarAppWidgetModel actual = CalendarAppWidgetService.buildAppWidgetModel(
+ MarkedEvents events = CalendarAppWidgetService.CalendarFactory.buildMarkedEvents(cursor, now);
+ CalendarAppWidgetModel actual = CalendarAppWidgetService.CalendarFactory.buildAppWidgetModel(
getContext(), cursor, events, now);
assertEquals(expected.toString(), actual.toString());